forked from qt-creator/qt-creator
QmlDesigner: Fix issues with Components and Repeaters
- Fixed state preview rendering by deparenting nodes under component wraps. - Ensure state preview image is always clipped to root item. The state preview bounding box broke when Repeater was direct child of root and had delegate with rotation. - Allow dragging items under Repeater to create implicit component. - Allow dragging Components under Repeater. - Do not ask for target property when Component is dragged under any node. - Update nodeSource properly when reparenting implicit component or removing last child of a Component item. - Fixed scene update when last child of a repeater is deleted/moved. Fixes: QDS-5197 Change-Id: Iaaf1745e25db3522ffc1dba7fd1b051da29f5aec Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io> Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -732,17 +732,22 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in
|
||||
ModelNode targetNode = targetProperty.parentModelNode();
|
||||
NodeMetaInfo metaInfo = targetNode.metaInfo();
|
||||
TypeName typeName = newModelNode.type();
|
||||
const PropertyNameList nameList = targetNode.metaInfo().directPropertyNames();
|
||||
for (const auto &propertyName : nameList) {
|
||||
auto testType = metaInfo.propertyTypeName(propertyName);
|
||||
if (testType == typeName || newModelNode.isSubclassOf(testType)) {
|
||||
ChooseFromPropertyListDialog *dialog = nullptr;
|
||||
dialog = new ChooseFromPropertyListDialog(targetNode, testType, Core::ICore::dialogParent());
|
||||
dialog->exec();
|
||||
if (dialog->result() == QDialog::Accepted)
|
||||
targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId());
|
||||
delete dialog;
|
||||
break;
|
||||
|
||||
// Empty components are not supported and having one as property value is generally
|
||||
// unstable, so let's not offer user to put a fresh Component into a property
|
||||
if (typeName != "QtQml.Component") {
|
||||
const PropertyNameList nameList = targetNode.metaInfo().directPropertyNames();
|
||||
for (const auto &propertyName : nameList) {
|
||||
auto testType = metaInfo.propertyTypeName(propertyName);
|
||||
if (testType == typeName || newModelNode.isSubclassOf(testType)) {
|
||||
ChooseFromPropertyListDialog *dialog = nullptr;
|
||||
dialog = new ChooseFromPropertyListDialog(targetNode, testType, Core::ICore::dialogParent());
|
||||
dialog->exec();
|
||||
if (dialog->result() == QDialog::Accepted)
|
||||
targetNode.bindingProperty(dialog->selectedProperty()).setExpression(newModelNode.validId());
|
||||
delete dialog;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1087,7 +1092,9 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProper
|
||||
&& !modelNode.isAncestorOf(parentProperty.parentModelNode())
|
||||
&& (modelNode.metaInfo().isSubclassOf(propertyQmlType)
|
||||
|| propertyQmlType == "alias"
|
||||
|| parentProperty.name() == "data")) {
|
||||
|| parentProperty.name() == "data"
|
||||
|| (parentProperty.parentModelNode().metaInfo().defaultPropertyName() == parentProperty.name()
|
||||
&& propertyQmlType == "<cpp>.QQmlComponent"))) {
|
||||
//### todo: allowing alias is just a heuristic
|
||||
//once the MetaInfo is part of instances we can do this right
|
||||
|
||||
@@ -1097,6 +1104,10 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProper
|
||||
// When the actual reparenting happens, model will create the "data" property if
|
||||
// it is missing.
|
||||
|
||||
// We allow move even if target property type doesn't match, if the target property
|
||||
// is the default property of the parent and is of Component type.
|
||||
// In that case an implicit component will be created.
|
||||
|
||||
bool nodeCanBeMovedToParentProperty = removeModelNodeFromNodeProperty(parentProperty, modelNode);
|
||||
if (nodeCanBeMovedToParentProperty) {
|
||||
reparentModelNodeToNodeProperty(parentProperty, modelNode);
|
||||
|
||||
@@ -1191,6 +1191,8 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
|
||||
|
||||
if (isComponentType(typeName) || isImplicitComponent)
|
||||
setupComponentDelayed(modelNode, differenceHandler.isAmender());
|
||||
else if (!modelNode.nodeSource().isEmpty() || modelNode.nodeSourceType() != ModelNode::NodeWithoutSource)
|
||||
clearImplicitComponentDelayed(modelNode, differenceHandler.isAmender());
|
||||
|
||||
if (isCustomParserType(typeName))
|
||||
setupCustomParserNodeDelayed(modelNode, differenceHandler.isAmender());
|
||||
@@ -2079,18 +2081,23 @@ void TextToModelMerger::setupComponent(const ModelNode &node)
|
||||
|
||||
QString componentText = m_rewriterView->extractText({node}).value(node);
|
||||
|
||||
if (componentText.isEmpty())
|
||||
if (componentText.isEmpty() && node.nodeSource().isEmpty())
|
||||
return;
|
||||
|
||||
QString result = extractComponentFromQml(componentText);
|
||||
|
||||
if (result.isEmpty())
|
||||
if (result.isEmpty() && node.nodeSource().isEmpty())
|
||||
return; //No object definition found
|
||||
|
||||
if (node.nodeSource() != result)
|
||||
ModelNode(node).setNodeSource(result, ModelNode::NodeWithComponentSource);
|
||||
}
|
||||
|
||||
void TextToModelMerger::clearImplicitComponent(const ModelNode &node)
|
||||
{
|
||||
ModelNode(node).setNodeSource({}, ModelNode::NodeWithoutSource);
|
||||
}
|
||||
|
||||
void TextToModelMerger::collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt)
|
||||
{
|
||||
foreach (const QmlJS::DiagnosticMessage &diagnosticMessage, ctxt.diagnosticLinkMessages()) {
|
||||
@@ -2237,9 +2244,9 @@ void TextToModelMerger::addIsoIconQrcMapping(const QUrl &fileUrl)
|
||||
} while (dir.cdUp());
|
||||
}
|
||||
|
||||
void TextToModelMerger::setupComponentDelayed(const ModelNode &node, bool synchron)
|
||||
void TextToModelMerger::setupComponentDelayed(const ModelNode &node, bool synchronous)
|
||||
{
|
||||
if (synchron) {
|
||||
if (synchronous) {
|
||||
setupComponent(node);
|
||||
} else {
|
||||
m_setupComponentList.insert(node);
|
||||
@@ -2254,7 +2261,7 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node)
|
||||
|
||||
QString modelText = m_rewriterView->extractText({node}).value(node);
|
||||
|
||||
if (modelText.isEmpty())
|
||||
if (modelText.isEmpty() && node.nodeSource().isEmpty())
|
||||
return;
|
||||
|
||||
if (node.nodeSource() != modelText)
|
||||
@@ -2262,11 +2269,11 @@ void TextToModelMerger::setupCustomParserNode(const ModelNode &node)
|
||||
|
||||
}
|
||||
|
||||
void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchron)
|
||||
void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous)
|
||||
{
|
||||
Q_ASSERT(isCustomParserType(node.type()));
|
||||
|
||||
if (synchron) {
|
||||
if (synchronous) {
|
||||
setupCustomParserNode(node);
|
||||
} else {
|
||||
m_setupCustomParserList.insert(node);
|
||||
@@ -2274,15 +2281,32 @@ void TextToModelMerger::setupCustomParserNodeDelayed(const ModelNode &node, bool
|
||||
}
|
||||
}
|
||||
|
||||
void TextToModelMerger::clearImplicitComponentDelayed(const ModelNode &node, bool synchronous)
|
||||
{
|
||||
Q_ASSERT(!isComponentType(node.type()));
|
||||
|
||||
if (synchronous) {
|
||||
clearImplicitComponent(node);
|
||||
} else {
|
||||
m_clearImplicitComponentList.insert(node);
|
||||
m_setupTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
void TextToModelMerger::delayedSetup()
|
||||
{
|
||||
foreach (const ModelNode node, m_setupComponentList)
|
||||
for (const ModelNode &node : std::as_const(m_setupComponentList))
|
||||
setupComponent(node);
|
||||
|
||||
foreach (const ModelNode node, m_setupCustomParserList)
|
||||
for (const ModelNode &node : std::as_const(m_setupCustomParserList))
|
||||
setupCustomParserNode(node);
|
||||
|
||||
for (const ModelNode &node : std::as_const(m_clearImplicitComponentList))
|
||||
clearImplicitComponent(node);
|
||||
|
||||
m_setupCustomParserList.clear();
|
||||
m_setupComponentList.clear();
|
||||
m_clearImplicitComponentList.clear();
|
||||
}
|
||||
|
||||
QSet<QPair<QString, QString> > TextToModelMerger::qrcMapping() const
|
||||
|
||||
@@ -128,8 +128,9 @@ public:
|
||||
ReadingContext *context,
|
||||
DifferenceHandler &differenceHandler);
|
||||
|
||||
void setupComponentDelayed(const ModelNode &node, bool synchron);
|
||||
void setupCustomParserNodeDelayed(const ModelNode &node, bool synchron);
|
||||
void setupComponentDelayed(const ModelNode &node, bool synchronous);
|
||||
void setupCustomParserNodeDelayed(const ModelNode &node, bool synchronous);
|
||||
void clearImplicitComponentDelayed(const ModelNode &node, bool synchronous);
|
||||
|
||||
void delayedSetup();
|
||||
|
||||
@@ -140,6 +141,7 @@ public:
|
||||
private:
|
||||
void setupCustomParserNode(const ModelNode &node);
|
||||
void setupComponent(const ModelNode &node);
|
||||
void clearImplicitComponent(const ModelNode &node);
|
||||
void collectLinkErrors(QList<DocumentMessage> *errors, const ReadingContext &ctxt);
|
||||
void collectImportErrors(QList<DocumentMessage> *errors);
|
||||
void collectSemanticErrorsAndWarnings(QList<DocumentMessage> *errors,
|
||||
@@ -163,6 +165,7 @@ private:
|
||||
QTimer m_setupTimer;
|
||||
QSet<ModelNode> m_setupComponentList;
|
||||
QSet<ModelNode> m_setupCustomParserList;
|
||||
QSet<ModelNode> m_clearImplicitComponentList;
|
||||
QmlJS::ViewerContext m_vContext;
|
||||
QSet<QPair<QString, QString> > m_qrcMapping;
|
||||
QSet<QmlJS::ImportKey> m_possibleImportKeys;
|
||||
|
||||
Reference in New Issue
Block a user