Add stylesheet merger

Adds classes to merge a template qml file and a qml stylesheet that have
been exported from other design tools into a resulting qml file that can
be used for further processing in Qt Design Studio.

Current issues:

* Sometimes it makes sense to define width and height
  if an anchor is present, but most of the time not.

* Actually if the hierachy was defined (e.g. Text item not child of
  background) most likely the anchors should be ignored.
  But this would be just a "dirty" heuristic. I suggest to let the
  template decide. If the template has anchors those have "precedence".
  It is always possible to define templates without anchors.

Task-number: QDS-2071
Change-Id: I9159514a8e884b7ffc31897aef4551b5efbbcb87
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Michael Brüning
2020-05-07 16:43:39 +02:00
committed by Thomas Hartmann
parent 5b00401850
commit de8eb93637
35 changed files with 2443 additions and 17 deletions

View File

@@ -457,6 +457,7 @@ extend_qtc_plugin(QmlDesigner
include/rewriterview.h
include/rewritingexception.h
include/signalhandlerproperty.h
include/stylesheetmerger.h
include/subcomponentmanager.h
include/textmodifier.h
include/variantproperty.h
@@ -530,6 +531,7 @@ extend_qtc_plugin(QmlDesigner
model/rewriteactioncompressor.cpp model/rewriteactioncompressor.h
model/rewriterview.cpp
model/signalhandlerproperty.cpp
model/stylesheetmerger.cpp
model/textmodifier.cpp
model/texttomodelmerger.cpp model/texttomodelmerger.h
model/variantproperty.cpp

View File

@@ -83,7 +83,8 @@ SOURCES += $$PWD/model/abstractview.cpp \
$$PWD/instances/puppetdialog.cpp \
$$PWD/model/qmltimeline.cpp \
$$PWD/model/qmltimelinekeyframegroup.cpp \
$$PWD/model/annotation.cpp
$$PWD/model/annotation.cpp \
$$PWD/model/stylesheetmerger.cpp
HEADERS += $$PWD/include/qmldesignercorelib_global.h \
$$PWD/include/abstractview.h \
@@ -160,7 +161,8 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \
$$PWD/instances/puppetdialog.h \
$$PWD/include/qmltimeline.h \
$$PWD/include/qmltimelinekeyframegroup.h \
$$PWD/include/annotation.h
$$PWD/include/annotation.h \
$$PWD/include/stylesheetmerger.h
FORMS += \
$$PWD/instances/puppetdialog.ui

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#ifndef STYLESHEETMERGER_H
#define STYLESHEETMERGER_H
#include <QString>
#include <QHash>
#include <modelnode.h>
namespace QmlDesigner {
class AbstractView;
struct ReparentInfo {
QString generatedId;
QString templateId;
QString templateParentId;
int parentIndex;
bool alreadyReparented;
};
class StylesheetMerger {
public:
StylesheetMerger(AbstractView*, AbstractView*);
void merge();
private:
void preprocessStyleSheet();
bool idExistsInBothModels(const QString& id);
void replaceNode(ModelNode&, ModelNode&);
void replaceRootNode(ModelNode& templateRootNode);
void applyStyleProperties(ModelNode &templateNode, const ModelNode &styleNode);
void adjustNodeIndex(ModelNode &node);
void setupIdRenamingHash();
ModelNode createReplacementNode(const ModelNode &styleNode, ModelNode &modelNode);
void syncNodeProperties(ModelNode &outputNode, const ModelNode &inputNode, bool skipDuplicates = false);
void syncNodeListProperties(ModelNode &outputNode, const ModelNode &inputNode, bool skipDuplicates = false);
void syncId(ModelNode &outputNode, ModelNode &inputNode);
void syncBindingProperties(ModelNode &outputNode, const ModelNode &inputNode);
void syncAuxiliaryProperties(ModelNode &outputNode, const ModelNode &inputNode);
void syncVariantProperties(ModelNode &outputNode, const ModelNode &inputNode);
AbstractView *m_templateView;
AbstractView *m_styleView;
QHash<QString, ReparentInfo> m_reparentInfoHash;
QHash<QString, QString> m_idReplacementHash;
};
}
#endif // STYLESHEETMERGER_H

View File

@@ -0,0 +1,404 @@
/****************************************************************************
**
** 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.
**
****************************************************************************/
#include "stylesheetmerger.h"
#include <abstractview.h>
#include <bindingproperty.h>
#include <modelmerger.h>
#include <nodeabstractproperty.h>
#include <nodelistproperty.h>
#include <nodemetainfo.h>
#include <nodeproperty.h>
#include <variantproperty.h>
#include <QQueue>
#include <QRegularExpression>
namespace QmlDesigner {
static void splitIdInBaseNameAndNumber(const QString &id, QString *baseId, int *number)
{
int counter = 0;
while (counter < id.count()) {
bool canConvertToInteger = false;
int newNumber = id.rightRef(counter +1).toInt(&canConvertToInteger);
if (canConvertToInteger)
*number = newNumber;
else
break;
counter++;
}
*baseId = id.left(id.count() - counter);
}
void StylesheetMerger::syncNodeProperties(ModelNode &outputNode, const ModelNode &inputNode, bool skipDuplicates)
{
foreach (const NodeProperty &nodeProperty, inputNode.nodeProperties()) {
ModelNode oldNode = nodeProperty.modelNode();
if (m_templateView->hasId(oldNode.id()) && skipDuplicates)
continue;
ModelNode newNode = createReplacementNode(oldNode, oldNode);
// cache the property name as removing it will invalidate it
PropertyName propertyName = nodeProperty.name();
// remove property first to prevent invalid reparenting situation
outputNode.removeProperty(propertyName);
outputNode.nodeProperty(propertyName).reparentHere(newNode);
}
}
void StylesheetMerger::syncNodeListProperties(ModelNode &outputNode, const ModelNode &inputNode, bool skipDuplicates)
{
foreach (const NodeListProperty &nodeListProperty, inputNode.nodeListProperties()) {
foreach (ModelNode node, nodeListProperty.toModelNodeList()) {
if (m_templateView->hasId(node.id()) && skipDuplicates)
continue;
ModelNode newNode = createReplacementNode(node, node);
outputNode.nodeListProperty(nodeListProperty.name()).reparentHere(newNode);
}
}
}
void StylesheetMerger::syncVariantProperties(ModelNode &outputNode, const ModelNode &inputNode)
{
foreach (const VariantProperty &variantProperty, inputNode.variantProperties()) {
outputNode.variantProperty(variantProperty.name()).setValue(variantProperty.value());
}
}
void StylesheetMerger::syncAuxiliaryProperties(ModelNode &outputNode, const ModelNode &inputNode)
{
auto tmp = inputNode.auxiliaryData();
for (auto iter = tmp.begin(); iter != tmp.end(); ++iter)
outputNode.setAuxiliaryData(iter.key(), iter.value());
}
void StylesheetMerger::syncBindingProperties(ModelNode &outputNode, const ModelNode &inputNode)
{
foreach (const BindingProperty &bindingProperty, inputNode.bindingProperties()) {
outputNode.bindingProperty(bindingProperty.name()).setExpression(bindingProperty.expression());
}
}
void StylesheetMerger::syncId(ModelNode &outputNode, ModelNode &inputNode)
{
if (!inputNode.id().isEmpty()) {
QString id = inputNode.id();
QString renamedId = m_idReplacementHash.value(inputNode.id());
inputNode.setIdWithoutRefactoring(renamedId);
outputNode.setIdWithoutRefactoring(id);
}
}
void StylesheetMerger::setupIdRenamingHash()
{
foreach (const ModelNode &node, m_templateView->rootModelNode().allSubModelNodesAndThisNode()) {
if (!node.id().isEmpty()) {
QString newId = node.id();
QString baseId;
int number = 1;
splitIdInBaseNameAndNumber(newId, &baseId, &number);
while (m_templateView->hasId(newId) || m_idReplacementHash.values().contains(newId)) {
newId = "stylesheet_auto_merge_" + baseId + QString::number(number);
number++;
}
m_idReplacementHash.insert(node.id(), newId);
}
}
}
ModelNode StylesheetMerger::createReplacementNode(const ModelNode& styleNode, ModelNode &modelNode)
{
QList<QPair<PropertyName, QVariant> > propertyList;
QList<QPair<PropertyName, QVariant> > variantPropertyList;
foreach (const VariantProperty &variantProperty, modelNode.variantProperties()) {
propertyList.append(QPair<PropertyName, QVariant>(variantProperty.name(), variantProperty.value()));
}
NodeMetaInfo nodeMetaInfo = m_templateView->model()->metaInfo(styleNode.type());
ModelNode newNode(m_templateView->createModelNode(styleNode.type(), nodeMetaInfo.majorVersion(), nodeMetaInfo.minorVersion(),
propertyList, variantPropertyList, styleNode.nodeSource(), styleNode.nodeSourceType()));
syncAuxiliaryProperties(newNode, modelNode);
syncBindingProperties(newNode, modelNode);
syncId(newNode, modelNode);
syncNodeProperties(newNode, modelNode);
syncNodeListProperties(newNode, modelNode);
return newNode;
}
StylesheetMerger::StylesheetMerger(AbstractView *templateView, AbstractView *styleView)
: m_templateView(templateView)
, m_styleView(styleView)
{
}
bool StylesheetMerger::idExistsInBothModels(const QString& id)
{
return m_templateView->hasId(id) && m_styleView->hasId(id);
}
QPoint pointForModelNode(const ModelNode &node)
{
int x = 0;
if (node.hasVariantProperty("x"))
x = node.variantProperty("x").value().toInt();
int y = 0;
if (node.hasVariantProperty("y"))
y = node.variantProperty("y").value().toInt();
return QPoint(x, y);
}
QPoint parentPosition(const ModelNode &node)
{
QPoint p;
ModelNode currentNode = node;
while (currentNode.hasParentProperty()) {
currentNode = currentNode.parentProperty().parentModelNode();
p += pointForModelNode(currentNode);
}
return p;
}
void StylesheetMerger::preprocessStyleSheet()
{
for (ModelNode currentStyleNode : m_styleView->rootModelNode().directSubModelNodes()) {
QString id = currentStyleNode.id();
if (!idExistsInBothModels(id))
continue;
ModelNode templateNode = m_templateView->modelNodeForId(id);
NodeAbstractProperty templateParentProperty = templateNode.parentProperty();
if (!templateNode.hasParentProperty()
|| templateParentProperty.parentModelNode().isRootNode())
continue;
ModelNode templateParentNode = templateParentProperty.parentModelNode();
const QString parentId = templateParentNode.id();
if (!idExistsInBothModels(parentId))
continue;
// Only get the position properties as the node should have a global
// position in the style sheet.
const QPoint oldGlobalPos = pointForModelNode(currentStyleNode);
ModelNode newStyleParent = m_styleView->modelNodeForId(parentId);
NodeListProperty newParentProperty = newStyleParent.defaultNodeListProperty();
newParentProperty.reparentHere(currentStyleNode);
// Get the parent position in global coordinates.
QPoint parentGlobalPos = parentPosition(currentStyleNode);
const QPoint newGlobalPos = oldGlobalPos - parentGlobalPos;
currentStyleNode.variantProperty("x").setValue(newGlobalPos.x());
currentStyleNode.variantProperty("y").setValue(newGlobalPos.y());
int templateParentIndex = templateParentProperty.isNodeListProperty()
? templateParentProperty.indexOf(templateNode) : -1;
int styleParentIndex = newParentProperty.indexOf(currentStyleNode);
if (templateParentIndex >= 0 && styleParentIndex != templateParentIndex)
newParentProperty.slide(styleParentIndex, templateParentIndex);
}
}
void StylesheetMerger::replaceNode(ModelNode &replacedNode, ModelNode &newNode)
{
NodeListProperty replacedNodeParent;
ModelNode parentModelNode = replacedNode.parentProperty().parentModelNode();
if (replacedNode.parentProperty().isNodeListProperty())
replacedNodeParent = replacedNode.parentProperty().toNodeListProperty();
bool isNodeProperty = false;
PropertyName reparentName;
for (NodeProperty prop : parentModelNode.nodeProperties()) {
if (prop.modelNode().id() == replacedNode.id()) {
isNodeProperty = true;
reparentName = prop.name();
}
}
ReparentInfo info;
info.parentIndex = replacedNodeParent.isValid() ? replacedNodeParent.indexOf(replacedNode) : -1;
info.templateId = replacedNode.id();
info.templateParentId = parentModelNode.id();
info.generatedId = newNode.id();
if (!isNodeProperty) {
replacedNodeParent.reparentHere(newNode);
replacedNode.destroy();
info.alreadyReparented = true;
} else {
parentModelNode.removeProperty(reparentName);
parentModelNode.nodeProperty(reparentName).reparentHere(newNode);
}
m_reparentInfoHash.insert(newNode.id(), info);
}
void StylesheetMerger::replaceRootNode(ModelNode& templateRootNode)
{
ModelMerger merger(m_templateView);
QString rootId = templateRootNode.id();
// If we shall replace the root node of the template with the style,
// we first replace the whole model.
ModelNode rootReplacer = m_styleView->modelNodeForId(rootId);
merger.replaceModel(rootReplacer);
// Then reset the id to the old root's one.
ModelNode newRoot = m_templateView->rootModelNode();
newRoot.setIdWithoutRefactoring(rootId);
}
// Move the newly created nodes to the correct position in the parent node
void StylesheetMerger::adjustNodeIndex(ModelNode &node)
{
if (!m_reparentInfoHash.contains(node.id()))
return;
ReparentInfo info = m_reparentInfoHash.value(node.id());
if (info.parentIndex < 0)
return;
if (!node.parentProperty().isNodeListProperty())
return;
NodeListProperty parentListProperty = node.parentProperty().toNodeListProperty();
int currentIndex = parentListProperty.indexOf(node);
if (currentIndex == info.parentIndex)
return;
parentListProperty.slide(currentIndex, info.parentIndex);
}
void StylesheetMerger::applyStyleProperties(ModelNode &templateNode, const ModelNode &styleNode)
{
QRegExp regEx("[a-z]", Qt::CaseInsensitive);
foreach (const VariantProperty &variantProperty, styleNode.variantProperties()) {
// check for existing bindings with that property name
if (templateNode.hasBindingProperty(variantProperty.name())) {
// if the binding does not contain any alpha letters (i.e. binds to a term rather than a property,
// replace it with the corresponding variant property.
if (!templateNode.bindingProperty(variantProperty.name()).expression().contains(regEx)) {
templateNode.removeProperty(variantProperty.name());
templateNode.variantProperty(variantProperty.name()).setValue(variantProperty.value());
}
} else {
templateNode.variantProperty(variantProperty.name()).setValue(variantProperty.value());
}
}
syncBindingProperties(templateNode, styleNode);
syncNodeProperties(templateNode, styleNode, true);
syncNodeListProperties(templateNode, styleNode, true);
}
static void removePropertyIfExists(ModelNode node, const PropertyName &propertyName)
{
if (node.hasProperty(propertyName))
node.removeProperty(propertyName);
}
void StylesheetMerger::merge()
{
ModelNode templateRootNode = m_templateView->rootModelNode();
ModelNode styleRootNode = m_styleView->rootModelNode();
// first, build up the hierarchy in the style sheet as we have it in the template
preprocessStyleSheet();
// build a hash of generated replacement ids
setupIdRenamingHash();
//in case we are replacing the root node, just do that and exit
if (m_styleView->hasId(templateRootNode.id())) {
replaceRootNode(templateRootNode);
return;
}
QQueue<ModelNode> replacementNodes;
QList<ModelNode> directRootSubNodes = styleRootNode.directSubModelNodes();
if (directRootSubNodes.length() == 0 && m_templateView->hasId(styleRootNode.id())) {
// if the style sheet has only one node, just replace that one
replacementNodes.enqueue(styleRootNode);
}
// otherwise, the nodes to replace are the direct sub nodes of the style sheet's root
for (ModelNode subNode : styleRootNode.allSubModelNodes()) {
if (m_templateView->hasId(subNode.id())) {
replacementNodes.enqueue(subNode);
}
}
for (ModelNode currentNode : replacementNodes) {
bool hasPos = false;
// create the replacement nodes for the styled nodes
{
RewriterTransaction transaction(m_templateView, "create-replacement-node");
ModelNode replacedNode = m_templateView->modelNodeForId(currentNode.id());
hasPos = replacedNode.hasProperty("x") || replacedNode.hasProperty("y");
ModelNode replacementNode = createReplacementNode(currentNode, replacedNode);
replaceNode(replacedNode, replacementNode);
transaction.commit();
}
// sync the properties from the stylesheet
{
RewriterTransaction transaction(m_templateView, "sync-style-node-properties");
ModelNode templateNode = m_templateView->modelNodeForId(currentNode.id());
applyStyleProperties(templateNode, currentNode);
adjustNodeIndex(templateNode);
/* This we want to do if the parent node in the style is not in the template */
if (!currentNode.hasParentProperty() ||
!m_templateView->modelNodeForId(currentNode.parentProperty().parentModelNode().id()).isValid()) {
if (!hasPos) { //If template had postition retain it
removePropertyIfExists(templateNode, "x");
removePropertyIfExists(templateNode, "y");
}
if (templateNode.hasProperty("anchors.fill")) {
/* Unfortuntly there are cases were width and height have to be defined - see Button
* Most likely we need options for this */
//removePropertyIfExists(templateNode, "width");
//removePropertyIfExists(templateNode, "height");
}
}
}
}
}
}

View File

@@ -297,6 +297,7 @@ Project {
"include/rewriterview.h",
"include/rewritingexception.h",
"include/signalhandlerproperty.h",
"include/stylesheetmerger.h",
"include/viewmanager.h",
"include/subcomponentmanager.h",
"include/textmodifier.h",
@@ -378,6 +379,7 @@ Project {
"model/documentmessage.cpp",
"model/rewriterview.cpp",
"model/signalhandlerproperty.cpp",
"model/stylesheetmerger.cpp",
"model/textmodifier.cpp",
"model/texttomodelmerger.cpp",
"model/texttomodelmerger.h",

View File

@@ -61,8 +61,10 @@ SOURCES += \
../testview.cpp \
testrewriterview.cpp \
tst_testcore.cpp
HEADERS += \
../testview.h \
testrewriterview.h \
tst_testcore.h
RESOURCES += ../data/testfiles.qrc

View File

@@ -28,6 +28,7 @@
#include <QScopedPointer>
#include <QLatin1String>
#include <QGraphicsObject>
#include <QQueue>
#include <QTest>
#include <QVariant>
@@ -42,6 +43,7 @@
#include <nodeinstanceview.h>
#include <nodeinstance.h>
#include <subcomponentmanager.h>
#include <stylesheetmerger.h>
#include <QDebug>
#include "../testview.h"
@@ -3704,6 +3706,131 @@ void tst_TestCore::testCopyModelRewriter1()
QCOMPARE(textEdit1.toPlainText(), expected);
}
static void adjustPreservedProperties(const ModelNode& replacedNode, ModelNode& newNode) {
QSet<PropertyName> preservedProperties;
preservedProperties.insert("x");
preservedProperties.insert("y");
preservedProperties.insert("width");
preservedProperties.insert("height");
preservedProperties.insert("anchors");
// preservedProperties.insert("text ");
for (VariantProperty originalProperty : replacedNode.variantProperties()) {
VariantProperty prop;
if (preservedProperties.contains(originalProperty.name())) {
newNode.variantProperty(originalProperty.name()).setValue(originalProperty.value());
}
}
}
static QString readQmlFromFile(const QString& fileName)
{
QFile qmlFile(fileName);
qmlFile.open(QIODevice::ReadOnly | QIODevice::Text);
QString qmlFileContents = QString::fromUtf8(qmlFile.readAll());
return qmlFileContents;
}
void tst_TestCore::testMergeModelRewriter1_data()
{
QTest::addColumn<QString>("qmlTemplateString");
QTest::addColumn<QString>("qmlStyleString");
QTest::addColumn<QString>("qmlExpectedString");
QString simpleTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SimpleTemplate.qml");
QString simpleStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SimpleStyle.qml");
QString simpleExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SimpleExpected.qml");
QString complexTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ComplexTemplate.qml");
QString complexStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ComplexStyle.qml");
QString complexExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ComplexExpected.qml");
QString emptyTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/EmptyTemplate.qml");
QString emptyStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/EmptyStyle.qml");
QString emptyExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/EmptyExpected.qml");
QString rootReplacementTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/RootReplacementTemplate.qml");
QString rootReplacementStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/RootReplacementStyle.qml");
QString rootReplacementExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/RootReplacementExpected.qml");
QString switchTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SwitchTemplate.qml");
QString switchStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SwitchStyle.qml");
QString switchExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SwitchExpected.qml");
QString sliderTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SliderTemplate.qml");
QString sliderStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SliderStyle.qml");
QString sliderExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/SliderExpected.qml");
QString listViewTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ListViewTemplate.qml");
QString listViewStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ListViewStyle.qml");
QString listViewExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ListViewExpected.qml");
QString buttonTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ButtonTemplate.qml");
QString buttonInlineStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ButtonStyleInline.qml");
QString buttonInlineExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ButtonInlineExpected.qml");
QString buttonAbsoluteTemplateQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ButtonAbsoluteTemplate.qml");
QString buttonOutlineStyleQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ButtonStyleOutline.qml");
QString buttonOutlineExpectedQmlContents = readQmlFromFile(QString(TESTSRCDIR) + "/../data/merging/ButtonOutlineExpected.qml");
QTest::newRow("Simple style replacement") << simpleTemplateQmlContents << simpleStyleQmlContents << simpleExpectedQmlContents;
//QTest::newRow("Complex style replacement") << complexTemplateQmlContents << complexStyleQmlContents << complexExpectedQmlContents;
QTest::newRow("Empty stylesheet") << emptyTemplateQmlContents << emptyStyleQmlContents << emptyExpectedQmlContents;
QTest::newRow("Root node replacement") << rootReplacementTemplateQmlContents << rootReplacementStyleQmlContents << rootReplacementExpectedQmlContents;
QTest::newRow("Switch styling") << switchTemplateQmlContents << switchStyleQmlContents << switchExpectedQmlContents;
QTest::newRow("Slider styling") << sliderTemplateQmlContents << sliderStyleQmlContents << sliderExpectedQmlContents;
QTest::newRow("List View styling") << listViewTemplateQmlContents << listViewStyleQmlContents << listViewExpectedQmlContents;
QTest::newRow("Button Inline styling") << buttonTemplateQmlContents << buttonInlineStyleQmlContents << buttonInlineExpectedQmlContents;
QTest::newRow("Button Outline styling") << buttonAbsoluteTemplateQmlContents << buttonOutlineStyleQmlContents << buttonOutlineExpectedQmlContents;
}
void tst_TestCore::testMergeModelRewriter1()
{
QFETCH(QString, qmlTemplateString);
QFETCH(QString, qmlStyleString);
QFETCH(QString, qmlExpectedString);
QPlainTextEdit textEdit1;
textEdit1.setPlainText(qmlTemplateString);
NotIndentingTextEditModifier textModifier1(&textEdit1);
QScopedPointer<Model> templateModel(Model::create("QtQuick.Item", 2, 1));
QVERIFY(templateModel.data());
QScopedPointer<TestView> templateView(new TestView(templateModel.data()));
templateModel->attachView(templateView.data());
// read in 1
QScopedPointer<TestRewriterView> templateRewriterView(new TestRewriterView());
templateRewriterView->setTextModifier(&textModifier1);
templateModel->attachView(templateRewriterView.data());
ModelNode templateRootNode = templateView->rootModelNode();
QVERIFY(templateRootNode.isValid());
QPlainTextEdit textEdit2;
textEdit2.setPlainText(qmlStyleString);
NotIndentingTextEditModifier textModifier2(&textEdit2);
QScopedPointer<Model> styleModel(Model::create("QtQuick.Item", 2, 1));
QVERIFY(styleModel.data());
QScopedPointer<TestView> styleView(new TestView(styleModel.data()));
styleModel->attachView(styleView.data());
// read in 2
QScopedPointer<TestRewriterView> styleRewriterView(new TestRewriterView());
styleRewriterView->setTextModifier(&textModifier2);
styleModel->attachView(styleRewriterView.data());
StylesheetMerger merger(templateView.data(), styleView.data());
merger.merge();
QCOMPARE(textEdit1.toPlainText().trimmed(), qmlExpectedString.trimmed());
}
void tst_TestCore::testCopyModelRewriter2()
{
const QLatin1String qmlString1("\n"
@@ -3756,37 +3883,35 @@ void tst_TestCore::testCopyModelRewriter2()
textEdit1.setPlainText(qmlString1);
NotIndentingTextEditModifier textModifier1(&textEdit1);
QScopedPointer<Model> model1(Model::create("QtQuick.Item", 2, 1));
QVERIFY(model1.data());
QScopedPointer<Model> templateModel(Model::create("QtQuick.Item", 2, 1));
QVERIFY(templateModel.data());
QScopedPointer<TestView> view1(new TestView(model1.data()));
model1->attachView(view1.data());
QScopedPointer<TestView> view1(new TestView(templateModel.data()));
templateModel->attachView(view1.data());
// read in 1
QScopedPointer<TestRewriterView> testRewriterView1(new TestRewriterView());
testRewriterView1->setTextModifier(&textModifier1);
model1->attachView(testRewriterView1.data());
templateModel->attachView(testRewriterView1.data());
ModelNode rootNode1 = view1->rootModelNode();
QVERIFY(rootNode1.isValid());
QCOMPARE(rootNode1.type(), QmlDesigner::TypeName("QtQuick.Rectangle"));
// read in 2
// read in 2
QPlainTextEdit textEdit2;
textEdit2.setPlainText(qmlString2);
NotIndentingTextEditModifier textModifier2(&textEdit2);
QScopedPointer<Model> model2(Model::create("QtQuick.Item", 2, 1));
QVERIFY(model2.data());
QScopedPointer<Model> styleModel(Model::create("QtQuick.Item", 2, 1));
QVERIFY(styleModel.data());
QScopedPointer<TestView> view2(new TestView(model2.data()));
model2->attachView(view2.data());
QScopedPointer<TestView> view2(new TestView(styleModel.data()));
styleModel->attachView(view2.data());
QScopedPointer<TestRewriterView> testRewriterView2(new TestRewriterView());
testRewriterView2->setTextModifier(&textModifier2);
model2->attachView(testRewriterView2.data());
QScopedPointer<TestRewriterView> styleRewriterView(new TestRewriterView());
styleRewriterView->setTextModifier(&textModifier2);
styleModel->attachView(styleRewriterView.data());
ModelNode rootNode2 = view2->rootModelNode();
QVERIFY(rootNode2.isValid());

View File

@@ -184,6 +184,8 @@ private slots:
void testRewriterTransactionRewriter();
void testCopyModelRewriter1();
void testCopyModelRewriter2();
void testMergeModelRewriter1_data();
void testMergeModelRewriter1();
void testSubComponentManager();
void testAnchorsAndRewriting();
void testAnchorsAndRewritingCenter();

View File

@@ -0,0 +1,104 @@
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
T.Button {
id: control
implicitWidth: Math.max(
background ? background.implicitWidth : 0,
contentItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(
background ? background.implicitHeight : 0,
contentItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 4
rightPadding: 4
text: "My Button"
background: Item {
implicitWidth: buttonNormal.width
implicitHeight: buttonNormal.height
opacity: enabled ? 1 : 0.3
Rectangle {
id: buttonNormal
color: "#d4d4d4"
width: 100 //Bit of black magic to define the default size
height: 40
border.color: "gray"
border.width: 1
radius: 2
anchors.fill: parent //binding has to be preserved
Text {
id: normalText
x: 26
y: 14 //id only required to preserve binding
text: control.text //binding has to be preserved
//anchors.fill: parent
color: "gray"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
Rectangle {
id: buttonPressed
color: "#d4d4d4"
width: 100 //Bit of black magic to define the default size
height: 40
border.color: "gray"
border.width: 1
radius: 2
anchors.fill: parent //binding has to be preserved
Text {
x: 26
y: 14
id: pressedText //id only required to preserve binding
text: control.text //binding has to be preserved
//anchors.fill: parent
color: "black"
horizontalAlignment: Text.AlignHCenter // should not be preserved
verticalAlignment: Text.AlignVCenter // should not be preserved
elide: Text.ElideRight // should not be preserved
}
}
}
contentItem: Item {}
states: [
State {
name: "normal"
when: !control.down
PropertyChanges {
target: buttonPressed
visible: false
}
PropertyChanges {
target: buttonNormal
visible: true
}
},
State {
name: "down"
when: control.down
PropertyChanges {
target: buttonPressed
visible: true
}
PropertyChanges {
target: buttonNormal
visible: false
}
}
]
}

View File

@@ -0,0 +1,93 @@
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
T.Button {
id: control
implicitWidth: Math.max(
background ? background.implicitWidth : 0,
contentItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(
background ? background.implicitHeight : 0,
contentItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 4
rightPadding: 4
text: "My Button"
background: Item {
implicitWidth: buttonNormal.width
implicitHeight: buttonNormal.height
opacity: enabled ? 1 : 0.3
Rectangle {
id: buttonNormal
width: 100
height: 60
color: "#d4d4d4"
radius: 2
border.color: "#808080"
border.width: 1
anchors.fill: parent
Text {
id: normalText
color: "#808080"
text: control.text
elide: Text.ElideRight
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Rectangle {
id: buttonPressed
width: 100
height: 60
color: "#69b5ec"
radius: 2
border.color: "#808080"
border.width: 1
anchors.fill: parent
Text {
id: pressedText
color: "#000000"
text: control.text
elide: Text.ElideRight
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
contentItem: Item {}
states: [
State {
name: "normal"
when: !control.down
PropertyChanges {
target: buttonPressed
visible: false
}
PropertyChanges {
target: buttonNormal
visible: true
}
},
State {
name: "down"
when: control.down
PropertyChanges {
target: buttonPressed
visible: true
}
PropertyChanges {
target: buttonNormal
visible: false
}
}
]
}

View File

@@ -0,0 +1,97 @@
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
T.Button {
id: control
implicitWidth: Math.max(
background ? background.implicitWidth : 0,
contentItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(
background ? background.implicitHeight : 0,
contentItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 4
rightPadding: 4
text: "My Button"
background: Item {
implicitWidth: buttonNormal.width
implicitHeight: buttonNormal.height
opacity: enabled ? 1 : 0.3
Rectangle {
id: buttonNormal
width: 100
height: 60
color: "#d4d4d4"
radius: 2
border.color: "#808080"
border.width: 1
anchors.fill: parent
Text {
id: normalText
x: 20
y: 20
color: "#808080"
text: control.text
elide: Text.ElideRight
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
Rectangle {
id: buttonPressed
width: 100
height: 60
color: "#69b5ec"
radius: 2
border.color: "#808080"
border.width: 1
anchors.fill: parent
Text {
id: pressedText
x: 20
y: 40
color: "#000000"
text: control.text
elide: Text.ElideRight
anchors.fill: parent
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
}
contentItem: Item {}
states: [
State {
name: "normal"
when: !control.down
PropertyChanges {
target: buttonPressed
visible: false
}
PropertyChanges {
target: buttonNormal
visible: true
}
},
State {
name: "down"
when: control.down
PropertyChanges {
target: buttonPressed
visible: true
}
PropertyChanges {
target: buttonNormal
visible: false
}
}
]
}

View File

@@ -0,0 +1,79 @@
import QtQuick 2.12
Rectangle {
id: artboard
width: 640
height: 480
color: "#ee4040"
Rectangle {
id: buttonNormal
x: 286
y: 62
color: "#d4d4d4"
width: 100 //Bit of black magic to define the default size
height: 60
border.color: "gray"
border.width: 1
radius: 2
Text {
id: normalText //id only required to preserve binding
//binding has to be preserved
anchors.fill: parent //binding has to be preserved
color: "gray"
text: "Normal"
horizontalAlignment: Text.AlignHCenter
//luckily enums are interpreted as variant properties and not bindings
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
Rectangle {
id: buttonPressed
x: 123
y: 62
color: "#69b5ec"
width: 100 //Bit of black magic to define the default size
height: 60
border.color: "gray"
border.width: 1
radius: 2
Text {
id: pressedText //id only required to preserve binding
//binding has to be preserved
anchors.fill: parent
color: "black"
text: "pressed"
horizontalAlignment: Text.AlignHCenter // should not be preserved -
//luckily enums are interpreted as variant properties and not bindings
verticalAlignment: Text.AlignVCenter // should not be preserved
elide: Text.ElideRight // should not be preserved
}
}
Text {
id: element
x: 1
y: 362
color: "#eaeaea"
text: qsTrId("Some stuff for reference that is thrown away")
font.pixelSize: 32
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#000000"}
}
##^##*/

View File

@@ -0,0 +1,83 @@
import QtQuick 2.12
Rectangle {
id: artboard
width: 640
height: 480
color: "#ee4040"
Rectangle {
id: buttonNormal
x: 286
y: 62
color: "#d4d4d4"
width: 100 //Bit of black magic to define the default size
height: 60
border.color: "gray"
border.width: 1
radius: 2
}
Text {
x: 319
y: 86
id: normalText //id only required to preserve binding
//binding has to be preserved
color: "gray"
text: "Normal"
horizontalAlignment: Text.AlignHCenter
//luckily enums are interpreted as variant properties and not bindings
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
Rectangle {
id: buttonPressed
x: 123
y: 62
color: "#69b5ec"
width: 100 //Bit of black magic to define the default size
height: 60
border.color: "gray"
border.width: 1
radius: 2
}
Text {
x: 154
y: 86
id: pressedText //id only required to preserve binding
//binding has to be preserved
//anchors.fill: parent
color: "black"
text: "pressed"
horizontalAlignment: Text.AlignHCenter // should not be preserved -
//luckily enums are interpreted as variant properties and not bindings
verticalAlignment: Text.AlignVCenter // should not be preserved
elide: Text.ElideRight // should not be preserved
}
Text {
id: element
x: 1
y: 362
color: "#eaeaea"
text: qsTrId("Some stuff for reference that is thrown away")
font.pixelSize: 32
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#000000"}
}
##^##*/

View File

@@ -0,0 +1,100 @@
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
T.Button {
id: control
implicitWidth: Math.max(
background ? background.implicitWidth : 0,
contentItem.implicitWidth + leftPadding + rightPadding)
implicitHeight: Math.max(
background ? background.implicitHeight : 0,
contentItem.implicitHeight + topPadding + bottomPadding)
leftPadding: 4
rightPadding: 4
text: "My Button"
background: Item {
implicitWidth: buttonNormal.width
implicitHeight: buttonNormal.height
opacity: enabled ? 1 : 0.3
Rectangle {
id: buttonNormal
color: "#d4d4d4"
width: 100 //Bit of black magic to define the default size
height: 40
border.color: "gray"
border.width: 1
radius: 2
anchors.fill: parent //binding has to be preserved
Text {
id: normalText //id only required to preserve binding
text: control.text //binding has to be preserved
anchors.fill: parent
color: "gray"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
}
Rectangle {
id: buttonPressed
color: "#d4d4d4"
width: 100 //Bit of black magic to define the default size
height: 40
border.color: "gray"
border.width: 1
radius: 2
anchors.fill: parent //binding has to be preserved
Text {
id: pressedText //id only required to preserve binding
text: control.text //binding has to be preserved
anchors.fill: parent
color: "black"
horizontalAlignment: Text.AlignHCenter // should not be preserved
verticalAlignment: Text.AlignVCenter // should not be preserved
elide: Text.ElideRight // should not be preserved
}
}
}
contentItem: Item {}
states: [
State {
name: "normal"
when: !control.down
PropertyChanges {
target: buttonPressed
visible: false
}
PropertyChanges {
target: buttonNormal
visible: true
}
},
State {
name: "down"
when: control.down
PropertyChanges {
target: buttonPressed
visible: true
}
PropertyChanges {
target: buttonNormal
visible: false
}
}
]
}

View File

@@ -0,0 +1,62 @@
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Rectangle {
id: rectangle0
x: 10
y: 10
width: 200
height: 150
Image {
id: rectangle1
x: 10
y: 10
width: 100
height: 150
source: "qt/icon.png"
}
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
Image {
id: rectangle4
x: 10
y: 20
width: 200
height: 50
Rectangle {
id: rectangle5
x: 10
y: 20
width: 200
height: 50
}
source: "qt/realcool.jpg"
}
}
}

View File

@@ -0,0 +1,45 @@
import QtQuick 2.1
Item {
Rectangle {
id: rectangle0
Image {
id: rectangle1
x: 10
y: 10
height: 150
width: 100
source: "qt/icon.png"
}
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
}
Rectangle {
id: rectangle5
x: 10
y: 20
width: 200
height: 50
}
Image {
id: rectangle4
x: 10;
y: 10;
height: 150
width: 100
source: "qt/realcool.jpg"
}
}

View File

@@ -0,0 +1,49 @@
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Rectangle {
id: rectangle0
x: 10;
y: 10;
height: 150
width: 200
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "black"
}
GradientStop {
position: 1
color: "white"
}
}
Rectangle {
id: rectangle4
x: 10
y: 20
width: 200
height: 50
Rectangle {
id: rectangle5
x: 10
y: 20
width: 200
height: 50
}
}
}
}

View File

@@ -0,0 +1,43 @@
// Test that an empty style sheet will leave the original template untouched.
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Rectangle {
id: rectangle0
x: 10;
y: 10;
height: 150
width: 200
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
Rectangle {
id: rectangle4
x: 10
y: 20
width: 200
height: 50
}
}
}

View File

@@ -0,0 +1 @@
import QtQuick 2.1

View File

@@ -0,0 +1,43 @@
// Test that an empty style sheet will leave the original template untouched.
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Rectangle {
id: rectangle0
x: 10;
y: 10;
height: 150
width: 200
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
Rectangle {
id: rectangle4
x: 10
y: 20
width: 200
height: 50
}
}
}

View File

@@ -0,0 +1,135 @@
import QtQuick 2.10
ListView {
id: view
width: listViewBackground.width
height: listViewBackground.height
highlight: Rectangle {
id: listViewHighLight
width: view.width
height: 120
color: "#343434"
radius: 4
border.color: "#0d52a4"
border.width: 8
}
highlightMoveDuration: 0
children: [
Item {
z: -1
anchors.fill: parent
Rectangle {
id: listViewBackground
width: 420
height: 420
color: "#69b5ec"
anchors.fill: parent
}
}
]
model: ListModel {
ListElement {
name: "Music"
}
ListElement {
name: "Movies"
}
ListElement {
name: "Camera"
}
ListElement {
name: "Map"
}
ListElement {
name: "Calendar"
}
ListElement {
name: "Messaging"
}
ListElement {
name: "Todo List"
}
ListElement {
name: "Contacts"
}
ListElement {
name: "Settings"
}
}
delegate: Item {
id: delegate
width: ListView.view.width
height: delegateNormal.height
Rectangle {
id: delegateNormal
width: 420
height: 120
visible: true
color: "#bdbdbd"
radius: 4
anchors.fill: parent
anchors.margins: 12
Text {
id: labelNormal
color: "#343434"
text: name
anchors.top: parent.top
anchors.margins: 24
anchors.horizontalCenter: parent.horizontalCenter
}
}
Rectangle {
id: delegateHighlighted
width: 420
height: 120
visible: false
color: "#8125eb29"
radius: 4
anchors.fill: parent
anchors.margins: 12
Text {
id: labelHighlighted
color: "#efefef"
text: name
anchors.top: parent.top
anchors.margins: 52
anchors.horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
anchors.fill: parent
onClicked: delegate.ListView.view.currentIndex = index
}
states: [
State {
name: "Highlighted"
when: delegate.ListView.isCurrentItem
PropertyChanges {
target: delegateHighlighted
visible: true
}
PropertyChanges {
target: delegateNormal
visible: false
}
}
]
}
}

View File

@@ -0,0 +1,82 @@
import QtQuick 2.12
Rectangle {
id: artboard
width: 800
height: 600
color: "#ee4040"
Rectangle {
id: listViewBackground
x: 19
y: 34
width: 420
height: 420
color: "#69b5ec"
}
Rectangle {
id: delegateNormal
x: 19
y: 51
color: "#bdbdbd"
height: 120
width: 420
radius: 4
Text {
id: labelNormal //id required for binding preservation
color: "#343434"
text: "some text"
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: 24
}
}
Rectangle {
id: delegateHighlighted
x: 19
y: 177
color: "#8125eb29"
height: 120
width: 420
radius: 4
Text {
id: labelHighlighted //id required for binding preservation
color: "#efefef"
text: "some text"
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
anchors.margins: 52
}
}
Rectangle {
id: listViewHighLight
x: 19
y: 323
width: 420
height: 120
color: "#343434"
radius: 4
border.color: "#0d52a4"
border.width: 8
}
}

View File

@@ -0,0 +1,133 @@
import QtQuick 2.10
ListView {
id: view
width: listViewBackground.width
height: listViewBackground.height
highlightMoveDuration: 0
children: [
Item {
z: -1
anchors.fill: parent
Rectangle {
id: listViewBackground
width: 420
height: 420
color: "#d80e0e"
anchors.fill: parent // hsa to be preserved
}
}
]
model: ListModel {
ListElement {
name: "Music"
}
ListElement {
name: "Movies"
}
ListElement {
name: "Camera"
}
ListElement {
name: "Map"
}
ListElement {
name: "Calendar"
}
ListElement {
name: "Messaging"
}
ListElement {
name: "Todo List"
}
ListElement {
name: "Contacts"
}
ListElement {
name: "Settings"
}
}
highlight: Rectangle {
id: listViewHighLight
width: view.width // has to be preserved
height: 120
color: "#343434"
radius: 4
border.color: "#0d52a4"
border.width: 8
}
delegate: Item {
id: delegate
width: ListView.view.width
height: delegateNormal.height
Rectangle {
id: delegateNormal
color: "#bdbdbd"
anchors.fill: parent
height: 140
anchors.margins: 12
visible: true
radius: 4
Text {
id: labelNormal //id required for binding preservation
color: "#343434"
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text: name
anchors.margins: 24
}
}
Rectangle {
id: delegateHighlighted
color: "#36bdbdbd"
anchors.fill: parent
anchors.margins: 12
visible: false
radius: 4
Text {
id: labelHighlighted //id required for binding preservation
color: "#efefef"
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
text: name
anchors.margins: 32
}
}
MouseArea {
anchors.fill: parent
onClicked: delegate.ListView.view.currentIndex = index
}
states: [
State {
name: "Highlighted"
when: delegate.ListView.isCurrentItem
PropertyChanges {
target: delegateHighlighted
visible: true
}
PropertyChanges {
target: delegateNormal
visible: false
}
}
]
}
}

View File

@@ -0,0 +1,26 @@
import QtQuick 2.1
Item {
id: root
Rectangle {
id: rectangle0
Image {
id: rectangle1
x: 10
y: 10
width: 100
height: 150
source: "qt/icon.png"
}
}
Image {
id: rectangle4
x: 10
y: 10
width: 100
height: 150
source: "qt/realcool.jpg"
}
}

View File

@@ -0,0 +1,24 @@
import QtQuick 2.1
Item {
id: root
Rectangle {
id: rectangle0
Image {
id: rectangle1
x: 10
y: 10
height: 150
width: 100
source: "qt/icon.png"
}
}
Image {
id: rectangle4
x: 10;
y: 10;
height: 150
width: 100
source: "qt/realcool.jpg"
}
}

View File

@@ -0,0 +1,35 @@
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Rectangle {
id: rectangle1
x: 10;
y: 10;
height: 150
width: 200
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
}
}

View File

@@ -0,0 +1,38 @@
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Image {
id: rectangle1
x: 10
y: 10
width: 100
height: 150
source: "qt/icon.png"
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
}
}

View File

@@ -0,0 +1,10 @@
import QtQuick 2.1
Image {
id: rectangle1
x: 10;
y: 10;
height: 150
width: 100
source: "qt/icon.png"
}

View File

@@ -0,0 +1,38 @@
import QtQuick 2.1
Rectangle {
id: root
x: 10;
y: 10;
Rectangle {
id: rectangle1
x: 10;
y: 10;
height: 150
width: 200
}
Rectangle {
id: rectangle2
x: 100;
y: 100;
anchors.fill: root
}
Rectangle {
id: rectangle3
x: 140;
y: 180;
gradient: Gradient {
GradientStop {
position: 0
color: "white"
}
GradientStop {
position: 1
color: "black"
}
}
}
}

View File

@@ -0,0 +1,68 @@
import QtQuick 2.6
import QtQuick.Controls 2.0
Slider {
id: control
value: 0.5
background: Item {
x: control.leftPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: sliderGroove.width
implicitHeight: sliderGroove.height
height: implicitHeight
width: control.availableWidth
Rectangle {
id: sliderGroove
width: 200
height: 6
color: "#bdbebf"
radius: 2
anchors.fill: parent
}
Item {
width: control.visualPosition * sliderGroove.width // should be preserved
height: sliderGrooveLeft.height
clip: true
Rectangle {
id: sliderGrooveLeft
width: 200
height: 6
color: "#21be2b"
radius: 2
}
}
}
handle: Item {
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: handleNormal.width
implicitHeight: handleNormal.height
Rectangle {
id: handleNormal
width: 32
height: 32
visible: !control.pressed
color: "#f6f6f6"
radius: 13
border.color: "#bdbebf"
}
Rectangle {
id: handlePressed
width: 32
height: 32
visible: control.pressed
color: "#221bdb"
radius: 13
border.color: "#bdbebf"
}
}
}

View File

@@ -0,0 +1,65 @@
import QtQuick 2.12
Item {
id: artboard
width: 640
height: 480
Rectangle {
id: sliderGroove
x: 78
y: 127
width: 200
height: 6
color: "#bdbebf"
}
Rectangle {
id: sliderGrooveLeft
x: 78
y: 165
width: 200
height: 6
color: "#21be2b"
radius: 2
}
Rectangle {
id: handleNormal
x: 130
y: 74
width: 32
height: 32
radius: 13
color: "#f6f6f6"
border.color: "#bdbebf"
}
Rectangle {
id: handlePressed
x: 78
y: 74
width: 32
height: 32
radius: 13
color: "#221bdb"
border.color: "#bdbebf"
}
Text {
id: element
x: 8
y: 320
color: "#eaeaea"
text: qsTrId("Some stuff for reference that is thrown away")
font.pixelSize: 32
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#000000"}
}
##^##*/

View File

@@ -0,0 +1,66 @@
import QtQuick 2.6
import QtQuick.Controls 2.0
Slider {
id: control
value: 0.5
background: Item {
x: control.leftPadding
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: sliderGroove.width
implicitHeight: sliderGroove.height
height: implicitHeight
width: control.availableWidth
Rectangle {
id: sliderGroove
width: 200
height: 4
anchors.fill: parent // has to be preserved
radius: 2
color: "#bdbebf"
}
Item {
width: control.visualPosition * sliderGroove.width // should be preserved
height: sliderGrooveLeft.height
clip: true
Rectangle {
id: sliderGrooveLeft
width: 200
height: 4
color: "#21be2b"
radius: 2
}
}
}
handle: Item {
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
y: control.topPadding + control.availableHeight / 2 - height / 2
implicitWidth: handleNormal.width
implicitHeight: handleNormal.height
Rectangle {
id: handleNormal
width: 26
height: 26
radius: 13
color: "#f6f6f6"
visible: !control.pressed //has to be preserved
border.color: "#bdbebf"
}
Rectangle {
id: handlePressed
width: 26
height: 26
radius: 13
visible: control.pressed //has to be preserved
color: "#f0f0f0"
border.color: "#bdbebf"
}
}
}

View File

@@ -0,0 +1,111 @@
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
import TemplateMerging 1.0
T.Switch {
id: control
implicitWidth: background.implicitWidth
implicitHeight: background.implicitHeight
text: "test"
indicator: Rectangle {
id: switchIndicator
x: control.leftPadding
y: 34
width: 64
height: 44
color: "#e9e9e9"
radius: 16
border.color: "#dddddd"
anchors.verticalCenter: parent.verticalCenter
Rectangle {
id: switchHandle
width: 31
height: 44
color: "#e9e9e9"
radius: 16
border.color: "#808080"
}
}
background: Item {
implicitWidth: switchBackground.width
implicitHeight: switchBackground.height
Rectangle {
id: switchBackground
width: 144
height: 52
color: "#c2c2c2"
border.color: "#808080"
anchors.fill: parent
Text {
text: "background"
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
}
}
}
leftPadding: 4
contentItem: Item { //designer want to edit the label as part of background
}
states: [
State {
name: "off"
when: !control.checked && !control.down
},
State {
name: "on"
when: control.checked && !control.down
PropertyChanges {
target: switchIndicator
color: "#1713de"
border.color: "#1713de"
}
PropertyChanges {
target: switchHandle
x: parent.width - width
}
},
State {
name: "off_down"
when: !control.checked && control.down
PropertyChanges {
target: switchIndicator
color: "#e9e9e9"
}
PropertyChanges {
target: switchHandle
color: "#d2d2d2"
border.color: "#d2d2d2"
}
},
State {
name: "on_down"
when: control.checked && control.down
PropertyChanges {
target: switchHandle
x: parent.width - width
color: "#e9e9e9"
}
PropertyChanges {
target: switchIndicator
color: "#030381"
border.color: "#030381"
}
}
]
}

View File

@@ -0,0 +1,66 @@
import QtQuick 2.12
Item {
width: 640
height: 480
Rectangle {
id: switchIndicator
x: 219
y: 34
width: 64
height: 44
color: "#e9e9e9"
radius: 16
border.color: "#dddddd"
Rectangle {
id: switchHandle //id is required for states
width: 31
height: 44
radius: 16
color: "#e9e9e9"
border.color: "#808080"
}
}
Rectangle {
id: switchBackground
x: 346
y: 27
width: 144
height: 52
color: "#c2c2c2"
border.color: "#808080"
Text {
id: switchBackgroundText
text: "background"
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: 12
}
}
Text {
id: element
x: 1
y: 362
color: "#eaeaea"
text: qsTrId("Some stuff for reference that is thrown away")
font.pixelSize: 32
}
Rectangle { //This is ignored when merging
id: weirdStuff02
x: 8
y: 87
width: 624
height: 200
color: "#ffffff"
}
}

View File

@@ -0,0 +1,120 @@
import QtQuick 2.10
import QtQuick.Templates 2.1 as T
import TemplateMerging 1.0
T.Switch {
id: control
implicitWidth: background.implicitWidth
implicitHeight: background.implicitHeight
text: "test"
background: Item {
implicitWidth: switchBackground.width
implicitHeight: switchBackground.height
Rectangle {
id: switchBackground
color: "#ef1d1d"
border.color: "#808080"
width: 12 * 6.0
height: 12 * 3.8
anchors.fill: parent // has to be preserved
Text {
id: switchBackgroundText
anchors.right: parent.right // does have to be preserved -- how to handle this? - anchors preference from style if not "root"?
anchors.verticalCenter: parent.verticalCenter // does have to be preserved -- how to handle this? - anchors preference from style if not "root"?
text: control.text // has to be preserved
anchors.rightMargin: 12 * 5
}
Rectangle {
id: nonSenseRectangle
width: 5 * 12.0
height: 6 * 49.0
color: "#ff0000"
}
}
}
leftPadding: 4
contentItem: Item { //designer want to edit the label as part of background
}
indicator: Rectangle {
id: switchIndicator
width: 58
height: 31
x: control.leftPadding // has to be preserved
color: "#e9e9e9"
anchors.verticalCenter: parent.verticalCenter // has to be preserved
radius: 16
border.color: "#dddddd"
Rectangle {
id: switchHandle //id is required for states
width: 31
height: 31
radius: 16
color: "#e9e9e9"
border.color: "#808080"
}
}
states: [
State {
name: "off"
when: !control.checked && !control.down
},
State {
name: "on"
when: control.checked && !control.down
PropertyChanges {
target: switchIndicator
color: "#1713de"
border.color: "#1713de"
}
PropertyChanges {
target: switchHandle
x: parent.width - width
}
},
State {
name: "off_down"
when: !control.checked && control.down
PropertyChanges {
target: switchIndicator
color: "#e9e9e9"
}
PropertyChanges {
target: switchHandle
color: "#d2d2d2"
border.color: "#d2d2d2"
}
},
State {
name: "on_down"
when: control.checked && control.down
PropertyChanges {
target: switchHandle
x: parent.width - width
color: "#e9e9e9"
}
PropertyChanges {
target: switchIndicator
color: "#030381"
border.color: "#030381"
}
}
]
}