diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ColorEditorTemplate.template b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ColorEditorTemplate.template index 2f61a2bfa67..66e3ece1c91 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ColorEditorTemplate.template +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ColorEditorTemplate.template @@ -1,11 +1,14 @@ +// Dummy comment to consume the first argument and suppress warnings %1 Section { anchors.left: parent.left anchors.right: parent.right - caption: "%1" + caption: qsTr("Color") + expanded: false + level: 2 -ColorEditor { - backendValue: backendValues.%2 - supportGradient: false -} + ColorEditor { + backendValue: backendValues.%2 + supportGradient: false + } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/FontEditorTemplate.template b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/FontEditorTemplate.template index 8f12d7fff7a..1f56bfc5a4a 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/FontEditorTemplate.template +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/FontEditorTemplate.template @@ -1,7 +1,10 @@ +// Dummy comment to consume the first argument and suppress warnings %1 FontSection { anchors.left: parent.left anchors.right: parent.right - caption: "%1" + caption: qsTr("Font") fontName: "%2" + expanded: false + level: 2 } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ImageEditorTemplate.template b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ImageEditorTemplate.template new file mode 100644 index 00000000000..d1ffbf9dd0f --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/ImageEditorTemplate.template @@ -0,0 +1,215 @@ +// Dummy comment to consume the first argument and suppress warnings %1 + +Section { + anchors.left: parent.left + anchors.right: parent.right + caption: qsTr("Image") + expanded: false + level: 2 + + SectionLayout { + Label { + text: qsTr("Source") + } + + SecondColumnLayout { + UrlChooser { + Layout.fillWidth: true + backendValue: backendValues.%2_source + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Fill mode") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["Stretch", "PreserveAspectFit", "PreserveAspectCrop", "Tile", "TileVertically", "TileHorizontally", "Pad"] + backendValue: backendValues.%2_fillMode + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Source size") + disabledState: !backendValues.%2_sourceSize.isAvailable + } + + SecondColumnLayout { + Label { + text: "W" + width: 12 + disabledStateSoft: !backendValues.%2_sourceSize_width.isAvailable + } + + SpinBox { + backendValue: backendValues.%2_sourceSize_width + minimumValue: 0 + maximumValue: 8192 + decimals: 0 + enabled: backendValue.isAvailable + } + + Item { + width: 4 + height: 4 + } + + Label { + text: "H" + width: 12 + disabledStateSoft: !backendValues.%2_sourceSize_height.isAvailable + } + + SpinBox { + backendValue: backendValues.%2_sourceSize_height + minimumValue: 0 + maximumValue: 8192 + decimals: 0 + enabled: backendValue.isAvailable + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Horizontal alignment") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["AlignLeft", "AlignRight", "AlignHCenter"] + backendValue: backendValues.%2_horizontalAlignment + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Vertical alignment") + } + + SecondColumnLayout { + ComboBox { + scope: "Image" + model: ["AlignTop", "AlignBottom", "AlignVCenter"] + backendValue: backendValues.%2_verticalAlignment + implicitWidth: 180 + Layout.fillWidth: true + } + + ExpandingSpacer { + } + } + + Label { + text: qsTr("Asynchronous") + tooltip: qsTr("Loads images on the local filesystem asynchronously in a separate thread.") + disabledState: !backendValues.%2_asynchronous.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.%2_asynchronous.isAvailable + text: backendValues.%2_asynchronous.valueToString + backendValue: backendValues.%2_asynchronous + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Auto transform") + tooltip: qsTr("Automatically applies image transformation metadata such as EXIF orientation.") + disabledState: !backendValues.%2_autoTransform.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.%2_autoTransform.isAvailable + text: backendValues.%2_autoTransform.valueToString + backendValue: backendValues.%2_autoTransform + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Cache") + tooltip: qsTr("Caches the image.") + disabledState: !backendValues.%2_cache.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.%2_cache.isAvailable + text: backendValues.%2_cache.valueToString + backendValue: backendValues.%2_cache + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Mipmap") + tooltip: qsTr("Uses mipmap filtering when the image is scaled or transformed.") + disabledState: !backendValues.%2_mipmap.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.%2_mipmap.isAvailable + text: backendValues.%2_mipmap.valueToString + backendValue: backendValues.%2_mipmap + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Mirror") + tooltip: qsTr("Inverts the image horizontally.") + disabledState: !backendValues.%2_mirror.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.%2_mirror.isAvailable + text: backendValues.%2_mirror.valueToString + backendValue: backendValues.%2_mirror + implicitWidth: 180 + } + ExpandingSpacer {} + } + + Label { + text: qsTr("Smooth") + tooltip: qsTr("Smoothly filters the image when it is scaled or transformed.") + disabledState: !backendValues.%2_smooth.isAvailable + } + + SecondColumnLayout { + CheckBox { + enabled: backendValues.%2_smooth.isAvailable + text: backendValues.%2_smooth.valueToString + backendValue: backendValues.%2_smooth + implicitWidth: 180 + } + ExpandingSpacer {} + } + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/RectangleEditorTemplate.template b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/RectangleEditorTemplate.template index bfa9790b59a..87451c11b1d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/RectangleEditorTemplate.template +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/RectangleEditorTemplate.template @@ -1,21 +1,25 @@ +// Dummy comment to consume the first argument and suppress warnings %1 + Section { anchors.left: parent.left anchors.right: parent.right - caption: qsTr("%2 Color") + caption: qsTr("Color") + expanded: false + level: 2 ColorEditor { caption: qsTr("Color") backendValue: backendValues.%2_color supportGradient: true } - - } Section { anchors.left: parent.left anchors.right: parent.right - caption: qsTr("%2 Border Color") + caption: qsTr("Border Color") + expanded: false + level: 2 ColorEditor { caption: qsTr("Border Color") @@ -24,11 +28,12 @@ Section { } } - Section { anchors.left: parent.left anchors.right: parent.right - caption: "%2 Rectangle" + caption: qsTr("Rectangle") + expanded: false + level: 2 SectionLayout { rows: 2 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml index 8917e81d5eb..d5a49232013 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TemplateTypes.qml @@ -24,7 +24,7 @@ ****************************************************************************/ AutoTypes { - imports: [ "import HelperWidgets 2.0", "import QtQuick 2.1", "import QtQuick.Layouts 1.1" ] + imports: [ "import HelperWidgets 2.0", "import QtQuick 2.15", "import QtQuick.Layouts 1.15" ] Type { typeNames: ["int"] @@ -65,10 +65,15 @@ AutoTypes { separateSection: true } - Type { + Type { typeNames: ["Rectangle"] sourceFile: "RectangleEditorTemplate.template" separateSection: true } + Type { + typeNames: ["Image"] + sourceFile: "ImageEditorTemplate.template" + separateSection: true + } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TextEditorTemplate.template b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TextEditorTemplate.template index e9e8787f757..903e971a835 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TextEditorTemplate.template +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/PropertyTemplates/TextEditorTemplate.template @@ -1,7 +1,9 @@ Section { anchors.left: parent.left anchors.right: parent.right - caption: "%1" + caption: qsTr("Text") + expanded: false + level: 2 SectionLayout { columns: 2 diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml new file mode 100644 index 00000000000..601f4afc057 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComponentButton.qml @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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 StudioControls 1.0 as StudioControls +import StudioTheme 1.0 as StudioTheme + +Column { + id: column + width: parent.width + spacing: 10 + padding: 10 + + Label { + text: qsTr("This item is an instance of a Component") + anchors.horizontalCenter: parent.horizontalCenter + width: 220 + } + + StudioControls.AbstractButton { + id: testtest + anchors.horizontalCenter: parent.horizontalCenter + width: 180 + + buttonIcon: qsTr("Edit Master Component") + iconFont: StudioTheme.Constants.font + + onClicked: goIntoComponent() + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml index 583b11ee9a5..58fe3cf3d02 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/Section.qml @@ -23,9 +23,9 @@ ** ****************************************************************************/ -import QtQuick 2.1 +import QtQuick 2.15 import QtQuick.Controls 2.12 as Controls -import QtQuick.Layouts 1.0 +import QtQuick.Layouts 1.15 import QtQuickDesignerTheme 1.0 import StudioTheme 1.0 as StudioTheme @@ -35,28 +35,22 @@ Item { property int leftPadding: 8 property int topPadding: 4 property int rightPadding: 0 + property int bottomPadding: 4 property int animationDuration: 0 property bool expanded: true + property int level: 0 + property int levelShift: 10 clip: true Rectangle { id: header height: 20 - anchors.left: parent.left anchors.right: parent.right - - Controls.Label { - id: label - anchors.verticalCenter: parent.verticalCenter - color: StudioTheme.Values.themeTextColor - x: 22 - font.bold: true - font.pixelSize: StudioTheme.Values.myFontSize - } + color: Qt.lighter(StudioTheme.Values.themeSectionHeadBackground, 1.0 + (0.2 * level)) Image { id: arrow @@ -64,35 +58,27 @@ Item { height: 4 source: "image://icons/down-arrow" anchors.left: parent.left - anchors.leftMargin: 4 + anchors.leftMargin: 4 + (level * levelShift) anchors.verticalCenter: parent.verticalCenter Behavior on rotation { NumberAnimation { easing.type: Easing.OutCubic - duration: animationDuration + duration: section.animationDuration } } - } - color: StudioTheme.Values.themeSectionHeadBackground - - Rectangle { - visible: false - color:"#333" - width: parent.width - height: 1 - } - - Rectangle { - visible: false - color: "#333" - anchors.bottom: parent.bottom - width: parent.width - height: 1 + Controls.Label { + id: label + anchors.verticalCenter: parent.verticalCenter + color: StudioTheme.Values.themeTextColor + x: 22 + (level * levelShift) + font.bold: true + font.pixelSize: StudioTheme.Values.myFontSize } MouseArea { + id: mouseArea anchors.fill: parent onClicked: { section.animationDuration = 120 @@ -105,22 +91,23 @@ Item { readonly property alias contentItem: row - implicitHeight: Math.round(row.height + header.height + 8) + implicitHeight: Math.round(row.height + header.height + + section.topPadding + section.bottomPadding) Row { - anchors.left: parent.left - anchors.leftMargin: leftPadding - anchors.right: parent.right - anchors.rightMargin: rightPadding - anchors.top: header.bottom - anchors.topMargin: topPadding id: row + anchors.left: parent.left + anchors.leftMargin: section.leftPadding + anchors.right: parent.right + anchors.rightMargin: section.rightPadding + anchors.top: header.bottom + anchors.topMargin: section.topPadding } Behavior on implicitHeight { NumberAnimation { easing.type: Easing.OutCubic - duration: animationDuration + duration: section.animationDuration } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 0da64895519..459d40da0d6 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -14,6 +14,7 @@ ColorEditor 2.0 ColorEditor.qml ColorLine 2.0 ColorLine.qml ColorLogic 2.0 ColorLogic.qml ComboBox 2.0 ComboBox.qml +ComponentButton 2.0 ComponentButton.qml EditableListView 2.0 EditableListView.qml ExpandingSpacer 2.0 ExpandingSpacer.qml ExtendedFunctionLogic 2.0 ExtendedFunctionLogic.qml diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp index 798274af20a..92fdc2eed94 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.cpp @@ -180,6 +180,21 @@ void PropertyEditorContextObject::toogleExportAlias() } } +void PropertyEditorContextObject::goIntoComponent() +{ + QTC_ASSERT(m_model && m_model->rewriterView(), return); + + /* Ideally we should not missuse the rewriterView + * If we add more code here we have to forward the property editor view */ + RewriterView *rewriterView = m_model->rewriterView(); + + QTC_ASSERT(!rewriterView->selectedModelNodes().isEmpty(), return); + + const ModelNode selectedNode = rewriterView->selectedModelNodes().constFirst(); + + DocumentManager::goIntoComponent(selectedNode); +} + void PropertyEditorContextObject::changeTypeName(const QString &typeName) { QTC_ASSERT(m_model && m_model->rewriterView(), return); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h index d4c39306c13..04d5d074bb8 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorcontextobject.h @@ -86,6 +86,8 @@ public: Q_INVOKABLE void toogleExportAlias(); + Q_INVOKABLE void goIntoComponent(); + Q_INVOKABLE void changeTypeName(const QString &typeName); Q_INVOKABLE void insertKeyframe(const QString &propertyName); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index bf2243d09fc..9a23b78f790 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -542,7 +542,7 @@ inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo & if (name.count('.') > 1) return false; - QList list =name.split('.'); + QList list = name.split('.'); const PropertyName parentProperty = list.first(); const PropertyName itemProperty = list.last(); @@ -551,19 +551,15 @@ inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo & NodeMetaInfo itemInfo = node.view()->model()->metaInfo("QtQuick.Item"); NodeMetaInfo textInfo = node.view()->model()->metaInfo("QtQuick.Text"); NodeMetaInfo rectangleInfo = node.view()->model()->metaInfo("QtQuick.Rectangle"); + NodeMetaInfo imageInfo = node.view()->model()->metaInfo("QtQuick.Image"); - if (itemInfo.hasProperty(itemProperty)) + if (typeName == "font" + || itemInfo.hasProperty(itemProperty) + || textInfo.isSubclassOf(typeName) + || rectangleInfo.isSubclassOf(typeName) + || imageInfo.isSubclassOf(typeName)) return false; - if (typeName == "font") - return false; - - if (textInfo.isSubclassOf(typeName)) - return false; - - if (rectangleInfo.isSubclassOf(typeName)) - return false; - return true; } @@ -576,92 +572,188 @@ QString PropertyEditorQmlBackend::templateGeneration(const NodeMetaInfo &type, const auto nodes = templateConfiguration()->children(); - QStringList sectorTypes; + QStringList allTypes; // all template types + QStringList separateSectionTypes; // separate section types only for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) { if (node->propertyNames().contains("separateSection")) - sectorTypes.append(variantToStringList(node->property("typeNames"))); + separateSectionTypes.append(variantToStringList(node->property("typeNames"))); + + allTypes.append(variantToStringList(node->property("typeNames"))); } - QStringList imports = variantToStringList(templateConfiguration()->property(QStringLiteral("imports"))); + const QList allProperties = type.propertyNames(); - QString qmlTemplate = imports.join(QLatin1Char('\n')) + QLatin1Char('\n'); + QMap> propertyMap; + QList separateSectionProperties; - qmlTemplate += "Column {\n"; - qmlTemplate += "anchors.left: parent.left\n"; - qmlTemplate += "anchors.right: parent.right\n"; + // Iterate over all properties and isolate the properties which have their own template + for (const PropertyName &propertyName : allProperties) { + if (propertyName.startsWith("__")) + continue; // private API - QList orderedList = type.propertyNames(); - Utils::sort(orderedList, [type, §orTypes](const PropertyName &left, const PropertyName &right){ - const QString typeNameLeft = QString::fromLatin1(type.propertyTypeName(left)); - const QString typeNameRight = QString::fromLatin1(type.propertyTypeName(right)); - if (typeNameLeft == typeNameRight) - return left > right; + if (!superType.hasProperty(propertyName) + && type.propertyIsWritable(propertyName) + && dotPropertyHeuristic(node, type, propertyName)) { + const QString typeName = QString::fromLatin1(type.propertyTypeName(propertyName)); - if (sectorTypes.contains(typeNameLeft)) { - if (sectorTypes.contains(typeNameRight)) - return left > right; - return true; - } else if (sectorTypes.contains(typeNameRight)) { - return false; - } - return left > right; - }); + // Check if a template for the type exists + if (allTypes.contains(typeName)) { + if (separateSectionTypes.contains(typeName)) { // template enforces separate section + separateSectionProperties.append(propertyName); + } else { + if (propertyName.contains('.')) { + const PropertyName parentProperty = propertyName.split('.').first(); - bool emptyTemplate = true; - - bool sectionStarted = false; - - foreach (const PropertyName &name, orderedList) { - - if (name.startsWith("__")) - continue; //private API - PropertyName properName = name; - - properName.replace('.', '_'); - - TypeName typeName = type.propertyTypeName(name); - //alias resolution only possible with instance - if (typeName == "alias" && node.isValid()) - typeName = node.instanceType(name); - - auto nodes = templateConfiguration()->children(); - - if (!superType.hasProperty(name) && type.propertyIsWritable(name) && dotPropertyHeuristic(node, type, name)) { - - for (const QmlJS::SimpleReaderNode::Ptr &node : nodes) { - if (variantToStringList(node->property(QStringLiteral("typeNames"))).contains(QString::fromLatin1(typeName))) { - const QString fileName = propertyTemplatesPath() + node->property(QStringLiteral("sourceFile")).toString(); - QFile file(fileName); - if (file.open(QIODevice::ReadOnly)) { - QString source = QString::fromUtf8(file.readAll()); - file.close(); - const bool section = node->propertyNames().contains("separateSection"); - if (section) { - } else if (!sectionStarted) { - qmlTemplate += QStringLiteral("Section {\n"); - qmlTemplate += QStringLiteral("caption: \"%1\"\n").arg(QString::fromUtf8(type.simplifiedTypeName())); - qmlTemplate += "anchors.left: parent.left\n"; - qmlTemplate += "anchors.right: parent.right\n"; - qmlTemplate += QStringLiteral("SectionLayout {\n"); - sectionStarted = true; - } - - qmlTemplate += source.arg(QString::fromUtf8(name)).arg(QString::fromUtf8(properName)); - emptyTemplate = false; + if (propertyMap.contains(parentProperty)) + propertyMap[parentProperty].append(propertyName); + else + propertyMap[parentProperty] = { propertyName }; } else { - qWarning().nospace() << "template definition source file not found:" << fileName; + if (!propertyMap.contains(propertyName)) + propertyMap[propertyName] = {}; } } } } } - if (sectionStarted) { - qmlTemplate += QStringLiteral("}\n"); //Section - qmlTemplate += QStringLiteral("}\n"); //SectionLayout + + // Filter out the properties which have a basic type e.g. int, string, bool + QList basicProperties; + for (auto k : propertyMap.keys()) { + if (propertyMap.value(k).empty()) { + basicProperties.append(k); + propertyMap.remove(k); + } } - qmlTemplate += "}\n"; + Utils::sort(basicProperties); + + auto findAndFillTemplate = [&nodes, &node, &type](const PropertyName &label, + const PropertyName &property) { + PropertyName underscoreProperty = property; + underscoreProperty.replace('.', '_'); + + TypeName typeName = type.propertyTypeName(property); + // alias resolution only possible with instance + if (typeName == "alias" && node.isValid()) + typeName = node.instanceType(property); + + QString filledTemplate; + for (const QmlJS::SimpleReaderNode::Ptr &n : nodes) { + // Check if we have a template for the type + if (variantToStringList(n->property(QStringLiteral("typeNames"))).contains(QString::fromLatin1(typeName))) { + const QString fileName = propertyTemplatesPath() + n->property(QStringLiteral("sourceFile")).toString(); + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + QString source = QString::fromUtf8(file.readAll()); + file.close(); + filledTemplate = source.arg(QString::fromUtf8(label)).arg(QString::fromUtf8(underscoreProperty)); + } else { + qWarning().nospace() << "template definition source file not found:" << fileName; + } + } + } + return filledTemplate; + }; + + // QML specfics preparation + QStringList imports = variantToStringList(templateConfiguration()->property(QStringLiteral("imports"))); + QString qmlTemplate = imports.join(QLatin1Char('\n')) + QLatin1Char('\n'); + bool emptyTemplate = true; + + const QString anchorLeftRight = "anchors.left: parent.left\nanchors.right: parent.right\n"; + const QString paddingLeftTopBottom = "leftPadding: 0\ntopPadding: 0\nbottomPadding: 0\n"; + + qmlTemplate += "Column {\n"; + qmlTemplate += anchorLeftRight; + + if (node.modelNode().isComponent()) + qmlTemplate += "ComponentButton {}\n"; + + qmlTemplate += "Section {\n"; + qmlTemplate += "caption: \"User added properties\"\n"; + qmlTemplate += anchorLeftRight; + qmlTemplate += paddingLeftTopBottom; + qmlTemplate += "Column {\n"; + qmlTemplate += "width: parent.width\n"; + + // First the section containing properties of basic type e.g. int, string, bool + if (!basicProperties.empty()) { + emptyTemplate = false; + + qmlTemplate += "Column {\n"; + qmlTemplate += "width: parent.width\n"; + qmlTemplate += "leftPadding: 8\n"; + qmlTemplate += "rightPadding: 0\n"; + qmlTemplate += "topPadding: 4\n"; + qmlTemplate += "bottomPadding: 4\n"; + qmlTemplate += "SectionLayout {\n"; + + for (const auto &p : qAsConst(basicProperties)) + qmlTemplate += findAndFillTemplate(p, p); + + qmlTemplate += "}\n"; // SectionLayout + qmlTemplate += "}\n"; // Column + } + + // Second the section containing properties of complex type for which no specific template exists e.g. Button + if (!propertyMap.empty()) { + emptyTemplate = false; + for (const auto &k : propertyMap.keys()) { + TypeName parentTypeName = type.propertyTypeName(k); + // alias resolution only possible with instance + if (parentTypeName == "alias" && node.isValid()) + parentTypeName = node.instanceType(k); + + qmlTemplate += "Section {\n"; + qmlTemplate += QStringLiteral("caption: \"%1 - %2\"\n").arg(QString::fromUtf8(k)).arg(QString::fromUtf8(parentTypeName)); + qmlTemplate += anchorLeftRight; + qmlTemplate += "expanded: false\n"; + qmlTemplate += "level: 1\n"; + qmlTemplate += "SectionLayout {\n"; + + auto properties = propertyMap.value(k); + Utils::sort(properties); + + for (const auto &p : qAsConst(properties)) { + const PropertyName shortName = p.contains('.') ? p.split('.').last() : p; + qmlTemplate += findAndFillTemplate(shortName, p); + } + + qmlTemplate += "}\n"; // SectionLayout + qmlTemplate += "}\n"; // Section + } + } + + // Third the section containing properties of complex type for which a specific template exists e.g. Rectangle, Image + if (!separateSectionProperties.empty()) { + emptyTemplate = false; + Utils::sort(separateSectionProperties); + for (const auto &p : qAsConst(separateSectionProperties)) { + TypeName parentTypeName = type.propertyTypeName(p); + // alias resolution only possible with instance + if (parentTypeName == "alias" && node.isValid()) + parentTypeName = node.instanceType(p); + + qmlTemplate += "Section {\n"; + qmlTemplate += QStringLiteral("caption: \"%1 - %2\"\n").arg(QString::fromUtf8(p)).arg(QString::fromUtf8(parentTypeName)); + qmlTemplate += anchorLeftRight; + qmlTemplate += paddingLeftTopBottom; + qmlTemplate += "level: 1\n"; + qmlTemplate += "Column {\n"; + qmlTemplate += "width: parent.width\n"; + + qmlTemplate += findAndFillTemplate(p, p); + + qmlTemplate += "}\n"; // Column + qmlTemplate += "}\n"; // Section + } + } + + qmlTemplate += "}\n"; // Column + qmlTemplate += "}\n"; // Section + qmlTemplate += "}\n"; // Column if (emptyTemplate) return QString();