DesignSystem: R/W design system module from the library

Task-number: QDS-13713
Change-Id: Ibc13272ac1e0b26352b84e74216b2fbfcd69ff4e
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Vikas Pachdha
2024-09-19 15:35:51 +02:00
parent 51000a531f
commit 5cbcd454b2
7 changed files with 513 additions and 69 deletions

View File

@@ -1,9 +1,10 @@
add_qtc_library(DesignSystem STATIC
PUBLIC_INCLUDES ${CMAKE_CURRENT_LIST_DIR}
DEPENDS
Qt::Core Qt::Widgets QmlDesignerCore
Qt::Core Qt::Widgets QmlDesignerCore TextEditorSupport
SOURCES
dsconstants.h
dsstore.h dsstore.cpp
dsthemegroup.h dsthemegroup.cpp
dsthememanager.h dsthememanager.cpp
)

View File

@@ -0,0 +1,265 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "dsstore.h"
#include "dsthememanager.h"
#include <generatedcomponentutils.h>
#include <plaintexteditmodifier.h>
#include <qmljs/parser/qmldirparser_p.h>
#include <qmljs/qmljsreformatter.h>
#include <rewriterview.h>
#include <rewritingexception.h>
#include <uniquename.h>
#include <utils/fileutils.h>
#include <QLoggingCategory>
#include <QPlainTextEdit>
namespace {
constexpr char DesignModuleName[] = "DesignSystem";
QString capitalize(QStringView str)
{
if (str.isEmpty())
return QString();
QString tmp = str.toString();
tmp[0] = str[0].toUpper();
return tmp;
}
std::optional<Utils::FilePath> dsModuleDir(QmlDesigner::ExternalDependenciesInterface &ed)
{
auto componentsPath = QmlDesigner::GeneratedComponentUtils(ed).generatedComponentsPath();
if (componentsPath.exists())
return componentsPath.pathAppended(DesignModuleName);
return {};
}
static QByteArray reformatQml(const QString &content)
{
auto document = QmlJS::Document::create({}, QmlJS::Dialect::QmlQtQuick2);
document->setSource(content);
document->parseQml();
if (document->isParsedCorrectly())
return QmlJS::reformat(document).toUtf8();
return content.toUtf8();
}
std::optional<QString> modelSerializeHelper(QmlDesigner::ExternalDependenciesInterface &ed,
std::function<void(QmlDesigner::Model *)> callback,
const Utils::FilePath &targetDir,
const QString &typeName,
bool isSingelton = false)
{
QString qmlText{"import QtQuick\nQtObject {}\n"};
if (isSingelton)
qmlText.prepend("pragma Singleton\n");
QmlDesigner::ModelPointer model(QmlDesigner::Model::create("QtObject"));
QPlainTextEdit editor;
editor.setPlainText(qmlText);
QmlDesigner::NotIndentingTextEditModifier modifier(&editor);
QmlDesigner::RewriterView view(ed, QmlDesigner::RewriterView::Validate);
view.setPossibleImportsEnabled(false);
view.setCheckSemanticErrors(false);
view.setTextModifier(&modifier);
model->attachView(&view);
try {
callback(model.get());
} catch (const QmlDesigner::RewritingException &e) {
return e.description();
}
Utils::FileSaver saver(targetDir / (typeName + ".qml"), QIODevice::Text);
saver.write(reformatQml(modifier.text()));
if (!saver.finalize())
return saver.errorString();
return {};
}
} // namespace
namespace QmlDesigner {
DSStore::DSStore(ExternalDependenciesInterface &ed)
: m_ed(ed)
{}
DSStore::~DSStore() {}
QString DSStore::moduleImportStr() const
{
auto prefix = GeneratedComponentUtils(m_ed).generatedComponentTypePrefix();
if (!prefix.isEmpty())
return QString("%1.%2").arg(prefix).arg(DesignModuleName);
return DesignModuleName;
}
std::optional<QString> DSStore::load()
{
if (auto moduleDir = dsModuleDir(m_ed))
return load(*moduleDir);
return tr("Can not locate design system module");
}
std::optional<QString> DSStore::load(const Utils::FilePath &dsModuleDirPath)
{
// read qmldir
const auto qmldirFile = dsModuleDirPath / "qmldir";
const Utils::expected_str<QByteArray> contents = qmldirFile.fileContents();
if (!contents)
return tr("Can not read Design System qmldir");
m_collectionTypeNames.clear();
m_collections.clear();
// Parse qmldir
QString qmldirData = QString::fromUtf8(*contents);
QmlDirParser qmlDirParser;
qmlDirParser.parse(qmldirData);
// load collections
QStringList collectionErrors;
auto addCollectionErr = [&collectionErrors](const QString &name, const QString &e) {
collectionErrors << QString("Error loading collection %1. %2").arg(name, e);
};
for (auto component : qmlDirParser.components()) {
if (!component.fileName.isEmpty()) {
const auto collectionPath = dsModuleDirPath.pathAppended(component.fileName);
if (auto err = loadCollection(component.typeName, collectionPath))
addCollectionErr(component.typeName, *err);
} else {
addCollectionErr(component.typeName, tr("Can not find component file."));
}
}
if (!collectionErrors.isEmpty())
return collectionErrors.join("\n");
return {};
}
std::optional<QString> DSStore::save(bool mcuCompatible) const
{
if (auto moduleDir = dsModuleDir(m_ed))
return save(*moduleDir, mcuCompatible);
return tr("Can not locate design system module");
}
std::optional<QString> DSStore::save(const Utils::FilePath &moduleDirPath, bool mcuCompatible) const
{
if (!QDir().mkpath(moduleDirPath.absoluteFilePath().toString()))
return tr("Can not create design system module directory %1.").arg(moduleDirPath.toString());
// dump collections
QStringList singletons;
QStringList errors;
for (auto &[typeName, collection] : m_collections) {
if (auto err = writeQml(collection, typeName, moduleDirPath, mcuCompatible))
errors << *err;
singletons << QString("singleton %1 1.0 %1.qml").arg(typeName);
}
// Write qmldir
Utils::FileSaver saver(moduleDirPath / "qmldir", QIODevice::Text);
const QString qmldirContents = QString("Module %1\n%2").arg(moduleImportStr(), singletons.join("\n"));
saver.write(qmldirContents.toUtf8());
if (!saver.finalize())
errors << tr("Can not write design system qmldir. %1").arg(saver.errorString());
if (!errors.isEmpty())
return errors.join("\n");
return {};
}
DSThemeManager *DSStore::addCollection(const QString &qmlTypeName)
{
const QString uniqueTypeName = UniqueName::generateId(qmlTypeName,
"designSystem",
[this](const QString &t) {
return m_collections.contains(t);
});
const QString componentType = capitalize(uniqueTypeName);
auto [itr, success] = m_collections.try_emplace(componentType, DSThemeManager{});
if (success) {
m_collectionTypeNames.insert({&itr->second, itr->first});
return &itr->second;
}
return nullptr;
}
std::optional<QString> DSStore::typeName(DSThemeManager *collection) const
{
auto itr = m_collectionTypeNames.find(collection);
if (itr != m_collectionTypeNames.end())
return itr->second;
return {};
}
std::optional<QString> DSStore::loadCollection(const QString &typeName,
const Utils::FilePath &qmlFilePath)
{
Utils::FileReader reader;
if (!reader.fetch(qmlFilePath, QFile::Text))
return reader.errorString();
ModelPointer model(QmlDesigner::Model::create("QtObject"));
QPlainTextEdit editor;
QString qmlContent = QString::fromUtf8(reader.data());
editor.setPlainText(qmlContent);
QmlDesigner::NotIndentingTextEditModifier modifier(&editor);
RewriterView view(m_ed, QmlDesigner::RewriterView::Validate);
// QDS-8366
view.setPossibleImportsEnabled(false);
view.setCheckSemanticErrors(false);
view.setTextModifier(&modifier);
model->attachView(&view);
if (auto dsMgr = addCollection(typeName))
return dsMgr->load(model->rootModelNode());
return {};
}
std::optional<QString> DSStore::writeQml(const DSThemeManager &mgr,
const QString &typeName,
const Utils::FilePath &targetDir,
bool mcuCompatible) const
{
if (mgr.themeCount() == 0)
return {};
const QString themeInterfaceType = mcuCompatible ? QString("%1Theme").arg(typeName) : "QtObject";
if (mcuCompatible) {
auto decorateInterface = [&mgr](Model *interfaceModel) {
mgr.decorateThemeInterface(interfaceModel->rootModelNode());
};
if (auto error = modelSerializeHelper(m_ed, decorateInterface, targetDir, themeInterfaceType))
return tr("Can not write theme interface %1.\n%2").arg(themeInterfaceType, *error);
}
auto decorateCollection = [&](Model *collectionModel) {
mgr.decorate(collectionModel->rootModelNode(), themeInterfaceType.toUtf8(), mcuCompatible);
};
if (auto error = modelSerializeHelper(m_ed, decorateCollection, targetDir, typeName, true))
return tr("Can not write collection %1.\n%2").arg(typeName, *error);
return {};
}
} // namespace QmlDesigner

