QmlDesigner: Load DesignSystem automatically

* Connect to onCollectionsChanged and trigger load model in QML frontend
* Connect to project and document changes and load design system
* Fix DesignSystemView::loadDesignSystem
* Add saving of design system collection
* Fix late collections clear
* Change load to refresh button
* Add IgnoreErrors to design system view
* Add clear model function in frontend

When a project is loaded we now load the design system. We also load the
design system if a file in Generated/DesignSystem is changed. Here we
have to take care of reflection.

Change-Id: Ie6b8b4becfee4ed05760e31b766d8d0a9b79a666
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Thomas Hartmann
2025-02-28 17:38:55 +01:00
committed by Thomas Hartmann
parent b9349b9c22
commit b02f8123b0
7 changed files with 110 additions and 15 deletions

View File

@@ -44,7 +44,21 @@ Rectangle {
height: 400
color: StudioTheme.Values.themePanelBackground
function clearModel() {
root.currentCollectionName = ""
tableView.model = null
topLeftCell.visible = false
createModeButton.enabled = false
modelConnections.target = null
}
function loadModel(name) {
if (name === undefined) {
clearModel()
return
}
root.currentCollectionName = name
tableView.model = DesignSystemBackend.dsInterface.model(name)
@@ -256,6 +270,7 @@ Rectangle {
anchors.verticalCenter: parent.verticalCenter
actionIndicatorVisible: false
model: DesignSystemBackend.dsInterface.collections
enabled: collectionsComboBox.count
onActivated: root.loadModel(collectionsComboBox.currentText)
}
@@ -264,7 +279,8 @@ Rectangle {
style: StudioTheme.Values.viewBarControlStyle
anchors.verticalCenter: parent.verticalCenter
actionIndicatorVisible: false
model: tableView.model.themeNames
model: tableView.model?.themeNames ?? null
enabled: tableView.model
onActivated: tableView.model.setActiveTheme(themesComboBox.currentText)
}
@@ -274,6 +290,7 @@ Rectangle {
buttonIcon: StudioTheme.Constants.more_medium
checkable: true
checked: moreMenu.visible
tooltip: qsTr("More options")
onToggled: {
if (moreMenu.visible)
@@ -307,12 +324,21 @@ Rectangle {
}
// TODO this is only for debugging purposes
Button {
Connections {
target: DesignSystemBackend.dsInterface
function onCollectionsChanged() {
root.loadModel(DesignSystemBackend.dsInterface.collections[0])
}
}
StudioControls.IconTextButton {
id: refreshButton
anchors.verticalCenter: parent.verticalCenter
text: qsTr("load")
buttonIcon: StudioTheme.Constants.updateContent_medium
tooltip: qsTr("Refresh")
onClicked: {
DesignSystemBackend.dsInterface.loadDesignSystem()
root.loadModel(DesignSystemBackend.dsInterface.collections[0])
//root.loadModel(DesignSystemBackend.dsInterface.collections[0])
}
}
}

View File

@@ -10,10 +10,14 @@
namespace QmlDesigner {
CollectionModel::CollectionModel(DSThemeManager *collection, const DSStore *store)
CollectionModel::CollectionModel(DSThemeManager *collection, DSStore *store)
: m_collection(collection)
, m_store(store)
{
m_saveCompressionTimer.setSingleShot(true);
m_saveCompressionTimer.setInterval(200);
connect(&m_saveCompressionTimer, &QTimer::timeout, this, &CollectionModel::save);
updateCache();
}
@@ -28,8 +32,10 @@ QStringList CollectionModel::themeNameList() const
void CollectionModel::setActiveTheme(const QString &themeName)
{
if (const auto themeId = m_collection->themeId(themeName.toLatin1()))
if (const auto themeId = m_collection->themeId(themeName.toLatin1())) {
m_collection->setActiveTheme(*themeId);
aboutToSave();
}
}
int CollectionModel::columnCount(const QModelIndex &parent) const
@@ -149,6 +155,7 @@ bool CollectionModel::insertColumns([[maybe_unused]] int column, int count, cons
beginResetModel();
updateCache();
endResetModel();
aboutToSave();
emit themeNameChanged();
}
return true;
@@ -166,6 +173,7 @@ bool CollectionModel::removeColumns(int column, int count, const QModelIndex &pa
updateCache();
endResetModel();
aboutToSave();
emit themeNameChanged();
return true;
}
@@ -183,6 +191,7 @@ bool CollectionModel::removeRows(int row, int count, const QModelIndex &parent)
}
updateCache();
endResetModel();
aboutToSave();
return true;
}
@@ -207,6 +216,8 @@ void CollectionModel::addProperty(GroupType group, const QString &name, const QV
beginResetModel();
updateCache();
endResetModel();
aboutToSave();
}
}
@@ -227,6 +238,9 @@ bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, i
default:
break;
}
aboutToSave();
return false;
}
@@ -261,6 +275,8 @@ bool CollectionModel::setHeaderData(int section,
beginResetModel();
updateCache();
endResetModel();
aboutToSave();
}
return success;
@@ -277,4 +293,16 @@ std::optional<PropInfo> CollectionModel::findPropertyName(int row) const
QTC_ASSERT(row > -1 && row < static_cast<int>(m_propertyInfoList.size()), return {});
return m_propertyInfoList[row];
}
void CollectionModel::save()
{
QTC_ASSERT(m_store, return);
m_store->save();
}
void CollectionModel::aboutToSave()
{
m_saveCompressionTimer.start();
}
} // namespace QmlDesigner

