QmlDesigner: Add Loader and Repeater to component library

Also fixes some issues with Repeater usage.

Fixes: QDS-5149
Change-Id: I259dcb73be634150dd0c5e602165b63112ec958c
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io>
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2021-09-28 16:15:07 +03:00
parent 545da2f6a6
commit 4036c20f63
11 changed files with 268 additions and 2 deletions

View File

@@ -1287,8 +1287,15 @@ PixmapChangedCommand NodeInstanceServer::createPixmapChangedCommand(const QList<
QVector<ImageContainer> 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);

View File

@@ -37,6 +37,7 @@
#include <addimportcontainer.h>
#include <createscenecommand.h>
#include <reparentinstancescommand.h>
#include <removeinstancescommand.h>
#include <clearscenecommand.h>
#include <QDebug>
@@ -193,6 +194,19 @@ QList<QQuickItem*> Qt5NodeInstanceServer::allItems() const
return QList<QQuickItem*>();
}
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<ReparentContainer> &containerVector = command.reparentInstances();
for (const ReparentContainer &container : containerVector)
markRepeaterParentDirty(container.instanceId());
NodeInstanceServer::reparentInstances(command.reparentInstances());
startRenderTimer();
}
void Qt5NodeInstanceServer::removeInstances(const RemoveInstancesCommand &command)
{
const QVector<qint32> &idVector = command.instanceIds();
for (const qint32 id : idVector)
markRepeaterParentDirty(id);
NodeInstanceServer::removeInstances(command);
startRenderTimer();
}
} // QmlDesigner

View File

@@ -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<QQuickItem*> allItems() const;
void markRepeaterParentDirty(qint32 id) const;
struct RenderViewData {
QPointer<QQuickWindow> window = nullptr;

View File

@@ -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 {}
}
}
}
}

View File

@@ -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 {}
}
}
}
}

View File

@@ -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())

View File

@@ -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;

View File

@@ -105,6 +105,7 @@ public:
bool modelIsResizable() const;
bool modelIsRotatable() const;
bool modelIsInLayout() const;
bool hasFormEditorItem() const;
QRectF instanceBoundingRect() const;
QRectF instanceSceneBoundingRect() const;

View File

@@ -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())

View File

@@ -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());

View File

@@ -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"
}
}
}