Skip to content

Commit eecc41e

Browse files
committed
QRMA: hook inserted objects up to auto-connect
Auto-connecting properties is enabled in models where all item data is backed by the same QObject subclass. When assigning a new row, an entire branch of rows in a tree, or assigning a fresh range as the children of a row, then the new items need to be connected. Some of these items might be nullptr, in which case we have so far stopped the connection loop early (by returning false from the helpers). Fix that to only stop early if a connection failed (i.e. if role names and properties in the objects are not aligned), but continue if we encounter a nullptr entry in the item data. Change-Id: I2c4b5e5beedc7b38c40ee459c2e0437568b9b087 Reviewed-by: Artem Dyomin <[email protected]>
1 parent e22cd01 commit eecc41e

File tree

4 files changed

+126
-8
lines changed

4 files changed

+126
-8
lines changed

src/corelib/itemmodels/qrangemodel.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ static bool connectPropertiesHelper(const QModelIndex &index, QObject *item, QOb
154154
const QHash<int, QMetaProperty> &properties)
155155
{
156156
if (!item)
157-
return false;
157+
return true;
158158
for (auto &&[role, property] : properties.asKeyValueRange()) {
159159
if (property.hasNotifySignal()) {
160160
if (!Handler(index, item, context, role, property))
@@ -171,7 +171,7 @@ bool QRangeModelImplBase::connectProperty(const QModelIndex &index, QObject *ite
171171
int role, const QMetaProperty &property)
172172
{
173173
if (!item)
174-
return false;
174+
return true; // nothing to do, continue
175175
PropertyChangedHandler handler{index, role};
176176
auto connection = property.enclosingMetaObject()->connect(item, property.notifySignal(),
177177
context, std::move(handler));
@@ -199,7 +199,7 @@ bool QRangeModelImplBase::connectPropertyConst(const QModelIndex &index, QObject
199199
int role, const QMetaProperty &property)
200200
{
201201
if (!item)
202-
return false;
202+
return true; // nothing to do, continue
203203
ConstPropertyChangedHandler handler{index, role};
204204
if (!property.enclosingMetaObject()->connect(item, property.notifySignal(),
205205
context, std::move(handler))) {

src/corelib/itemmodels/qrangemodel_impl.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,7 +1715,7 @@ class QRangeModelImpl
17151715
bool autoConnectPropertiesInRow(const row_type &row, int rowIndex, const QModelIndex &parent) const
17161716
{
17171717
if (!QRangeModelDetails::isValid(row))
1718-
return false;
1718+
return true; // nothing to do
17191719
return forEachColumn(row, rowIndex, parent, [this](const QModelIndex &index, QObject *item) {
17201720
if constexpr (isMutable())
17211721
return Self::connectProperties(index, item, m_data.context, m_data.properties);
@@ -2450,6 +2450,14 @@ class QGenericTreeItemModelImpl
24502450
deleteRemovedRows(QRangeModelDetails::begin(range), QRangeModelDetails::end(range));
24512451
}
24522452

2453+
bool autoConnectProperties(const QModelIndex &parent) const
2454+
{
2455+
auto *children = this->childRange(parent);
2456+
if (!children)
2457+
return true;
2458+
return autoConnectPropertiesRange(QRangeModelDetails::refTo(children), parent);
2459+
}
2460+
24532461
protected:
24542462
QModelIndex indexImpl(int row, int column, const QModelIndex &parent) const
24552463
{
@@ -2682,9 +2690,11 @@ class QGenericTreeItemModelImpl
26822690
return false;
26832691
Q_ASSERT(QRangeModelDetails::isValid(row));
26842692
const auto &children = this->protocol().childRows(QRangeModelDetails::refTo(row));
2685-
if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
2686-
this->itemModel().index(rowIndex, 0, parent))) {
2687-
return false;
2693+
if (QRangeModelDetails::isValid(children)) {
2694+
if (!autoConnectPropertiesRange(QRangeModelDetails::refTo(children),
2695+
this->itemModel().index(rowIndex, 0, parent))) {
2696+
return false;
2697+
}
26882698
}
26892699
++rowIndex;
26902700
}

src/corelib/itemmodels/qrangemodeladapter.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,10 +757,18 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter
757757
}
758758
}
759759
} else {
760-
oldRow = other;
760+
oldRow = std::forward<R>(other);
761761
}
762762
this->m_adapter->emitDataChanged(this->m_index,
763763
this->m_index.siblingAtColumn(this->m_adapter->columnCount() - 1));
764+
if constexpr (Impl::itemsAreQObjects) {
765+
if (this->m_adapter->model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) {
766+
impl->autoConnectPropertiesInRow(oldRow, this->m_index.row(), this->m_index.parent());
767+
if constexpr (is_tree<Impl>)
768+
impl->autoConnectProperties(this->m_index);
769+
}
770+
771+
}
764772
}
765773

766774
#ifndef QT_NO_DATASTREAM
@@ -1048,6 +1056,15 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter
10481056
impl->endInsertRows();
10491057
}
10501058
}
1059+
if constexpr (Impl::itemsAreQObjects) {
1060+
if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) {
1061+
const auto begin = QRangeModelDetails::begin(refTo(oldRange));
1062+
const auto end = QRangeModelDetails::end(refTo(oldRange));
1063+
int rowIndex = 0;
1064+
for (auto it = begin; it != end; ++it, ++rowIndex)
1065+
impl->autoConnectPropertiesInRow(*it, rowIndex, root);
1066+
}
1067+
}
10511068
}
10521069

