diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/BlurHelper.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/BlurHelper.qml index 0227d0f54af..da68339603a 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/BlurHelper.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/BlurHelper.qml @@ -12,6 +12,7 @@ Item { property alias blurSrc3: blurredItemSource3 property alias blurSrc4: blurredItemSource4 property alias blurSrc5: blurredItemSource5 + property Item source: null component BlurItem: ShaderEffect { property vector2d offset: Qt.vector2d((1.0 + rootItem.blurMultiplier) / width, @@ -37,8 +38,8 @@ Item { // Size of the first blurred item is by default half of the source. // Increase for quality and decrease for performance & more blur. readonly property int blurItemSize: 8 - width: Math.ceil(rootItem.width / 16) * blurItemSize - height: Math.ceil(rootItem.height / 16) * blurItemSize + width: Math.ceil((rootItem.source ? rootItem.source.width : 16) / 16) * blurItemSize + height: Math.ceil((rootItem.source ? rootItem.source.height : 16) / 16) * blurItemSize } BlurItem { id: blurredItemSource2 diff --git a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml index 0df6bb5d8b4..16dc2bf3a1c 100644 --- a/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml +++ b/share/qtcreator/qmldesigner/effectMakerQmlSources/EffectMakerPreview.qml @@ -195,7 +195,7 @@ Column { BlurHelper { id: blurHelper - anchors.fill: parent + source: source property int blurMax: g_propertyData.blur_helper_max_level ? g_propertyData.blur_helper_max_level : 64 property real blurMultiplier: g_propertyData.blurMultiplier ? g_propertyData.blurMultiplier : 0 } diff --git a/src/plugins/effectmakernew/effectmakermodel.cpp b/src/plugins/effectmakernew/effectmakermodel.cpp index 8b3fb320706..79fa3814d5f 100644 --- a/src/plugins/effectmakernew/effectmakermodel.cpp +++ b/src/plugins/effectmakernew/effectmakermodel.cpp @@ -413,6 +413,7 @@ void EffectMakerModel::setEffectError(const QString &errorMessage, int type, int QString additionalErrorInfo = detectErrorMessage(errorMessage); error.m_message = additionalErrorInfo + errorMessage; m_effectErrors.insert(type, error); + qWarning() << QString("Effect error (line: %2): %1").arg(error.m_message, error.m_line); Q_EMIT effectErrorChanged(); } @@ -556,13 +557,23 @@ QString EffectMakerModel::getQmlEffectString() { QString s; - s += QString("// Created with Qt Design Studio (version %1), %2\n\n") - .arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); - s += "import QtQuick\n"; - s += '\n'; - s += "Item {\n"; - s += " id: rootItem\n"; - s += '\n'; + // _isEffectItem is type var to hide it from property view + QString header{ +R"( +// Created with Qt Design Studio (version %1), %2 + +import QtQuick + +Item { + id: rootItem + + property var _isEffectItem + property Item _oldParent: null +)" + }; + + s += header.arg(qApp->applicationVersion(), QDateTime::currentDateTime().toString()); + if (m_shaderFeatures.enabled(ShaderFeatures::Source)) { s += " // This is the main source for the effect\n"; s += " property Item source: null\n"; @@ -580,7 +591,33 @@ QString EffectMakerModel::getQmlEffectString() s += " // When timeRunning is false, this can be used to control iFrame manually\n"; s += " property int animatedFrame: frameAnimation.currentFrame\n"; } - s += '\n'; + + QString parentChanged{ +R"( + onParentChanged: { + if (_oldParent && _oldParent !== parent) { + _oldParent.layer.enabled = false + _oldParent.layer.effect = null + %2 + _oldParent.update() + _oldParent = null + } + if (parent) { + _oldParent = parent + parent.layer.enabled = true + parent.layer.effect = effectComponent + %1 + } + } +)" + }; + + parentChanged = parentChanged.arg(m_shaderFeatures.enabled(ShaderFeatures::Source) + ? QString("source = parent") : QString(), + m_shaderFeatures.enabled(ShaderFeatures::Source) + ? QString("source = null") : QString()); + s += parentChanged; + // Custom properties if (!m_exportedRootPropertiesString.isEmpty()) { s += m_exportedRootPropertiesString; @@ -595,19 +632,14 @@ QString EffectMakerModel::getQmlEffectString() s += '\n'; } - if (m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { - s += " BlurHelper {\n"; - s += " id: blurHelper\n"; - s += " anchors.fill: parent\n"; - int blurMax = 32; - if (g_propertyData.contains("BLUR_HELPER_MAX_LEVEL")) - blurMax = g_propertyData["BLUR_HELPER_MAX_LEVEL"].toInt(); - s += QString(" property int blurMax: %1\n").arg(blurMax); - s += " property real blurMultiplier: rootItem.blurMultiplier\n"; - s += " }\n"; - } + QString customImagesString = getQmlImagesString(true); + if (!customImagesString.isEmpty()) + s += customImagesString; + s += " Component {\n"; + s += " id: effectComponent\n"; s += getQmlComponentString(true); + s += " }\n"; s += "}\n"; return s; } @@ -785,10 +817,10 @@ void EffectMakerModel::saveResources(const QString &name) for (int i = 1; i < qmlStringList.size(); i++) { QString line = qmlStringList.at(i).trimmed(); if (line.startsWith("vertexShader")) { - QString vsLine = " vertexShader: '" + vsFilename + "'"; + QString vsLine = " vertexShader: '" + vsFilename + "'"; qmlStringList[i] = vsLine; } else if (line.startsWith("fragmentShader")) { - QString fsLine = " fragmentShader: '" + fsFilename + "'"; + QString fsLine = " fragmentShader: '" + fsFilename + "'"; qmlStringList[i] = fsLine; } } @@ -1287,17 +1319,17 @@ void EffectMakerModel::updateCustomUniforms() if (!uniform->description().isEmpty()) { const QStringList descriptionLines = uniform->description().split('\n'); for (const QString &line : descriptionLines) - exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; + exportedEffectPropertiesString += QStringLiteral(" // ") + line + '\n'; } - exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + exportedEffectPropertiesString += QStringLiteral(" ") + readOnly + "property " + propertyType + " " + propertyName + boundValueString + '\n'; } else { // Custom values are not added into root exportedRootPropertiesString += " property " + propertyType + " " + propertyName + valueString + '\n'; - exportedEffectPropertiesString += QStringLiteral(" ") - + readOnly + "property alias " + propertyName + exportedEffectPropertiesString += QStringLiteral(" ") + + readOnly + "property " + propertyType + " " + propertyName + ": rootItem." + uniform->name() + '\n'; } } @@ -1488,22 +1520,26 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) { if (localFiles) { const QString parent = blurHelper ? QString("blurHelper.") : QString("rootItem."); - return QString("readonly property alias %1: %2%3\n").arg(name, parent, var); + return QString("readonly property %1 %2: %3%4\n").arg(type, name, parent, var); } else { const QString parent = blurHelper ? "blurHelper." : QString(); return QString("readonly property %1 %2: %3%4\n").arg(type, name, parent, var); } }; - QString customImagesString = getQmlImagesString(localFiles); QString s; - QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); - QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); - QString l3 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); + QString l1 = localFiles ? QStringLiteral(" ") : QStringLiteral(""); + QString l2 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); + QString l3 = localFiles ? QStringLiteral(" ") : QStringLiteral(" "); if (!localFiles) s += "import QtQuick\n"; s += l1 + "ShaderEffect {\n"; + + if (localFiles) { + // Explicit "source" property is required for render puppet to detect effect correctly + s += l2 + "property Item source: null\n"; + } if (m_shaderFeatures.enabled(ShaderFeatures::Source)) s += l2 + addProperty("iSource", "source", "Item"); if (m_shaderFeatures.enabled(ShaderFeatures::Time)) @@ -1529,15 +1565,18 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) // and when in exported component, property with binding to root value. s += localFiles ? m_exportedEffectPropertiesString : m_previewEffectPropertiesString; - if (!customImagesString.isEmpty()) - s += '\n' + customImagesString; + if (!localFiles) { + QString customImagesString = getQmlImagesString(false); + if (!customImagesString.isEmpty()) + s += '\n' + customImagesString; + } s += '\n'; const QString vertFile = localFiles ? m_vertexShaderFilename : m_vertexShaderPreviewFilename; const QString fragFile = localFiles ? m_fragmentShaderFilename : m_fragmentShaderPreviewFilename; s += l2 + "vertexShader: 'file:///" + vertFile + "'\n"; s += l2 + "fragmentShader: 'file:///" + fragFile + "'\n"; - s += l2 + "anchors.fill: parent\n"; + s += l2 + "anchors.fill: " + (localFiles ? "rootItem.source" : "parent") + "\n"; if (m_shaderFeatures.enabled(ShaderFeatures::GridMesh)) { QString gridSize = QString("%1, %2").arg(m_shaderFeatures.gridMeshWidth()) .arg(m_shaderFeatures.gridMeshHeight()); @@ -1545,6 +1584,18 @@ QString EffectMakerModel::getQmlComponentString(bool localFiles) s += l3 + QString("resolution: Qt.size(%1)\n").arg(gridSize); s += l2 + "}\n"; } + if (localFiles && m_shaderFeatures.enabled(ShaderFeatures::BlurSources)) { + s += l2 + "BlurHelper {\n"; + s += l3 + "id: blurHelper\n"; + s += l3 + "source: rootItem.source\n"; + int blurMax = 32; + if (g_propertyData.contains("BLUR_HELPER_MAX_LEVEL")) + blurMax = g_propertyData["BLUR_HELPER_MAX_LEVEL"].toInt(); + s += l3 + QString("property int blurMax: %1\n").arg(blurMax); + s += l3 + "property real blurMultiplier: rootItem.blurMultiplier\n"; + s += l2 + "}\n"; + } + s += l1 + "}\n"; return s; } diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 79ff364c403..dd00cd48d45 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -1731,7 +1731,7 @@ bool useLayerEffect() QtcSettings *settings = Core::ICore::settings(); const Key layerEffectEntry = "QML/Designer/UseLayerEffect"; - return settings->value(layerEffectEntry, true).toBool(); + return settings->value(layerEffectEntry, false).toBool(); } bool validateEffect(const QString &effectPath) diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp index 404a5f57309..496f0e4b2c7 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp @@ -262,6 +262,27 @@ void FormEditorItem::setFrameColor(const QColor &color) update(); } +void FormEditorItem::setHasEffect(bool hasEffect) +{ + m_hasEffect = hasEffect; +} + +bool FormEditorItem::hasEffect() const +{ + return m_hasEffect; +} + +bool FormEditorItem::parentHasEffect() const +{ + FormEditorItem *pi = parentItem(); + while (pi) { + if (pi->hasEffect()) + return true; + pi = pi->parentItem(); + } + return false; +} + FormEditorItem::~FormEditorItem() { scene()->removeItemFromHash(this); @@ -421,7 +442,7 @@ void FormEditorItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, painter->setClipRegion(boundingRect().toRect()); painter->setClipping(true); - if (!hideCompletely) { + if (!hideCompletely && !parentHasEffect()) { if (showPlaceHolder) { if (scene()->showBoundingRects() && m_boundingRect.width() > 15 && m_boundingRect.height() > 15) paintPlaceHolderForInvisbleItem(painter); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h index 69b67d00067..b035699772e 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.h @@ -98,6 +98,10 @@ public: void setFrameColor(const QColor &color); + void setHasEffect(bool hasEffect); + bool hasEffect() const; + bool parentHasEffect() const; + protected: AbstractFormEditorTool* tool() const; void paintBoundingRect(QPainter *painter) const; @@ -129,6 +133,7 @@ private: // variables bool m_highlightBoundingRect; bool m_blurContent; bool m_isContentVisible; + bool m_hasEffect; }; class FormEditorFlowItem : public FormEditorItem diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 9ddec21f350..e57e93c480d 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -140,7 +140,7 @@ void FormEditorView::setupFormEditorItemTree(const QmlItemNode &qmlItemNode) setupFormEditorItemTree(childNode.toQmlItemNode()); } } - } else { + } else if (!qmlItemNode.isEffectItem()) { m_scene->addFormEditorItem(qmlItemNode, FormEditorScene::Default); for (const QmlObjectNode &nextNode : qmlItemNode.allDirectSubNodes()) //TODO instance children //If the node has source for components/custom parsers we ignore it. @@ -280,6 +280,13 @@ void FormEditorView::nodeAboutToBeRemoved(const ModelNode &removedNode) removeNodeFromScene(qmlItemNode); } +void FormEditorView::nodeRemoved(const ModelNode &/*removedNode*/, + const NodeAbstractProperty &/*parentProperty*/, + PropertyChangeFlags /*propertyChange*/) +{ + updateHasEffects(); +} + void FormEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/) { const QList items = m_scene->allFormEditorItems(); @@ -343,6 +350,8 @@ static inline bool hasNodeSourceOrNonItemParent(const ModelNode &node) void FormEditorView::nodeReparented(const ModelNode &node, const NodeAbstractProperty &/*newPropertyParent*/, const NodeAbstractProperty &/*oldPropertyParent*/, AbstractView::PropertyChangeFlags /*propertyChange*/) { addOrRemoveFormEditorItem(node); + + updateHasEffects(); } void FormEditorView::nodeSourceChanged(const ModelNode &node, @@ -830,6 +839,8 @@ void FormEditorView::setupFormEditorWidget() m_formEditorWidget->showWarningMessageBox(rewriterView()->warnings()); checkRootModelNode(); + + updateHasEffects(); } QmlItemNode findRecursiveQmlItemNode(const QmlObjectNode &firstQmlObjectNode) @@ -991,6 +1002,23 @@ void FormEditorView::setupRootItemSize() } } +void FormEditorView::updateHasEffects() +{ + if (model()) { + const QList nodes = allModelNodes(); + for (const auto &node : nodes) { + QmlItemNode qmlNode(node); + FormEditorItem *item = m_scene->itemForQmlItemNode(qmlNode); + if (item) + item->setHasEffect(false); + if (qmlNode.isEffectItem()) { + FormEditorItem *parentItem = m_scene->itemForQmlItemNode(qmlNode.modelParentItem()); + parentItem->setHasEffect(true); + } + } + } +} + void FormEditorView::reset() { QTimer::singleShot(200, this, &FormEditorView::delayedReset); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.h b/src/plugins/qmldesigner/components/formeditor/formeditorview.h index 1a9f15d016e..d3c6cb21dbd 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.h +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.h @@ -52,6 +52,8 @@ public: void nodeCreated(const ModelNode &createdNode) override; void nodeAboutToBeRemoved(const ModelNode &removedNode) override; + void nodeRemoved(const ModelNode &removedNode, const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override; void nodeReparented(const ModelNode &node, const NodeAbstractProperty &newPropertyParent, const NodeAbstractProperty &oldPropertyParent, AbstractView::PropertyChangeFlags propertyChange) override; void nodeSourceChanged(const ModelNode &node, const QString &newNodeSource) override; void nodeIdChanged(const ModelNode& node, const QString& newId, const QString& oldId) override; @@ -138,6 +140,7 @@ private: void checkRootModelNode(); void setupFormEditor3DView(); void setupRootItemSize(); + void updateHasEffects(); QPointer m_formEditorWidget; QPointer m_scene; diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index 5816d60ce1b..5948ec7ab17 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -139,6 +139,8 @@ public: bool isFlowActionArea() const; ModelNode rootModelNode() const; + bool isEffectItem() const; + friend auto qHash(const QmlItemNode &node) { return qHash(node.modelNode()); } }; diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 0ce155ca6b9..e01bdaaec35 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -182,14 +182,19 @@ QmlItemNode QmlItemNode::createQmlItemNodeForEffect(AbstractView *view, const QString effectName = QFileInfo(effectPath).baseName(); Import import = Import::createLibraryImport("Effects." + effectName, "1.0"); try { - if (!view->model()->hasImport(import, true, true)) + if (!view->model()->hasImport(import, true, true)) { view->model()->changeImports({import}, {}); + // Trigger async reset puppet to ensure full transaction is done before reset + view->resetPuppet(); + } } catch (const Exception &) { QTC_ASSERT(false, return); } TypeName type(effectName.toUtf8()); - newQmlItemNode = QmlItemNode(view->createModelNode(type, -1, -1)); + ModelNode newModelNode = view->createModelNode(type, -1, -1); + newModelNode.setIdWithoutRefactoring(view->model()->generateNewId(effectName)); + newQmlItemNode = QmlItemNode(newModelNode); placeEffectNode(parentProperty, newQmlItemNode, isLayerEffect); }; @@ -206,12 +211,8 @@ void QmlItemNode::placeEffectNode(NodeAbstractProperty &parentProperty, const Qm parentProperty.reparentHere(effectNode); - if (!isLayerEffect) { - effectNode.modelNode().bindingProperty("source").setExpression("parent"); - effectNode.modelNode().bindingProperty("anchors.fill").setExpression("parent"); - } else { + if (isLayerEffect) parentProperty.parentModelNode().variantProperty("layer.enabled").setValue(true); - } if (effectNode.modelNode().metaInfo().hasProperty("timeRunning")) effectNode.modelNode().variantProperty("timeRunning").setValue(true); @@ -617,6 +618,11 @@ ModelNode QmlItemNode::rootModelNode() const return {}; } +bool QmlItemNode::isEffectItem() const +{ + return modelNode().metaInfo().hasProperty("_isEffectItem"); +} + void QmlItemNode::setSize(const QSizeF &size) { if (!hasBindingProperty("width") && !(anchors().instanceHasAnchor(AnchorLineRight)