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 displayValue = property->isBinding
|
||||
? m_store->resolvedDSBinding(propertyValue.toString()).value
|
||||
? m_store->resolvedDSBinding(propertyValue.toString())
|
||||
.value_or(ThemeProperty{})
|
||||
.value
|
||||
: property->value;
|
||||
|
||||
switch (role) {
|
||||
@@ -193,6 +195,7 @@ bool CollectionModel::removeRows(int row, int count, const QModelIndex &parent)
|
||||
beginResetModel();
|
||||
while (row < sentinelIndex) {
|
||||
auto [groupType, name] = m_propertyInfoList[row++];
|
||||
m_store->breakBindings(m_collection, name);
|
||||
m_collection->removeProperty(groupType, name);
|
||||
}
|
||||
updateCache();
|
||||
@@ -235,7 +238,7 @@ bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, i
|
||||
switch (role) {
|
||||
case Qt::EditRole: {
|
||||
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.
|
||||
}
|
||||
|
||||
@@ -280,6 +283,10 @@ bool CollectionModel::setHeaderData(int section,
|
||||
if (auto propInfo = findPropertyName(section)) {
|
||||
auto [groupType, propName] = *propInfo;
|
||||
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 {
|
||||
// Theme
|
||||
|
@@ -8,6 +8,8 @@
|
||||
|
||||
#include <QVariant>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace QmlDesigner
|
||||
{
|
||||
|
||||
@@ -35,4 +37,5 @@ constexpr const char *GroupId(const GroupType type) {
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
using DSBindingInfo = std::tuple<PropertyName, ThemeId, GroupType, QStringView>;
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@
|
||||
#include <QScopeGuard>
|
||||
|
||||
namespace {
|
||||
|
||||
Q_LOGGING_CATEGORY(dsLog, "qtc.designer.designSystem", QtInfoMsg)
|
||||
constexpr char DesignModuleName[] = "DesignSystem";
|
||||
|
||||
std::optional<Utils::FilePath> dsModuleDir(QmlDesigner::ExternalDependenciesInterface &ed)
|
||||
@@ -83,6 +83,15 @@ std::optional<QString> modelSerializeHelper(
|
||||
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 QmlDesigner {
|
||||
@@ -214,7 +223,16 @@ std::optional<QString> DSStore::typeName(DSThemeManager *collection) const
|
||||
|
||||
bool DSStore::removeCollection(const QString &name)
|
||||
{
|
||||
return m_collections.erase(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 false;
|
||||
}
|
||||
|
||||
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);
|
||||
handle.key() = uniqueTypeName;
|
||||
m_collections.insert(std::move(handle));
|
||||
|
||||
refactorBindings(oldName, uniqueTypeName);
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -251,21 +272,29 @@ QStringList DSStore::collectionNames() const
|
||||
return names;
|
||||
}
|
||||
|
||||
ThemeProperty DSStore::resolvedDSBinding(QStringView binding) const
|
||||
std::optional<ThemeProperty> DSStore::resolvedDSBinding(QStringView binding) const
|
||||
{
|
||||
const auto parts = binding.split('.', Qt::SkipEmptyParts);
|
||||
if (parts.size() != 3)
|
||||
return {};
|
||||
if (auto parts = unpackDSBinding(binding)) {
|
||||
auto &[collectionName, _, propertyName] = *parts;
|
||||
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());
|
||||
if (itr == m_collections.end())
|
||||
return {};
|
||||
|
||||
const DSThemeManager &boundCollection = itr->second;
|
||||
const auto &propertyName = parts[2].toLatin1();
|
||||
if (const auto group = boundCollection.groupType(propertyName)) {
|
||||
auto property = boundCollection.property(boundCollection.activeTheme(), *group, propertyName);
|
||||
if (const auto group = boundCollection.groupType(propertyName.toLatin1())) {
|
||||
auto property = boundCollection.property(boundCollection.activeTheme(),
|
||||
*group,
|
||||
propertyName.toLatin1());
|
||||
if (property)
|
||||
return property->isBinding ? resolvedDSBinding(property->value.toString()) : *property;
|
||||
}
|
||||
@@ -273,6 +302,92 @@ ThemeProperty DSStore::resolvedDSBinding(QStringView binding) const
|
||||
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
|
||||
{
|
||||
return UniqueName::generateTypeName(hint, "Collection", [this](const QString &t) {
|
||||
|
@@ -38,7 +38,15 @@ public:
|
||||
std::optional<Utils::FilePath> moduleDirPath() 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;
|
||||
|
||||
|
@@ -357,6 +357,22 @@ std::optional<QString> DSThemeManager::load(ModelNode rootModelNode)
|
||||
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,
|
||||
ThemeProperty *themeProp,
|
||||
GroupType *gt) const
|
||||
|
@@ -66,6 +66,8 @@ public:
|
||||
|
||||
std::optional<QString> load(ModelNode rootModelNode);
|
||||
|
||||
std::vector<DSBindingInfo> boundProperties() const;
|
||||
|
||||
private:
|
||||
DSThemeGroup *propertyGroup(GroupType type);
|
||||
void addGroupAliases(ModelNode rootNode) const;
|
||||
|
Reference in New Issue
Block a user