forked from qt-creator/qt-creator
DesignSystem: Refactor design system bindings
refactor binding on collection or property name change break bindings to resolved values on property or collection deletion Task-number: QDS-14670 Change-Id: I38a7bcbf32b5dfbd580ee0a44107926725fdce6a Reviewed-by: Henning Gründl <henning.gruendl@qt.io> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
committed by
Thomas Hartmann
parent
0c6f18ac2a
commit
df486a809f
@@ -63,7 +63,9 @@ QVariant CollectionModel::data(const QModelIndex &index, int role) const
|
|||||||
|
|
||||||
const QVariant propertyValue = property->value.toString();
|
const QVariant propertyValue = property->value.toString();
|
||||||
const QVariant displayValue = property->isBinding
|
const QVariant displayValue = property->isBinding
|
||||||
? m_store->resolvedDSBinding(propertyValue.toString()).value
|
? m_store->resolvedDSBinding(propertyValue.toString())
|
||||||
|
.value_or(ThemeProperty{})
|
||||||
|
.value
|
||||||
: property->value;
|
: property->value;
|
||||||
|
|
||||||
switch (role) {
|
switch (role) {
|
||||||
@@ -193,6 +195,7 @@ bool CollectionModel::removeRows(int row, int count, const QModelIndex &parent)
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
while (row < sentinelIndex) {
|
while (row < sentinelIndex) {
|
||||||
auto [groupType, name] = m_propertyInfoList[row++];
|
auto [groupType, name] = m_propertyInfoList[row++];
|
||||||
|
m_store->breakBindings(m_collection, name);
|
||||||
m_collection->removeProperty(groupType, name);
|
m_collection->removeProperty(groupType, name);
|
||||||
}
|
}
|
||||||
updateCache();
|
updateCache();
|
||||||
@@ -235,7 +238,7 @@ bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||||||
switch (role) {
|
switch (role) {
|
||||||
case Qt::EditRole: {
|
case Qt::EditRole: {
|
||||||
if (p.isBinding) {
|
if (p.isBinding) {
|
||||||
if (!m_store->resolvedDSBinding(p.value.toString()).isValid())
|
if (!m_store->resolvedDSBinding(p.value.toString()))
|
||||||
return false; // Invalid binding, it must resolved to a valid property.
|
return false; // Invalid binding, it must resolved to a valid property.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,6 +283,10 @@ bool CollectionModel::setHeaderData(int section,
|
|||||||
if (auto propInfo = findPropertyName(section)) {
|
if (auto propInfo = findPropertyName(section)) {
|
||||||
auto [groupType, propName] = *propInfo;
|
auto [groupType, propName] = *propInfo;
|
||||||
success = m_collection->renameProperty(groupType, propName, newName);
|
success = m_collection->renameProperty(groupType, propName, newName);
|
||||||
|
if (success) {
|
||||||
|
const auto collectionName = m_store->typeName(m_collection);
|
||||||
|
m_store->refactorBindings(m_collection, propName, newName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Theme
|
// Theme
|
||||||
|
@@ -8,6 +8,8 @@
|
|||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace QmlDesigner
|
namespace QmlDesigner
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -35,4 +37,5 @@ constexpr const char *GroupId(const GroupType type) {
|
|||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using DSBindingInfo = std::tuple<PropertyName, ThemeId, GroupType, QStringView>;
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,7 @@
|
|||||||
#include <QScopeGuard>
|
#include <QScopeGuard>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
Q_LOGGING_CATEGORY(dsLog, "qtc.designer.designSystem", QtInfoMsg)
|
||||||
constexpr char DesignModuleName[] = "DesignSystem";
|
constexpr char DesignModuleName[] = "DesignSystem";
|
||||||
|
|
||||||
std::optional<Utils::FilePath> dsModuleDir(QmlDesigner::ExternalDependenciesInterface &ed)
|
std::optional<Utils::FilePath> dsModuleDir(QmlDesigner::ExternalDependenciesInterface &ed)
|
||||||
@@ -83,6 +83,15 @@ std::optional<QString> modelSerializeHelper(
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::tuple<QStringView, QStringView, QStringView>> unpackDSBinding(QStringView binding)
|
||||||
|
{
|
||||||
|
const auto parts = binding.split('.', Qt::SkipEmptyParts);
|
||||||
|
if (parts.size() != 3)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return std::make_tuple(parts[0], parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
@@ -214,7 +223,16 @@ std::optional<QString> DSStore::typeName(DSThemeManager *collection) const
|
|||||||
|
|
||||||
bool DSStore::removeCollection(const QString &name)
|
bool DSStore::removeCollection(const QString &name)
|
||||||
{
|
{
|
||||||
|
if (auto toRemove = collection(name)) {
|
||||||
|
for (auto &[_, currentCollection] : m_collections) {
|
||||||
|
if (toRemove == ¤tCollection)
|
||||||
|
continue;
|
||||||
|
breakBindings(¤tCollection, name);
|
||||||
|
}
|
||||||
|
save();
|
||||||
return m_collections.erase(name);
|
return m_collections.erase(name);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DSStore::renameCollection(const QString &oldName, const QString &newName)
|
bool DSStore::renameCollection(const QString &oldName, const QString &newName)
|
||||||
@@ -233,6 +251,9 @@ bool DSStore::renameCollection(const QString &oldName, const QString &newName)
|
|||||||
auto handle = m_collections.extract(oldName);
|
auto handle = m_collections.extract(oldName);
|
||||||
handle.key() = uniqueTypeName;
|
handle.key() = uniqueTypeName;
|
||||||
m_collections.insert(std::move(handle));
|
m_collections.insert(std::move(handle));
|
||||||
|
|
||||||
|
refactorBindings(oldName, uniqueTypeName);
|
||||||
|
save();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,21 +272,29 @@ QStringList DSStore::collectionNames() const
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThemeProperty DSStore::resolvedDSBinding(QStringView binding) const
|
std::optional<ThemeProperty> DSStore::resolvedDSBinding(QStringView binding) const
|
||||||
{
|
{
|
||||||
const auto parts = binding.split('.', Qt::SkipEmptyParts);
|
if (auto parts = unpackDSBinding(binding)) {
|
||||||
if (parts.size() != 3)
|
auto &[collectionName, _, propertyName] = *parts;
|
||||||
return {};
|
return resolvedDSBinding(collectionName, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
const auto &collectionName = parts[0];
|
qCDebug(dsLog) << "Resolving binding failed. Unexpected binding" << binding;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ThemeProperty> DSStore::resolvedDSBinding(QStringView collectionName,
|
||||||
|
QStringView propertyName) const
|
||||||
|
{
|
||||||
auto itr = m_collections.find(collectionName.toString());
|
auto itr = m_collections.find(collectionName.toString());
|
||||||
if (itr == m_collections.end())
|
if (itr == m_collections.end())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
const DSThemeManager &boundCollection = itr->second;
|
const DSThemeManager &boundCollection = itr->second;
|
||||||
const auto &propertyName = parts[2].toLatin1();
|
if (const auto group = boundCollection.groupType(propertyName.toLatin1())) {
|
||||||
if (const auto group = boundCollection.groupType(propertyName)) {
|
auto property = boundCollection.property(boundCollection.activeTheme(),
|
||||||
auto property = boundCollection.property(boundCollection.activeTheme(), *group, propertyName);
|
*group,
|
||||||
|
propertyName.toLatin1());
|
||||||
if (property)
|
if (property)
|
||||||
return property->isBinding ? resolvedDSBinding(property->value.toString()) : *property;
|
return property->isBinding ? resolvedDSBinding(property->value.toString()) : *property;
|
||||||
}
|
}
|
||||||
@@ -273,6 +302,92 @@ ThemeProperty DSStore::resolvedDSBinding(QStringView binding) const
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DSStore::refactorBindings(QStringView oldCollectionName, QStringView newCollectionName)
|
||||||
|
{
|
||||||
|
for (auto &[_, currentCollection] : m_collections) {
|
||||||
|
for (const auto &[propName, themeId, gt, expression] : currentCollection.boundProperties()) {
|
||||||
|
auto bindingParts = unpackDSBinding(expression);
|
||||||
|
if (!bindingParts) {
|
||||||
|
qCDebug(dsLog) << "Refactor binding error. Unexpected binding" << expression;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &[boundCollection, groupName, boundProp] = *bindingParts;
|
||||||
|
if (boundCollection != oldCollectionName)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto newBinding = QString("%1.%2.%3").arg(newCollectionName, groupName, boundProp);
|
||||||
|
currentCollection.updateProperty(themeId, gt, {propName, newBinding, true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSStore::refactorBindings(DSThemeManager *srcCollection, PropertyName from, PropertyName to)
|
||||||
|
{
|
||||||
|
auto srcCollectionName = typeName(srcCollection);
|
||||||
|
if (!srcCollectionName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto &[_, currentCollection] : m_collections) {
|
||||||
|
for (const auto &[propName, themeId, gt, expression] : currentCollection.boundProperties()) {
|
||||||
|
auto bindingParts = unpackDSBinding(expression);
|
||||||
|
if (!bindingParts) {
|
||||||
|
qCDebug(dsLog) << "Refactor binding error. Unexpected binding" << expression;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &[boundCollection, groupName, boundProp] = *bindingParts;
|
||||||
|
if (boundCollection != srcCollectionName || from != boundProp.toLatin1())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto newBinding = QString("%1.%2.%3")
|
||||||
|
.arg(boundCollection, groupName, QString::fromUtf8(to));
|
||||||
|
currentCollection.updateProperty(themeId, gt, {propName, newBinding, true});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSStore::breakBindings(DSThemeManager *collection, PropertyName propertyName)
|
||||||
|
{
|
||||||
|
auto collectionName = typeName(collection);
|
||||||
|
if (!collectionName)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (auto &[_, currentCollection] : m_collections) {
|
||||||
|
for (const auto &[propName, themeId, gt, expression] : currentCollection.boundProperties()) {
|
||||||
|
auto bindingParts = unpackDSBinding(expression);
|
||||||
|
if (!bindingParts) {
|
||||||
|
qCDebug(dsLog) << "Error breaking binding. Unexpected binding" << expression;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto &[boundCollection, _, boundProp] = *bindingParts;
|
||||||
|
if (boundCollection != collectionName || propertyName != boundProp.toLatin1())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (auto value = resolvedDSBinding(*collectionName, boundProp))
|
||||||
|
currentCollection.updateProperty(themeId, gt, {propName, value->value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSStore::breakBindings(DSThemeManager *collection, QStringView removeCollection)
|
||||||
|
{
|
||||||
|
for (const auto &[propName, themeId, gt, expression] : collection->boundProperties()) {
|
||||||
|
auto bindingParts = unpackDSBinding(expression);
|
||||||
|
if (!bindingParts) {
|
||||||
|
qCDebug(dsLog) << "Error breaking binding. Unexpected binding" << expression;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto &[boundCollection, _, boundProp] = *bindingParts;
|
||||||
|
if (boundCollection != removeCollection)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (auto value = resolvedDSBinding(boundCollection, boundProp))
|
||||||
|
collection->updateProperty(themeId, gt, {propName, value->value});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QString DSStore::uniqueCollectionName(const QString &hint) const
|
QString DSStore::uniqueCollectionName(const QString &hint) const
|
||||||
{
|
{
|
||||||
return UniqueName::generateTypeName(hint, "Collection", [this](const QString &t) {
|
return UniqueName::generateTypeName(hint, "Collection", [this](const QString &t) {
|
||||||
|
@@ -38,7 +38,15 @@ public:
|
|||||||
std::optional<Utils::FilePath> moduleDirPath() const;
|
std::optional<Utils::FilePath> moduleDirPath() const;
|
||||||
QStringList collectionNames() const;
|
QStringList collectionNames() const;
|
||||||
|
|
||||||
ThemeProperty resolvedDSBinding(QStringView binding) const;
|
std::optional<ThemeProperty> resolvedDSBinding(QStringView binding) const;
|
||||||
|
std::optional<ThemeProperty> resolvedDSBinding(QStringView collectionName,
|
||||||
|
QStringView propertyName) const;
|
||||||
|
|
||||||
|
void refactorBindings(QStringView oldCollectionName, QStringView newCollectionName);
|
||||||
|
void refactorBindings(DSThemeManager *srcCollection, PropertyName from, PropertyName to);
|
||||||
|
|
||||||
|
void breakBindings(DSThemeManager *collection, PropertyName propertyName);
|
||||||
|
void breakBindings(DSThemeManager *collection, QStringView removeCollection);
|
||||||
|
|
||||||
QString uniqueCollectionName(const QString &hint) const;
|
QString uniqueCollectionName(const QString &hint) const;
|
||||||
|
|
||||||
|
@@ -357,6 +357,22 @@ std::optional<QString> DSThemeManager::load(ModelNode rootModelNode)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<DSBindingInfo> DSThemeManager::boundProperties() const
|
||||||
|
{
|
||||||
|
std::vector<DSBindingInfo> bindings;
|
||||||
|
for (auto &[gt, group] : m_groups) {
|
||||||
|
for (auto &[id, _] : m_themes) {
|
||||||
|
for (const auto &propName : group.propertyNames()) {
|
||||||
|
if (auto p = group.propertyValue(id, propName)) {
|
||||||
|
if (p->isBinding)
|
||||||
|
bindings.push_back({propName, id, gt, p->value.toString()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
bool DSThemeManager::findPropertyType(const AbstractProperty &p,
|
bool DSThemeManager::findPropertyType(const AbstractProperty &p,
|
||||||
ThemeProperty *themeProp,
|
ThemeProperty *themeProp,
|
||||||
GroupType *gt) const
|
GroupType *gt) const
|
||||||
|
@@ -66,6 +66,8 @@ public:
|
|||||||
|
|
||||||
std::optional<QString> load(ModelNode rootModelNode);
|
std::optional<QString> load(ModelNode rootModelNode);
|
||||||
|
|
||||||
|
std::vector<DSBindingInfo> boundProperties() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DSThemeGroup *propertyGroup(GroupType type);
|
DSThemeGroup *propertyGroup(GroupType type);
|
||||||
void addGroupAliases(ModelNode rootNode) const;
|
void addGroupAliases(ModelNode rootNode) const;
|
||||||
|
Reference in New Issue
Block a user