QGIM: add reusable concepts for unifying pointer and reference types
Standardize the way to get a reference or pointer to an element, and use that to implement begin/end helpers that work with values of and pointers/references to ranges. This allows us to support copies, references, raw pointers, and smart pointers holding a range, both as the data itself and as rows of data. Change-Id: Ief680d87fcfd6fcabcfe2fa4841d3641b49f0c43 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
ec200659da
commit
424d6ebf13
@ -28,7 +28,7 @@ public:
|
|||||||
MultiColumn<T>>, bool>;
|
MultiColumn<T>>, bool>;
|
||||||
|
|
||||||
template <typename V = T,
|
template <typename V = T,
|
||||||
std::enable_if_t<std::is_constructible_v<bool, V>, bool> = true>
|
std::enable_if_t<QGenericItemModelDetails::is_validatable<V>::value, bool> = true>
|
||||||
constexpr explicit operator bool() const noexcept { return bool(data); }
|
constexpr explicit operator bool() const noexcept { return bool(data); }
|
||||||
|
|
||||||
// unconstrained on size_t I, gcc internal error #3280
|
// unconstrained on size_t I, gcc internal error #3280
|
||||||
@ -166,9 +166,9 @@ class QGenericItemModelImpl : public QGenericItemModelImplBase
|
|||||||
{
|
{
|
||||||
Q_DISABLE_COPY_MOVE(QGenericItemModelImpl)
|
Q_DISABLE_COPY_MOVE(QGenericItemModelImpl)
|
||||||
public:
|
public:
|
||||||
using range_type = QGenericItemModelDetails::remove_ptr_and_ref_t<Range>;
|
using range_type = QGenericItemModelDetails::wrapped_t<Range>;
|
||||||
using row_reference = decltype(*std::begin(std::declval<range_type&>()));
|
using row_reference = decltype(*QGenericItemModelDetails::begin(std::declval<range_type&>()));
|
||||||
using const_row_reference = decltype(*std::cbegin(std::declval<range_type&>()));
|
using const_row_reference = decltype(*QGenericItemModelDetails::cbegin(std::declval<range_type&>()));
|
||||||
using row_type = std::remove_reference_t<row_reference>;
|
using row_type = std::remove_reference_t<row_reference>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -177,24 +177,29 @@ protected:
|
|||||||
const Structure& that() const { return static_cast<const Structure &>(*this); }
|
const Structure& that() const { return static_cast<const Structure &>(*this); }
|
||||||
|
|
||||||
template <typename C>
|
template <typename C>
|
||||||
static constexpr auto size(const C &c)
|
static constexpr int size(const C &c)
|
||||||
{
|
{
|
||||||
if constexpr (QGenericItemModelDetails::test_size<C>())
|
if (!QGenericItemModelDetails::isValid(c))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if constexpr (QGenericItemModelDetails::test_size<C>()) {
|
||||||
return std::size(c);
|
return std::size(c);
|
||||||
else
|
} else {
|
||||||
#if defined(__cpp_lib_ranges)
|
#if defined(__cpp_lib_ranges)
|
||||||
return std::ranges::distance(std::begin(c), std::end(c));
|
return int(std::ranges::distance(QGenericItemModelDetails::begin(c),
|
||||||
|
QGenericItemModelDetails::end(c)));
|
||||||
#else
|
#else
|
||||||
return std::distance(std::begin(c), std::end(c));
|
return int(std::distance(QGenericItemModelDetails::begin(c),
|
||||||
|
QGenericItemModelDetails::end(c)));
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
friend class tst_QGenericItemModel;
|
friend class tst_QGenericItemModel;
|
||||||
using range_features = QGenericItemModelDetails::range_traits<range_type>;
|
using range_features = QGenericItemModelDetails::range_traits<range_type>;
|
||||||
using row_features = QGenericItemModelDetails::range_traits<row_type>;
|
using wrapped_row_type = QGenericItemModelDetails::wrapped_t<row_type>;
|
||||||
|
using row_features = QGenericItemModelDetails::range_traits<wrapped_row_type>;
|
||||||
using row_traits = QGenericItemModelDetails::row_traits<q20::remove_cvref_t<
|
using row_traits = QGenericItemModelDetails::row_traits<std::remove_cv_t<wrapped_row_type>>;
|
||||||
std::remove_pointer_t<row_type>>>;
|
|
||||||
|
|
||||||
static constexpr bool isMutable()
|
static constexpr bool isMutable()
|
||||||
{
|
{
|
||||||
@ -204,7 +209,9 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int static_row_count = QGenericItemModelDetails::static_size_v<range_type>;
|
static constexpr int static_row_count = QGenericItemModelDetails::static_size_v<range_type>;
|
||||||
static constexpr bool rows_are_pointers = std::is_pointer_v<row_type>;
|
static constexpr bool rows_are_raw_pointers = std::is_pointer_v<row_type>;
|
||||||
|
static constexpr bool rows_are_owning_or_raw_pointers =
|
||||||
|
QGenericItemModelDetails::is_owning_or_raw_pointer<row_type>();
|
||||||
static constexpr int static_column_count = QGenericItemModelDetails::static_size_v<row_type>;
|
static constexpr int static_column_count = QGenericItemModelDetails::static_size_v<row_type>;
|
||||||
static constexpr bool one_dimensional_range = static_column_count == 0;
|
static constexpr bool one_dimensional_range = static_column_count == 0;
|
||||||
|
|
||||||
@ -213,8 +220,8 @@ protected:
|
|||||||
|
|
||||||
// A row might be a value (or range of values), or a pointer.
|
// A row might be a value (or range of values), or a pointer.
|
||||||
// row_ptr is always a pointer, and const_row_ptr is a pointer to const.
|
// row_ptr is always a pointer, and const_row_ptr is a pointer to const.
|
||||||
using row_ptr = std::conditional_t<rows_are_pointers, row_type, row_type *>;
|
using row_ptr = wrapped_row_type *;
|
||||||
using const_row_ptr = const std::remove_pointer_t<row_type> *;
|
using const_row_ptr = const wrapped_row_type *;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr bool has_metaobject =
|
static constexpr bool has_metaobject =
|
||||||
@ -444,7 +451,7 @@ public:
|
|||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
const_row_reference row = rowData(index);
|
const_row_reference row = rowData(index);
|
||||||
if constexpr (dynamicColumns())
|
if constexpr (dynamicColumns())
|
||||||
readData(*std::next(std::cbegin(row), index.column()));
|
readData(*QGenericItemModelDetails::cpos(row, index.column()));
|
||||||
else if constexpr (one_dimensional_range)
|
else if constexpr (one_dimensional_range)
|
||||||
readData(row);
|
readData(row);
|
||||||
else if (QGenericItemModelDetails::isValid(row))
|
else if (QGenericItemModelDetails::isValid(row))
|
||||||
@ -507,7 +514,7 @@ public:
|
|||||||
if (index.isValid()) {
|
if (index.isValid()) {
|
||||||
const_row_reference row = rowData(index);
|
const_row_reference row = rowData(index);
|
||||||
if constexpr (dynamicColumns())
|
if constexpr (dynamicColumns())
|
||||||
readItemData(*std::next(std::cbegin(row), index.column()));
|
readItemData(*QGenericItemModelDetails::cpos(row, index.column()));
|
||||||
else if constexpr (one_dimensional_range)
|
else if constexpr (one_dimensional_range)
|
||||||
readItemData(row);
|
readItemData(row);
|
||||||
else if (QGenericItemModelDetails::isValid(row))
|
else if (QGenericItemModelDetails::isValid(row))
|
||||||
@ -576,7 +583,7 @@ public:
|
|||||||
|
|
||||||
row_reference row = rowData(index);
|
row_reference row = rowData(index);
|
||||||
if constexpr (dynamicColumns()) {
|
if constexpr (dynamicColumns()) {
|
||||||
success = writeData(*std::next(std::begin(row), index.column()));
|
success = writeData(*QGenericItemModelDetails::pos(row, index.column()));
|
||||||
} else if constexpr (one_dimensional_range) {
|
} else if constexpr (one_dimensional_range) {
|
||||||
success = writeData(row);
|
success = writeData(row);
|
||||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||||
@ -688,7 +695,7 @@ public:
|
|||||||
|
|
||||||
row_reference row = rowData(index);
|
row_reference row = rowData(index);
|
||||||
if constexpr (dynamicColumns()) {
|
if constexpr (dynamicColumns()) {
|
||||||
success = writeItemData(*std::next(std::begin(row), index.column()));
|
success = writeItemData(*QGenericItemModelDetails::pos(row, index.column()));
|
||||||
} else if constexpr (one_dimensional_range) {
|
} else if constexpr (one_dimensional_range) {
|
||||||
success = writeItemData(row);
|
success = writeItemData(row);
|
||||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||||
@ -740,7 +747,7 @@ public:
|
|||||||
|
|
||||||
row_reference row = rowData(index);
|
row_reference row = rowData(index);
|
||||||
if constexpr (dynamicColumns()) {
|
if constexpr (dynamicColumns()) {
|
||||||
success = clearData(*std::next(std::begin(row), index.column()));
|
success = clearData(*QGenericItemModelDetails::pos(row, index.column()));
|
||||||
} else if constexpr (one_dimensional_range) {
|
} else if constexpr (one_dimensional_range) {
|
||||||
success = clearData(row);
|
success = clearData(row);
|
||||||
} else if (QGenericItemModelDetails::isValid(row)) {
|
} else if (QGenericItemModelDetails::isValid(row)) {
|
||||||
@ -766,8 +773,10 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
beginInsertColumns(parent, column, column + count - 1);
|
beginInsertColumns(parent, column, column + count - 1);
|
||||||
for (auto &child : *children)
|
for (auto &child : *children) {
|
||||||
child.insert(std::next(std::begin(child), column), count, {});
|
auto it = QGenericItemModelDetails::pos(child, column);
|
||||||
|
QGenericItemModelDetails::refTo(child).insert(it, count, {});
|
||||||
|
}
|
||||||
endInsertColumns();
|
endInsertColumns();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -786,8 +795,8 @@ public:
|
|||||||
|
|
||||||
beginRemoveColumns(parent, column, column + count - 1);
|
beginRemoveColumns(parent, column, column + count - 1);
|
||||||
for (auto &child : *children) {
|
for (auto &child : *children) {
|
||||||
const auto start = std::next(std::begin(child), column);
|
const auto start = QGenericItemModelDetails::pos(child, column);
|
||||||
child.erase(start, std::next(start, count));
|
QGenericItemModelDetails::refTo(child).erase(start, std::next(start, count));
|
||||||
}
|
}
|
||||||
endRemoveColumns();
|
endRemoveColumns();
|
||||||
return true;
|
return true;
|
||||||
@ -818,10 +827,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &child : *children) {
|
for (auto &child : *children) {
|
||||||
const auto begin = std::begin(child);
|
const auto first = QGenericItemModelDetails::pos(child, sourceColumn);
|
||||||
const auto first = std::next(begin, sourceColumn);
|
const auto middle = std::next(first, count);
|
||||||
const auto middle = std::next(begin, sourceColumn + count);
|
const auto last = QGenericItemModelDetails::pos(child, destColumn);
|
||||||
const auto last = std::next(begin, destColumn);
|
|
||||||
|
|
||||||
if (sourceColumn < destColumn) // moving right
|
if (sourceColumn < destColumn) // moving right
|
||||||
std::rotate(first, middle, last);
|
std::rotate(first, middle, last);
|
||||||
@ -849,12 +857,12 @@ public:
|
|||||||
|
|
||||||
beginInsertRows(parent, row, row + count - 1);
|
beginInsertRows(parent, row, row + count - 1);
|
||||||
|
|
||||||
const auto pos = std::next(std::begin(*children), row);
|
const auto pos = QGenericItemModelDetails::pos(children, row);
|
||||||
if constexpr (range_features::has_insert_range) {
|
if constexpr (range_features::has_insert_range) {
|
||||||
EmptyRowGenerator first{0, that(), parent};
|
EmptyRowGenerator first{0, that(), parent};
|
||||||
EmptyRowGenerator last{count, that(), parent};
|
EmptyRowGenerator last{count, that(), parent};
|
||||||
children->insert(pos, first, last);
|
children->insert(pos, first, last);
|
||||||
} else if constexpr (rows_are_pointers) {
|
} else if constexpr (rows_are_raw_pointers) {
|
||||||
auto start = children->insert(pos, count, nullptr);
|
auto start = children->insert(pos, count, nullptr);
|
||||||
auto end = std::next(start, count);
|
auto end = std::next(start, count);
|
||||||
for (auto it = start; it != end; ++it)
|
for (auto it = start; it != end; ++it)
|
||||||
@ -862,7 +870,7 @@ public:
|
|||||||
} else {
|
} else {
|
||||||
children->insert(pos, count, that().makeEmptyRow(parent));
|
children->insert(pos, count, that().makeEmptyRow(parent));
|
||||||
}
|
}
|
||||||
if constexpr (!rows_are_pointers) {
|
if constexpr (!rows_are_raw_pointers) {
|
||||||
// fix the parent in all children of the modified row, as the
|
// fix the parent in all children of the modified row, as the
|
||||||
// references back to the parent might have become invalid.
|
// references back to the parent might have become invalid.
|
||||||
that().resetParentInChildren(children);
|
that().resetParentInChildren(children);
|
||||||
@ -899,15 +907,15 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
{ // erase invalidates iterators
|
{ // erase invalidates iterators
|
||||||
const auto begin = std::next(std::begin(*children), row);
|
const auto begin = QGenericItemModelDetails::pos(children, row);
|
||||||
const auto end = std::next(begin, count);
|
const auto end = std::next(begin, count);
|
||||||
if constexpr (rows_are_pointers)
|
if constexpr (rows_are_raw_pointers)
|
||||||
that().deleteRemovedRows(begin, end);
|
that().deleteRemovedRows(begin, end);
|
||||||
children->erase(begin, end);
|
children->erase(begin, end);
|
||||||
}
|
}
|
||||||
// fix the parent in all children of the modified row, as the
|
// fix the parent in all children of the modified row, as the
|
||||||
// references back to the parent might have become invalid.
|
// references back to the parent might have become invalid.
|
||||||
if constexpr (!rows_are_pointers)
|
if constexpr (!rows_are_raw_pointers)
|
||||||
that().resetParentInChildren(children);
|
that().resetParentInChildren(children);
|
||||||
if constexpr (dynamicColumns()) {
|
if constexpr (dynamicColumns()) {
|
||||||
if (callEndRemoveColumns) {
|
if (callEndRemoveColumns) {
|
||||||
@ -945,16 +953,16 @@ public:
|
|||||||
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
|
if (!beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, destParent, destRow))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const auto first = std::next(std::begin(*source), sourceRow);
|
const auto first = QGenericItemModelDetails::pos(source, sourceRow);
|
||||||
const auto middle = std::next(first, count);
|
const auto middle = std::next(first, count);
|
||||||
const auto last = std::next(std::begin(*source), destRow);
|
const auto last = QGenericItemModelDetails::pos(source, destRow);
|
||||||
|
|
||||||
if (sourceRow < destRow) // moving down
|
if (sourceRow < destRow) // moving down
|
||||||
std::rotate(first, middle, last);
|
std::rotate(first, middle, last);
|
||||||
else // moving up
|
else // moving up
|
||||||
std::rotate(last, first, middle);
|
std::rotate(last, first, middle);
|
||||||
|
|
||||||
if constexpr (!rows_are_pointers)
|
if constexpr (!rows_are_raw_pointers)
|
||||||
that().resetParentInChildren(source);
|
that().resetParentInChildren(source);
|
||||||
|
|
||||||
endMoveRows();
|
endMoveRows();
|
||||||
@ -967,7 +975,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
~QGenericItemModelImpl()
|
~QGenericItemModelImpl()
|
||||||
{
|
{
|
||||||
if constexpr (rows_are_pointers && !std::is_pointer_v<Range>) {
|
if constexpr (rows_are_raw_pointers && !std::is_pointer_v<Range>) {
|
||||||
// If data with rows as pointers was moved in, then we own it and
|
// If data with rows as pointers was moved in, then we own it and
|
||||||
// have to delete those rows.
|
// have to delete those rows.
|
||||||
using ref = decltype(std::forward<Range>(std::declval<range_type>()));
|
using ref = decltype(std::forward<Range>(std::declval<range_type>()));
|
||||||
@ -1212,7 +1220,7 @@ protected:
|
|||||||
|
|
||||||
const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
|
const_row_ptr grandParent = static_cast<const_row_ptr>(parent.constInternalPointer());
|
||||||
const auto &parentSiblings = childrenOf(grandParent);
|
const auto &parentSiblings = childrenOf(grandParent);
|
||||||
const auto it = std::next(std::cbegin(parentSiblings), parent.row());
|
const auto it = QGenericItemModelDetails::cpos(parentSiblings, parent.row());
|
||||||
return this->createIndex(row, column, QGenericItemModelDetails::pointerTo(*it));
|
return this->createIndex(row, column, QGenericItemModelDetails::pointerTo(*it));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1228,19 +1236,20 @@ protected:
|
|||||||
|
|
||||||
// get the siblings of the parent via the grand parent
|
// get the siblings of the parent via the grand parent
|
||||||
const_row_ptr grandParent;
|
const_row_ptr grandParent;
|
||||||
if constexpr (Base::rows_are_pointers)
|
if constexpr (Base::rows_are_raw_pointers)
|
||||||
grandParent = m_protocol.parentRow(parentRow);
|
grandParent = m_protocol.parentRow(parentRow);
|
||||||
else
|
else
|
||||||
grandParent = m_protocol.parentRow(*parentRow);
|
grandParent = m_protocol.parentRow(*parentRow);
|
||||||
const range_type &parentSiblings = childrenOf(grandParent);
|
const range_type &parentSiblings = childrenOf(grandParent);
|
||||||
// find the index of parentRow
|
// find the index of parentRow
|
||||||
const auto begin = std::cbegin(parentSiblings);
|
const auto begin = QGenericItemModelDetails::cbegin(parentSiblings);
|
||||||
const auto end = std::cend(parentSiblings);
|
const auto end = QGenericItemModelDetails::cend(parentSiblings);
|
||||||
const auto it = std::find_if(begin, end, [parentRow](auto &&s){
|
const auto it = std::find_if(begin, end, [parentRow](auto &&s){
|
||||||
return QGenericItemModelDetails::pointerTo(s) == parentRow;
|
return QGenericItemModelDetails::pointerTo(s) == parentRow;
|
||||||
});
|
});
|
||||||
if (it != end)
|
if (it != end)
|
||||||
return this->createIndex(std::distance(begin, it), 0, grandParent);
|
return this->createIndex(std::distance(begin, it), 0,
|
||||||
|
QGenericItemModelDetails::pointerTo(grandParent));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1266,7 +1275,7 @@ protected:
|
|||||||
// We must not insert rows if we cannot adjust the parents of the
|
// We must not insert rows if we cannot adjust the parents of the
|
||||||
// children of the following rows. We don't have to do that if the
|
// children of the following rows. We don't have to do that if the
|
||||||
// range operates on pointers.
|
// range operates on pointers.
|
||||||
return (Base::rows_are_pointers || tree_traits::has_setParentRow)
|
return (Base::rows_are_raw_pointers || tree_traits::has_setParentRow)
|
||||||
&& Base::dynamicRows() && range_features::has_insert;
|
&& Base::dynamicRows() && range_features::has_insert;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1275,7 +1284,7 @@ protected:
|
|||||||
// We must not remove rows if we cannot adjust the parents of the
|
// We must not remove rows if we cannot adjust the parents of the
|
||||||
// children of the following rows. We don't have to do that if the
|
// children of the following rows. We don't have to do that if the
|
||||||
// range operates on pointers.
|
// range operates on pointers.
|
||||||
return (Base::rows_are_pointers || tree_traits::has_setParentRow)
|
return (Base::rows_are_raw_pointers || tree_traits::has_setParentRow)
|
||||||
&& Base::dynamicRows() && range_features::has_erase;
|
&& Base::dynamicRows() && range_features::has_erase;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1295,7 +1304,7 @@ protected:
|
|||||||
// If rows are pointers, then reference to the parent row don't
|
// If rows are pointers, then reference to the parent row don't
|
||||||
// change, so we can move them around freely. Otherwise we need to
|
// change, so we can move them around freely. Otherwise we need to
|
||||||
// be able to explicitly update the parent pointer.
|
// be able to explicitly update the parent pointer.
|
||||||
if constexpr (!Base::rows_are_pointers && !tree_traits::has_setParentRow) {
|
if constexpr (!Base::rows_are_raw_pointers && !tree_traits::has_setParentRow) {
|
||||||
return false;
|
return false;
|
||||||
} else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
|
} else if constexpr (!(range_features::has_insert && range_features::has_erase)) {
|
||||||
return false;
|
return false;
|
||||||
@ -1309,9 +1318,9 @@ protected:
|
|||||||
|
|
||||||
// If we can insert data from another range into, then
|
// If we can insert data from another range into, then
|
||||||
// use that to move the old data over.
|
// use that to move the old data over.
|
||||||
const auto destStart = std::next(std::begin(*destination), destRow);
|
const auto destStart = QGenericItemModelDetails::pos(destination, destRow);
|
||||||
if constexpr (range_features::has_insert_range) {
|
if constexpr (range_features::has_insert_range) {
|
||||||
const auto sourceStart = std::next(std::begin(*source), sourceRow);
|
const auto sourceStart = QGenericItemModelDetails::pos(*source, sourceRow);
|
||||||
const auto sourceEnd = std::next(sourceStart, count);
|
const auto sourceEnd = std::next(sourceStart, count);
|
||||||
|
|
||||||
destination->insert(destStart, std::move_iterator(sourceStart),
|
destination->insert(destStart, std::move_iterator(sourceStart),
|
||||||
@ -1339,9 +1348,9 @@ protected:
|
|||||||
|
|
||||||
// move the data over and update the parent pointer
|
// move the data over and update the parent pointer
|
||||||
{
|
{
|
||||||
const auto writeStart = std::next(std::begin(*destination), destRow);
|
const auto writeStart = QGenericItemModelDetails::pos(destination, destRow);
|
||||||
const auto writeEnd = std::next(writeStart, count);
|
const auto writeEnd = std::next(writeStart, count);
|
||||||
const auto sourceStart = std::next(std::begin(*source), sourceRow);
|
const auto sourceStart = QGenericItemModelDetails::pos(source, sourceRow);
|
||||||
const auto sourceEnd = std::next(sourceStart, count);
|
const auto sourceEnd = std::next(sourceStart, count);
|
||||||
|
|
||||||
for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
|
for (auto write = writeStart, read = sourceStart; write != writeEnd; ++write, ++read) {
|
||||||
@ -1359,7 +1368,7 @@ protected:
|
|||||||
// ranges, as the references to the entries might have become invalid.
|
// ranges, as the references to the entries might have become invalid.
|
||||||
// We don't have to do that if the rows are pointers, as in that case
|
// We don't have to do that if the rows are pointers, as in that case
|
||||||
// the references to the entries are stable.
|
// the references to the entries are stable.
|
||||||
if constexpr (!Base::rows_are_pointers) {
|
if constexpr (!Base::rows_are_raw_pointers) {
|
||||||
resetParentInChildren(destination);
|
resetParentInChildren(destination);
|
||||||
resetParentInChildren(source);
|
resetParentInChildren(source);
|
||||||
}
|
}
|
||||||
@ -1384,8 +1393,8 @@ protected:
|
|||||||
template <typename R>
|
template <typename R>
|
||||||
void destroyOwnedModel(R &&range)
|
void destroyOwnedModel(R &&range)
|
||||||
{
|
{
|
||||||
const auto begin = std::begin(range);
|
const auto begin = QGenericItemModelDetails::begin(range);
|
||||||
const auto end = std::end(range);
|
const auto end = QGenericItemModelDetails::end(range);
|
||||||
deleteRemovedRows(begin, end);
|
deleteRemovedRows(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1399,8 +1408,8 @@ protected:
|
|||||||
void resetParentInChildren(range_type *children)
|
void resetParentInChildren(range_type *children)
|
||||||
{
|
{
|
||||||
if constexpr (tree_traits::has_setParentRow) {
|
if constexpr (tree_traits::has_setParentRow) {
|
||||||
const auto begin = std::begin(*children);
|
const auto begin = QGenericItemModelDetails::begin(*children);
|
||||||
const auto end = std::end(*children);
|
const auto end = QGenericItemModelDetails::end(*children);
|
||||||
for (auto it = begin; it != end; ++it) {
|
for (auto it = begin; it != end; ++it) {
|
||||||
if (auto &maybeChildren = m_protocol.childRows(*it)) {
|
if (auto &maybeChildren = m_protocol.childRows(*it)) {
|
||||||
QModelIndexList fromIndexes;
|
QModelIndexList fromIndexes;
|
||||||
@ -1431,7 +1440,7 @@ protected:
|
|||||||
const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
|
const_row_ptr parentRow = static_cast<const_row_ptr>(index.constInternalPointer());
|
||||||
const range_type &siblings = childrenOf(parentRow);
|
const range_type &siblings = childrenOf(parentRow);
|
||||||
Q_ASSERT(index.row() < int(Base::size(siblings)));
|
Q_ASSERT(index.row() < int(Base::size(siblings)));
|
||||||
return *(std::next(std::cbegin(siblings), index.row()));
|
return *QGenericItemModelDetails::cpos(siblings, index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
decltype(auto) rowDataImpl(const QModelIndex &index)
|
decltype(auto) rowDataImpl(const QModelIndex &index)
|
||||||
@ -1439,13 +1448,13 @@ protected:
|
|||||||
row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
|
row_ptr parentRow = static_cast<row_ptr>(index.internalPointer());
|
||||||
range_type &siblings = childrenOf(parentRow);
|
range_type &siblings = childrenOf(parentRow);
|
||||||
Q_ASSERT(index.row() < int(Base::size(siblings)));
|
Q_ASSERT(index.row() < int(Base::size(siblings)));
|
||||||
return *(std::next(std::begin(siblings), index.row()));
|
return *QGenericItemModelDetails::pos(siblings, index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto childRangeImpl(const QModelIndex &index) const
|
auto childRangeImpl(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
const auto &row = this->rowData(index);
|
const auto &row = this->rowData(index);
|
||||||
if constexpr (Base::rows_are_pointers) {
|
if constexpr (Base::rows_are_raw_pointers) {
|
||||||
if (!row)
|
if (!row)
|
||||||
return static_cast<const range_type *>(nullptr);
|
return static_cast<const range_type *>(nullptr);
|
||||||
}
|
}
|
||||||
@ -1456,7 +1465,7 @@ protected:
|
|||||||
auto childRangeImpl(const QModelIndex &index)
|
auto childRangeImpl(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
auto &row = this->rowData(index);
|
auto &row = this->rowData(index);
|
||||||
if constexpr (Base::rows_are_pointers) {
|
if constexpr (Base::rows_are_raw_pointers) {
|
||||||
if (!row)
|
if (!row)
|
||||||
return static_cast<range_type *>(nullptr);
|
return static_cast<range_type *>(nullptr);
|
||||||
}
|
}
|
||||||
@ -1471,7 +1480,7 @@ protected:
|
|||||||
{
|
{
|
||||||
if (!row)
|
if (!row)
|
||||||
return *this->m_data.model();
|
return *this->m_data.model();
|
||||||
if constexpr (Base::rows_are_pointers)
|
if constexpr (Base::rows_are_raw_pointers)
|
||||||
return *m_protocol.childRows(row);
|
return *m_protocol.childRows(row);
|
||||||
else
|
else
|
||||||
return *m_protocol.childRows(*row);
|
return *m_protocol.childRows(*row);
|
||||||
@ -1482,7 +1491,7 @@ private:
|
|||||||
{
|
{
|
||||||
if (!row)
|
if (!row)
|
||||||
return *this->m_data.model();
|
return *this->m_data.model();
|
||||||
if constexpr (Base::rows_are_pointers)
|
if constexpr (Base::rows_are_raw_pointers)
|
||||||
return *m_protocol.childRows(row);
|
return *m_protocol.childRows(row);
|
||||||
else
|
else
|
||||||
return *m_protocol.childRows(*row);
|
return *m_protocol.childRows(*row);
|
||||||
@ -1515,7 +1524,7 @@ protected:
|
|||||||
QModelIndex indexImpl(int row, int column, const QModelIndex &) const
|
QModelIndex indexImpl(int row, int column, const QModelIndex &) const
|
||||||
{
|
{
|
||||||
if constexpr (Base::dynamicColumns()) {
|
if constexpr (Base::dynamicColumns()) {
|
||||||
if (column < int(Base::size(*std::next(std::cbegin(*this->m_data.model()), row))))
|
if (column < int(Base::size(*QGenericItemModelDetails::cpos(*this->m_data.model(), row))))
|
||||||
return this->createIndex(row, column);
|
return this->createIndex(row, column);
|
||||||
// if we got here, then column < columnCount(), but this row is to short
|
// if we got here, then column < columnCount(), but this row is to short
|
||||||
qCritical("QGenericItemModel: Column-range at row %d is not large enough!", row);
|
qCritical("QGenericItemModel: Column-range at row %d is not large enough!", row);
|
||||||
@ -1546,7 +1555,7 @@ protected:
|
|||||||
if constexpr (Base::dynamicColumns()) {
|
if constexpr (Base::dynamicColumns()) {
|
||||||
return int(Base::size(*this->m_data.model()) == 0
|
return int(Base::size(*this->m_data.model()) == 0
|
||||||
? 0
|
? 0
|
||||||
: Base::size(*std::cbegin(*this->m_data.model())));
|
: Base::size(*QGenericItemModelDetails::cbegin(*this->m_data.model())));
|
||||||
} else if constexpr (Base::one_dimensional_range) {
|
} else if constexpr (Base::one_dimensional_range) {
|
||||||
return row_traits::fixed_size();
|
return row_traits::fixed_size();
|
||||||
} else {
|
} else {
|
||||||
@ -1602,8 +1611,8 @@ protected:
|
|||||||
template <typename R>
|
template <typename R>
|
||||||
void destroyOwnedModel(R &&range)
|
void destroyOwnedModel(R &&range)
|
||||||
{
|
{
|
||||||
const auto begin = std::begin(range);
|
const auto begin = QGenericItemModelDetails::begin(range);
|
||||||
const auto end = std::end(range);
|
const auto end = QGenericItemModelDetails::end(range);
|
||||||
for (auto it = begin; it != end; ++it)
|
for (auto it = begin; it != end; ++it)
|
||||||
delete *it;
|
delete *it;
|
||||||
}
|
}
|
||||||
@ -1617,13 +1626,13 @@ protected:
|
|||||||
decltype(auto) rowDataImpl(const QModelIndex &index) const
|
decltype(auto) rowDataImpl(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
|
Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
|
||||||
return *(std::next(std::cbegin(*this->m_data.model()), index.row()));
|
return *QGenericItemModelDetails::cpos(*this->m_data.model(), index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
decltype(auto) rowDataImpl(const QModelIndex &index)
|
decltype(auto) rowDataImpl(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
|
Q_ASSERT(index.row() < int(Base::size(*this->m_data.model())));
|
||||||
return *(std::next(std::begin(*this->m_data.model()), index.row()));
|
return *QGenericItemModelDetails::pos(*this->m_data.model(), index.row());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto childRangeImpl(const QModelIndex &) const
|
auto childRangeImpl(const QModelIndex &) const
|
||||||
|
@ -30,14 +30,93 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
namespace QGenericItemModelDetails
|
namespace QGenericItemModelDetails
|
||||||
{
|
{
|
||||||
template <typename T, size_t N> static decltype(auto) refTo(T (&t)[N]) { return t; }
|
template <typename T, template <typename...> typename... Templates>
|
||||||
template <typename T> static T&& refTo(T&& t) { return std::forward<T>(t); }
|
struct is_any_of_impl : std::false_type {};
|
||||||
template <typename T> static T& refTo(std::reference_wrapper<T> t) { return t.get(); }
|
|
||||||
// template <typename T> static auto refTo(T& t) -> decltype(*t) { return *t; }
|
|
||||||
|
|
||||||
template <typename T> static auto pointerTo(T *t) { return t; }
|
template <template <typename...> typename Template,
|
||||||
template <typename T> static auto pointerTo(T &t) { return std::addressof(refTo(t)); }
|
typename... Params,
|
||||||
template <typename T> static auto pointerTo(const T &&t) = delete;
|
template <typename...> typename... Templates>
|
||||||
|
struct is_any_of_impl<Template<Params...>, Template, Templates...> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename T,
|
||||||
|
template <typename...> typename Template,
|
||||||
|
template <typename...> typename... Templates>
|
||||||
|
struct is_any_of_impl<T, Template, Templates...> : is_any_of_impl<T, Templates...> {};
|
||||||
|
|
||||||
|
template <typename T, template <typename...> typename... Templates>
|
||||||
|
using is_any_of = is_any_of_impl<std::remove_cv_t<T>, Templates...>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_validatable = std::is_constructible<bool, T>;
|
||||||
|
|
||||||
|
template <typename T, typename = void>
|
||||||
|
struct is_smart_ptr : std::false_type {};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_smart_ptr<T,
|
||||||
|
std::enable_if_t<std::conjunction_v<
|
||||||
|
std::is_pointer<decltype(std::declval<T&>().get())>,
|
||||||
|
std::is_same<decltype(*std::declval<T&>().get()), decltype(*std::declval<T&>())>,
|
||||||
|
is_validatable<T>
|
||||||
|
>>>
|
||||||
|
: std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
// TODO: shouldn't we check is_smart_ptr && !is_copy_constructible && !is_copy_assignable
|
||||||
|
// to support users-specific ptrs?
|
||||||
|
template <typename T>
|
||||||
|
using is_any_unique_ptr = is_any_of<T, std::unique_ptr, QScopedPointer>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_any_shared_ptr = is_any_of<T, std::shared_ptr, QSharedPointer,
|
||||||
|
QExplicitlySharedDataPointer, QSharedDataPointer>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_owning_or_raw_pointer = std::disjunction<is_any_shared_ptr<T>, is_any_unique_ptr<T>, std::is_pointer<T>>;
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static auto pointerTo(T&& t) {
|
||||||
|
using Type = q20::remove_cvref_t<T>;
|
||||||
|
if constexpr (is_any_of<Type, std::optional>())
|
||||||
|
return t ? std::addressof(*std::forward<T>(t)) : nullptr;
|
||||||
|
else if constexpr (std::is_pointer<Type>())
|
||||||
|
return t;
|
||||||
|
else if constexpr (is_smart_ptr<Type>())
|
||||||
|
return t.get();
|
||||||
|
else if constexpr (is_any_of<Type, std::reference_wrapper>())
|
||||||
|
return std::addressof(t.get());
|
||||||
|
else
|
||||||
|
return std::addressof(std::forward<T>(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using wrapped_t = std::remove_pointer_t<decltype(pointerTo(std::declval<T&>()))>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using is_wrapped = std::negation<std::is_same<wrapped_t<T>, std::remove_reference_t<T>>>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr bool isValid(const T &t) noexcept
|
||||||
|
{
|
||||||
|
if constexpr (is_validatable<T>())
|
||||||
|
return bool(t);
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static decltype(auto) refTo(T&& t) {
|
||||||
|
Q_ASSERT(isValid(t));
|
||||||
|
// it's allowed to move only if the object holds unique ownership of the wrapped data
|
||||||
|
using Type = q20::remove_cvref_t<T>;
|
||||||
|
if constexpr (is_any_of<T, std::optional>())
|
||||||
|
return *std::forward<T>(t); // let std::optional resolve dereferencing
|
||||||
|
if constexpr (!is_wrapped<Type>() || is_any_unique_ptr<Type>())
|
||||||
|
return q23::forward_like<T>(*pointerTo(t));
|
||||||
|
else
|
||||||
|
return *pointerTo(t);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename It>
|
template <typename It>
|
||||||
auto key(It&& it) -> decltype(it.key()) { return it.key(); }
|
auto key(It&& it) -> decltype(it.key()) { return it.key(); }
|
||||||
@ -49,14 +128,25 @@ namespace QGenericItemModelDetails
|
|||||||
template <typename It>
|
template <typename It>
|
||||||
auto value(It&& it) -> decltype((it->second)) { return it->second; }
|
auto value(It&& it) -> decltype((it->second)) { return it->second; }
|
||||||
|
|
||||||
template <typename T>
|
// use our own version of begin/end so that we can overload for pointers
|
||||||
static constexpr bool isValid(const T &t) noexcept
|
template <typename C>
|
||||||
{
|
static auto begin(C &&c) -> decltype(std::begin(refTo(std::forward<C>(c))))
|
||||||
if constexpr (std::is_constructible_v<bool, T>)
|
{ return std::begin(refTo(std::forward<C>(c))); }
|
||||||
return bool(t);
|
template <typename C>
|
||||||
else
|
static auto end(C &&c) -> decltype(std::end(refTo(std::forward<C>(c))))
|
||||||
return true;
|
{ return std::end(refTo(std::forward<C>(c))); }
|
||||||
}
|
template <typename C>
|
||||||
|
static auto cbegin(C &&c) -> decltype(std::cbegin(refTo(std::forward<C>(c))))
|
||||||
|
{ return std::cbegin(refTo(std::forward<C>(c))); }
|
||||||
|
template <typename C>
|
||||||
|
static auto cend(C &&c) -> decltype(std::cend(refTo(std::forward<C>(c))))
|
||||||
|
{ return std::cend(refTo(std::forward<C>(c))); }
|
||||||
|
template <typename C>
|
||||||
|
static auto pos(C &&c, int i)
|
||||||
|
{ return std::next(QGenericItemModelDetails::begin(std::forward<C>(c)), i); }
|
||||||
|
template <typename C>
|
||||||
|
static auto cpos(C &&c, int i)
|
||||||
|
{ return std::next(QGenericItemModelDetails::cbegin(std::forward<C>(c)), i); }
|
||||||
|
|
||||||
// Test if a type is a range, and whether we can modify it using the
|
// Test if a type is a range, and whether we can modify it using the
|
||||||
// standard C++ container member functions insert, erase, and resize.
|
// standard C++ container member functions insert, erase, and resize.
|
||||||
@ -147,12 +237,13 @@ namespace QGenericItemModelDetails
|
|||||||
static constexpr bool has_resize = false;
|
static constexpr bool has_resize = false;
|
||||||
};
|
};
|
||||||
template <typename C>
|
template <typename C>
|
||||||
struct range_traits<C, std::void_t<decltype(std::cbegin(std::declval<C&>())),
|
struct range_traits<C, std::void_t<decltype(cbegin(std::declval<C&>())),
|
||||||
decltype(std::cend(std::declval<C&>())),
|
decltype(cend(std::declval<C&>())),
|
||||||
std::enable_if_t<!is_multi_role_v<C>>
|
std::enable_if_t<!is_multi_role_v<C>>
|
||||||
>> : std::true_type
|
>> : std::true_type
|
||||||
{
|
{
|
||||||
using value_type = std::remove_reference_t<decltype(*std::begin(std::declval<C&>()))>;
|
using iterator = decltype(begin(std::declval<C&>()));
|
||||||
|
using value_type = std::remove_reference_t<decltype(*std::declval<iterator&>())>;
|
||||||
static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
|
static constexpr bool is_mutable = !std::is_const_v<C> && !std::is_const_v<value_type>;
|
||||||
static constexpr bool has_insert = test_insert<C>();
|
static constexpr bool has_insert = test_insert<C>();
|
||||||
static constexpr bool has_insert_range = test_insert_range<C>();
|
static constexpr bool has_insert_range = test_insert_range<C>();
|
||||||
@ -180,10 +271,6 @@ namespace QGenericItemModelDetails
|
|||||||
template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
|
template <typename T> struct range_traits<const T *> : iterable_value<Mutable::No> {};
|
||||||
template <> struct range_traits<QLatin1StringView> : iterable_value<Mutable::No> {};
|
template <> struct range_traits<QLatin1StringView> : iterable_value<Mutable::No> {};
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using remove_ptr_and_ref_t =
|
|
||||||
std::remove_pointer_t<std::remove_reference_t<decltype(refTo(std::declval<T&>()))>>;
|
|
||||||
|
|
||||||
template <typename C>
|
template <typename C>
|
||||||
[[maybe_unused]] static constexpr bool is_range_v = range_traits<C>();
|
[[maybe_unused]] static constexpr bool is_range_v = range_traits<C>();
|
||||||
|
|
||||||
@ -239,7 +326,7 @@ namespace QGenericItemModelDetails
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
[[maybe_unused]] static constexpr int static_size_v =
|
[[maybe_unused]] static constexpr int static_size_v =
|
||||||
row_traits<q20::remove_cvref_t<std::remove_pointer_t<T>>>::static_size;
|
row_traits<std::remove_cv_t<wrapped_t<T>>>::static_size;
|
||||||
|
|
||||||
// tests for tree protocol implementation in the row type
|
// tests for tree protocol implementation in the row type
|
||||||
template <typename R, typename = void>
|
template <typename R, typename = void>
|
||||||
@ -375,18 +462,18 @@ namespace QGenericItemModelDetails
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename C, typename TreeProtocol = void,
|
template <typename C, typename TreeProtocol = void,
|
||||||
typename range_type = remove_ptr_and_ref_t<C>>
|
typename range_type = wrapped_t<C>>
|
||||||
using tree_protocol_t = typename tree_traits<
|
using tree_protocol_t = typename tree_traits<
|
||||||
range_type,
|
range_type,
|
||||||
std::remove_reference_t<decltype(*std::begin(std::declval<range_type&>()))>,
|
std::remove_reference_t<decltype(*std::begin(std::declval<range_type&>()))>,
|
||||||
TreeProtocol>::tree_protocol;
|
TreeProtocol>::tree_protocol;
|
||||||
|
|
||||||
template <typename C, typename range_type = remove_ptr_and_ref_t<C>>
|
template <typename C, typename range_type = wrapped_t<C>>
|
||||||
using if_is_table_range = std::enable_if_t<
|
using if_is_table_range = std::enable_if_t<
|
||||||
is_range_v<range_type> && std::is_void_v<tree_protocol_t<range_type>>,
|
is_range_v<range_type> && std::is_void_v<tree_protocol_t<range_type>>,
|
||||||
bool>;
|
bool>;
|
||||||
|
|
||||||
template <typename C, typename Protocol = void, typename range_type = remove_ptr_and_ref_t<C>>
|
template <typename C, typename Protocol = void, typename range_type = wrapped_t<C>>
|
||||||
using if_is_tree_range = std::enable_if_t<
|
using if_is_tree_range = std::enable_if_t<
|
||||||
is_range_v<range_type> && !std::is_void_v<tree_protocol_t<range_type, Protocol>>,
|
is_range_v<range_type> && !std::is_void_v<tree_protocol_t<range_type, Protocol>>,
|
||||||
bool>;
|
bool>;
|
||||||
@ -427,7 +514,7 @@ protected:
|
|||||||
template <typename T, typename F>
|
template <typename T, typename F>
|
||||||
static auto for_element_at(T &&tuple, size_t idx, F &&function)
|
static auto for_element_at(T &&tuple, size_t idx, F &&function)
|
||||||
{
|
{
|
||||||
using type = std::remove_pointer_t<std::remove_reference_t<T>>;
|
using type = QGenericItemModelDetails::wrapped_t<T>;
|
||||||
constexpr size_t size = std::tuple_size_v<type>;
|
constexpr size_t size = std::tuple_size_v<type>;
|
||||||
Q_ASSERT(idx < size);
|
Q_ASSERT(idx < size);
|
||||||
return call_at(std::forward<T>(tuple), idx, std::make_index_sequence<size>{},
|
return call_at(std::forward<T>(tuple), idx, std::make_index_sequence<size>{},
|
||||||
@ -444,7 +531,7 @@ protected:
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr QMetaType meta_type_at(size_t idx)
|
static constexpr QMetaType meta_type_at(size_t idx)
|
||||||
{
|
{
|
||||||
using type = std::remove_pointer_t<std::remove_reference_t<T>>;
|
using type = QGenericItemModelDetails::wrapped_t<T>;
|
||||||
constexpr auto size = std::tuple_size_v<type>;
|
constexpr auto size = std::tuple_size_v<type>;
|
||||||
Q_ASSERT(idx < size);
|
Q_ASSERT(idx < size);
|
||||||
return makeMetaTypes<type>(std::make_index_sequence<size>{}).at(idx);
|
return makeMetaTypes<type>(std::make_index_sequence<size>{}).at(idx);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user