diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index 1fda59c964e..62ef8f246ab 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1287,8 +1287,15 @@ PixmapChangedCommand NodeInstanceServer::createPixmapChangedCommand(const QList< QVector imageVector; for (const ServerNodeInstance &instance : instanceList) { - if (instance.isValid() && instance.hasContent()) - imageVector.append(ImageContainer(instance.instanceId(), instance.renderImage(), instance.instanceId())); + if (!instance.isValid()) + continue; + + QImage renderImage; + // We need to return empty image if instance has no content to correctly update the + // item image in case the instance changed from having content to not having content. + if (instance.hasContent()) + renderImage = instance.renderImage(); + imageVector.append(ImageContainer(instance.instanceId(), renderImage, instance.instanceId())); } return PixmapChangedCommand(imageVector); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp index 89e8c5d2db5..20611eef163 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -193,6 +194,19 @@ QList Qt5NodeInstanceServer::allItems() const return QList(); } +void Qt5NodeInstanceServer::markRepeaterParentDirty(qint32 id) const +{ + if (!hasInstanceForId(id)) + return; + + // If a Repeater instance was moved/removed, the old parent must be marked dirty to rerender it + ServerNodeInstance instance = instanceForId(id); + if (instance.isValid() && instance.isSubclassOf("QQuickRepeater") && instance.hasParent()) { + ServerNodeInstance parentInstance = instance.parent(); + DesignerSupport::addDirty(parentInstance.rootQuickItem(), QQuickDesignerSupport::Content); + } +} + bool Qt5NodeInstanceServer::initRhi(RenderViewData &viewData) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) @@ -523,9 +537,23 @@ void Qt5NodeInstanceServer::clearScene(const ClearSceneCommand &command) void Qt5NodeInstanceServer::reparentInstances(const ReparentInstancesCommand &command) { + const QVector &containerVector = command.reparentInstances(); + for (const ReparentContainer &container : containerVector) + markRepeaterParentDirty(container.instanceId()); + NodeInstanceServer::reparentInstances(command.reparentInstances()); startRenderTimer(); } +void Qt5NodeInstanceServer::removeInstances(const RemoveInstancesCommand &command) +{ + const QVector &idVector = command.instanceIds(); + for (const qint32 id : idVector) + markRepeaterParentDirty(id); + + NodeInstanceServer::removeInstances(command); + startRenderTimer(); +} + } // QmlDesigner diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h index 4af451b61ae..d93ecad84ed 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.h @@ -67,6 +67,7 @@ public: void createScene(const CreateSceneCommand &command) override; void clearScene(const ClearSceneCommand &command) override; void reparentInstances(const ReparentInstancesCommand &command) override; + void removeInstances(const RemoveInstancesCommand &command) override; QImage grabWindow() override; QImage grabItem(QQuickItem *item) override; @@ -79,6 +80,7 @@ protected: void resetAllItems(); void setupScene(const CreateSceneCommand &command) override; QList allItems() const; + void markRepeaterParentDirty(qint32 id) const; struct RenderViewData { QPointer window = nullptr; diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml new file mode 100644 index 00000000000..2417c545876 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/LoaderSpecifics.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import HelperWidgets 2.0 +import StudioTheme 1.0 as StudioTheme + +Column { + anchors.left: parent.left + anchors.right: parent.right + + Section { + caption: qsTr("Loader") + anchors.left: parent.left + anchors.right: parent.right + + SectionLayout { + PropertyLabel { + text: qsTr("Active") + tooltip: qsTr("This property is true if the Loader is currently active.") + } + + SecondColumnLayout { + CheckBox { + text: backendValues.active.valueToString + backendValue: backendValues.active + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Source") + tooltip: qsTr("This property holds the URL of the QML component to instantiate.") + } + + SecondColumnLayout { + UrlChooser { + filter: "*.qml" + backendValue: backendValues.source + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Source Component") + tooltip: qsTr("This property holds the component to instantiate.") + } + + SecondColumnLayout { + ItemFilterComboBox { + typeFilter: "Component" + validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ } + backendValue: backendValues.sourceComponent + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Asynchronous") + tooltip: qsTr("This property holds whether the component will be instantiated asynchronously.") + } + + SecondColumnLayout { + CheckBox { + text: backendValues.asynchronous.valueToString + backendValue: backendValues.asynchronous + implicitWidth: StudioTheme.Values.twoControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + + ExpandingSpacer {} + } + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml new file mode 100644 index 00000000000..33dfc387b1a --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/QtQuick/RepeaterSpecifics.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +import QtQuick 2.15 +import QtQuick.Layouts 1.15 +import HelperWidgets 2.0 +import StudioTheme 1.0 as StudioTheme + +Column { + anchors.left: parent.left + anchors.right: parent.right + + Section { + caption: qsTr("Repeater") + anchors.left: parent.left + anchors.right: parent.right + + SectionLayout { + PropertyLabel { + text: qsTr("Model") + tooltip: qsTr("The model providing data for the repeater. This can simply specify the number of delegate instances to create or it can be bound to an actual model.") + } + + SecondColumnLayout { + LineEdit { + backendValue: backendValues.model + showTranslateCheckBox: false + writeAsExpression: true + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + width: implicitWidth + } + + ExpandingSpacer {} + } + + PropertyLabel { + text: qsTr("Delegate") + tooltip: qsTr("The delegate provides a template defining each object instantiated by the repeater.") + } + + SecondColumnLayout { + ItemFilterComboBox { + typeFilter: "Component" + validator: RegExpValidator { regExp: /(^$|^[a-z_]\w*)/ } + backendValue: backendValues.delegate + implicitWidth: StudioTheme.Values.singleControlColumnWidth + + StudioTheme.Values.actionIndicatorWidth + } + + ExpandingSpacer {} + } + } + } +} diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp index 9a01bbd9401..10e1373dde2 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorview.cpp @@ -91,6 +91,9 @@ void FormEditorView::modelAttached(Model *model) //This function does the setup of the initial FormEditorItem tree in the scene void FormEditorView::setupFormEditorItemTree(const QmlItemNode &qmlItemNode) { + if (!qmlItemNode.hasFormEditorItem()) + return; + if (qmlItemNode.isFlowTransition()) { m_scene->addFormEditorItem(qmlItemNode, FormEditorScene::FlowTransition); if (qmlItemNode.hasNodeParent()) diff --git a/src/plugins/qmldesigner/designercore/include/nodehints.h b/src/plugins/qmldesigner/designercore/include/nodehints.h index a22dd40f403..16fe18320cd 100644 --- a/src/plugins/qmldesigner/designercore/include/nodehints.h +++ b/src/plugins/qmldesigner/designercore/include/nodehints.h @@ -62,6 +62,7 @@ public: bool canBeDroppedInView3D() const; bool isMovable() const; bool isResizable() const; + bool hasFormEditorItem() const; bool isStackedContainer() const; bool canBeReparentedTo(const ModelNode &potenialParent); QString indexPropertyForStackedContainer() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h index 579a4ca7b85..9be07edfbfa 100644 --- a/src/plugins/qmldesigner/designercore/include/qmlitemnode.h +++ b/src/plugins/qmldesigner/designercore/include/qmlitemnode.h @@ -105,6 +105,7 @@ public: bool modelIsResizable() const; bool modelIsRotatable() const; bool modelIsInLayout() const; + bool hasFormEditorItem() const; QRectF instanceBoundingRect() const; QRectF instanceSceneBoundingRect() const; diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp index 26b06cdc42f..84cc5650747 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/nodehints.cpp @@ -183,6 +183,11 @@ bool NodeHints::isResizable() const return evaluateBooleanExpression("isResizable", true); } +bool NodeHints::hasFormEditorItem() const +{ + return evaluateBooleanExpression("hasFormEditorItem", true); +} + bool NodeHints::isStackedContainer() const { if (!isValid()) diff --git a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp index 03ae0ecd1dc..02ecb64efde 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlitemnode.cpp @@ -395,6 +395,11 @@ bool QmlItemNode::modelIsInLayout() const return false; } +bool QmlItemNode::hasFormEditorItem() const +{ + return NodeHints::fromModelNode(modelNode()).hasFormEditorItem(); +} + QRectF QmlItemNode::instanceBoundingRect() const { return QRectF(QPointF(0, 0), nodeInstance().size()); diff --git a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo index 877f1203390..019be099732 100644 --- a/src/plugins/qmldesigner/qtquickplugin/quick.metainfo +++ b/src/plugins/qmldesigner/qtquickplugin/quick.metainfo @@ -448,4 +448,35 @@ MetaInfo { version: "2.0" } } + + Type { + name: "QtQuick.Loader" + icon: ":/qtquickplugin/images/item-icon16.png" + + ItemLibraryEntry { + name: "Loader" + category: "e.Qt Quick - Component" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + Property { name: "width"; type: "int"; value: 200; } + Property { name: "height"; type: "int"; value: 200; } + } + } + + Type { + name: "QtQuick.Repeater" + icon: ":/qtquickplugin/images/item-icon16.png" + + Hints { + canBeDroppedInFormEditor: false + hasFormEditorItem: false + } + + ItemLibraryEntry { + name: "Repeater" + category: "e.Qt Quick - Component" + libraryIcon: ":/qtquickplugin/images/item-icon.png" + version: "2.0" + } + } }