From 5203c478a5bbb3fc81dd1eb6766f05adf0cb3bf0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 15 Apr 2021 15:10:18 +0200 Subject: [PATCH] QmlDesigner: Add some useful iterator algorithms to NodeListProperty Now there is iter_swap, rotate and reverse in the node list property. With that methods we can implement slide too. Task-number: QDS-4159 Change-Id: Ie7f80f64fd26e517ca696158a878262928dc82c4 Reviewed-by: Thomas Hartmann --- .../designercore/include/nodelistproperty.h | 25 +++++- .../designercore/include/rewriterview.h | 1 + .../model/internalnodelistproperty.h | 3 + .../qmldesigner/designercore/model/model.cpp | 8 ++ .../qmldesigner/designercore/model/model_p.h | 1 + .../designercore/model/nodelistproperty.cpp | 72 +++++++++++++--- tests/unit/unittest/abstractviewmock.h | 1 + tests/unit/unittest/nodelistproperty-test.cpp | 83 ++++++++++++++++++- 8 files changed, 181 insertions(+), 13 deletions(-) diff --git a/src/plugins/qmldesigner/designercore/include/nodelistproperty.h b/src/plugins/qmldesigner/designercore/include/nodelistproperty.h index 91add8c9e7d..679408d34ad 100644 --- a/src/plugins/qmldesigner/designercore/include/nodelistproperty.h +++ b/src/plugins/qmldesigner/designercore/include/nodelistproperty.h @@ -42,6 +42,8 @@ using InternalNodeListPropertyPointer = QSharedPointer class NodeListPropertyIterator { + friend class QMLDESIGNERCORE_EXPORT NodeListProperty; + public: using iterator_category = std::random_access_iterator_tag; using difference_type = int; @@ -49,6 +51,7 @@ public: using pointer = InternalNodeListPropertyPointer; using reference = ModelNode; + NodeListPropertyIterator() = default; NodeListPropertyIterator(int currentIndex, class InternalNodeListProperty *nodeListProperty, Model *model, @@ -191,6 +194,19 @@ public: void swap(int, int) const; void reparentHere(const ModelNode &modelNode); ModelNode at(int index) const; + void iterSwap(iterator &first, iterator &second); + iterator rotate(iterator first, iterator newFirst, iterator last); + template + iterator rotate(Range &range, iterator newFirst) + { + return rotate(range.begin(), newFirst, range.end()); + } + void reverse(iterator first, iterator last); + template + void reverse(Range &range) + { + reverse(range.begin(), range.end()); + } static void reverseModelNodes(const QList &nodes); @@ -202,6 +218,13 @@ public: protected: NodeListProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); - NodeListProperty(const Internal::InternalNodeListPropertyPointer &internalNodeListProperty, Model* model, AbstractView *view); + NodeListProperty(const Internal::InternalNodeListPropertyPointer &internalNodeListProperty, + Model *model, + AbstractView *view); + + Internal::InternalNodeListPropertyPointer &internalNodeListProperty() const; + +private: + mutable Internal::InternalNodeListPropertyPointer m_internalNodeListProperty{}; }; } diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index 0af08934068..ac251ef064c 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -94,6 +94,7 @@ public: void nodeOrderChanged(const NodeListProperty &listProperty, const ModelNode &movedNode, int /*oldIndex*/) override; + void nodeOrderChanged(const NodeListProperty &listProperty) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; void nodeTypeChanged(const ModelNode& node, const TypeName &type, int majorVersion, int minorVersion) override; void customNotification(const AbstractView *view, const QString &identifier, diff --git a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h index e650a6e389e..a0ba6d07eb1 100644 --- a/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h +++ b/src/plugins/qmldesigner/designercore/model/internalnodelistproperty.h @@ -72,6 +72,9 @@ public: const QList &nodeList() const; void slide(int from, int to); + QList::iterator begin() { return m_nodeList.begin(); } + QList::iterator end() { return m_nodeList.end(); } + protected: InternalNodeListProperty(const PropertyName &name, const InternalNodePointer &propertyOwner); void add(const InternalNodePointer &node) override; diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 9d6c23d9f12..ef21c1ef45d 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -889,6 +889,14 @@ void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListPropertyPointer }); } +void ModelPrivate::notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty) +{ + notifyNodeInstanceViewLast([&](AbstractView *view) { + NodeListProperty nodeListProperty(internalListProperty, m_model, view); + view->nodeOrderChanged(nodeListProperty); + }); +} + void ModelPrivate::setSelectedNodes(const QList &selectedNodeList) { QList sortedSelectedList = Utils::filtered(selectedNodeList, diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 1e0627c4f18..20374189751 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -159,6 +159,7 @@ public: void notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty, const InternalNodePointer &node, int oldIndex); + void notifyNodeOrderChanged(const InternalNodeListPropertyPointer &internalListProperty); void notifyAuxiliaryDataChanged(const InternalNodePointer &node, const PropertyName &name, const QVariant &data); void notifyNodeSourceChanged(const InternalNodePointer &node, const QString &newNodeSource); diff --git a/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp b/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp index 8a0f54b87c7..512f37722a9 100644 --- a/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/nodelistproperty.cpp @@ -59,6 +59,20 @@ NodeListProperty::NodeListProperty(const Internal::InternalNodeListProperty::Poi { } +Internal::InternalNodeListPropertyPointer &NodeListProperty::internalNodeListProperty() const +{ + if (m_internalNodeListProperty) + return m_internalNodeListProperty; + + if (internalNode()->hasProperty(name())) { + Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); + if (internalProperty->isNodeListProperty()) + m_internalNodeListProperty = internalProperty->toNodeListProperty(); + } + + return m_internalNodeListProperty; +} + static QList internalNodesToModelNodes(const QList &inputList, Model* model, AbstractView *view) { QList modelNodeList; @@ -73,11 +87,10 @@ QList NodeListProperty::toModelNodeList() const if (!isValid()) throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, ""); - if (internalNode()->hasProperty(name())) { - Internal::InternalProperty::Pointer internalProperty = internalNode()->property(name()); - if (internalProperty->isNodeListProperty()) - return internalNodesToModelNodes(internalProperty->toNodeListProperty()->nodeList(), model(), view()); - } + if (internalNodeListProperty()) + return internalNodesToModelNodes(m_internalNodeListProperty->toNodeListProperty()->nodeList(), + model(), + view()); return QList(); } @@ -134,14 +147,51 @@ ModelNode NodeListProperty::at(int index) const if (!isValid()) throw InvalidPropertyException(__LINE__, __FUNCTION__, __FILE__, ""); - Internal::InternalNodeListProperty::Pointer internalProperty = internalNode()->nodeListProperty(name()); - if (internalProperty) - return ModelNode(internalProperty->at(index), model(), view()); - + if (internalNodeListProperty()) + return ModelNode(m_internalNodeListProperty->at(index), model(), view()); return ModelNode(); } +void NodeListProperty::iterSwap(NodeListProperty::iterator &first, NodeListProperty::iterator &second) +{ + if (!internalNodeListProperty()) + return; + + std::swap(m_internalNodeListProperty->at(first.m_currentIndex), + m_internalNodeListProperty->at(second.m_currentIndex)); +} + +NodeListProperty::iterator NodeListProperty::rotate(NodeListProperty::iterator first, + NodeListProperty::iterator newFirst, + NodeListProperty::iterator last) +{ + if (!internalNodeListProperty()) + return {}; + + auto begin = m_internalNodeListProperty->begin(); + + auto iter = std::rotate(std::next(begin, first.m_currentIndex), + std::next(begin, newFirst.m_currentIndex), + std::next(begin, last.m_currentIndex)); + + privateModel()->notifyNodeOrderChanged(m_internalNodeListProperty); + + return {iter - begin, internalNodeListProperty().data(), model(), view()}; +} + +void NodeListProperty::reverse(NodeListProperty::iterator first, NodeListProperty::iterator last) +{ + if (!internalNodeListProperty()) + return; + + auto begin = m_internalNodeListProperty->begin(); + + std::reverse(std::next(begin, first.m_currentIndex), std::next(begin, last.m_currentIndex)); + + privateModel()->notifyNodeOrderChanged(m_internalNodeListProperty); +} + void NodeListProperty::reverseModelNodes(const QList &nodes) { ModelNode firstNode = nodes.first(); @@ -174,12 +224,12 @@ Internal::NodeListPropertyIterator NodeListProperty::end() Internal::NodeListPropertyIterator NodeListProperty::begin() const { - return {0, internalNode()->nodeListProperty(name()).data(), model(), view()}; + return {0, internalNodeListProperty().data(), model(), view()}; } Internal::NodeListPropertyIterator NodeListProperty::end() const { - auto nodeListProperty = internalNode()->nodeListProperty(name()); + auto nodeListProperty = internalNodeListProperty(); auto size = nodeListProperty ? nodeListProperty->size() : 0; return {size, nodeListProperty.data(), model(), view()}; diff --git a/tests/unit/unittest/abstractviewmock.h b/tests/unit/unittest/abstractviewmock.h index eee51f270f0..d2bdbb79fe3 100644 --- a/tests/unit/unittest/abstractviewmock.h +++ b/tests/unit/unittest/abstractviewmock.h @@ -32,4 +32,5 @@ class AbstractViewMock : public QmlDesigner::AbstractView { public: + MOCK_METHOD(void, nodeOrderChanged, (const QmlDesigner::NodeListProperty &listProperty), (override)); }; diff --git a/tests/unit/unittest/nodelistproperty-test.cpp b/tests/unit/unittest/nodelistproperty-test.cpp index 052ff0541b2..5d053388dc5 100644 --- a/tests/unit/unittest/nodelistproperty-test.cpp +++ b/tests/unit/unittest/nodelistproperty-test.cpp @@ -70,7 +70,7 @@ protected: protected: std::unique_ptr model{QmlDesigner::Model::create("QtQuick.Item")}; - AbstractViewMock abstractViewMock; + NiceMock abstractViewMock; QmlDesigner::NodeListProperty nodeListProperty; ModelNode node1; ModelNode node2; @@ -436,4 +436,85 @@ TEST_F(NodeListProperty, DereferenceIterator) ASSERT_THAT(node, Eq(node2)); } +TEST_F(NodeListProperty, IterSwap) +{ + auto first = std::next(nodeListProperty.begin(), 2); + auto second = nodeListProperty.begin(); + + nodeListProperty.iterSwap(first, second); + + ASSERT_THAT(nodes(), ElementsAre(node3, node2, node1, node4, node5)); +} + +TEST_F(NodeListProperty, Rotate) +{ + auto first = std::next(nodeListProperty.begin()); + auto newFirst = std::next(nodeListProperty.begin(), 2); + auto last = std::prev(nodeListProperty.end()); + + nodeListProperty.rotate(first, newFirst, last); + + ASSERT_THAT(nodes(), ElementsAre(node1, node3, node4, node2, node5)); +} + +TEST_F(NodeListProperty, RotateCallsNodeOrderedChanged) +{ + auto first = std::next(nodeListProperty.begin()); + auto newFirst = std::next(nodeListProperty.begin(), 2); + auto last = std::prev(nodeListProperty.end()); + + EXPECT_CALL(abstractViewMock, nodeOrderChanged(ElementsAre(node1, node3, node4, node2, node5))); + + nodeListProperty.rotate(first, newFirst, last); +} + +TEST_F(NodeListProperty, RotateRange) +{ + auto newFirst = std::prev(nodeListProperty.end(), 2); + + nodeListProperty.rotate(nodeListProperty, newFirst); + + ASSERT_THAT(nodes(), ElementsAre(node4, node5, node1, node2, node3)); +} + +TEST_F(NodeListProperty, RotateReturnsIterator) +{ + auto first = std::next(nodeListProperty.begin()); + auto newFirst = std::next(nodeListProperty.begin(), 2); + auto last = std::prev(nodeListProperty.end()); + + auto iterator = nodeListProperty.rotate(first, newFirst, last); + + ASSERT_THAT(iterator, Eq(first + (last - newFirst))); +} + +TEST_F(NodeListProperty, RotateRangeReturnsIterator) +{ + auto newFirst = std::prev(nodeListProperty.end(), 2); + + auto iterator = nodeListProperty.rotate(nodeListProperty, newFirst); + + ASSERT_THAT(iterator, Eq(nodeListProperty.begin() + (nodeListProperty.end() - newFirst))); +} + +TEST_F(NodeListProperty, Reverse) +{ + auto first = std::next(nodeListProperty.begin()); + auto last = std::prev(nodeListProperty.end()); + + nodeListProperty.reverse(first, last); + + ASSERT_THAT(nodes(), ElementsAre(node1, node4, node3, node2, node5)); +} + +TEST_F(NodeListProperty, ReverseCallsNodeOrderedChanged) +{ + auto first = std::next(nodeListProperty.begin()); + auto last = std::prev(nodeListProperty.end()); + + EXPECT_CALL(abstractViewMock, nodeOrderChanged(ElementsAre(node1, node4, node3, node2, node5))); + + nodeListProperty.reverse(first, last); +} + } // namespace