View File

@@ -0,0 +1,48 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#pragma once
#include <dsthememanager.h>
#include <externaldependenciesinterface.h>
namespace QmlDesigner {
class DSThemeManager;
class ExternalDependenciesInterface;
using DSCollections = std::map<QString, DSThemeManager>;
class DSStore
{
Q_DECLARE_TR_FUNCTIONS(DSStore);
public:
DSStore(ExternalDependenciesInterface &ed);
~DSStore();
QString moduleImportStr() const;
std::optional<QString> load();
std::optional<QString> load(const Utils::FilePath &dsModuleDirPath);
std::optional<QString> save(bool mcuCompatible = false) const;
std::optional<QString> save(const Utils::FilePath &moduleDirPath, bool mcuCompatible = false) const;
size_t collectionCount() const { return m_collections.size(); }
DSThemeManager *addCollection(const QString &qmlTypeName);
std::optional<QString> typeName(DSThemeManager *collection) const;
private:
std::optional<QString> loadCollection(const QString &typeName, const Utils::FilePath &qmlFilePath);
std::optional<QString> writeQml(const DSThemeManager &mgr,
const QString &typeName,
const Utils::FilePath &targetDir,
bool mcuCompatible) const;
private:
ExternalDependenciesInterface &m_ed;
DSCollections m_collections;
std::map<DSThemeManager *, const QString &> m_collectionTypeNames;
};
} // namespace QmlDesigner

