forked from qt-creator/qt-creator
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:
@@ -15,6 +15,21 @@ CollectionModel::CollectionModel(DSThemeManager *collection)
|
|||||||
updateCache();
|
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
|
int CollectionModel::columnCount(const QModelIndex &parent) const
|
||||||
{
|
{
|
||||||
return parent.isValid() ? 0 : static_cast<int>(m_collection->themeCount());
|
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);
|
return QVariant::fromValue<GroupType>(groupType);
|
||||||
case static_cast<int>(Roles::BindingRole):
|
case static_cast<int>(Roles::BindingRole):
|
||||||
return property->isBinding;
|
return property->isBinding;
|
||||||
|
case static_cast<int>(Roles::ActiveThemeRole):
|
||||||
|
return m_collection->activeTheme() == themeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
@@ -64,8 +81,17 @@ int CollectionModel::rowCount(const QModelIndex &parent) const
|
|||||||
|
|
||||||
QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, int role) const
|
QVariant CollectionModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||||
{
|
{
|
||||||
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
|
if (orientation == Qt::Horizontal) {
|
||||||
return QString::fromLatin1(m_collection->themeName(findThemeId(section)));
|
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 (orientation == Qt::Vertical) {
|
||||||
if (auto propInfo = findPropertyName(section)) {
|
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 QVariant::fromValue<GroupType>(propInfo->first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,6 +114,7 @@ QHash<int, QByteArray> CollectionModel::roleNames() const
|
|||||||
auto roles = QAbstractItemModel::roleNames();
|
auto roles = QAbstractItemModel::roleNames();
|
||||||
roles.insert(static_cast<int>(Roles::GroupRole), "group");
|
roles.insert(static_cast<int>(Roles::GroupRole), "group");
|
||||||
roles.insert(static_cast<int>(Roles::BindingRole), "isBinding");
|
roles.insert(static_cast<int>(Roles::BindingRole), "isBinding");
|
||||||
|
roles.insert(static_cast<int>(Roles::ActiveThemeRole), "isActive");
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,6 +132,7 @@ bool CollectionModel::insertColumns([[maybe_unused]] int column, int count, cons
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
emit themeNameChanged();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -122,6 +149,7 @@ bool CollectionModel::removeColumns(int column, int count, const QModelIndex &pa
|
|||||||
|
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
emit themeNameChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,25 +218,26 @@ bool CollectionModel::setHeaderData(int section,
|
|||||||
const QVariant &value,
|
const QVariant &value,
|
||||||
int role)
|
int role)
|
||||||
{
|
{
|
||||||
if (role != Qt::EditRole)
|
if (role != Qt::EditRole || section < 0
|
||||||
return false;
|
|| (orientation == Qt::Horizontal && section >= columnCount())
|
||||||
|
|
||||||
if (section < 0 || (orientation == Qt::Horizontal && section >= columnCount())
|
|
||||||
|| (orientation == Qt::Vertical && section >= rowCount())) {
|
|| (orientation == Qt::Vertical && section >= rowCount())) {
|
||||||
return false; // Out of bounds
|
return false; // Out of bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &newName = value.toString().toUtf8();
|
const auto &newName = value.toString().toUtf8();
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (orientation == Qt::Horizontal) {
|
if (orientation == Qt::Vertical) {
|
||||||
// Theme
|
|
||||||
success = m_collection->renameTheme(findThemeId(section), newName);
|
|
||||||
} else {
|
|
||||||
// Property Name
|
// Property Name
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Theme
|
||||||
|
const auto themeId = findThemeId(section);
|
||||||
|
success = m_collection->renameTheme(themeId, newName);
|
||||||
|
if (success)
|
||||||
|
emit themeNameChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@@ -17,10 +17,15 @@ class CollectionModel : public QAbstractItemModel
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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);
|
CollectionModel(DSThemeManager *collection);
|
||||||
|
|
||||||
|
QStringList themeNameList() const;
|
||||||
|
Q_INVOKABLE void setActiveTheme(const QString &themeName);
|
||||||
|
|
||||||
// QAbstractItemModel Interface
|
// QAbstractItemModel Interface
|
||||||
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
@@ -52,6 +57,9 @@ public:
|
|||||||
const QVariant &value,
|
const QVariant &value,
|
||||||
int role = Qt::EditRole) override;
|
int role = Qt::EditRole) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void themeNameChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ThemeId findThemeId(int column) const;
|
ThemeId findThemeId(int column) const;
|
||||||
std::optional<PropInfo> findPropertyName(int row) const;
|
std::optional<PropInfo> findPropertyName(int row) const;
|
||||||
|
@@ -48,6 +48,9 @@ std::optional<ThemeId> DSThemeManager::addTheme(const ThemeName &themeNameHint)
|
|||||||
if (!m_themes.try_emplace(newThemeId, assignedThemeName).second)
|
if (!m_themes.try_emplace(newThemeId, assignedThemeName).second)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
if (m_themes.size() == 1) // First theme. Make it active.
|
||||||
|
reviewActiveTheme();
|
||||||
|
|
||||||
// Copy the new theme properties from an old theme(first one).
|
// Copy the new theme properties from an old theme(first one).
|
||||||
if (m_themes.size() > 1)
|
if (m_themes.size() > 1)
|
||||||
duplicateTheme(m_themes.begin()->first, newThemeId);
|
duplicateTheme(m_themes.begin()->first, newThemeId);
|
||||||
@@ -101,6 +104,12 @@ const std::vector<ThemeId> DSThemeManager::allThemeIds() const
|
|||||||
return ids;
|
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
|
void DSThemeManager::forAllGroups(std::function<void(GroupType, DSThemeGroup *)> callback) const
|
||||||
{
|
{
|
||||||
if (!callback)
|
if (!callback)
|
||||||
@@ -131,7 +140,8 @@ void DSThemeManager::removeTheme(ThemeId id)
|
|||||||
for (auto &[gt, group] : m_groups)
|
for (auto &[gt, group] : m_groups)
|
||||||
group->removeTheme(id);
|
group->removeTheme(id);
|
||||||
|
|
||||||
m_themes.erase(id);
|
if (m_themes.erase(id))
|
||||||
|
reviewActiveTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSThemeManager::duplicateTheme(ThemeId from, ThemeId to)
|
void DSThemeManager::duplicateTheme(ThemeId from, ThemeId to)
|
||||||
@@ -217,7 +227,7 @@ void DSThemeManager::decorate(ModelNode rootNode, const QByteArray &nodeType, bo
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
auto p = rootNode.bindingProperty("currentTheme");
|
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)
|
if (!isMCU)
|
||||||
addGroupAliases(rootNode);
|
addGroupAliases(rootNode);
|
||||||
auto model = rootNode.model();
|
auto model = rootNode.model();
|
||||||
@@ -390,4 +400,15 @@ PropertyName DSThemeManager::uniquePropertyName(const PropertyName &hint) const
|
|||||||
});
|
});
|
||||||
return propName.toUtf8();
|
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
|
} // namespace QmlDesigner
|
||||||
|
@@ -39,6 +39,9 @@ public:
|
|||||||
bool renameTheme(ThemeId id, const ThemeName &newName);
|
bool renameTheme(ThemeId id, const ThemeName &newName);
|
||||||
const std::vector<ThemeId> allThemeIds() const;
|
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 forAllGroups(std::function<void(GroupType, DSThemeGroup *)> callback) const;
|
||||||
|
|
||||||
void removeTheme(ThemeId id);
|
void removeTheme(ThemeId id);
|
||||||
@@ -70,9 +73,12 @@ private:
|
|||||||
ThemeName uniqueThemeName(const ThemeName &hint) const;
|
ThemeName uniqueThemeName(const ThemeName &hint) const;
|
||||||
PropertyName uniquePropertyName(const PropertyName &hint) const;
|
PropertyName uniquePropertyName(const PropertyName &hint) const;
|
||||||
|
|
||||||
|
void reviewActiveTheme();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<ThemeId, ThemeName> m_themes;
|
std::map<ThemeId, ThemeName> m_themes;
|
||||||
std::map<GroupType, std::shared_ptr<DSThemeGroup>> m_groups;
|
std::map<GroupType, std::shared_ptr<DSThemeGroup>> m_groups;
|
||||||
|
ThemeId m_activeTheme = static_cast<ThemeId>(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
using DSCollections = std::map<QString, DSThemeManager>;
|
using DSCollections = std::map<QString, DSThemeManager>;
|
||||||
|
Reference in New Issue
Block a user