forked from qt-creator/qt-creator
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:
committed by
Thomas Hartmann
parent
b9349b9c22
commit
b02f8123b0
@@ -44,7 +44,21 @@ Rectangle {
|
|||||||
height: 400
|
height: 400
|
||||||
color: StudioTheme.Values.themePanelBackground
|
color: StudioTheme.Values.themePanelBackground
|
||||||
|
|
||||||
|
function clearModel() {
|
||||||
|
root.currentCollectionName = ""
|
||||||
|
tableView.model = null
|
||||||
|
|
||||||
|
topLeftCell.visible = false
|
||||||
|
createModeButton.enabled = false
|
||||||
|
modelConnections.target = null
|
||||||
|
}
|
||||||
|
|
||||||
function loadModel(name) {
|
function loadModel(name) {
|
||||||
|
if (name === undefined) {
|
||||||
|
clearModel()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
root.currentCollectionName = name
|
root.currentCollectionName = name
|
||||||
tableView.model = DesignSystemBackend.dsInterface.model(name)
|
tableView.model = DesignSystemBackend.dsInterface.model(name)
|
||||||
|
|
||||||
@@ -256,6 +270,7 @@ Rectangle {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
actionIndicatorVisible: false
|
actionIndicatorVisible: false
|
||||||
model: DesignSystemBackend.dsInterface.collections
|
model: DesignSystemBackend.dsInterface.collections
|
||||||
|
enabled: collectionsComboBox.count
|
||||||
onActivated: root.loadModel(collectionsComboBox.currentText)
|
onActivated: root.loadModel(collectionsComboBox.currentText)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,7 +279,8 @@ Rectangle {
|
|||||||
style: StudioTheme.Values.viewBarControlStyle
|
style: StudioTheme.Values.viewBarControlStyle
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
actionIndicatorVisible: false
|
actionIndicatorVisible: false
|
||||||
model: tableView.model.themeNames
|
model: tableView.model?.themeNames ?? null
|
||||||
|
enabled: tableView.model
|
||||||
onActivated: tableView.model.setActiveTheme(themesComboBox.currentText)
|
onActivated: tableView.model.setActiveTheme(themesComboBox.currentText)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,6 +290,7 @@ Rectangle {
|
|||||||
buttonIcon: StudioTheme.Constants.more_medium
|
buttonIcon: StudioTheme.Constants.more_medium
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: moreMenu.visible
|
checked: moreMenu.visible
|
||||||
|
tooltip: qsTr("More options")
|
||||||
|
|
||||||
onToggled: {
|
onToggled: {
|
||||||
if (moreMenu.visible)
|
if (moreMenu.visible)
|
||||||
@@ -307,12 +324,21 @@ Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO this is only for debugging purposes
|
// 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
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: qsTr("load")
|
buttonIcon: StudioTheme.Constants.updateContent_medium
|
||||||
|
tooltip: qsTr("Refresh")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
DesignSystemBackend.dsInterface.loadDesignSystem()
|
DesignSystemBackend.dsInterface.loadDesignSystem()
|
||||||
root.loadModel(DesignSystemBackend.dsInterface.collections[0])
|
//root.loadModel(DesignSystemBackend.dsInterface.collections[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,10 +10,14 @@
|
|||||||
|
|
||||||
namespace QmlDesigner {
|
namespace QmlDesigner {
|
||||||
|
|
||||||
CollectionModel::CollectionModel(DSThemeManager *collection, const DSStore *store)
|
CollectionModel::CollectionModel(DSThemeManager *collection, DSStore *store)
|
||||||
: m_collection(collection)
|
: m_collection(collection)
|
||||||
, m_store(store)
|
, m_store(store)
|
||||||
{
|
{
|
||||||
|
m_saveCompressionTimer.setSingleShot(true);
|
||||||
|
m_saveCompressionTimer.setInterval(200);
|
||||||
|
connect(&m_saveCompressionTimer, &QTimer::timeout, this, &CollectionModel::save);
|
||||||
|
|
||||||
updateCache();
|
updateCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,8 +32,10 @@ QStringList CollectionModel::themeNameList() const
|
|||||||
|
|
||||||
void CollectionModel::setActiveTheme(const QString &themeName)
|
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);
|
m_collection->setActiveTheme(*themeId);
|
||||||
|
aboutToSave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int CollectionModel::columnCount(const QModelIndex &parent) const
|
int CollectionModel::columnCount(const QModelIndex &parent) const
|
||||||
@@ -149,6 +155,7 @@ bool CollectionModel::insertColumns([[maybe_unused]] int column, int count, cons
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
aboutToSave();
|
||||||
emit themeNameChanged();
|
emit themeNameChanged();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -166,6 +173,7 @@ bool CollectionModel::removeColumns(int column, int count, const QModelIndex &pa
|
|||||||
|
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
aboutToSave();
|
||||||
emit themeNameChanged();
|
emit themeNameChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -183,6 +191,7 @@ bool CollectionModel::removeRows(int row, int count, const QModelIndex &parent)
|
|||||||
}
|
}
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
aboutToSave();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +216,8 @@ void CollectionModel::addProperty(GroupType group, const QString &name, const QV
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
|
aboutToSave();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +238,9 @@ bool CollectionModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aboutToSave();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,6 +275,8 @@ bool CollectionModel::setHeaderData(int section,
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
updateCache();
|
updateCache();
|
||||||
endResetModel();
|
endResetModel();
|
||||||
|
|
||||||
|
aboutToSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
return success;
|
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 {});
|
QTC_ASSERT(row > -1 && row < static_cast<int>(m_propertyInfoList.size()), return {});
|
||||||
return m_propertyInfoList[row];
|
return m_propertyInfoList[row];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectionModel::save()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(m_store, return);
|
||||||
|
m_store->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CollectionModel::aboutToSave()
|
||||||
|
{
|
||||||
|
m_saveCompressionTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <designsystem/dsconstants.h>
|
#include <designsystem/dsconstants.h>
|
||||||
#include <QAbstractItemModel>
|
#include <QAbstractItemModel>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ public:
|
|||||||
|
|
||||||
Q_PROPERTY(QStringList themeNames READ themeNameList NOTIFY themeNameChanged FINAL)
|
Q_PROPERTY(QStringList themeNames READ themeNameList NOTIFY themeNameChanged FINAL)
|
||||||
|
|
||||||
CollectionModel(DSThemeManager *collection, const DSStore *store);
|
CollectionModel(DSThemeManager *collection, DSStore *store);
|
||||||
|
|
||||||
QStringList themeNameList() const;
|
QStringList themeNameList() const;
|
||||||
Q_INVOKABLE void setActiveTheme(const QString &themeName);
|
Q_INVOKABLE void setActiveTheme(const QString &themeName);
|
||||||
@@ -72,12 +73,17 @@ 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;
|
||||||
|
|
||||||
|
void save();
|
||||||
|
void aboutToSave();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DSThemeManager *m_collection = nullptr;
|
DSThemeManager *m_collection = nullptr;
|
||||||
const DSStore *m_store;
|
DSStore *m_store;
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
std::vector<ThemeId> m_themeIdList;
|
std::vector<ThemeId> m_themeIdList;
|
||||||
std::vector<PropInfo> m_propertyInfoList;
|
std::vector<PropInfo> m_propertyInfoList;
|
||||||
|
|
||||||
|
QTimer m_saveCompressionTimer;
|
||||||
};
|
};
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -22,7 +22,10 @@ DesignSystemInterface::~DesignSystemInterface() {}
|
|||||||
void DesignSystemInterface::loadDesignSystem()
|
void DesignSystemInterface::loadDesignSystem()
|
||||||
{
|
{
|
||||||
m_models.clear();
|
m_models.clear();
|
||||||
m_store->load();
|
|
||||||
|
if (auto err = m_store->load())
|
||||||
|
qDebug() << err;
|
||||||
|
|
||||||
emit collectionsChanged();
|
emit collectionsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,13 @@
|
|||||||
#include "designsystemwidget.h"
|
#include "designsystemwidget.h"
|
||||||
#include "dsstore.h"
|
#include "dsstore.h"
|
||||||
|
|
||||||
|
#include <qmldesignertr.h>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
|
||||||
|
#include <projectexplorer/project.h>
|
||||||
|
#include <projectexplorer/projectmanager.h>
|
||||||
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QQuickWidget>
|
#include <QQuickWidget>
|
||||||
@@ -21,7 +27,21 @@ DesignSystemView::DesignSystemView(ExternalDependenciesInterface &externalDepend
|
|||||||
, m_externalDependencies(externalDependencies)
|
, m_externalDependencies(externalDependencies)
|
||||||
, m_dsStore(std::make_unique<DSStore>(m_externalDependencies, projectStorageDependencies))
|
, m_dsStore(std::make_unique<DSStore>(m_externalDependencies, projectStorageDependencies))
|
||||||
, m_dsInterface(m_dsStore.get())
|
, 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() {}
|
DesignSystemView::~DesignSystemView() {}
|
||||||
|
|
||||||
@@ -33,7 +53,9 @@ WidgetInfo DesignSystemView::widgetInfo()
|
|||||||
return createWidgetInfo(m_designSystemWidget,
|
return createWidgetInfo(m_designSystemWidget,
|
||||||
"DesignSystemView",
|
"DesignSystemView",
|
||||||
WidgetInfo::RightPane,
|
WidgetInfo::RightPane,
|
||||||
tr("Design System"));
|
Tr::tr("Design System"),
|
||||||
|
Tr::tr("Design System view"),
|
||||||
|
DesignerWidgetFlags::IgnoreErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DesignSystemView::hasWidget() const
|
bool DesignSystemView::hasWidget() const
|
||||||
@@ -43,8 +65,10 @@ bool DesignSystemView::hasWidget() const
|
|||||||
|
|
||||||
void DesignSystemView::loadDesignSystem()
|
void DesignSystemView::loadDesignSystem()
|
||||||
{
|
{
|
||||||
if (auto err = m_dsStore->load())
|
/*This is only used to load internally - when saving we have to take care of reflection.
|
||||||
qDebug() << *err;
|
* Saving should not trigger a load again.
|
||||||
|
*/
|
||||||
|
m_dsInterface.loadDesignSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
|
#include <QScopeGuard>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@@ -113,14 +114,17 @@ std::optional<QString> DSStore::load()
|
|||||||
|
|
||||||
std::optional<QString> DSStore::load(const Utils::FilePath &dsModuleDirPath)
|
std::optional<QString> DSStore::load(const Utils::FilePath &dsModuleDirPath)
|
||||||
{
|
{
|
||||||
|
if (m_blockLoading)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
m_collections.clear();
|
||||||
|
|
||||||
// read qmldir
|
// read qmldir
|
||||||
const auto qmldirFile = dsModuleDirPath / "qmldir";
|
const auto qmldirFile = dsModuleDirPath / "qmldir";
|
||||||
const Utils::expected_str<QByteArray> contents = qmldirFile.fileContents();
|
const Utils::expected_str<QByteArray> contents = qmldirFile.fileContents();
|
||||||
if (!contents)
|
if (!contents)
|
||||||
return tr("Can not read Design System qmldir");
|
return tr("Can not read Design System qmldir");
|
||||||
|
|
||||||
m_collections.clear();
|
|
||||||
|
|
||||||
// Parse qmldir
|
// Parse qmldir
|
||||||
QString qmldirData = QString::fromUtf8(*contents);
|
QString qmldirData = QString::fromUtf8(*contents);
|
||||||
QmlDirParser qmlDirParser;
|
QmlDirParser qmlDirParser;
|
||||||
@@ -160,6 +164,9 @@ std::optional<QString> DSStore::save(const Utils::FilePath &moduleDirPath, bool
|
|||||||
if (!QDir().mkpath(moduleDirPath.absoluteFilePath().toString()))
|
if (!QDir().mkpath(moduleDirPath.absoluteFilePath().toString()))
|
||||||
return tr("Can not create design system module directory %1.").arg(moduleDirPath.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
|
// dump collections
|
||||||
QStringList singletons;
|
QStringList singletons;
|
||||||
QStringList errors;
|
QStringList errors;
|
||||||
|
@@ -52,5 +52,6 @@ private:
|
|||||||
ExternalDependenciesInterface &m_ed;
|
ExternalDependenciesInterface &m_ed;
|
||||||
ProjectStorageDependencies m_projectStorageDependencies;
|
ProjectStorageDependencies m_projectStorageDependencies;
|
||||||
DSCollections m_collections;
|
DSCollections m_collections;
|
||||||
|
bool m_blockLoading = false;
|
||||||
};
|
};
|
||||||
} // namespace QmlDesigner
|
} // namespace QmlDesigner
|
||||||
|
Reference in New Issue
Block a user