forked from qt-creator/qt-creator
QmlDesigner: Make it possible to reparent and slide in a transaction
NodeListProperty can slide and re-parent nodes in the same transaction Fixes: QDS-7476 Change-Id: Ibc62e2bce5baca9465c320f0dfa1b9d408d04384 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -10,12 +10,14 @@ using namespace QmlDesigner::Internal;
|
||||
|
||||
AddObjectVisitor::AddObjectVisitor(QmlDesigner::TextModifier &modifier,
|
||||
quint32 parentLocation,
|
||||
quint32 nodeLocation,
|
||||
const QString &content,
|
||||
const PropertyNameList &propertyOrder):
|
||||
QMLRewriter(modifier),
|
||||
m_parentLocation(parentLocation),
|
||||
m_content(content),
|
||||
m_propertyOrder(propertyOrder)
|
||||
const PropertyNameList &propertyOrder)
|
||||
: QMLRewriter(modifier)
|
||||
, m_parentLocation(parentLocation)
|
||||
, m_nodeLocation(nodeLocation)
|
||||
, m_content(content)
|
||||
, m_propertyOrder(propertyOrder)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -44,7 +46,9 @@ bool AddObjectVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
|
||||
// FIXME: duplicate code in the QmlJS::Rewriter class, remove this
|
||||
void AddObjectVisitor::insertInto(QmlJS::AST::UiObjectInitializer *ast)
|
||||
{
|
||||
QmlJS::AST::UiObjectMemberList *insertAfter = searchMemberToInsertAfter(ast->members, m_propertyOrder);
|
||||
QmlJS::AST::UiObjectMemberList *insertAfter = searchChildrenToInsertAfter(ast->members,
|
||||
m_propertyOrder,
|
||||
m_nodeLocation - 1);
|
||||
|
||||
int insertionPoint;
|
||||
int depth;
|
||||
|
@@ -13,6 +13,7 @@ class AddObjectVisitor: public QMLRewriter
|
||||
public:
|
||||
AddObjectVisitor(QmlDesigner::TextModifier &modifier,
|
||||
quint32 parentLocation,
|
||||
quint32 nodeLocation,
|
||||
const QString &content,
|
||||
const PropertyNameList &propertyOrder);
|
||||
|
||||
@@ -25,6 +26,7 @@ private:
|
||||
|
||||
private:
|
||||
quint32 m_parentLocation;
|
||||
quint32 m_nodeLocation;
|
||||
QString m_content;
|
||||
PropertyNameList m_propertyOrder;
|
||||
};
|
||||
|
@@ -75,12 +75,12 @@ bool QmlRefactoring::addToArrayMemberList(int parentLocation,
|
||||
return visit(qmlDocument->qmlProgram());
|
||||
}
|
||||
|
||||
bool QmlRefactoring::addToObjectMemberList(int parentLocation, const QString &content)
|
||||
bool QmlRefactoring::addToObjectMemberList(int parentLocation, int nodeLocation, const QString &content)
|
||||
{
|
||||
if (parentLocation < 0)
|
||||
return false;
|
||||
|
||||
AddObjectVisitor visit(*textModifier, parentLocation, content, m_propertyOrder);
|
||||
AddObjectVisitor visit(*textModifier, parentLocation, nodeLocation, content, m_propertyOrder);
|
||||
return visit(qmlDocument->qmlProgram());
|
||||
}
|
||||
|
||||
|
@@ -33,7 +33,7 @@ public:
|
||||
bool removeImport(const Import &import);
|
||||
|
||||
bool addToArrayMemberList(int parentLocation, PropertyNameView propertyName, const QString &content);
|
||||
bool addToObjectMemberList(int parentLocation, const QString &content);
|
||||
bool addToObjectMemberList(int parentLocation, int nodeLocation, const QString &content);
|
||||
bool addProperty(int parentLocation,
|
||||
PropertyNameView name,
|
||||
const QString &value,
|
||||
|
@@ -272,6 +272,47 @@ QmlJS::AST::UiObjectMemberList *QMLRewriter::searchMemberToInsertAfter(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QmlJS::AST::UiObjectMemberList *QMLRewriter::searchChildrenToInsertAfter(
|
||||
QmlJS::AST::UiObjectMemberList *members, const PropertyNameList &propertyOrder, int pos)
|
||||
{
|
||||
if (pos < 0)
|
||||
return searchMemberToInsertAfter(members, propertyOrder);
|
||||
|
||||
// An empty property name should be available in the propertyOrder List, which is the right place
|
||||
// to define the objects there.
|
||||
const int objectDefinitionInsertionPoint = propertyOrder.indexOf(PropertyName());
|
||||
|
||||
QmlJS::AST::UiObjectMemberList *lastObjectDef = nullptr;
|
||||
QmlJS::AST::UiObjectMemberList *lastNonObjectDef = nullptr;
|
||||
int objectPos = 0;
|
||||
|
||||
for (QmlJS::AST::UiObjectMemberList *iter = members; iter; iter = iter->next) {
|
||||
QmlJS::AST::UiObjectMember *member = iter->member;
|
||||
int idx = -1;
|
||||
|
||||
if (QmlJS::AST::cast<QmlJS::AST::UiObjectDefinition *>(member)) {
|
||||
lastObjectDef = iter;
|
||||
if (objectPos++ == pos)
|
||||
break;
|
||||
} else if (auto arrayBinding = QmlJS::AST::cast<QmlJS::AST::UiArrayBinding *>(member))
|
||||
idx = propertyOrder.indexOf(toString(arrayBinding->qualifiedId).toUtf8());
|
||||
else if (auto objectBinding = QmlJS::AST::cast<QmlJS::AST::UiObjectBinding *>(member))
|
||||
idx = propertyOrder.indexOf(toString(objectBinding->qualifiedId).toUtf8());
|
||||
else if (auto scriptBinding = QmlJS::AST::cast<QmlJS::AST::UiScriptBinding *>(member))
|
||||
idx = propertyOrder.indexOf(toString(scriptBinding->qualifiedId).toUtf8());
|
||||
else if (QmlJS::AST::cast<QmlJS::AST::UiPublicMember *>(member))
|
||||
idx = propertyOrder.indexOf("property");
|
||||
|
||||
if (idx < objectDefinitionInsertionPoint)
|
||||
lastNonObjectDef = iter;
|
||||
}
|
||||
|
||||
if (lastObjectDef)
|
||||
return lastObjectDef;
|
||||
else
|
||||
return lastNonObjectDef;
|
||||
}
|
||||
|
||||
void QMLRewriter::dump(const ASTPath &path)
|
||||
{
|
||||
qCDebug(qmlRewriter) << "AST path with" << path.size() << "node(s):";
|
||||
|
@@ -56,6 +56,8 @@ protected:
|
||||
QmlJS::AST::UiObjectMemberList *members,
|
||||
PropertyNameView propertyName,
|
||||
const PropertyNameList &propertyOrder);
|
||||
static QmlJS::AST::UiObjectMemberList *searchChildrenToInsertAfter(
|
||||
QmlJS::AST::UiObjectMemberList *members, const PropertyNameList &propertyOrder, int pos = -1);
|
||||
|
||||
protected:
|
||||
bool didRewriting() const
|
||||
|
@@ -63,34 +63,38 @@ QStringView toString(QmlRefactoring::PropertyType type)
|
||||
bool AddPropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore)
|
||||
{
|
||||
if (m_sheduledInHierarchy) {
|
||||
const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode());
|
||||
const int parentLocation = positionStore.nodeOffset(m_property.parentModelNode());
|
||||
bool result = false;
|
||||
|
||||
if (m_propertyType != QmlRefactoring::ScriptBinding && m_property.isDefaultProperty()) {
|
||||
result = refactoring.addToObjectMemberList(nodeLocation, m_valueText);
|
||||
const int nodeLocation = movedAfterCreation()
|
||||
? m_property.toNodeListProperty().indexOf(m_containedModelNode)
|
||||
: -1;
|
||||
result = refactoring.addToObjectMemberList(parentLocation, nodeLocation, m_valueText);
|
||||
|
||||
if (!result) {
|
||||
qDebug() << "*** AddPropertyRewriteAction::execute failed in addToObjectMemberList("
|
||||
<< nodeLocation << ','
|
||||
<< m_valueText << ") **"
|
||||
<< parentLocation << ',' << nodeLocation << ',' << m_valueText << ") **"
|
||||
<< info();
|
||||
}
|
||||
} else if (m_property.isNodeListProperty() && m_property.toNodeListProperty().count() > 1) {
|
||||
result = refactoring.addToArrayMemberList(nodeLocation, m_property.name(), m_valueText);
|
||||
result = refactoring.addToArrayMemberList(parentLocation, m_property.name(), m_valueText);
|
||||
|
||||
if (!result) {
|
||||
qDebug() << "*** AddPropertyRewriteAction::execute failed in addToArrayMemberList("
|
||||
<< nodeLocation << ','
|
||||
<< m_property.name() << ','
|
||||
<< m_valueText << ") **"
|
||||
<< info();
|
||||
<< parentLocation << ',' << m_property.name() << ',' << m_valueText
|
||||
<< ") **" << info();
|
||||
}
|
||||
} else {
|
||||
result = refactoring.addProperty(nodeLocation, m_property.name(), m_valueText, m_propertyType, m_property.dynamicTypeName());
|
||||
result = refactoring.addProperty(parentLocation,
|
||||
m_property.name(),
|
||||
m_valueText,
|
||||
m_propertyType,
|
||||
m_property.dynamicTypeName());
|
||||
|
||||
if (!result) {
|
||||
qDebug() << "*** AddPropertyRewriteAction::execute failed in addProperty("
|
||||
<< nodeLocation << ',' << m_property.name() << ',' << m_valueText << ","
|
||||
<< parentLocation << ',' << m_property.name() << ',' << m_valueText << ","
|
||||
<< toString(m_propertyType) << ") **" << info();
|
||||
}
|
||||
}
|
||||
@@ -163,38 +167,44 @@ QString ChangeIdRewriteAction::info() const
|
||||
bool ChangePropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNodePositionStorage &positionStore)
|
||||
{
|
||||
if (m_sheduledInHierarchy) {
|
||||
const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode());
|
||||
if (nodeLocation < 0) {
|
||||
const int parentLocation = positionStore.nodeOffset(m_property.parentModelNode());
|
||||
if (parentLocation < 0) {
|
||||
qWarning() << "*** ChangePropertyRewriteAction::execute ignored. Invalid node location";
|
||||
return true;
|
||||
}
|
||||
bool result = false;
|
||||
|
||||
if (m_propertyType != QmlRefactoring::ScriptBinding && m_property.isDefaultProperty()) {
|
||||
result = refactoring.addToObjectMemberList(nodeLocation, m_valueText);
|
||||
const int nodeLocation = movedAfterCreation()
|
||||
? m_property.toNodeListProperty().indexOf(m_containedModelNode)
|
||||
: -1;
|
||||
|
||||
result = refactoring.addToObjectMemberList(parentLocation, nodeLocation, m_valueText);
|
||||
|
||||
if (!result) {
|
||||
qDebug() << "*** ChangePropertyRewriteAction::execute failed in addToObjectMemberList("
|
||||
<< nodeLocation << ','
|
||||
<< m_valueText << ") **"
|
||||
qDebug()
|
||||
<< "*** ChangePropertyRewriteAction::execute failed in addToObjectMemberList("
|
||||
<< parentLocation << ',' << nodeLocation << ',' << m_valueText << ") **"
|
||||
<< info();
|
||||
}
|
||||
} else if (m_propertyType == QmlRefactoring::ArrayBinding) {
|
||||
result = refactoring.addToArrayMemberList(nodeLocation, m_property.name(), m_valueText);
|
||||
result = refactoring.addToArrayMemberList(parentLocation, m_property.name(), m_valueText);
|
||||
|
||||
if (!result) {
|
||||
qDebug() << "*** ChangePropertyRewriteAction::execute failed in addToArrayMemberList("
|
||||
<< nodeLocation << ','
|
||||
<< m_property.name() << ','
|
||||
<< m_valueText << ") **"
|
||||
qDebug()
|
||||
<< "*** ChangePropertyRewriteAction::execute failed in addToArrayMemberList("
|
||||
<< parentLocation << ',' << m_property.name() << ',' << m_valueText << ") **"
|
||||
<< info();
|
||||
}
|
||||
} else {
|
||||
result = refactoring.changeProperty(nodeLocation, m_property.name(), m_valueText, m_propertyType);
|
||||
result = refactoring.changeProperty(parentLocation,
|
||||
m_property.name(),
|
||||
m_valueText,
|
||||
m_propertyType);
|
||||
|
||||
if (!result) {
|
||||
qDebug() << "*** ChangePropertyRewriteAction::execute failed in changeProperty("
|
||||
<< nodeLocation << ',' << m_property.name() << ',' << m_valueText << ','
|
||||
<< parentLocation << ',' << m_property.name() << ',' << m_valueText << ','
|
||||
<< toString(m_propertyType) << ") **" << info();
|
||||
}
|
||||
}
|
||||
@@ -327,12 +337,13 @@ bool MoveNodeRewriteAction::execute(QmlRefactoring &refactoring,
|
||||
|
||||
bool inDefaultProperty = (m_movingNode.parentProperty().parentModelNode().metaInfo().defaultPropertyName() == m_movingNode.parentProperty().name());
|
||||
|
||||
result = refactoring.moveObjectBeforeObject(movingNodeLocation, newTrailingNodeLocation, inDefaultProperty);
|
||||
result = refactoring.moveObjectBeforeObject(movingNodeLocation,
|
||||
newTrailingNodeLocation,
|
||||
inDefaultProperty);
|
||||
if (!result) {
|
||||
qDebug() << "*** MoveNodeRewriteAction::execute failed in moveObjectBeforeObject("
|
||||
<< movingNodeLocation << ','
|
||||
<< newTrailingNodeLocation << ") **"
|
||||
<< info();
|
||||
<< movingNodeLocation << ',' << newTrailingNodeLocation << ',' << inDefaultProperty
|
||||
<< ") **" << info();
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@@ -73,12 +73,16 @@ public:
|
||||
ModelNode containedModelNode() const
|
||||
{ return m_containedModelNode; }
|
||||
|
||||
bool movedAfterCreation() const { return m_movedAfterCreation; }
|
||||
void setMovedAfterCreation(bool moved) { m_movedAfterCreation = moved; }
|
||||
|
||||
private:
|
||||
AbstractProperty m_property;
|
||||
QString m_valueText;
|
||||
QmlDesigner::QmlRefactoring::PropertyType m_propertyType;
|
||||
ModelNode m_containedModelNode;
|
||||
bool m_sheduledInHierarchy;
|
||||
bool m_movedAfterCreation = false;
|
||||
};
|
||||
|
||||
class ChangeIdRewriteAction: public RewriteAction
|
||||
@@ -127,12 +131,16 @@ public:
|
||||
ModelNode containedModelNode() const
|
||||
{ return m_containedModelNode; }
|
||||
|
||||
bool movedAfterCreation() const { return m_movedAfterCreation; }
|
||||
void setMovedAfterCreation(bool moved) { m_movedAfterCreation = moved; }
|
||||
|
||||
private:
|
||||
AbstractProperty m_property;
|
||||
QString m_valueText;
|
||||
QmlDesigner::QmlRefactoring::PropertyType m_propertyType;
|
||||
ModelNode m_containedModelNode;
|
||||
bool m_sheduledInHierarchy;
|
||||
bool m_movedAfterCreation;
|
||||
};
|
||||
|
||||
class ChangeTypeRewriteAction:public RewriteAction
|
||||
@@ -238,6 +246,8 @@ public:
|
||||
|
||||
MoveNodeRewriteAction *asMoveNodeRewriteAction() override { return this; }
|
||||
|
||||
ModelNode movingNode() const { return m_movingNode; }
|
||||
|
||||
private:
|
||||
ModelNode m_movingNode;
|
||||
ModelNode m_newTrailingNode;
|
||||
|
@@ -39,6 +39,7 @@ void RewriteActionCompressor::operator()(QList<RewriteAction *> &actions,
|
||||
compressAddEditRemoveNodeActions(actions);
|
||||
compressAddEditActions(actions, tabSettings);
|
||||
compressAddReparentActions(actions);
|
||||
compressSlidesIntoNewNode(actions);
|
||||
}
|
||||
|
||||
void RewriteActionCompressor::compressImports(QList<RewriteAction *> &actions) const
|
||||
@@ -378,3 +379,45 @@ void RewriteActionCompressor::compressAddReparentActions(QList<RewriteAction *>
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteActionCompressor::compressSlidesIntoNewNode(QList<RewriteAction *> &actions) const
|
||||
{
|
||||
QList<RewriteAction *> actionsToRemove;
|
||||
QMap<ModelNode, RewriteAction *> addedNodes;
|
||||
for (RewriteAction *action : std::as_const(actions)) {
|
||||
if (action->asAddPropertyRewriteAction() || action->asChangePropertyRewriteAction()) {
|
||||
ModelNode containedNode;
|
||||
|
||||
if (AddPropertyRewriteAction *addAction = action->asAddPropertyRewriteAction())
|
||||
containedNode = addAction->containedModelNode();
|
||||
else if (ChangePropertyRewriteAction *changeAction = action->asChangePropertyRewriteAction())
|
||||
containedNode = changeAction->containedModelNode();
|
||||
|
||||
if (!containedNode.isValid())
|
||||
continue;
|
||||
|
||||
if (m_positionStore->nodeOffset(containedNode) != ModelNodePositionStorage::INVALID_LOCATION)
|
||||
continue;
|
||||
|
||||
addedNodes.insert(containedNode, action);
|
||||
} else if (MoveNodeRewriteAction *moveNodeAction = action->asMoveNodeRewriteAction()) {
|
||||
RewriteAction *previousAction = addedNodes[moveNodeAction->movingNode()];
|
||||
if (!previousAction)
|
||||
continue;
|
||||
|
||||
if (AddPropertyRewriteAction *addAction = previousAction->asAddPropertyRewriteAction()) {
|
||||
actionsToRemove.append(moveNodeAction);
|
||||
addAction->setMovedAfterCreation(true);
|
||||
} else if (ChangePropertyRewriteAction *changeAction = previousAction
|
||||
->asChangePropertyRewriteAction()) {
|
||||
actionsToRemove.append(moveNodeAction);
|
||||
changeAction->setMovedAfterCreation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (RewriteAction *action : std::as_const(actionsToRemove)) {
|
||||
actions.removeOne(action);
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ private:
|
||||
void compressPropertyActions(QList<RewriteAction *> &actions) const;
|
||||
void compressAddEditActions(QList<RewriteAction *> &actions, const TextEditor::TabSettings &tabSettings) const;
|
||||
void compressAddReparentActions(QList<RewriteAction *> &actions) const;
|
||||
void compressSlidesIntoNewNode(QList<RewriteAction *> &actions) const;
|
||||
|
||||
private:
|
||||
PropertyNameList m_propertyOrder;
|
||||
|
Reference in New Issue
Block a user