View File

@@ -5,6 +5,7 @@
#include <designsystem/dsconstants.h>
#include <QAbstractItemModel>
#include <QTimer>
#include <optional>
@@ -29,7 +30,7 @@ public:
Q_PROPERTY(QStringList themeNames READ themeNameList NOTIFY themeNameChanged FINAL)
CollectionModel(DSThemeManager *collection, const DSStore *store);
CollectionModel(DSThemeManager *collection, DSStore *store);
QStringList themeNameList() const;
Q_INVOKABLE void setActiveTheme(const QString &themeName);
@@ -72,12 +73,17 @@ private:
ThemeId findThemeId(int column) const;
std::optional<PropInfo> findPropertyName(int row) const;
void save();
void aboutToSave();
private:
DSThemeManager *m_collection = nullptr;
const DSStore *m_store;
DSStore *m_store;
// cache
std::vector<ThemeId> m_themeIdList;
std::vector<PropInfo> m_propertyInfoList;
QTimer m_saveCompressionTimer;
};
} // namespace QmlDesigner

View File

@@ -22,7 +22,10 @@ DesignSystemInterface::~DesignSystemInterface() {}
void DesignSystemInterface::loadDesignSystem()
{
m_models.clear();
m_store->load();
if (auto err = m_store->load())
qDebug() << err;
emit collectionsChanged();
}

View File

@@ -6,7 +6,13 @@
#include "designsystemwidget.h"
#include "dsstore.h"
#include <qmldesignertr.h>
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
#include <QPushButton>
#include <QQuickWidget>
@@ -21,7 +27,21 @@ DesignSystemView::DesignSystemView(ExternalDependenciesInterface &externalDepend
, m_externalDependencies(externalDependencies)
, m_dsStore(std::make_unique<DSStore>(m_externalDependencies, projectStorageDependencies))
, m_dsInterface(m_dsStore.get())
{}
{
connect(ProjectExplorer::ProjectManager::instance(),
&ProjectExplorer::ProjectManager::startupProjectChanged,
this,
[this](ProjectExplorer::Project *) { loadDesignSystem(); });
connect(Core::EditorManager::instance(),
&Core::EditorManager::saved,
this,
[this](Core::IDocument *document) {
if (document->filePath().contains("Generated/DesignSystem")) {
loadDesignSystem();
}
});
}
DesignSystemView::~DesignSystemView() {}
@@ -33,7 +53,9 @@ WidgetInfo DesignSystemView::widgetInfo()
return createWidgetInfo(m_designSystemWidget,
"DesignSystemView",
WidgetInfo::RightPane,
tr("Design System"));
Tr::tr("Design System"),
Tr::tr("Design System view"),
DesignerWidgetFlags::IgnoreErrors);
}
bool DesignSystemView::hasWidget() const
@@ -43,8 +65,10 @@ bool DesignSystemView::hasWidget() const
void DesignSystemView::loadDesignSystem()
{
if (auto err = m_dsStore->load())
qDebug() << *err;
/*This is only used to load internally - when saving we have to take care of reflection.
* Saving should not trigger a load again.
*/
m_dsInterface.loadDesignSystem();
}
} // namespace QmlDesigner

View File

@@ -16,6 +16,7 @@
#include <QLoggingCategory>
#include <QPlainTextEdit>
#include <QScopeGuard>
namespace {
@@ -113,14 +114,17 @@ std::optional<QString> DSStore::load()
std::optional<QString> DSStore::load(const Utils::FilePath &dsModuleDirPath)
{
if (m_blockLoading)
return {};
m_collections.clear();
// 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_collections.clear();
// Parse qmldir
QString qmldirData = QString::fromUtf8(*contents);
QmlDirParser qmlDirParser;
@@ -160,6 +164,9 @@ std::optional<QString> DSStore::save(const Utils::FilePath &moduleDirPath, bool
if (!QDir().mkpath(moduleDirPath.absoluteFilePath().toString()))
return tr("Can not create design system module directory %1.").arg(moduleDirPath.toString());
const QScopeGuard cleanup([&] { m_blockLoading = false; });
m_blockLoading = true;
// dump collections
QStringList singletons;
QStringList errors;

View File

@@ -52,5 +52,6 @@ private:
ExternalDependenciesInterface &m_ed;
ProjectStorageDependencies m_projectStorageDependencies;
DSCollections m_collections;
bool m_blockLoading = false;
};
} // namespace QmlDesigner