From fe6d2aef37b2fe36a7dfa651dd5f5171c465495e Mon Sep 17 00:00:00 2001 From: Miikka Heikkinen Date: Fri, 22 Dec 2023 16:56:11 +0200 Subject: [PATCH] Effect Maker: Make effects children of items they affect ...instead of being inlined into layer.effect. This fixes the issue with dynamic properties not properly updating for inlined instances at reset and in general makes the effect properties more accessible. Task-number: QDS-11357 Change-Id: Ie372b99752ceda5bdfe248dd576352d2c6a4c4f7 Reviewed-by: Thomas Hartmann --- .../effectMakerQmlSources/BlurHelper.qml | 5 +- .../EffectMakerPreview.qml | 2 +- .../effectmakernew/effectmakermodel.cpp | 117 +++++++++++++----- .../componentcore/modelnodeoperations.cpp | 2 +- .../components/formeditor/formeditoritem.cpp | 23 +++- .../components/formeditor/formeditoritem.h | 5 + .../components/formeditor/formeditorview.cpp | 30 ++++- .../components/formeditor/formeditorview.h | 3 + .../designercore/include/qmlitemnode.h | 2 + .../designercore/model/qmlitemnode.cpp | 20 +-- 10 files changed, 163 insertions(+), 46 deletions(-) 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)