DesignSystem: Enable setting a theme active

A collection can have only one theme active. First theme added to an
empty collection is made active by default

Task-number: QDS-14261
Change-Id: Ibf9a697ae0a817c45c4bef1a6fb4a9bd28462491
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Vikas Pachdha
2024-12-06 13:39:55 +02:00
parent da38404d72
commit 4789783d12
4 changed files with 78 additions and 14 deletions

View File

@@ -15,6 +15,21 @@ CollectionModel::CollectionModel(DSThemeManager *collection)
updateCache();
}
QStringList CollectionModel::themeNameList() const
{
QStringList themeNames(m_themeIdList.size());
std::transform(m_themeIdList.begin(), m_themeIdList.end(), themeNames.begin(), [this](ThemeId id) {
return QString::fromLatin1(m_collection->themeName(id));
});
return themeNames;
}
void CollectionModel::setActiveTheme(const QString &themeName)
{
if (const auto themeId = m_collection->themeId(themeName.toLatin1()))
m_collection->setActiveTheme(*themeId);
}
int CollectionModel::columnCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : static_cast<int>(m_collection->themeCount());
@@ -39,6 +54,8 @@ QVariant CollectionModel::data(const QModelIndex &index, int role) const
return QVariant::fromValue<GroupType>(groupType);
case static_cast<int>(Roles::BindingRole):
return property->isBinding;
case static_cast<int>(Roles::ActiveThemeRole):
return m_collection->activeTheme() == themeId;
}
return {};
@@ -64,8 +81,17 @@ int CollectionModel::rowCount(const QModelIndex &parent) const
QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return QString::fromLatin1(m_collection->themeName(findThemeId(section)));
if (orientation == Qt::Horizontal) {
auto themeId = findThemeId(section);
switch (role) {
case Qt::DisplayRole:
return QString::fromLatin1(m_collection->themeName(themeId));
case static_cast<int>(Roles::ActiveThemeRole):
return m_collection->activeTheme() == themeId;
default:
break;
}
}
if (orientation == Qt::Vertical) {
if (auto propInfo = findPropertyName(section)) {
@@ -75,7 +101,6 @@ QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, i
return QVariant::fromValue<GroupType>(propInfo->first);
}
}
return {};
}
@@ -89,6 +114,7 @@ QHash<int, QByteArray> CollectionModel::roleNames() const
auto roles = QAbstractItemModel::roleNames();
roles.insert(static_cast<int>(Roles::GroupRole), "group");
roles.insert(static_cast<int>(Roles::BindingRole), "isBinding");
roles.insert(static_cast<int>(Roles::ActiveThemeRole), "isActive");
return roles;
}
@@ -106,6 +132,7 @@ bool CollectionModel::insertColumns([[maybe_unused]] int column, int count, cons
beginResetModel();
updateCache();
endResetModel();
emit themeNameChanged();
}
return true;
}
@@ -122,6 +149,7 @@ bool CollectionModel::removeColumns(int column, int count, const QModelIndex &pa
updateCache();
endResetModel();
emit themeNameChanged();
return true;
}
@@ -190,25 +218,26 @@ bool CollectionModel::setHeaderData(int section,
const QVariant &value,
int role)
{
if (role != Qt::EditRole)
return false;
if (section < 0 || (orientation == Qt::Horizontal && section >= columnCount())
if (role != Qt::EditRole || section < 0
|| (orientation == Qt::Horizontal && section >= columnCount())
|| (orientation == Qt::Vertical && section >= rowCount())) {
return false; // Out of bounds
}
const auto &newName = value.toString().toUtf8();
bool success = false;
if (orientation == Qt::Horizontal) {
// Theme
success = m_collection->renameTheme(findThemeId(section), newName);
} else {
if (orientation == Qt::Vertical) {
// Property Name
if (auto propInfo = findPropertyName(section)) {
auto [groupType, propName] = *propInfo;
success = m_collection->renameProperty(groupType, propName, newName);
}
} else {
// Theme
const auto themeId = findThemeId(section);
success = m_collection->renameTheme(themeId, newName);
if (success)
emit themeNameChanged();
}
if (success) {

View File

@@ -17,10 +17,15 @@ class CollectionModel : public QAbstractItemModel
Q_OBJECT
public:
enum class Roles { GroupRole = Qt::UserRole + 1, BindingRole };
enum class Roles { GroupRole = Qt::UserRole + 1, BindingRole, ActiveThemeRole };
Q_PROPERTY(QStringList themeNames READ themeNameList NOTIFY themeNameChanged FINAL)
CollectionModel(DSThemeManager *collection);
QStringList themeNameList() const;
Q_INVOKABLE void setActiveTheme(const QString &themeName);
// QAbstractItemModel Interface
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
@@ -52,6 +57,9 @@ public:
const QVariant &value,
int role = Qt::EditRole) override;
signals:
void themeNameChanged();
private:
ThemeId findThemeId(int column) const;
std::optional<PropInfo> findPropertyName(int row) const;

View File

@@ -48,6 +48,9 @@ std::optional<ThemeId> DSThemeManager::addTheme(const ThemeName &themeNameHint)
if (!m_themes.try_emplace(newThemeId, assignedThemeName).second)
return {};
if (m_themes.size() == 1) // First theme. Make it active.
reviewActiveTheme();
// Copy the new theme properties from an old theme(first one).
if (m_themes.size() > 1)
duplicateTheme(m_themes.begin()->first, newThemeId);
@@ -101,6 +104,12 @@ const std::vector<ThemeId> DSThemeManager::allThemeIds() const
return ids;
}
void DSThemeManager::setActiveTheme(ThemeId id)
{
if (m_themes.contains(id))
m_activeTheme = id;
}
void DSThemeManager::forAllGroups(std::function<void(GroupType, DSThemeGroup *)> callback) const
{
if (!callback)
@@ -131,7 +140,8 @@ void DSThemeManager::removeTheme(ThemeId id)
for (auto &[gt, group] : m_groups)
group->removeTheme(id);
m_themes.erase(id);
if (m_themes.erase(id))
reviewActiveTheme();
}
void DSThemeManager::duplicateTheme(ThemeId from, ThemeId to)
@@ -217,7 +227,7 @@ void DSThemeManager::decorate(ModelNode rootNode, const QByteArray &nodeType, bo
return;
auto p = rootNode.bindingProperty("currentTheme");
p.setDynamicTypeNameAndExpression(nodeType, QString::fromLatin1(m_themes.begin()->second));
p.setDynamicTypeNameAndExpression(nodeType, QString::fromLatin1(m_themes.at(m_activeTheme)));
if (!isMCU)
addGroupAliases(rootNode);
auto model = rootNode.model();
@@ -390,4 +400,15 @@ PropertyName DSThemeManager::uniquePropertyName(const PropertyName &hint) const
});
return propName.toUtf8();
}
void DSThemeManager::reviewActiveTheme()
{
// Active theme removed. Make the first one active
if (!m_themes.contains(m_activeTheme)) {
if (m_themes.size() > 0)
setActiveTheme(m_themes.begin()->first);
else
m_activeTheme = static_cast<ThemeId>(0);
}
}
} // namespace QmlDesigner

View File

@@ -39,6 +39,9 @@ public:
bool renameTheme(ThemeId id, const ThemeName &newName);
const std::vector<ThemeId> allThemeIds() const;
ThemeId activeTheme() const { return m_activeTheme; }
void setActiveTheme(ThemeId id);
void forAllGroups(std::function<void(GroupType, DSThemeGroup *)> callback) const;
void removeTheme(ThemeId id);
@@ -70,9 +73,12 @@ private:
ThemeName uniqueThemeName(const ThemeName &hint) const;
PropertyName uniquePropertyName(const PropertyName &hint) const;
void reviewActiveTheme();
private:
std::map<ThemeId, ThemeName> m_themes;
std::map<GroupType, std::shared_ptr<DSThemeGroup>> m_groups;
ThemeId m_activeTheme = static_cast<ThemeId>(0);
};
using DSCollections = std::map<QString, DSThemeManager>;