10531070
template <typename NewRange = range_type, if_assignable_range<NewRange> = true>

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ private slots:
9191
void buildValueTree();
9292
void buildPointerTree();
9393

94+
void insertAutoConnectObjects();
95+
9496
private:
9597
void expectInvalidIndex(int count)
9698
{
@@ -2612,6 +2614,95 @@ void tst_QRangeModelAdapter::buildPointerTree()
26122614
}
26132615
}
26142616

2617+
class ObjectTreeItem;
2618+
using ObjectTree = std::vector<ObjectTreeItem>;
2619+
2620+
class ObjectTreeItem : public ObjectRow
2621+
{
2622+
public:
2623+
ObjectTreeItem(Object *item = nullptr)
2624+
{
2625+
m_objects[0] = item;
2626+
}
2627+
2628+
ObjectTreeItem *parentRow() const { return m_parentRow; }
2629+
void setParentRow(ObjectTreeItem *parentRow) { m_parentRow = parentRow; }
2630+
const auto &childRows() const { return m_children; }
2631+
auto &childRows() { return m_children; }
2632+
2633+
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]; }
2636+
2637+
ObjectTreeItem *m_parentRow = nullptr;
2638+
std::optional<ObjectTree> m_children = std::nullopt;
2639+
};
2640+
2641+
namespace std {
2642+
template <> struct tuple_size<ObjectTreeItem> : tuple_size<ObjectRow> {};
2643+
template <std::size_t I> struct tuple_element<I, ObjectTreeItem> : tuple_element<I, ObjectRow> {};
2644+
}
2645+
2646+
void tst_QRangeModelAdapter::insertAutoConnectObjects()
2647+
{
2648+
ObjectTree emptyTree;
2649+
2650+
QRangeModelAdapter adapter(emptyTree);
2651+
QSignalSpy dataChangedSpy(adapter.model(), &QAbstractItemModel::dataChanged);
2652+
adapter.model()->setAutoConnectPolicy(QRangeModel::AutoConnectPolicy::Full);
2653+
2654+
Object *newObject = new Object;
2655+
adapter.insertRow(0, ObjectTreeItem{newObject});
2656+
newObject->setString("0");
2657+
newObject->setNumber(0);
2658+
2659+
QCOMPARE(dataChangedSpy.count(), 2);
2660+
dataChangedSpy.clear();
2661+
2662+
Object *newChild = new Object;
2663+
auto firstRow = adapter.begin();
2664+
(*firstRow).children() = ObjectTree{
2665+
ObjectTreeItem(newChild),
2666+
ObjectTreeItem(),
2667+
ObjectTreeItem()
2668+
};
2669+
QCOMPARE(dataChangedSpy.count(), 0);
2670+
QVERIFY(adapter.hasChildren(0));
2671+
newChild->setString("0.0");
2672+
QCOMPARE(dataChangedSpy.count(), 1);
2673+
dataChangedSpy.clear();
2674+
2675+
newChild = new Object;
2676+
newChild->setString("0.1");
2677+
adapter.at({0, 1}) = ObjectTreeItem(newChild);
2678+
QCOMPARE(dataChangedSpy.count(), 1);
2679+
newChild->setNumber(1);
2680+
QCOMPARE(dataChangedSpy.count(), 2);
2681+
dataChangedSpy.clear();
2682+
2683+
newChild = new Object;
2684+
Object *newGrandChild = new Object;
2685+
ObjectTreeItem newBranch(newChild);
2686+
newBranch.childRows() = ObjectTree{
2687+
ObjectTreeItem(), // skip the first column
2688+
ObjectTreeItem(newGrandChild),
2689+
ObjectTreeItem()
2690+
};
2691+
adapter.at({0, 2}) = newBranch;
2692+
QCOMPARE(dataChangedSpy.count(), 1);
2693+
newChild->setNumber(1);
2694+
QCOMPARE(dataChangedSpy.count(), 2);
2695+
dataChangedSpy.clear();
2696+
2697+
newGrandChild->setString("0.2.1");
2698+
QCOMPARE(dataChangedSpy.count(), 1);
2699+
dataChangedSpy.clear();
2700+
2701+
// newGrandChild = new Object;
2702+
// adapter.at({0, 2, 0}, 0) = newGrandChild;
2703+
// newGrandChild->setString("0.2.0");
2704+
}
2705+
26152706
QTEST_MAIN(tst_QRangeModelAdapter)
26162707
#include "tst_qrangemodeladapter.moc"
26172708

0 commit comments

Comments
 (0)