From eb68868544d030fe9a5ef9ddeb66ef4c4e7336b3 Mon Sep 17 00:00:00 2001 From: Andrii Semkiv Date: Fri, 11 Apr 2025 11:38:52 +0200 Subject: [PATCH] QML Designer: Load relevant fonts in MCU projects For MCU projects, QDS will use fonts that are closer to those that are used in QUL. This mostly applies to the cases of implicitly assumed fonts (i.e. there's no user-specified font properties). In this case text items will use the font set in `MCU.Config.defaultFontFamily` property, and, if this property is not set, DejaVu Sans, which corresponds to the behavior of Qt for MCUs. Also only the relevant fonts will be suggested in the font picker combobox. These come from the MCU installation and replace the standard set of fonts (Arial, Courier, Times New Roman, etc.) in MCU projects. From a technical perspective: * added a new model - `FontResourceModel` - as font handling logic is now too complex for a more generic `FileResourceModel`. * naturally, some wiring to be able to access MCU font throughout various parts of the project. Fixes: QDS-10597 Fixes: QDS-15079 Fixes: QDS-15156 Change-Id: I25aa4b0f89cca864f8fd41e0812f448fe307b104 Reviewed-by: Knud Dollereder --- .../imports/HelperWidgets/FontComboBox.qml | 17 +- src/plugins/mcusupport/mcusupportplugin.cpp | 27 +++ src/plugins/qmldesigner/CMakeLists.txt | 1 + .../components/integration/designdocument.cpp | 11 ++ .../components/integration/designdocument.h | 1 + .../propertyeditor/fontresourcesmodel.cpp | 156 ++++++++++++++++++ .../propertyeditor/fontresourcesmodel.h | 47 ++++++ .../quick2propertyeditorview.cpp | 2 + .../qmldesigner/designermcumanager.cpp | 17 ++ src/plugins/qmldesigner/designermcumanager.h | 1 + .../qmldesigner/puppetenvironmentbuilder.cpp | 27 ++- .../qmldesigner/puppetenvironmentbuilder.h | 1 + .../buildsystem/projectitem/converters.cpp | 30 +--- .../buildsystem/qmlbuildsystem.cpp | 44 +++-- .../buildsystem/qmlbuildsystem.h | 3 + src/plugins/qmlprojectmanager/qmlproject.cpp | 51 +++++- src/plugins/qmlprojectmanager/qmlproject.h | 7 + .../qmlprojectmanager/qmlprojectconstants.h | 10 ++ .../instances/nodeinstanceserver.cpp | 5 + src/tools/qmlpuppet/qmlpuppet/qmlbase.h | 12 ++ .../qmlpuppet/qmlpuppet/runner/qmlruntime.cpp | 6 + 21 files changed, 417 insertions(+), 59 deletions(-) create mode 100644 src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.cpp create mode 100644 src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.h diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml index 37286ce7701..eee1fd956ba 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml @@ -20,10 +20,9 @@ StudioControls.ComboBox { onTextColorChanged: root.setColor() - FileResourcesModel { + FontResourcesModel { id: fileModel modelNodeBackendProperty: modelNodeBackend - filter: root.fontFilter } DropArea { @@ -59,19 +58,7 @@ StudioControls.ComboBox { } function setupModel() { - // default fonts - var familyNames = ["Arial", "Times New Roman", "Courier", "Verdana", "Tahoma"] - - for (var i = 0; i < fileModel.model.length; ++i) { // add custom fonts - var fontLoader = root.createFontLoader(fileModel.docPath + "/" - + fileModel.model[i].relativeFilePath) - familyNames.push(fontLoader.name) - } - - // Remove duplicate family names - familyNames = [...new Set(familyNames)] - familyNames.sort() - root.model = familyNames + root.model = fileModel.model root.currentIndex = root.find(root.backendValue.value) } diff --git a/src/plugins/mcusupport/mcusupportplugin.cpp b/src/plugins/mcusupport/mcusupportplugin.cpp index ea8ceabae60..f93da50ad78 100644 --- a/src/plugins/mcusupport/mcusupportplugin.cpp +++ b/src/plugins/mcusupport/mcusupportplugin.cpp @@ -3,6 +3,7 @@ #include "mcubuildstep.h" #include "mcukitmanager.h" +#include "mculegacyconstants.h" #include "mcuqmlprojectnode.h" #include "mcusupportconstants.h" #include "mcusupportdevice.h" @@ -16,6 +17,8 @@ #include "test/unittest.h" #endif +#include + #include #include #include @@ -38,6 +41,7 @@ #include #include +#include #include #include @@ -49,6 +53,8 @@ using namespace Core; using namespace ProjectExplorer; +using namespace Qt::Literals::StringLiterals; + namespace McuSupport::Internal { const char setupMcuSupportKits[] = "SetupMcuSupportKits"; @@ -192,6 +198,7 @@ public: void extensionsInitialized() final; Q_INVOKABLE static void updateDeployStep(ProjectExplorer::Target *target, bool enabled); + Q_INVOKABLE static expected_str installationRoot(); }; void McuSupportPlugin::initialize() @@ -286,6 +293,26 @@ void McuSupportPlugin::updateDeployStep(ProjectExplorer::Target *target, bool en MCUBuildStepFactory::updateDeployStep(target, enabled); } +expected_str McuSupportPlugin::installationRoot() +{ + const ProjectExplorer::Kit *kit = MCUBuildStepFactory::findMostRecentQulKit(); + if (kit == nullptr) { + return make_unexpected("No QUL kits installed"_L1); + } + + const auto config + = CMakeProjectManager::CMakeConfigurationKitAspect::configuration(kit).toList(); + const auto key = QString{Internal::Legacy::Constants::QUL_CMAKE_VAR}.toUtf8(); + for (const CMakeProjectManager::CMakeConfigItem &configItem : config) { + if (configItem.key == key) { + return Utils::FilePath::fromUserInput(QString::fromUtf8(configItem.value)); + } + } + return make_unexpected("No QUL installation root ('%1') key in kit '%2' configuration"_L1 + .arg(Internal::Legacy::Constants::QUL_CMAKE_VAR) + .arg(kit->displayName())); +} + } // namespace McuSupport::Internal #include "mcusupportplugin.moc" diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 227e7067dac..e146a30f18e 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -409,6 +409,7 @@ extend_qtc_plugin(QmlDesigner compatibleproperties.cpp compatibleproperties.h designerpropertymap.cpp designerpropertymap.h fileresourcesmodel.cpp fileresourcesmodel.h + fontresourcesmodel.cpp fontresourcesmodel.h itemfiltermodel.cpp itemfiltermodel.h listvalidator.cpp listvalidator.h gradientmodel.cpp gradientmodel.h diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index bb0dc231301..9925f3b785e 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -423,6 +424,16 @@ bool DesignDocument::isQtForMCUsProject() const return false; } +QString DesignDocument::defaultFontFamilyMCU() const +{ + if (m_currentTarget == nullptr) { + return QmlProjectManager::Constants::FALLBACK_MCU_FONT_FAMILY; + } + + return m_currentTarget->additionalData(QmlProjectManager::Constants::customDefaultFontFamilyMCU) + .toString(); +} + Utils::FilePath DesignDocument::projectFolder() const { ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectManager::projectForFile(fileName()); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.h b/src/plugins/qmldesigner/components/integration/designdocument.h index 1f67ff4b30b..eaab3e1a8b1 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.h +++ b/src/plugins/qmldesigner/components/integration/designdocument.h @@ -87,6 +87,7 @@ public: void changeToDocumentModel(); bool isQtForMCUsProject() const; + [[nodiscard]] QString defaultFontFamilyMCU() const; Utils::FilePath projectFolder() const; bool hasProject() const; diff --git a/src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.cpp new file mode 100644 index 00000000000..e8086058385 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.cpp @@ -0,0 +1,156 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "fontresourcesmodel.h" +#include "fileresourcesmodel.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Qt::Literals::StringLiterals; + +namespace { + +QStringList makeFontFilesFilterList() +{ + QStringList filterList; + filterList.reserve(std::size(QmlProjectManager::Constants::QDS_FONT_FILES_FILTERS)); + std::ranges::transform(QmlProjectManager::Constants::QDS_FONT_FILES_FILTERS, + std::back_inserter(filterList), + [](const char *filter) { return QString::fromLatin1(filter); }); + + return filterList; +} + +const QStringList &fontFilesFilterList() +{ + static const QStringList list = makeFontFilesFilterList(); + return list; +} + +QString fontFamily(const QString &fontPath) +{ + const int fontId = QFontDatabase::addApplicationFont(fontPath); + QString fontFamily = QFontDatabase::applicationFontFamilies(fontId).front(); + QFontDatabase::removeApplicationFont(fontId); + return fontFamily; +} + +Utils::expected_str> mcuFonts() +{ + Utils::expected_str mcuFontsDir = QmlProjectManager::mcuFontsDir(); + if (!mcuFontsDir) { + return Utils::make_unexpected(mcuFontsDir.error()); + } + + QSet fonts; + const Utils::FilePaths fontFiles = mcuFontsDir->dirEntries({fontFilesFilterList(), QDir::Files}); + for (const auto &file : fontFiles) { + fonts.insert(fontFamily(file.absoluteFilePath().toFSPathString())); + } + + return fonts; +} + +QSet systemFonts() +{ + QSet defaultFonts{ + "Arial", + "Courier", + "Tahoma", + "Times New Roman", + "Verdana", + }; + + if (QmlDesigner::DesignerMcuManager::instance().isMCUProject()) { + const auto fonts = mcuFonts(); + if (!fonts) { + qWarning() << "Failed to load MCU fonts." << fonts.error(); + return defaultFonts; + } + + return *fonts; + } + + return defaultFonts; +} + +} // namespace + +void FontResourcesModel::registerDeclarativeType() +{ + qmlRegisterType("HelperWidgets", 2, 0, "FontResourcesModel"); +} + +QVariant FontResourcesModel::modelNodeBackend() +{ + return {}; +} + +FontResourcesModel::FontResourcesModel(QObject *parent) + : QObject{parent} + , m_resourceModel{new FileResourcesModel{this}} +{ + m_resourceModel->setFilter(fontFilesFilterList().join(' ')); +} + +void FontResourcesModel::setModelNodeBackend(const QVariant &modelNodeBackend) +{ + m_resourceModel->setModelNodeBackend(modelNodeBackend); +} + +QStringList FontResourcesModel::model() const +{ + QSet fonts; + for (const auto &item : m_resourceModel->model()) { + fonts.insert(fontFamily(item.absoluteFilePath())); + } + + fonts.unite(systemFonts()); + + auto ret = QStringList{fonts.cbegin(), fonts.cend()}; + ret.sort(); + + return ret; +} + +void FontResourcesModel::refreshModel() +{ + m_resourceModel->refreshModel(); +} + +void FontResourcesModel::openFileDialog(const QString &customPath) +{ + m_resourceModel->openFileDialog(customPath); +} + +QString FontResourcesModel::resolve(const QString &relative) const +{ + return m_resourceModel->resolve(relative); +} + +bool FontResourcesModel::isLocal(const QString &path) const +{ + return m_resourceModel->isLocal(path); +} diff --git a/src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.h b/src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.h new file mode 100644 index 00000000000..d317ba284a5 --- /dev/null +++ b/src/plugins/qmldesigner/components/propertyeditor/fontresourcesmodel.h @@ -0,0 +1,47 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "fileresourcesmodel.h" + +#include +#include +#include +#include + +class FontResourcesModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant modelNodeBackendProperty READ modelNodeBackend WRITE setModelNodeBackend + NOTIFY modelNodeBackendChanged) + Q_PROPERTY(QStringList model READ model NOTIFY modelChanged) + +public: + static void registerDeclarativeType(); + [[nodiscard]] static QVariant modelNodeBackend(); + + explicit FontResourcesModel(QObject *parent = nullptr); + + void setModelNodeBackend(const QVariant &modelNodeBackend); + [[nodiscard]] QStringList model() const; + + void refreshModel(); + + Q_INVOKABLE void openFileDialog(const QString &customPath = {}); + Q_INVOKABLE [[nodiscard]] QString resolve(const QString &relative) const; + Q_INVOKABLE [[nodiscard]] bool isLocal(const QString &path) const; + +signals: + void fileNameChanged(const QUrl &fileName); + void filterChanged(const QString &filter); + void modelNodeBackendChanged(); + void pathChanged(const QUrl &path); + void modelChanged(); + +private: + FileResourcesModel *m_resourceModel; +}; + +QML_DECLARE_TYPE(FontResourcesModel) diff --git a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp index 30e9f6908f6..87e92578520 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/quick2propertyeditorview.cpp @@ -13,6 +13,7 @@ #include "bindingeditor/bindingeditor.h" #include "colorpalettebackend.h" #include "fileresourcesmodel.h" +#include "fontresourcesmodel.h" #include "gradientmodel.h" #include "gradientpresetcustomlistmodel.h" #include "gradientpresetdefaultlistmodel.h" @@ -60,6 +61,7 @@ void Quick2PropertyEditorView::registerQmlTypes() declarativeTypesRegistered = true; PropertyEditorValue::registerDeclarativeTypes(); FileResourcesModel::registerDeclarativeType(); + FontResourcesModel::registerDeclarativeType(); GradientModel::registerDeclarativeType(); GradientPresetDefaultListModel::registerDeclarativeType(); GradientPresetCustomListModel::registerDeclarativeType(); diff --git a/src/plugins/qmldesigner/designermcumanager.cpp b/src/plugins/qmldesigner/designermcumanager.cpp index e42c28ac3ef..ab52f579e34 100644 --- a/src/plugins/qmldesigner/designermcumanager.cpp +++ b/src/plugins/qmldesigner/designermcumanager.cpp @@ -8,6 +8,7 @@ #include "designdocument.h" #include +#include #include @@ -45,6 +46,22 @@ QString DesignerMcuManager::mcuResourcesPath() return Core::ICore::resourcePath("qmldesigner/qt4mcu").toUrlishString(); } +QString DesignerMcuManager::defaultFontFamilyMCU() +{ + const QmlDesignerPlugin *designerPlugin = QmlDesigner::QmlDesignerPlugin::instance(); + if (designerPlugin == nullptr) { + return QmlProjectManager::Constants::FALLBACK_MCU_FONT_FAMILY; + } + + const QmlDesigner::DesignDocument *designDocument = designerPlugin->documentManager() + .currentDesignDocument(); + if (designDocument == nullptr) { + return QmlProjectManager::Constants::FALLBACK_MCU_FONT_FAMILY; + } + + return designDocument->defaultFontFamilyMCU(); +} + bool DesignerMcuManager::isMCUProject() const { QmlDesigner::DesignDocument *designDocument = QmlDesigner::QmlDesignerPlugin::instance() diff --git a/src/plugins/qmldesigner/designermcumanager.h b/src/plugins/qmldesigner/designermcumanager.h index 59ea892d468..9ca1c3f9414 100644 --- a/src/plugins/qmldesigner/designermcumanager.h +++ b/src/plugins/qmldesigner/designermcumanager.h @@ -31,6 +31,7 @@ public: static DesignerMcuManager& instance(); static QString mcuResourcesPath(); + static QString defaultFontFamilyMCU(); bool isMCUProject() const; diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index c7488558831..2fca452a123 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -7,14 +7,18 @@ #include +#include +#include +#include #include #include -#include -#include #include #include #include #include +#include +#include +#include #include @@ -51,6 +55,7 @@ QProcessEnvironment PuppetEnvironmentBuilder::processEnvironment() const addCustomFileSelectors(); addDisableDeferredProperties(); addResolveUrlsOnAssignment(); + addMcuFonts(); qCInfo(puppetEnvirmentBuild) << "Puppet environment:" << m_environment.toStringList(); @@ -249,6 +254,24 @@ void PuppetEnvironmentBuilder::addResolveUrlsOnAssignment() const m_environment.set("QML_COMPAT_RESOLVE_URLS_ON_ASSIGNMENT", "true"); } +void PuppetEnvironmentBuilder::addMcuFonts() const +{ + const Utils::expected_str mcuFontsDir = QmlProjectManager::mcuFontsDir(); + if (!mcuFontsDir) { + qCWarning(puppetEnvirmentBuild) + << "Failed to locate MCU installation." << mcuFontsDir.error(); + return; + } + + m_environment.set(QmlProjectManager::Constants::QMLPUPPET_ENV_MCU_FONTS_DIR, + mcuFontsDir->toUserOutput()); + if (QmlDesigner::DesignerMcuManager::instance().isMCUProject()) { + const QString defaultFontFamily = DesignerMcuManager::defaultFontFamilyMCU(); + m_environment.set(QmlProjectManager::Constants::QMLPUPPET_ENV_DEFAULT_FONT_FAMILY, + defaultFontFamily); + } +} + PuppetType PuppetEnvironmentBuilder::determinePuppetType() const { if (m_target && m_target->kit() && m_target->kit()->isValid()) { diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.h b/src/plugins/qmldesigner/puppetenvironmentbuilder.h index 8179a36b90d..5b69309ff52 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.h +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.h @@ -51,6 +51,7 @@ private: void addCustomFileSelectors() const; void addDisableDeferredProperties() const; void addResolveUrlsOnAssignment() const; + void addMcuFonts() const; private: ProjectExplorer::Target *m_target = nullptr; diff --git a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp index 26e31d71f45..e43b5418528 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/projectitem/converters.cpp @@ -4,6 +4,7 @@ #include "converters.h" #include "utils/algorithm.h" #include "../../qmlprojectexporter/filetypes.h" +#include "qmlprojectconstants.h" #include @@ -11,27 +12,6 @@ namespace QmlProjectManager::Converters { const static QStringList qmlFilesFilter{QStringLiteral("*.qml")}; const static QStringList javaScriptFilesFilter{QStringLiteral("*.js"), QStringLiteral("*.ts")}; -const static QStringList fontFilesFilter{ - QStringLiteral("*.afm"), - QStringLiteral("*.bdf"), - QStringLiteral("*.ccc"), - QStringLiteral("*.cff"), - QStringLiteral("*.fmp"), - QStringLiteral("*.fnt"), - QStringLiteral("*.otc"), - QStringLiteral("*.otf"), - QStringLiteral("*.pcf"), - QStringLiteral("*.pfa"), - QStringLiteral("*.pfb"), - QStringLiteral("*.pfm"), - QStringLiteral("*.pfr"), - QStringLiteral("*.ttc"), - QStringLiteral("*.ttcf"), - QStringLiteral("*.tte"), - QStringLiteral("*.ttf"), - QStringLiteral("*.woff"), - QStringLiteral("*.woff2"), -}; const QStringList imageFilesFilter() { return imageFiles([](const QString& suffix) { return "*." + suffix; }); @@ -486,12 +466,12 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) // and all files are prefixed such as "directory/". // if directory is empty, then the files are prefixed with the project directory if (childNodeFiles.empty()) { - auto inserter = [&childNodeFilters](const QStringList &filterSource) { + auto inserter = [&childNodeFilters](auto &filterSource) { if (!childNodeFilters.empty()) return; - std::for_each(filterSource.begin(), - filterSource.end(), + std::for_each(std::cbegin(filterSource), + std::cend(filterSource), [&childNodeFilters](const auto &value) { if (!childNodeFilters.contains(value)) { childNodeFilters << value; @@ -510,7 +490,7 @@ QJsonObject qmlProjectTojson(const Utils::FilePath &projectFile) } else if (childNodeName == "imagefiles") { inserter(imageFilesFilter()); } else if (childNodeName == "fontfiles") { - inserter(fontFilesFilter); + inserter(QmlProjectManager::Constants::QDS_FONT_FILES_FILTERS); } } diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 5f60c0f61cb..802cb6c733d 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -22,10 +22,6 @@ #include #include -#include -#include -#include - #include #include #include @@ -51,17 +47,6 @@ namespace { Q_LOGGING_CATEGORY(infoLogger, "QmlProjectManager.QmlBuildSystem", QtInfoMsg) } -ExtensionSystem::IPlugin *findMcuSupportPlugin() -{ - const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault( - ExtensionSystem::PluginManager::plugins(), - Utils::equal(&ExtensionSystem::PluginSpec::id, QString("mcusupport"))); - - if (pluginSpec) - return pluginSpec->plugin(); - return nullptr; -} - void updateMcuBuildStep(Target *target, bool mcuEnabled) { if (auto plugin = findMcuSupportPlugin()) { @@ -123,6 +108,18 @@ void QmlBuildSystem::updateDeploymentData() setDeploymentData(deploymentData); } +QString QmlBuildSystem::defaultFontFamilyMCU() const +{ + const QJsonObject project = m_projectItem->project(); + QString defaultFontFamily = project["mcu"].toObject()["config"].toObject()["defaultFontFamily"].toString(); + + if (!defaultFontFamily.isEmpty()) { + return defaultFontFamily; + } + + return QmlProjectManager::Constants::FALLBACK_MCU_FONT_FAMILY; +} + //probably this method needs to be moved into QmlProjectPlugin::initialize to be called only once void QmlBuildSystem::registerMenuButtons() { @@ -607,6 +604,8 @@ QVariant QmlBuildSystem::additionalData(Utils::Id id) const return mainFilePath().toUrlishString(); if (id == Constants::canonicalProjectDir) return canonicalProjectDir().toUrlishString(); + if (id == Constants::customDefaultFontFamilyMCU) + return defaultFontFamilyMCU(); return {}; } @@ -701,7 +700,20 @@ bool QmlBuildSystem::qt6Project() const Utils::EnvironmentItems QmlBuildSystem::environment() const { - return m_projectItem->environment(); + Utils::EnvironmentItems env = m_projectItem->environment(); + + Utils::expected_str fontsDir = mcuFontsDir(); + if (!fontsDir) { + qWarning() << "Failed to locate MCU installation." << fontsDir.error(); + return env; + } + + env.append({Constants::QMLPUPPET_ENV_MCU_FONTS_DIR, fontsDir->toUserOutput()}); + if (qtForMCUs()) { + env.append({Constants::QMLPUPPET_ENV_DEFAULT_FONT_FAMILY, defaultFontFamilyMCU()}); + } + + return env; } QStringList QmlBuildSystem::fileSelectors() const diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index 2017f9b3f77..83e4e9c341f 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -144,6 +144,9 @@ private: void registerMenuButtons(); void updateDeploymentData(); + + [[nodiscard]] QString defaultFontFamilyMCU() const; + friend class FilesUpdateBlocker; QmlProjectExporter::Exporter* m_fileGen; diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index c37e56a6eaa..3a02801f03f 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -10,6 +10,10 @@ #include #include +#include +#include +#include + #include #include #include @@ -26,10 +30,12 @@ #include #include +#include #include #include -#include +#include #include +#include #include #include @@ -40,9 +46,52 @@ using namespace Core; using namespace ProjectExplorer; using namespace Utils; +using namespace Qt::Literals::StringLiterals; + +namespace { +expected_str mcuInstallationRoot() +{ + ExtensionSystem::IPlugin *mcuSupportPlugin = QmlProjectManager::findMcuSupportPlugin(); + if (mcuSupportPlugin == nullptr) { + return make_unexpected("Failed to find MCU Support plugin"_L1); + } + + expected_str root; + QMetaObject::invokeMethod(mcuSupportPlugin, + "installationRoot", + Qt::DirectConnection, + Q_RETURN_ARG(expected_str, root)); + + return root; +} +} // namespace namespace QmlProjectManager { +ExtensionSystem::IPlugin *findMcuSupportPlugin() +{ + const QString pluginId = "mcusupport"; + const ExtensionSystem::PluginSpec *pluginSpec = Utils::findOrDefault( + ExtensionSystem::PluginManager::plugins(), + Utils::equal(&ExtensionSystem::PluginSpec::id, pluginId)); + + if (pluginSpec == nullptr) { + return nullptr; + } + + return pluginSpec->plugin(); +} + +expected_str mcuFontsDir() +{ + expected_str mcuRoot = mcuInstallationRoot(); + if (!mcuRoot) { + return mcuRoot; + } + + return *mcuRoot / "src" / "3rdparty" / "fonts"; +} + QmlProject::QmlProject(const Utils::FilePath &fileName) : Project(Utils::Constants::QMLPROJECT_MIMETYPE, fileName) { diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index 39d903bffc9..04b69d8f548 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -5,7 +5,11 @@ #include "buildsystem/qmlbuildsystem.h" // IWYU pragma: keep #include "qmlprojectmanager_global.h" + +#include #include +#include +#include #include @@ -13,6 +17,9 @@ namespace QmlProjectManager { class QmlProject; +ExtensionSystem::IPlugin *findMcuSupportPlugin(); +QMLPROJECTMANAGER_EXPORT Utils::expected_str mcuFontsDir(); + class QMLPROJECTMANAGER_EXPORT QmlProject : public ProjectExplorer::Project { Q_OBJECT diff --git a/src/plugins/qmlprojectmanager/qmlprojectconstants.h b/src/plugins/qmlprojectmanager/qmlprojectconstants.h index 0d0a35e884a..26a98635109 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectconstants.h +++ b/src/plugins/qmlprojectmanager/qmlprojectconstants.h @@ -11,6 +11,7 @@ const char primaryLanguageData[] = "PrimaryLanguageData"; const char customForceFreeTypeData[] = "CustomForceFreeType"; const char customQtForMCUs[] = "CustomQtForMCUs"; const char customQt6Project[] = "CustomQt6Project"; +const char customDefaultFontFamilyMCU[] = "CustomDefaultFontFamilyMCU"; const char mainFilePath[] = "MainFilePath"; const char canonicalProjectDir[] ="CanonicalProjectDir"; @@ -37,4 +38,13 @@ const char G_EXPORT_CONVERT[] = "QmlDesigner.Group.ConvertProject"; const char fakeProjectName[] = "fake85673.qmlproject"; +inline constexpr const char *QDS_FONT_FILES_FILTERS[] = { + "*.afm", "*.bdf", "*.ccc", "*.cff", "*.fmp", "*.fnt", "*.otc", "*.otf", "*.pcf", "*.pfa", + "*.pfb", "*.pfm", "*.pfr", "*.ttc", "*.ttcf", "*.tte", "*.ttf", "*.woff", "*.woff2", +}; +constexpr char FALLBACK_MCU_FONT_FAMILY[] = "DejaVu Sans"; +// These constants should be kept in sync with their counterparts in qmlbase.h +constexpr char QMLPUPPET_ENV_MCU_FONTS_DIR[] = "QMLPUPPET_MCU_FONTS_DIR"; +constexpr char QMLPUPPET_ENV_DEFAULT_FONT_FAMILY[] = "QMLPUPPET_DEFAULT_FONT_FAMILY"; + } // QmlProjectManager::Constants diff --git a/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserver.cpp b/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserver.cpp index eb8d2083e2e..1761ea45dbb 100644 --- a/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserver.cpp +++ b/src/tools/qmlpuppet/qmlpuppet/instances/nodeinstanceserver.cpp @@ -8,6 +8,7 @@ #include "childrenchangeeventfilter.h" #include "dummycontextobject.h" +#include "qmlpuppet/qmlbase.h" #include #include @@ -312,6 +313,10 @@ void NodeInstanceServer::stopRenderTimer() void NodeInstanceServer::createScene(const CreateSceneCommand &command) { initializeView(); + if (const QString mcuFontsFolder = qEnvironmentVariable(QmlBase::QMLPUPPET_ENV_MCU_FONTS_DIR); + !mcuFontsFolder.isEmpty()) { + registerFonts(QUrl::fromLocalFile(mcuFontsFolder)); + } registerFonts(command.resourceUrl); setTranslationLanguage(command.language); diff --git a/src/tools/qmlpuppet/qmlpuppet/qmlbase.h b/src/tools/qmlpuppet/qmlpuppet/qmlbase.h index 7c1333544a9..6f461bb5a75 100644 --- a/src/tools/qmlpuppet/qmlpuppet/qmlbase.h +++ b/src/tools/qmlpuppet/qmlpuppet/qmlbase.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -13,6 +14,10 @@ class QmlBase : public QObject { Q_OBJECT public: + // These constants should be kept in sync with their counterparts in qmlprojectconstants.h + static constexpr char QMLPUPPET_ENV_MCU_FONTS_DIR[] = "QMLPUPPET_MCU_FONTS_DIR"; + static constexpr char QMLPUPPET_ENV_DEFAULT_FONT_FAMILY[] = "QMLPUPPET_DEFAULT_FONT_FAMILY"; + struct AppArgs { public: @@ -65,6 +70,13 @@ protected: void createCoreApp() { m_coreApp.reset(new T(m_args.argc, m_args.argv)); + if (const QString defaultFontFamily = qEnvironmentVariable( + QMLPUPPET_ENV_DEFAULT_FONT_FAMILY); + !defaultFontFamily.isEmpty()) { + if (qobject_cast(QCoreApplication::instance()) != nullptr) { + QGuiApplication::setFont(QFont{defaultFontFamily}); + } + } } QSharedPointer m_coreApp; diff --git a/src/tools/qmlpuppet/qmlpuppet/runner/qmlruntime.cpp b/src/tools/qmlpuppet/qmlpuppet/runner/qmlruntime.cpp index 2876339a903..063ae273790 100644 --- a/src/tools/qmlpuppet/qmlpuppet/runner/qmlruntime.cpp +++ b/src/tools/qmlpuppet/qmlpuppet/runner/qmlruntime.cpp @@ -162,6 +162,12 @@ void QmlRuntime::initCoreApp() void QmlRuntime::initQmlRunner() { registerFonts(findProjectFolder(QDir::current())); + + if (const QString mcuFontsFolder = qEnvironmentVariable(QmlBase::QMLPUPPET_ENV_MCU_FONTS_DIR); + !mcuFontsFolder.isEmpty()) { + registerFonts(mcuFontsFolder); + } + m_qmlEngine.reset(new QQmlApplicationEngine()); QStringList files;