QmlDesigner: Add button to component specifics

* Add a button to component specifics for simpler access to the
  component. Currently it is only possible to access a component via
  the context menu "Go into Component" or shortcut F2.
* Add image template
* Fix other templates
* Cleanup component specifics and introduce nested sections

Task-number: QDS-3062
Task-number: QDS-2358
Change-Id: I6b245b013fbf4b960509b0a357ae62d20e0383cc
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Henning Gruendl
2020-11-05 16:11:13 +01:00
committed by Henning Gründl
parent 50405d7153
commit 6a1f51b195
12 changed files with 514 additions and 132 deletions

View File

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

View File

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

View File

@@ -542,7 +542,7 @@ inline bool dotPropertyHeuristic(const QmlObjectNode &node, const NodeMetaInfo &
if (name.count('.') > 1)
return false;
QList<QByteArray> list =name.split('.');
QList<QByteArray> 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<PropertyName> allProperties = type.propertyNames();
QString qmlTemplate = imports.join(QLatin1Char('\n')) + QLatin1Char('\n');
QMap<PropertyName, QList<PropertyName>> propertyMap;
QList<PropertyName> 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<PropertyName> orderedList = type.propertyNames();
Utils::sort(orderedList, [type, &sectorTypes](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<PropertyName> 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();