Skip to content

Commit 56ca7ca

Browse files
committed
QRM(A): Allow initializing a raw nullptr with an object
The ownership mess is not a problem if there's nothing stored at the position yet, and it allows gradually populating a row of raw object pointers. Hook newly inserted objects up to the autoConnectProperties mechanism. Add test case. Change-Id: Ie029a2a358e6a1ed5f24869039be9c2ad542dff9 Reviewed-by: Artem Dyomin <[email protected]>
1 parent 55d5609 commit 56ca7ca

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

src/corelib/itemmodels/qrangemodel_impl.h

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,25 +1373,34 @@ class QRangeModelImpl
13731373
using multi_role = QRangeModelDetails::is_multi_role<value_type>;
13741374

13751375
auto setRangeModelDataRole = [&target, &data]{
1376-
auto &targetRef = QRangeModelDetails::refTo(target);
13771376
constexpr auto targetMetaType = QMetaType::fromType<value_type>();
13781377
const auto dataMetaType = data.metaType();
13791378
constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>();
13801379
if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) {
13811380
// we don't support replacing objects that are stored as raw pointers,
13821381
// as this makes object ownership very messy. But we can replace objects
1383-
// stored in smart pointers.
1384-
if constexpr (isWrapped && !std::is_pointer_v<value_type>
1385-
&& std::is_copy_assignable_v<value_type>) {
1386-
if (data.canConvert(targetMetaType)) {
1387-
target = data.value<value_type>();
1388-
return true;
1382+
// stored in smart pointers, and we can initialize raw nullptr objects.
1383+
if constexpr (isWrapped) {
1384+
constexpr bool is_raw_pointer = std::is_pointer_v<value_type>;
1385+
if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) {
1386+
if (data.canConvert(targetMetaType)) {
1387+
target = data.value<value_type>();
1388+
return true;
1389+
}
1390+
} else if constexpr (is_raw_pointer) {
1391+
if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) {
1392+
target = data.value<value_type>();
1393+
return true;
1394+
}
1395+
} else {
1396+
Q_UNUSED(target);
13891397
}
13901398
}
13911399
// Otherwise we have a move-only or polymorph type. fall through to
13921400
// error handling.
13931401
} else if constexpr (isWrapped) {
13941402
if (QRangeModelDetails::isValid(target)) {
1403+
auto &targetRef = QRangeModelDetails::refTo(target);
13951404
// we need to get a wrapped value type out of the QVariant, which
13961405
// might carry a pointer. We have to try all alternatives.
13971406
if (const auto mt = QMetaType::fromType<wrapped_value_type>();
@@ -1405,10 +1414,10 @@ class QRangeModelImpl
14051414
}
14061415
}
14071416
} else if (targetMetaType == dataMetaType) {
1408-
targetRef = data.value<value_type>();
1417+
QRangeModelDetails::refTo(target) = data.value<value_type>();
14091418
return true;
14101419
} else if (dataMetaType.flags() & QMetaType::PointerToGadget) {
1411-
targetRef = *data.value<value_type *>();
1420+
QRangeModelDetails::refTo(target) = *data.value<value_type *>();
14121421
return true;
14131422
}
14141423
#ifndef QT_NO_DEBUG
@@ -1463,6 +1472,13 @@ class QRangeModelImpl
14631472
};
14641473

14651474
success = writeAt(index, writeData);
1475+
1476+
if constexpr (itemsAreQObjects) {
1477+
if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) {
1478+
if (QObject *item = data.value<QObject *>())
1479+
Self::connectProperties(index, item, m_data.context, m_data.properties);
1480+
}
1481+
}
14661482
}
14671483
return success;
14681484
}

tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2631,8 +2631,9 @@ class ObjectTreeItem : public ObjectRow
26312631
auto &childRows() { return m_children; }
26322632

26332633
private:
2634-
template <std::size_t I> // read-only is enough for this
2635-
friend decltype(auto) get(const ObjectTreeItem &row) { return row.m_objects[I]; }
2634+
template <std::size_t I, typename Item,
2635+
std::enable_if_t<std::is_same_v<q20::remove_cvref_t<Item>, ObjectTreeItem>, bool> = true>
2636+
friend decltype(auto) get(Item &&row) { return q23::forward_like<Item>(row.m_objects[I]); }
26362637

26372638
ObjectTreeItem *m_parentRow = nullptr;
26382639
std::optional<ObjectTree> m_children = std::nullopt;
@@ -2684,7 +2685,7 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
26842685
Object *newGrandChild = new Object;
26852686
ObjectTreeItem newBranch(newChild);
26862687
newBranch.childRows() = ObjectTree{
2687-
ObjectTreeItem(), // skip the first column
2688+
ObjectTreeItem(), // skip the first row to verify that we continue through nullptr
26882689
ObjectTreeItem(newGrandChild),
26892690
ObjectTreeItem()
26902691
};
@@ -2698,9 +2699,11 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects()
26982699
QCOMPARE(dataChangedSpy.count(), 1);
26992700
dataChangedSpy.clear();
27002701

2701-
// newGrandChild = new Object;
2702-
// adapter.at({0, 2, 0}, 0) = newGrandChild;
2703-
// newGrandChild->setString("0.2.0");
2702+
newGrandChild = new Object;
2703+
adapter.at({0, 2, 0}, 0) = newGrandChild;
2704+
QCOMPARE(dataChangedSpy.count(), 1);
2705+
newGrandChild->setString("0.2.0");
2706+
QCOMPARE(dataChangedSpy.count(), 2);
27042707
}
27052708

27062709
QTEST_MAIN(tst_QRangeModelAdapter)

0 commit comments

Comments
 (0)