View File

@@ -3,10 +3,12 @@
#include "dsthemegroup.h"
#include <abstractproperty.h>
#include <model.h>
#include <nodemetainfo.h>
#include <nodeproperty.h>
#include <variantproperty.h>
#include <utils/qtcassert.h>
#include <variantproperty.h>
#include <QLoggingCategory>
#include <QVariant>
@@ -142,64 +144,73 @@ void DSThemeGroup::removeTheme(ThemeId theme)
void DSThemeGroup::duplicateValues(ThemeId from, ThemeId to)
{
for (auto itr = m_values.begin(); itr != m_values.end(); ++itr) {
auto &[propName, values] = *itr;
auto fromValueItr = values.find(from);
for (auto &[propName, values] : m_values) {
ThemeValues::iterator fromValueItr = values.find(from);
if (fromValueItr != values.end())
values[to] = fromValueItr->second;
}
}
void DSThemeGroup::decorate(ThemeId theme, ModelNode themeNode, DECORATION_CONTEXT decorationContext)
void DSThemeGroup::decorate(ThemeId theme, ModelNode themeNode, bool wrapInGroups)
{
if (!count(theme))
return; // No props for this theme in this group.
ModelNode *targetNode = &themeNode;
ModelNode targetNode = themeNode;
const auto typeName = groupTypeName(m_type);
if (decorationContext == DECORATION_CONTEXT::MPU) {
if (wrapInGroups) {
// Create a group node
const auto groupName = GroupId(m_type);
auto groupNode = themeNode.model()->createModelNode("QtObject");
auto groupProperty = themeNode.nodeProperty(groupName);
NodeProperty groupProperty = themeNode.nodeProperty(groupName);
if (!groupProperty || !typeName || !groupNode) {
qCDebug(dsLog) << "Adding group node failed." << groupName << theme;
return;
}
groupProperty.setDynamicTypeNameAndsetModelNode("QtObject", groupNode);
targetNode = &groupNode;
targetNode = groupNode;
}
// Add properties
for (auto itr = m_values.begin(); itr != m_values.end(); ++itr) {
auto &[propName, values] = *itr;
for (auto &[propName, values] : m_values) {
auto themeValue = values.find(theme);
if (themeValue != values.end()) {
auto &propData = themeValue->second;
if (propData.isBinding) {
auto bindingProp = targetNode->bindingProperty(propName);
if (!bindingProp)
continue;
if (decorationContext == DECORATION_CONTEXT::MCU)
bindingProp.setExpression(propData.value.toString());
else
bindingProp.setDynamicTypeNameAndExpression(*typeName, propData.value.toString());
} else {
auto nodeProp = targetNode->variantProperty(propName);
if (!nodeProp)
continue;
if (decorationContext == DECORATION_CONTEXT::MCU)
nodeProp.setValue(propData.value);
else
nodeProp.setDynamicTypeNameAndValue(*typeName, propData.value);
}
}
if (themeValue != values.end())
addProperty(targetNode, propName, themeValue->second);
}
}
void DSThemeGroup::decorateComponent(ModelNode node)
{
const auto typeName = groupTypeName(m_type);
// Add properties with type to the node
for (auto &[propName, values] : m_values) {
auto nodeProp = node.variantProperty(propName);
nodeProp.setDynamicTypeNameAndValue(*typeName, nodeProp.value());
}
}
void DSThemeGroup::addProperty(ModelNode n, PropertyNameView propName, const PropertyData &data) const
{
auto metaInfo = n.model()->metaInfo(n.type());
const bool propDefined = metaInfo.property(propName).isValid();
const auto typeName = groupTypeName(m_type);
if (data.isBinding) {
if (propDefined)
n.bindingProperty(propName).setExpression(data.value.toString());
else if (auto bindingProp = n.bindingProperty(propName))
bindingProp.setDynamicTypeNameAndExpression(*typeName, data.value.toString());
else
qCDebug(dsLog) << "Assigning invalid binding" << propName << n.id();
} else {
if (propDefined)
n.variantProperty(propName).setValue(data.value);
else if (auto nodeProp = n.variantProperty(propName))
nodeProp.setDynamicTypeNameAndValue(*typeName, data.value);
else
qCDebug(dsLog) << "Assigning invalid variant property" << propName << n.id();
}
}
}

View File

@@ -12,12 +12,6 @@
#include <optional>
namespace QmlDesigner {
enum class DECORATION_CONTEXT {
MCU,
MPU,
COMPONENT_THEME,
};
class DESIGNSYSTEM_EXPORT DSThemeGroup
{
struct PropertyData
@@ -46,8 +40,6 @@ public:
void updateProperty(ThemeId theme, PropertyName newName, const ThemeProperty &prop);
void removeProperty(const PropertyName &name);
GroupType type() const { return m_type; }
size_t count(ThemeId theme) const;
size_t count() const;
bool isEmpty() const;
@@ -55,7 +47,11 @@ public:
void removeTheme(ThemeId theme);
void duplicateValues(ThemeId from, ThemeId to);
void decorate(ThemeId theme, ModelNode themeNode, DECORATION_CONTEXT decorationContext);
void decorate(ThemeId theme, ModelNode themeNode, bool wrapInGroups = true);
void decorateComponent(ModelNode node);
private:
void addProperty(ModelNode n, PropertyNameView propName, const PropertyData &data) const;
private:
const GroupType m_type;

View File

@@ -5,21 +5,39 @@
#include "dsconstants.h"
#include "dsthemegroup.h"
#include "variantproperty.h"
#include <nodeproperty.h>
#include <model.h>
#include <nodeproperty.h>
#include <utils/qtcassert.h>
#include <QLoggingCategory>
#include <QVariant>
#include <set>
namespace {
Q_LOGGING_CATEGORY(dsLog, "qtc.designer.designSystem", QtInfoMsg)
std::optional<QmlDesigner::GroupType> typeToGroupType(const QmlDesigner::TypeName type)
{
if (type == "color")
return QmlDesigner::GroupType::Colors;
if (type == "bool")
return QmlDesigner::GroupType::Flags;
if (type == "real")
return QmlDesigner::GroupType::Numbers;
if (type == "string")
return QmlDesigner::GroupType::Strings;
return {};
}
}
namespace QmlDesigner {
DSThemeManager::DSThemeManager() {}
DSThemeManager::DSThemeManager()
{}
DSThemeManager::~DSThemeManager() {}
@@ -31,7 +49,8 @@ std::optional<ThemeId> DSThemeManager::addTheme(const ThemeName &themeName)
}
const ThemeId newThemeId = m_themes.empty() ? 1 : m_themes.rbegin()->first + 1;
m_themes.insert({newThemeId, themeName});
if (!m_themes.try_emplace(newThemeId, themeName).second)
return {};
// Copy the new theme properties from an old theme(first one).
if (m_themes.size() > 1)
@@ -59,16 +78,16 @@ void DSThemeManager::removeTheme(ThemeId id)
if (!m_themes.contains(id))
return;
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->removeTheme(id);
for (auto &[gt, group] : m_groups)
group->removeTheme(id);
m_themes.erase(id);
}
void DSThemeManager::duplicateTheme(ThemeId from, ThemeId to)
{
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->duplicateValues(from, to);
for (auto &[gt, group] : m_groups)
group->duplicateValues(from, to);
}
std::optional<ThemeProperty> DSThemeManager::property(ThemeId themeId,
@@ -88,7 +107,7 @@ std::optional<ThemeProperty> DSThemeManager::property(ThemeId themeId,
bool DSThemeManager::addProperty(GroupType gType, const ThemeProperty &p)
{
if (!m_themes.size()) {
qCDebug(dsLog) << "Can not add proprty. Themes empty";
qCDebug(dsLog) << "Can not add property. Themes empty";
return false;
}
@@ -141,44 +160,41 @@ void DSThemeManager::decorate(ModelNode rootNode, const QByteArray &nodeType, bo
addGroupAliases(rootNode);
auto model = rootNode.model();
for (auto itr = m_themes.begin(); itr != m_themes.end(); ++itr) {
for (auto &[themeId, themeName] : m_themes) {
auto themeNode = model->createModelNode(nodeType);
auto themeProperty = model->rootModelNode().nodeProperty(itr->second);
auto themeProperty = model->rootModelNode().nodeProperty(themeName);
themeProperty.setDynamicTypeNameAndsetModelNode(nodeType, themeNode);
// Add property groups
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->decorate(itr->first, themeNode, isMCU ? DECORATION_CONTEXT::MCU : DECORATION_CONTEXT::MPU);
for (auto &[gt, group] : m_groups)
group->decorate(themeId, themeNode, !isMCU);
}
}
void DSThemeManager::decorateThemeComponent(ModelNode rootNode) const
void DSThemeManager::decorateThemeInterface(ModelNode rootNode) const
{
if (!m_themes.size())
return;
auto itr = m_themes.begin();
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr)
groupItr->second->decorate(itr->first, rootNode, DECORATION_CONTEXT::COMPONENT_THEME);
for (auto &[gt, group] : m_groups)
group->decorateComponent(rootNode);
}
DSThemeGroup *DSThemeManager::propertyGroup(GroupType type)
{
auto itr = m_groups.find(type);
if (itr == m_groups.end())
itr = m_groups.insert({type, std::make_unique<DSThemeGroup>(type)}).first;
itr = m_groups.try_emplace(type, std::make_unique<DSThemeGroup>(type)).first;
return itr->second.get();
}
void DSThemeManager::addGroupAliases(ModelNode rootNode) const
{
QSet<PropertyName> groupNames;
for (auto groupItr = m_groups.begin(); groupItr != m_groups.end(); ++groupItr) {
DSThemeGroup *group = groupItr->second.get();
const PropertyName groupName = GroupId(group->type());
std::set<PropertyName> groupNames;
for (auto &[groupType, group] : m_groups) {
if (group->count())
groupNames.insert(groupName);
groupNames.emplace(GroupId(groupType));
}
for (const auto &name : groupNames) {
@@ -187,4 +203,103 @@ void DSThemeManager::addGroupAliases(ModelNode rootNode) const
p.setDynamicTypeNameAndExpression("QtObject", binding);
}
}
std::optional<QString> DSThemeManager::load(ModelNode rootModelNode)
{
// We need all properties under the theme node and its child nodes.
// The properties must have a unique name.
auto propWithSameName = [](const AbstractProperty &p1, const AbstractProperty &p2) {
return p1.name() == p2.name();
};
using PropMap = std::set<AbstractProperty, decltype(propWithSameName)>;
using ThemeProps = std::map<ThemeId, PropMap>;
auto getAllProps = [](const ModelNode &n) -> PropMap {
PropMap props;
auto nodesUnderTheme = n.allSubModelNodesAndThisNode();
for (auto &n : nodesUnderTheme) {
for (const AbstractProperty &p : n.properties()) {
if (!props.insert(p).second)
qCDebug(dsLog) << "Duplicate Property, Skipping" << n << p;
}
}
return props;
};
// First level child nodes are assumed to be the theme nodes.
QList<NodeProperty> themes = rootModelNode.nodeProperties();
if (themes.isEmpty())
return tr("No themes objects in the collection.");
// Collect properties under each theme node.
ThemeProps themeProps;
for (auto &themeNodeProp : themes) {
ModelNode themeNode = themeNodeProp.modelNode();
if (auto themeId = addTheme(themeNodeProp.name().toByteArray()))
themeProps.insert({*themeId, getAllProps(themeNode)});
}
// Get properties from the first theme. We expect other theme nodes
// have prpperties with same name and type. If not we don't consider those properties.
auto themeItr = themeProps.begin();
// Add default properties
const PropMap &baseProps = themeItr->second;
for (const AbstractProperty &baseNodeProp : baseProps) {
GroupType basePropGroupType;
ThemeProperty basethemeProp;
if (findPropertyType(baseNodeProp, &basethemeProp, &basePropGroupType))
addProperty(basePropGroupType, basethemeProp);
else
continue;
// Update values for rest of the themes.
for (auto otherTheme = std::next(themeItr); otherTheme != themeProps.end(); ++otherTheme) {
const PropMap &otherThemeProps = otherTheme->second;
auto otherThemePropItr = otherThemeProps.find(baseNodeProp);
if (otherThemePropItr == otherThemeProps.end()) {
qCDebug(dsLog) << "Can't find expected prop" << baseNodeProp.name() << "in theme"
<< otherTheme->first;
continue;
}
GroupType otherGroup;
ThemeProperty otherThemeProp;
if (findPropertyType(*otherThemePropItr, &otherThemeProp, &otherGroup)
&& otherGroup == basePropGroupType) {
updateProperty(otherTheme->first, basePropGroupType, otherThemeProp);
} else {
qCDebug(dsLog) << "Incompatible property" << baseNodeProp.name()
<< " found in theme" << otherTheme->first;
}
}
}
return {};
}
bool DSThemeManager::findPropertyType(const AbstractProperty &p,
ThemeProperty *themeProp,
GroupType *gt) const
{
auto group = typeToGroupType(p.dynamicTypeName());
if (!group) {
qCDebug(dsLog) << "Can't find suitable group for the property" << p.name();
return false;
}
*gt = *group;
PropertyName pName = p.name().toByteArray();
if (auto variantProp = p.toVariantProperty()) {
themeProp->value = variantProp.value();
themeProp->isBinding = false;
} else if (auto binding = p.toBindingProperty()) {
themeProp->value = binding.expression();
themeProp->isBinding = true;
} else {
qCDebug(dsLog) << "Property type not supported for design system" << pName;
return false;
}
themeProp->name = pName;
return true;
}
}

View File

@@ -8,8 +8,11 @@
#include "dsconstants.h"
#include "dsthemegroup.h"
#include <externaldependenciesinterface.h>
#include <modelnode.h>
#include <QCoreApplication>
namespace QmlDesigner {
using ThemeName = PropertyName;
@@ -18,6 +21,7 @@ class DSTheme;
class DESIGNSYSTEM_EXPORT DSThemeManager
{
Q_DECLARE_TR_FUNCTIONS(DSThemeManager);
public:
DSThemeManager();
@@ -27,7 +31,7 @@ public:
DSThemeManager& operator=(const DSThemeManager&) = delete;
DSThemeManager(DSThemeManager&&) = default;
DSThemeManager& operator=(DSThemeManager&&) = default;
DSThemeManager &operator=(DSThemeManager &&) = default;
std::optional<ThemeId> addTheme(const ThemeName &themeName);
std::optional<ThemeId> themeId(const ThemeName &themeName) const;
@@ -44,13 +48,17 @@ public:
void updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p);
void updateProperty(ThemeId id, GroupType gType, const ThemeProperty &p, const PropertyName &newName);
void decorate(ModelNode rootNode, const QByteArray& nodeType, bool isMCU) const;
void decorateThemeComponent(ModelNode rootNode) const;
void decorate(ModelNode rootNode, const QByteArray &nodeType = "QtObject", bool isMCU = false) const;
void decorateThemeInterface(ModelNode rootNode) const;
std::optional<QString> load(ModelNode rootModelNode);
private:
DSThemeGroup *propertyGroup(GroupType type);
void addGroupAliases(ModelNode rootNode) const;
bool findPropertyType(const AbstractProperty &p, ThemeProperty *themeProp, GroupType *gt) const;
private:
std::map<ThemeId, ThemeName> m_themes;
std::map<GroupType, std::unique_ptr<DSThemeGroup>> m_groups;