From a6fabc48a39fd9d0db55d3153164b8ee48813738 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 3 Jun 2024 09:42:33 +0200 Subject: [PATCH 01/69] Utils: Add effective binary getter to FilePathAspect It is quite common to provide a single command as default value for commands to be executed. In several cases we fail to handle this correctly when trying to validate or using the executable although it is validated correctly inside the underlying path chooser. This patch provides a method to explicitly get the effective binary, means either the configured path or if it just had been a single command it retrieves the full path information by looking up the command in PATH. This functionality is limited to non-device file paths which are expected to be an (optionally existing) command. Change-Id: I17db69f546f5fb9fec8809db91232f212d21e9a6 Reviewed-by: Marcus Tillmanns --- src/libs/utils/aspects.cpp | 35 ++++++++++++++++++++++++++++++++--- src/libs/utils/aspects.h | 1 + 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index bc233ed188a..13db6a1ecef 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -1410,6 +1410,7 @@ public: FilePath m_baseFileName; StringAspect::ValueAcceptor m_valueAcceptor; std::optional m_validator; + std::optional m_effectiveBinary; std::function m_openTerminal; CheckableAspectImplementation m_checkerImpl; @@ -1430,6 +1431,8 @@ FilePathAspect::FilePathAspect(AspectContainer *container) addDataExtractor(this, &FilePathAspect::value, &Data::value); addDataExtractor(this, &FilePathAspect::operator(), &Data::filePath); + + connect(this, &BaseAspect::changed, this, [this] { d->m_effectiveBinary.reset(); }); } FilePathAspect::~FilePathAspect() = default; @@ -1455,6 +1458,29 @@ FilePath FilePathAspect::expandedValue() const return FilePath::fromUserInput(value); } +/*! + Returns the full path of the set command. Only makes a difference if + expected kind is \c Command or \c ExistingCommand and the current + file path is an executable provided without its path. + Performs a lookup in PATH if necessary. + */ +FilePath FilePathAspect::effectiveBinary() const +{ + if (d->m_effectiveBinary) + return *d->m_effectiveBinary; + + const FilePath current = expandedValue(); + const PathChooser::Kind kind = d->m_expectedKind; + if (kind != PathChooser::ExistingCommand && kind != PathChooser::Command) + return current; + + if (current.needsDevice()) + return current; + + d->m_effectiveBinary.emplace(current.searchInPath()); + return *d->m_effectiveBinary; +} + QString FilePathAspect::value() const { return TypedAspect::value(); @@ -1693,9 +1719,12 @@ void FilePathAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished) */ void FilePathAspect::setExpectedKind(const PathChooser::Kind expectedKind) { - d->m_expectedKind = expectedKind; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setExpectedKind(expectedKind); + if (d->m_expectedKind != expectedKind) { + d->m_expectedKind = expectedKind; + d->m_effectiveBinary.reset(); + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setExpectedKind(expectedKind); + } } void FilePathAspect::setEnvironment(const Environment &env) diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index 47aeb35458b..8faeaf7a9e3 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -663,6 +663,7 @@ public: }; FilePath operator()() const; + FilePath effectiveBinary() const; FilePath expandedValue() const; QString value() const; void setValue(const FilePath &filePath, Announcement howToAnnounce = DoEmit); From c3a1b41fb33f105c27af0edc8f1c34e8ac826555 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Thu, 30 May 2024 15:10:23 +0200 Subject: [PATCH 02/69] Cppcheck: Fix handling of default executable On Unix we set the default just to cppcheck and mark it even as valid if it can be found in PATH. But when running we silently fail as we expect a full path before launching the executable. Use the effective binary instead. Change-Id: I03393b1d227c595da5142fce6b8bea5210a15747 Reviewed-by: Marcus Tillmanns Reviewed-by: Orgad Shaneh --- src/plugins/cppcheck/cppchecktool.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp index 84479a78be5..85a17d92df4 100644 --- a/src/plugins/cppcheck/cppchecktool.cpp +++ b/src/plugins/cppcheck/cppchecktool.cpp @@ -101,7 +101,7 @@ void CppcheckTool::updateArguments() arguments.push_back("--template=\"{file},{line},{severity},{id},{message}\""); - m_runner->reconfigure(s.binary(), arguments.join(' ')); + m_runner->reconfigure(s.binary.effectiveBinary(), arguments.join(' ')); } QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part) const From f870e7ffdfa04cc657571123ed61f045a2a9377a Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 27 May 2024 15:53:07 +0200 Subject: [PATCH 03/69] FindFilter: Move settings saving to Store This makes it possible to save it for the session instead/in addition. Task-number: QTCREATORBUG-793 Change-Id: I95bc20f4912a97863cb88849e32699a689ba6f3f Reviewed-by: David Schulz --- src/plugins/coreplugin/find/ifindfilter.cpp | 68 +++++++++++++++---- src/plugins/coreplugin/find/ifindfilter.h | 10 ++- src/plugins/cppeditor/symbolsfindfilter.cpp | 24 ++++--- src/plugins/cppeditor/symbolsfindfilter.h | 7 +- src/plugins/git/gitgrep.cpp | 9 +-- src/plugins/git/gitgrep.h | 4 +- .../projectexplorer/allprojectsfind.cpp | 19 +++--- src/plugins/projectexplorer/allprojectsfind.h | 8 ++- .../projectexplorer/currentprojectfind.cpp | 26 ++++--- .../filesinallprojectsfind.cpp | 19 +++--- .../projectexplorer/filesinallprojectsfind.h | 7 +- .../findinfilessilversearcher.cpp | 9 +-- src/plugins/texteditor/basefilefind.cpp | 41 +++++------ src/plugins/texteditor/basefilefind.h | 9 +-- src/plugins/texteditor/findincurrentfile.cpp | 26 ++++--- src/plugins/texteditor/findinfiles.cpp | 19 +++--- src/plugins/texteditor/findinfiles.h | 7 +- src/plugins/texteditor/findinopenfiles.cpp | 26 ++++--- 18 files changed, 216 insertions(+), 122 deletions(-) diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp index 49a891e659f..eca6c4f2a91 100644 --- a/src/plugins/coreplugin/find/ifindfilter.cpp +++ b/src/plugins/coreplugin/find/ifindfilter.cpp @@ -6,6 +6,8 @@ #include "../coreicons.h" #include "../coreplugintr.h" +#include + #include #include #include @@ -177,18 +179,6 @@ using namespace Utils; dialog. It will be reparented and deleted by the find plugin. */ -/*! - \fn void Core::IFindFilter::writeSettings(Utils::QtcSettings *settings) - Called at shutdown to write the state of the additional options - for this find filter to the \a settings. -*/ - -/*! - \fn void Core::IFindFilter::readSettings(Utils::QtcSettings *settings) - Called at startup to read the state of the additional options - for this find filter from the \a settings. -*/ - /*! \fn void Core::IFindFilter::enabledChanged(bool enabled) @@ -264,6 +254,60 @@ FindFlags IFindFilter::supportedFindFlags() const | FindWholeWords; } +/*! + Returns a Store with the find filter's settings to store + in the session. Default values should not be saved. + The default implementation returns an empty store. + + \sa restore() +*/ +Store IFindFilter::save() const +{ + return {}; +} + +/*! + Restores the find filter's settings from the Store \a s. + Settings that are not present in the store should be reset to + the default. + The default implementation does nothing. + + \sa save() +*/ +void IFindFilter::restore([[maybe_unused]] const Utils::Store &s) {} + +/*! + Called at shutdown to write the state of the additional options + for this find filter to the \a settings. + + \deprecated [14.0] Implement save() instead. +*/ +void IFindFilter::writeSettings(Utils::QtcSettings *settings) +{ + settings->remove(settingsKey()); // make sure defaults are removed + storeToSettings(settingsKey(), settings, save()); +} + +/*! + Called at startup to read the state of the additional options + for this find filter from the \a settings. + + \deprecated [14.0] Implement restore() instead. +*/ +void IFindFilter::readSettings(Utils::QtcSettings *settings) +{ + restore(storeFromSettings(settingsKey(), settings)); +} + +/*! + \internal + \deprecated [14.0] +*/ +QByteArray IFindFilter::settingsKey() const +{ + return id().toUtf8(); +} + /*! Returns icons for the find flags \a flags. */ diff --git a/src/plugins/coreplugin/find/ifindfilter.h b/src/plugins/coreplugin/find/ifindfilter.h index 2d6325eda01..a76dd2808f9 100644 --- a/src/plugins/coreplugin/find/ifindfilter.h +++ b/src/plugins/coreplugin/find/ifindfilter.h @@ -6,6 +6,7 @@ #include "../core_global.h" #include +#include QT_BEGIN_NAMESPACE class QWidget; @@ -42,8 +43,13 @@ public: { Q_UNUSED(txt) Q_UNUSED(findFlags) } virtual QWidget *createConfigWidget() { return nullptr; } - virtual void writeSettings(Utils::QtcSettings *settings) { Q_UNUSED(settings) } - virtual void readSettings(Utils::QtcSettings *settings) { Q_UNUSED(settings) } + virtual Utils::Store save() const; + virtual void restore(const Utils::Store &s); + + // deprecated in 14.0 + virtual void writeSettings(Utils::QtcSettings *settings); + virtual void readSettings(Utils::QtcSettings *settings); + virtual QByteArray settingsKey() const; static QPixmap pixmapForFindFlags(Utils::FindFlags flags); static QString descriptionForFindFlags(Utils::FindFlags flags); diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp index f53fd34c4d1..c1e50b1f10f 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.cpp +++ b/src/plugins/cppeditor/symbolsfindfilter.cpp @@ -166,26 +166,28 @@ QWidget *SymbolsFindFilter::createConfigWidget() return new SymbolsFindFilterConfigWidget(this); } -void SymbolsFindFilter::writeSettings(QtcSettings *settings) +Store SymbolsFindFilter::save() const { - settings->beginGroup(SETTINGS_GROUP); - settings->setValue(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch)); - settings->setValue(SETTINGS_SEARCHSCOPE, int(m_scope)); - settings->endGroup(); + Store s; + s.insert(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch)); + s.insert(SETTINGS_SEARCHSCOPE, int(m_scope)); + return s; } -void SymbolsFindFilter::readSettings(QtcSettings *settings) +void SymbolsFindFilter::restore(const Utils::Store &s) { - settings->beginGroup(SETTINGS_GROUP); m_symbolsToSearch = static_cast( - settings->value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt()); + s.value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt()); m_scope = static_cast( - settings->value(SETTINGS_SEARCHSCOPE, - int(SymbolSearcher::SearchProjectsOnly)).toInt()); - settings->endGroup(); + s.value(SETTINGS_SEARCHSCOPE, int(SymbolSearcher::SearchProjectsOnly)).toInt()); emit symbolsToSearchChanged(); } +QByteArray SymbolsFindFilter::settingsKey() const +{ + return SETTINGS_GROUP; +} + void SymbolsFindFilter::onTaskStarted(Id type) { if (type == Constants::TASK_INDEX) { diff --git a/src/plugins/cppeditor/symbolsfindfilter.h b/src/plugins/cppeditor/symbolsfindfilter.h index e9c997d219b..2513111cdc9 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.h +++ b/src/plugins/cppeditor/symbolsfindfilter.h @@ -37,8 +37,8 @@ public: void findAll(const QString &txt, Utils::FindFlags findFlags) override; QWidget *createConfigWidget() override; - void writeSettings(Utils::QtcSettings *settings) override; - void readSettings(Utils::QtcSettings *settings) override; + Utils::Store save() const override; + void restore(const Utils::Store &s) override; void setSymbolsToSearch(const SearchSymbols::SymbolTypes &types) { m_symbolsToSearch = types; } SearchSymbols::SymbolTypes symbolsToSearch() const { return m_symbolsToSearch; } @@ -46,6 +46,9 @@ public: void setSearchScope(SearchScope scope) { m_scope = scope; } SearchScope searchScope() const { return m_scope; } + // deprecated + QByteArray settingsKey() const override; + signals: void symbolsToSearchChanged(); diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index 73feffd68f8..84bf26238f6 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -245,14 +245,15 @@ GitGrepParameters GitGrep::gitParameters() const return {m_treeLineEdit->text(), m_recurseSubmodules && m_recurseSubmodules->isChecked()}; } -void GitGrep::readSettings(QtcSettings *settings) +void GitGrep::readSettings(const Store &s) { - m_treeLineEdit->setText(settings->value(GitGrepRef).toString()); + m_treeLineEdit->setText(s.value(GitGrepRef).toString()); } -void GitGrep::writeSettings(QtcSettings *settings) const +void GitGrep::writeSettings(Store &s) const { - settings->setValue(GitGrepRef, m_treeLineEdit->text()); + if (!m_treeLineEdit->text().isEmpty()) + s.insert(GitGrepRef, m_treeLineEdit->text()); } SearchExecutor GitGrep::searchExecutor() const diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h index bb6967eb675..4e5c75d97ab 100644 --- a/src/plugins/git/gitgrep.h +++ b/src/plugins/git/gitgrep.h @@ -24,8 +24,8 @@ public: QString title() const override; QString toolTip() const override; QWidget *widget() const override; - void readSettings(Utils::QtcSettings *settings) override; - void writeSettings(Utils::QtcSettings *settings) const override; + void readSettings(const Utils::Store &settings) override; + void writeSettings(Utils::Store &settings) const override; TextEditor::SearchExecutor searchExecutor() const override; TextEditor::EditorOpener editorOpener() const override; diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index c2cee55e309..509e40797ca 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -113,16 +113,19 @@ QWidget *AllProjectsFind::createConfigWidget() return m_configWidget; } -void AllProjectsFind::writeSettings(QtcSettings *settings) +Store AllProjectsFind::save() const { - settings->beginGroup("AllProjectsFind"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s); + return s; } -void AllProjectsFind::readSettings(QtcSettings *settings) +void AllProjectsFind::restore(const Utils::Store &s) { - settings->beginGroup("AllProjectsFind"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, "*", ""); +} + +QByteArray AllProjectsFind::settingsKey() const +{ + return "AllProjectsFind"; } diff --git a/src/plugins/projectexplorer/allprojectsfind.h b/src/plugins/projectexplorer/allprojectsfind.h index 02dfc2a27ae..4b43f1ed4be 100644 --- a/src/plugins/projectexplorer/allprojectsfind.h +++ b/src/plugins/projectexplorer/allprojectsfind.h @@ -26,8 +26,12 @@ public: bool isEnabled() const override; QWidget *createConfigWidget() override; - void writeSettings(Utils::QtcSettings *settings) override; - void readSettings(Utils::QtcSettings *settings) override; + + Utils::Store save() const override; + void restore(const Utils::Store &s) override; + + // deprecated + QByteArray settingsKey() const override; protected: static Utils::FileContainer filesForProjects(const QStringList &nameFilters, diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 76439d93b74..5cbafa46886 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -29,8 +29,11 @@ private: bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; + + // deprecated + QByteArray settingsKey() const final; QString label() const final; @@ -115,18 +118,21 @@ void CurrentProjectFind::setupSearch(Core::SearchResult *search) }); } -void CurrentProjectFind::writeSettings(QtcSettings *settings) +Store CurrentProjectFind::save() const { - settings->beginGroup("CurrentProjectFind"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s); + return s; } -void CurrentProjectFind::readSettings(QtcSettings *settings) +void CurrentProjectFind::restore(const Store &s) { - settings->beginGroup("CurrentProjectFind"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, "*", ""); +} + +QByteArray CurrentProjectFind::settingsKey() const +{ + return "CurrentProjectFind"; } void setupCurrentProjectFind() diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp index 92c2fc80596..42f6f526929 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp +++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp @@ -30,21 +30,24 @@ QString FilesInAllProjectsFind::displayName() const const char kSettingsKey[] = "FilesInAllProjectDirectories"; -void FilesInAllProjectsFind::writeSettings(QtcSettings *settings) +Store FilesInAllProjectsFind::save() const { - settings->beginGroup(kSettingsKey); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s); + return s; } -void FilesInAllProjectsFind::readSettings(QtcSettings *settings) +void FilesInAllProjectsFind::restore(const Utils::Store &s) { - settings->beginGroup(kSettingsKey); readCommonSettings( - settings, + s, "CMakeLists.txt,*.cmake,*.pro,*.pri,*.qbs,*.cpp,*.h,*.mm,*.qml,*.md,*.txt,*.qdoc", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); - settings->endGroup(); +} + +QByteArray FilesInAllProjectsFind::settingsKey() const +{ + return kSettingsKey; } FileContainerProvider FilesInAllProjectsFind::fileContainerProvider() const diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.h b/src/plugins/projectexplorer/filesinallprojectsfind.h index f96d2a6df9a..39d8e2c0ed6 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.h +++ b/src/plugins/projectexplorer/filesinallprojectsfind.h @@ -16,8 +16,11 @@ public: QString id() const override; QString displayName() const override; - void writeSettings(Utils::QtcSettings *settings) override; - void readSettings(Utils::QtcSettings *settings) override; + Utils::Store save() const override; + void restore(const Utils::Store &s) override; + + // deprecated + QByteArray settingsKey() const override; protected: TextEditor::FileContainerProvider fileContainerProvider() const override; diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp index cb290e046d8..57a2683428c 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.cpp +++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp @@ -128,14 +128,15 @@ public: QString toolTip() const final { return {}; } QWidget *widget() const final { return m_widget; } - void readSettings(QtcSettings *settings) final + void readSettings(const Store &s) final { - m_searchOptionsLineEdit->setText(settings->value(s_searchOptionsString).toString()); + m_searchOptionsLineEdit->setText(s.value(s_searchOptionsString).toString()); } - void writeSettings(QtcSettings *settings) const final + void writeSettings(Store &s) const final { - settings->setValue(s_searchOptionsString, m_searchOptionsLineEdit->text()); + if (!m_searchOptionsLineEdit->text().isEmpty()) + s.insert(s_searchOptionsString, m_searchOptionsLineEdit->text()); } SearchExecutor searchExecutor() const final diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 0faa871b5ce..7076878b3e2 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -139,8 +139,8 @@ public: QString title() const override { return Tr::tr("Internal"); } QString toolTip() const override { return {}; } QWidget *widget() const override { return m_widget; } - void readSettings(QtcSettings * /*settings*/) override {} - void writeSettings(QtcSettings * /*settings*/) const override {} + void readSettings(const Store &) override {} + void writeSettings(Store &) const override {} SearchExecutor searchExecutor() const override { return [](const FileFindParameters ¶meters) { @@ -442,47 +442,48 @@ FilePath BaseFileFind::searchDir() const return d->m_searchDir; } -void BaseFileFind::writeCommonSettings(QtcSettings *settings) +void BaseFileFind::writeCommonSettings(Store &s) const { const auto fromNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::fromNativeSeparators); }; - settings->setValue("filters", fromNativeSeparators(d->m_filterStrings.stringList())); + s.insert("filters", fromNativeSeparators(d->m_filterStrings.stringList())); if (d->m_filterCombo) - settings->setValue("currentFilter", - QDir::fromNativeSeparators(d->m_filterCombo->currentText())); - settings->setValue("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList())); - if (d->m_exclusionCombo) - settings->setValue("currentExclusionFilter", - QDir::fromNativeSeparators(d->m_exclusionCombo->currentText())); + s.insert("currentFilter", QDir::fromNativeSeparators(d->m_filterCombo->currentText())); + s.insert("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList())); + if (d->m_exclusionCombo) { + s.insert( + "currentExclusionFilter", + QDir::fromNativeSeparators(d->m_exclusionCombo->currentText())); + } for (const SearchEngine *searchEngine : std::as_const(d->m_searchEngines)) - searchEngine->writeSettings(settings); - settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex); + searchEngine->writeSettings(s); + s.insert("currentSearchEngineIndex", d->m_currentSearchEngineIndex); } -void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defaultFilter, - const QString &defaultExclusionFilter) +void BaseFileFind::readCommonSettings( + const Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) { const auto toNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::toNativeSeparators); }; - const QStringList filterSetting = settings->value("filters").toStringList(); + const QStringList filterSetting = s.value("filters").toStringList(); const QStringList filters = filterSetting.isEmpty() ? QStringList(defaultFilter) : filterSetting; - const QVariant currentFilter = settings->value("currentFilter"); + const QVariant currentFilter = s.value("currentFilter"); d->m_filterSetting = currentFilter.isValid() ? currentFilter.toString() : filters.first(); d->m_filterStrings.setStringList(toNativeSeparators(filters)); if (d->m_filterCombo) syncComboWithSettings(d->m_filterCombo, d->m_filterSetting); - QStringList exclusionFilters = settings->value("exclusionFilters").toStringList(); + QStringList exclusionFilters = s.value("exclusionFilters").toStringList(); if (!exclusionFilters.contains(defaultExclusionFilter)) exclusionFilters << defaultExclusionFilter; - const QVariant currentExclusionFilter = settings->value("currentExclusionFilter"); + const QVariant currentExclusionFilter = s.value("currentExclusionFilter"); d->m_exclusionSetting = currentExclusionFilter.isValid() ? currentExclusionFilter.toString() : exclusionFilters.first(); d->m_exclusionStrings.setStringList(toNativeSeparators(exclusionFilters)); @@ -490,8 +491,8 @@ void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defa syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting); for (SearchEngine* searchEngine : std::as_const(d->m_searchEngines)) - searchEngine->readSettings(settings); - const int currentSearchEngineIndex = settings->value("currentSearchEngineIndex", 0).toInt(); + searchEngine->readSettings(s); + const int currentSearchEngineIndex = s.value("currentSearchEngineIndex", 0).toInt(); syncSearchEngineCombo(currentSearchEngineIndex); } diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index 47adae4b866..2c0f6696500 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -69,8 +69,8 @@ public: virtual QString title() const = 0; virtual QString toolTip() const = 0; // add %1 placeholder where the find flags should be put virtual QWidget *widget() const = 0; - virtual void readSettings(Utils::QtcSettings *settings) = 0; - virtual void writeSettings(Utils::QtcSettings *settings) const = 0; + virtual void readSettings(const Utils::Store &s) = 0; + virtual void writeSettings(Utils::Store &settings) const = 0; virtual SearchExecutor searchExecutor() const = 0; virtual EditorOpener editorOpener() const { return {}; } bool isEnabled() const; @@ -108,8 +108,9 @@ protected: virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch, // add %1 placeholder where the find flags should be put - void writeCommonSettings(Utils::QtcSettings *settings); - void readCommonSettings(Utils::QtcSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter); + void writeCommonSettings(Utils::Store &s) const; + void readCommonSettings( + const Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter); QList> createPatternWidgets(); QStringList fileNameFilters() const; QStringList fileExclusionFilters() const; diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index 7d3ad849972..f30fc732c81 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -27,8 +27,8 @@ private: QString id() const final; QString displayName() const final; bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; QString label() const final; QString toolTip() const final; @@ -37,6 +37,9 @@ private: void handleFileChange(Core::IEditor *editor); QPointer m_currentDocument; + + // deprecated + QByteArray settingsKey() const final; }; FindInCurrentFile::FindInCurrentFile() @@ -97,18 +100,21 @@ void FindInCurrentFile::handleFileChange(Core::IEditor *editor) } } -void FindInCurrentFile::writeSettings(QtcSettings *settings) +Store FindInCurrentFile::save() const { - settings->beginGroup("FindInCurrentFile"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s); + return s; } -void FindInCurrentFile::readSettings(QtcSettings *settings) +void FindInCurrentFile::restore(const Store &s) { - settings->beginGroup("FindInCurrentFile"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, "*", ""); +} + +QByteArray FindInCurrentFile::settingsKey() const +{ + return "FindInCurrentFile"; } void setupFindInCurrentFile() diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 7348c600e21..1aea811b705 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -188,18 +188,21 @@ QWidget *FindInFiles::createConfigWidget() return m_configWidget; } -void FindInFiles::writeSettings(QtcSettings *settings) +Store FindInFiles::save() const { - settings->beginGroup("FindInFiles"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s); + return s; } -void FindInFiles::readSettings(QtcSettings *settings) +void FindInFiles::restore(const Utils::Store &s) { - settings->beginGroup("FindInFiles"); - readCommonSettings(settings, "*.cpp,*.h", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); - settings->endGroup(); + readCommonSettings(s, "*.cpp,*.h", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); +} + +QByteArray FindInFiles::settingsKey() const +{ + return "FindInFiles"; } void FindInFiles::setBaseDirectory(const FilePath &directory) diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h index c7803a16ed4..b3bd7b7095b 100644 --- a/src/plugins/texteditor/findinfiles.h +++ b/src/plugins/texteditor/findinfiles.h @@ -30,8 +30,8 @@ public: QString id() const override; QString displayName() const override; QWidget *createConfigWidget() override; - void writeSettings(Utils::QtcSettings *settings) override; - void readSettings(Utils::QtcSettings *settings) override; + Utils::Store save() const override; + void restore(const Utils::Store &s) override; bool isValid() const override; void setDirectory(const Utils::FilePath &directory); @@ -39,6 +39,9 @@ public: static void findOnFileSystem(const QString &path); static FindInFiles *instance(); + // deprecated + QByteArray settingsKey() const override; + protected: QString label() const override; QString toolTip() const override; diff --git a/src/plugins/texteditor/findinopenfiles.cpp b/src/plugins/texteditor/findinopenfiles.cpp index dcd505cbe1f..d98e189db11 100644 --- a/src/plugins/texteditor/findinopenfiles.cpp +++ b/src/plugins/texteditor/findinopenfiles.cpp @@ -25,14 +25,17 @@ private: QString id() const final; QString displayName() const final; bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; QString label() const final; QString toolTip() const final; FileContainerProvider fileContainerProvider() const final; void updateEnabledState() { emit enabledChanged(isEnabled()); } + + // deprecated + QByteArray settingsKey() const final; }; FindInOpenFiles::FindInOpenFiles() @@ -90,21 +93,22 @@ bool FindInOpenFiles::isEnabled() const return Core::DocumentModel::entryCount() > 0; } -void FindInOpenFiles::writeSettings(QtcSettings *settings) +Store FindInOpenFiles::save() const { - settings->beginGroup("FindInOpenFiles"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s); + return s; } -void FindInOpenFiles::readSettings(QtcSettings *settings) +void FindInOpenFiles::restore(const Store &s) { - settings->beginGroup("FindInOpenFiles"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, "*", ""); } - +QByteArray FindInOpenFiles::settingsKey() const +{ + return "FindInOpenFiles"; +} void setupFindInOpenFiles() { From f7c7cd0ae93a584821854447393c8730963a0145 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 28 May 2024 15:05:06 +0200 Subject: [PATCH 04/69] Find: Avoid saving default values for advanced search Change-Id: I69454675c15a9eb92d0665ea6f88f0969a532eb8 Reviewed-by: David Schulz --- src/plugins/cppeditor/symbolsfindfilter.cpp | 6 ++-- .../projectexplorer/allprojectsfind.cpp | 7 +++-- .../projectexplorer/currentprojectfind.cpp | 7 +++-- .../filesinallprojectsfind.cpp | 10 +++--- src/plugins/texteditor/basefilefind.cpp | 31 ++++++++++++------- src/plugins/texteditor/basefilefind.h | 3 +- src/plugins/texteditor/findincurrentfile.cpp | 7 +++-- src/plugins/texteditor/findinfiles.cpp | 7 +++-- src/plugins/texteditor/findinopenfiles.cpp | 7 +++-- 9 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp index c1e50b1f10f..2888cf3604a 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.cpp +++ b/src/plugins/cppeditor/symbolsfindfilter.cpp @@ -169,8 +169,10 @@ QWidget *SymbolsFindFilter::createConfigWidget() Store SymbolsFindFilter::save() const { Store s; - s.insert(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch)); - s.insert(SETTINGS_SEARCHSCOPE, int(m_scope)); + if (m_symbolsToSearch != SearchSymbols::AllTypes) + s.insert(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch)); + if (m_scope != SymbolSearcher::SearchProjectsOnly) + s.insert(SETTINGS_SEARCHSCOPE, int(m_scope)); return s; } diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index 509e40797ca..5d8f285b001 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -113,16 +113,19 @@ QWidget *AllProjectsFind::createConfigWidget() return m_configWidget; } +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + Store AllProjectsFind::save() const { Store s; - writeCommonSettings(s); + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); return s; } void AllProjectsFind::restore(const Utils::Store &s) { - readCommonSettings(s, "*", ""); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } QByteArray AllProjectsFind::settingsKey() const diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 5cbafa46886..5ec53f3dd2c 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -118,16 +118,19 @@ void CurrentProjectFind::setupSearch(Core::SearchResult *search) }); } +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + Store CurrentProjectFind::save() const { Store s; - writeCommonSettings(s); + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); return s; } void CurrentProjectFind::restore(const Store &s) { - readCommonSettings(s, "*", ""); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } QByteArray CurrentProjectFind::settingsKey() const diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp index 42f6f526929..62ac4e48392 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp +++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp @@ -29,20 +29,20 @@ QString FilesInAllProjectsFind::displayName() const } const char kSettingsKey[] = "FilesInAllProjectDirectories"; +const char kDefaultInclusion[] + = "CMakeLists.txt,*.cmake,*.pro,*.pri,*.qbs,*.cpp,*.h,*.mm,*.qml,*.md,*.txt,*.qdoc"; +const char kDefaultExclusion[] = "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"; Store FilesInAllProjectsFind::save() const { Store s; - writeCommonSettings(s); + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); return s; } void FilesInAllProjectsFind::restore(const Utils::Store &s) { - readCommonSettings( - s, - "CMakeLists.txt,*.cmake,*.pro,*.pri,*.qbs,*.cpp,*.h,*.mm,*.qml,*.md,*.txt,*.qdoc", - "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } QByteArray FilesInAllProjectsFind::settingsKey() const diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 7076878b3e2..d18a75531b3 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -442,25 +442,34 @@ FilePath BaseFileFind::searchDir() const return d->m_searchDir; } -void BaseFileFind::writeCommonSettings(Store &s) const +void BaseFileFind::writeCommonSettings( + Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) const { const auto fromNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::fromNativeSeparators); }; - s.insert("filters", fromNativeSeparators(d->m_filterStrings.stringList())); - if (d->m_filterCombo) - s.insert("currentFilter", QDir::fromNativeSeparators(d->m_filterCombo->currentText())); - s.insert("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList())); - if (d->m_exclusionCombo) { - s.insert( - "currentExclusionFilter", - QDir::fromNativeSeparators(d->m_exclusionCombo->currentText())); - } + const QStringList filterStrings = fromNativeSeparators(d->m_filterStrings.stringList()); + if (filterStrings.size() != 1 || filterStrings.first() != defaultFilter) + s.insert("filters", filterStrings); + const QString currentFilter = d->m_filterCombo + ? QDir::fromNativeSeparators(d->m_filterCombo->currentText()) + : d->m_filterSetting; + if (currentFilter != defaultFilter) + s.insert("currentFilter", currentFilter); + const QStringList exclusionFilters = fromNativeSeparators(d->m_exclusionStrings.stringList()); + if (exclusionFilters.size() != 1 || exclusionFilters.first() != defaultExclusionFilter) + s.insert("exclusionFilters", exclusionFilters); + const QString currentExclusionFilter = d->m_exclusionCombo ? QDir::fromNativeSeparators( + d->m_exclusionCombo->currentText()) + : d->m_exclusionSetting; + if (currentExclusionFilter != defaultExclusionFilter) + s.insert("currentExclusionFilter", currentExclusionFilter); for (const SearchEngine *searchEngine : std::as_const(d->m_searchEngines)) searchEngine->writeSettings(s); - s.insert("currentSearchEngineIndex", d->m_currentSearchEngineIndex); + if (d->m_currentSearchEngineIndex != 0) + s.insert("currentSearchEngineIndex", d->m_currentSearchEngineIndex); } void BaseFileFind::readCommonSettings( diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index 2c0f6696500..de4286c31b6 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -108,7 +108,8 @@ protected: virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch, // add %1 placeholder where the find flags should be put - void writeCommonSettings(Utils::Store &s) const; + void writeCommonSettings( + Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) const; void readCommonSettings( const Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter); QList> createPatternWidgets(); diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index f30fc732c81..45121f72d84 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -100,16 +100,19 @@ void FindInCurrentFile::handleFileChange(Core::IEditor *editor) } } +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + Store FindInCurrentFile::save() const { Store s; - writeCommonSettings(s); + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); return s; } void FindInCurrentFile::restore(const Store &s) { - readCommonSettings(s, "*", ""); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } QByteArray FindInCurrentFile::settingsKey() const diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 1aea811b705..9dc68cd6532 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -188,16 +188,19 @@ QWidget *FindInFiles::createConfigWidget() return m_configWidget; } +const char kDefaultInclusion[] = "*.cpp,*.h"; +const char kDefaultExclusion[] = "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"; + Store FindInFiles::save() const { Store s; - writeCommonSettings(s); + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); return s; } void FindInFiles::restore(const Utils::Store &s) { - readCommonSettings(s, "*.cpp,*.h", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } QByteArray FindInFiles::settingsKey() const diff --git a/src/plugins/texteditor/findinopenfiles.cpp b/src/plugins/texteditor/findinopenfiles.cpp index d98e189db11..deeb66e9f5f 100644 --- a/src/plugins/texteditor/findinopenfiles.cpp +++ b/src/plugins/texteditor/findinopenfiles.cpp @@ -93,16 +93,19 @@ bool FindInOpenFiles::isEnabled() const return Core::DocumentModel::entryCount() > 0; } +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + Store FindInOpenFiles::save() const { Store s; - writeCommonSettings(s); + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); return s; } void FindInOpenFiles::restore(const Store &s) { - readCommonSettings(s, "*", ""); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } QByteArray FindInOpenFiles::settingsKey() const From 5941c68dbd323f9cc6b85b1a34c045ca3d76d04d Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 28 May 2024 14:13:11 +0200 Subject: [PATCH 05/69] Find: Save settings in session Usually the directories and search terms relate to the project(s) worked on, so save them in the session. Fixes: QTCREATORBUG-793 Change-Id: Ia00597a91fa3a902d6b8b4f2d8cb93634cdc333c Reviewed-by: David Schulz --- src/plugins/coreplugin/find/findplugin.cpp | 137 +++++++++++++++--- src/plugins/coreplugin/find/findtoolbar.cpp | 33 +++++ src/plugins/coreplugin/find/findtoolbar.h | 9 +- .../coreplugin/find/findtoolwindow.cpp | 24 +++ src/plugins/coreplugin/find/findtoolwindow.h | 7 + 5 files changed, 187 insertions(+), 23 deletions(-) diff --git a/src/plugins/coreplugin/find/findplugin.cpp b/src/plugins/coreplugin/find/findplugin.cpp index 0e5ef38be8b..56042cb478c 100644 --- a/src/plugins/coreplugin/find/findplugin.cpp +++ b/src/plugins/coreplugin/find/findplugin.cpp @@ -3,12 +3,6 @@ #include "findplugin.h" -#include "currentdocumentfind.h" -#include "findtoolbar.h" -#include "findtoolwindow.h" -#include "ifindfilter.h" -#include "searchresultwindow.h" -#include "textfindconstants.h" #include "../actionmanager/actioncontainer.h" #include "../actionmanager/actionmanager.h" #include "../actionmanager/command.h" @@ -16,6 +10,13 @@ #include "../coreplugintr.h" #include "../icontext.h" #include "../icore.h" +#include "../session.h" +#include "currentdocumentfind.h" +#include "findtoolbar.h" +#include "findtoolwindow.h" +#include "ifindfilter.h" +#include "searchresultwindow.h" +#include "textfindconstants.h" #include @@ -75,6 +76,10 @@ public: QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + void restore(const Store &s); + Store save() const; + + // TODO deprecated since QtC 14.0 void writeSettings(QtcSettings *settings) const; void readSettings(QtcSettings *settings); @@ -105,6 +110,41 @@ static Utils::Key completionSettingsArrayPrefix() { return "FindCompletions"; } static Utils::Key completionSettingsTextKey() { return "Text"; } static Utils::Key completionSettingsFlagsKey() { return "Flags"; } +void CompletionModel::restore(const Store &s) +{ + beginResetModel(); + const QStringList texts = s.value(completionSettingsTextKey()).toStringList(); + const QList flags + = transform(s.value(completionSettingsFlagsKey()).toList(), [](const QVariant &v) { + return FindFlags(v.toInt()); + }); + const int size = texts.size(); + m_entries.clear(); + m_entries.reserve(size); + for (int i = 0; i < size; ++i) { + CompletionEntry entry; + entry.text = texts.at(i); + entry.findFlags = i < flags.size() ? flags.at(i) : FindFlags(); + if (!entry.text.isEmpty()) + m_entries.append(entry); + } + endResetModel(); +} + +Store CompletionModel::save() const +{ + if (m_entries.isEmpty()) + return {}; + const QStringList texts = transform(m_entries, [](const CompletionEntry &e) { return e.text; }); + const QVariantList flags = transform(m_entries, [](const CompletionEntry &e) { + return QVariant::fromValue(int(e.findFlags)); + }); + Store s; + s.insert(completionSettingsTextKey(), texts); + s.insert(completionSettingsFlagsKey(), flags); + return s; +} + void CompletionModel::writeSettings(QtcSettings *settings) const { if (m_entries.isEmpty()) { @@ -215,13 +255,20 @@ void Find::initialize() d->m_findDialog = new Internal::FindToolWindow; d->m_searchResultWindow = new SearchResultWindow(d->m_findDialog); ExtensionSystem::PluginManager::addObject(d->m_searchResultWindow); + QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, d, &FindPrivate::writeSettings); + QObject::connect( + SessionManager::instance(), + &SessionManager::aboutToSaveSession, + d, + &FindPrivate::writeSettings); + QObject::connect( + SessionManager::instance(), &SessionManager::sessionLoaded, d, &FindPrivate::readSettings); } void Find::extensionsInitialized() { d->setupFilterMenuItems(); - d->readSettings(); } void Find::aboutToShutdown() @@ -369,6 +416,8 @@ bool Find::hasFindFlag(FindFlag flag) void FindPrivate::writeSettings() { + // TODO for backwards compatibility + // deprecated since QtC 14.0 QtcSettings *settings = ICore::settings(); settings->beginGroup("Find"); settings->setValueWithDefault("Backward", bool(m_findFlags & FindBackward), false); @@ -384,26 +433,70 @@ void FindPrivate::writeSettings() m_findToolBar->writeSettings(); m_findDialog->writeSettings(); m_searchResultWindow->writeSettings(); + + // save in session + Store s; + if (m_findFlags & FindBackward) + s.insert("Backward", true); + if (m_findFlags & FindCaseSensitively) + s.insert("CaseSensitively", true); + if (m_findFlags & FindWholeWords) + s.insert("WholeWords", true); + if (m_findFlags & FindRegularExpression) + s.insert("RegularExpression", true); + if (m_findFlags & FindPreserveCase) + s.insert("PreserveCase", true); + const Store completion = m_findCompletionModel.save(); + if (!completion.isEmpty()) + s.insert("FindCompletions", variantFromStore(completion)); + if (!m_replaceCompletions.isEmpty()) + s.insert("ReplaceStrings", m_replaceCompletions); + const Store toolbar = m_findToolBar->save(); + if (!toolbar.isEmpty()) + s.insert("ToolBar", variantFromStore(toolbar)); + const Store advanced = m_findDialog->save(); + if (!advanced.isEmpty()) + s.insert("AdvancedSearch", variantFromStore(advanced)); + SessionManager::setValue("Find", variantFromStore(s)); } void FindPrivate::readSettings() { - QtcSettings *settings = ICore::settings(); - settings->beginGroup("Find"); - { - QSignalBlocker blocker(m_instance); - Find::setBackward(settings->value("Backward", false).toBool()); - Find::setCaseSensitive(settings->value("CaseSensitively", false).toBool()); - Find::setWholeWord(settings->value("WholeWords", false).toBool()); - Find::setRegularExpression(settings->value("RegularExpression", false).toBool()); - Find::setPreserveCase(settings->value("PreserveCase", false).toBool()); + const Store s = storeFromVariant(SessionManager::value("Find")); + if (s.isEmpty() && SessionManager::isDefaultVirgin()) { + // TODO compatibility path when opening Qt Creator + // TODO deprecated since QtC 14.0 + QtcSettings *settings = ICore::settings(); + settings->beginGroup("Find"); + { + QSignalBlocker blocker(m_instance); + Find::setBackward(settings->value("Backward", false).toBool()); + Find::setCaseSensitive(settings->value("CaseSensitively", false).toBool()); + Find::setWholeWord(settings->value("WholeWords", false).toBool()); + Find::setRegularExpression(settings->value("RegularExpression", false).toBool()); + Find::setPreserveCase(settings->value("PreserveCase", false).toBool()); + } + m_findCompletionModel.readSettings(settings); + m_replaceCompletions = settings->value("ReplaceStrings").toStringList(); + m_replaceCompletionModel.setStringList(m_replaceCompletions); + settings->endGroup(); + m_findToolBar->readSettings(); + m_findDialog->readSettings(); + } else if (!s.empty()) { + { + QSignalBlocker blocker(m_instance); + Find::setBackward(s.value("Backward", false).toBool()); + Find::setCaseSensitive(s.value("CaseSensitively", false).toBool()); + Find::setWholeWord(s.value("WholeWords", false).toBool()); + Find::setRegularExpression(s.value("RegularExpression", false).toBool()); + Find::setPreserveCase(s.value("PreserveCase", false).toBool()); + } + m_findCompletionModel.restore(storeFromVariant(s.value("FindCompletions"))); + m_replaceCompletions = s.value("ReplaceStrings").toStringList(); + m_replaceCompletionModel.setStringList(m_replaceCompletions); + m_findToolBar->restore(storeFromVariant(s.value("ToolBar"))); + m_findDialog->restore(storeFromVariant(s.value("AdvancedSearch"))); } - m_findCompletionModel.readSettings(settings); - m_replaceCompletions = settings->value("ReplaceStrings").toStringList(); - m_replaceCompletionModel.setStringList(m_replaceCompletions); - settings->endGroup(); - m_findToolBar->readSettings(); - m_findDialog->readSettings(); emit m_instance->findFlagsChanged(); // would have been done in the setXXX methods above } diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp index 4460367896a..681fa407063 100644 --- a/src/plugins/coreplugin/find/findtoolbar.cpp +++ b/src/plugins/coreplugin/find/findtoolbar.cpp @@ -1047,6 +1047,39 @@ void FindToolBar::resizeEvent(QResizeEvent *event) QMetaObject::invokeMethod(this, &FindToolBar::updateToolBar, Qt::QueuedConnection); } +void FindToolBar::restore(const Store &s) +{ + FindFlags flags; + if (s.value("Backward", false).toBool()) + flags |= FindBackward; + if (s.value("CaseSensitively", false).toBool()) + flags |= FindCaseSensitively; + if (s.value("WholeWords", false).toBool()) + flags |= FindWholeWords; + if (s.value("RegularExpression", false).toBool()) + flags |= FindRegularExpression; + if (s.value("PreserveCase", false).toBool()) + flags |= FindPreserveCase; + m_findFlags = flags; + findFlagsChanged(); +} + +Store FindToolBar::save() const +{ + Store s; + if (m_findFlags & FindBackward) + s.insert("Backward", true); + if (m_findFlags & FindCaseSensitively) + s.insert("CaseSensitively", true); + if (m_findFlags & FindWholeWords) + s.insert("WholeWords", true); + if (m_findFlags & FindRegularExpression) + s.insert("RegularExpression", true); + if (m_findFlags & FindPreserveCase) + s.insert("PreserveCase", true); + return s; +} + void FindToolBar::writeSettings() { Utils::QtcSettings *settings = ICore::settings(); diff --git a/src/plugins/coreplugin/find/findtoolbar.h b/src/plugins/coreplugin/find/findtoolbar.h index baca57597ca..1080407dae9 100644 --- a/src/plugins/coreplugin/find/findtoolbar.h +++ b/src/plugins/coreplugin/find/findtoolbar.h @@ -6,6 +6,7 @@ #include "currentdocumentfind.h" #include +#include #include #include @@ -18,7 +19,9 @@ class QSpacerItem; class QToolButton; QT_END_NAMESPACE -namespace Utils { class FancyLineEdit; } +namespace Utils { +class FancyLineEdit; +} // namespace Utils namespace Core { @@ -43,6 +46,10 @@ public: explicit FindToolBar(CurrentDocumentFind *currentDocumentFind); ~FindToolBar() override; + void restore(const Utils::Store &s); + Utils::Store save() const; + + // TODO deprecated since QtC 14.0 void readSettings(); void writeSettings(); diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp index a239d310be6..bc8e241dd40 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.cpp +++ b/src/plugins/coreplugin/find/findtoolwindow.cpp @@ -346,6 +346,30 @@ void FindToolWindow::replace() filter->replaceAll(term, Find::findFlags()); } +void FindToolWindow::restore(const Utils::Store &s) +{ + const QString currentFilter = s.value("CurrentFilter").toString(); + for (int i = 0; i < m_filters.size(); ++i) { + IFindFilter *filter = m_filters.at(i); + filter->restore(storeFromVariant(s.value(filter->id().toUtf8()))); + if (filter->id() == currentFilter) + setCurrentFilterIndex(i); + } +} + +Store FindToolWindow::save() const +{ + Store s; + if (m_currentFilter && (m_filters.isEmpty() || m_filters.first() != m_currentFilter)) + s.insert("CurrentFilter", m_currentFilter->id()); + for (IFindFilter *filter : std::as_const(m_filters)) { + const Store store = filter->save(); + if (!store.isEmpty()) + s.insert(filter->id().toUtf8(), variantFromStore(store)); + } + return s; +} + void FindToolWindow::writeSettings() { Utils::QtcSettings *settings = ICore::settings(); diff --git a/src/plugins/coreplugin/find/findtoolwindow.h b/src/plugins/coreplugin/find/findtoolwindow.h index 91b045295a3..77226a6b435 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.h +++ b/src/plugins/coreplugin/find/findtoolwindow.h @@ -3,6 +3,8 @@ #pragma once +#include + #include #include @@ -33,6 +35,11 @@ public: void setFindText(const QString &text); void setCurrentFilter(IFindFilter *filter); + + void restore(const Utils::Store &s); + Utils::Store save() const; + + // TODO deprecated since QtC 14.0 void readSettings(); void writeSettings(); From ff12f2f680acbd1f31c0b2ba27302d780319e56e Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 30 May 2024 10:16:17 +0200 Subject: [PATCH 06/69] ProjectExplorer: avoid scanning file contents to detect the node type Change-Id: I735464bed8d546475036e0d5ea61dc7cbbe78975 Reviewed-by: Eike Ziller --- src/plugins/projectexplorer/treescanner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp index dac83598fb7..49680621ec2 100644 --- a/src/plugins/projectexplorer/treescanner.cpp +++ b/src/plugins/projectexplorer/treescanner.cpp @@ -155,7 +155,7 @@ void TreeScanner::scanForFiles( { QList nodes = ProjectExplorer::scanForFiles( promise, directory, dirFilter, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * { - const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn); + const Utils::MimeType mimeType = Utils::mimeTypesForFileName(fn.path()).value(0); // Skip some files during scan. if (filter && filter(mimeType, fn)) From 181d18e9250ee207ac7787a96cad8bf83f72bf56 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 3 Jun 2024 14:01:11 +0200 Subject: [PATCH 07/69] Fix another plugin json Amends 6cd7aed8ebe6b1e18d875f1559c70ff46bc09881 Change-Id: I73667ca94aa746ef9e91984c2257efa842387adb Reviewed-by: Christian Stenger --- src/plugins/insight/Insight.json.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/insight/Insight.json.in b/src/plugins/insight/Insight.json.in index 12b9e51f2b9..1f60965d80a 100644 --- a/src/plugins/insight/Insight.json.in +++ b/src/plugins/insight/Insight.json.in @@ -18,7 +18,7 @@ "LongDescription" : [ "Use Qt Insight with Qt Design Studio.", "You also need:", - "- Qt Insight", + "- Qt Insight" ], "DisabledByDefault" : true, "Url" : "https://www.qt.io", From 5d159e6185a9973ceff2dd91a78302f48b6be715 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 3 Jun 2024 13:04:01 +0200 Subject: [PATCH 08/69] CMakePM: Only add external "bin" dirs to PATH At QTCREATORBUG-29662 we have the issue when a program cannot be started because the PATH environment variable had content longer than 2048 characters. Qt Creator 12 had only one place that added paths to librarySeachPaths namely the "bin" directories. Qt Creator 13 tried to filter these "bin" directories to the ones that have .dll files. This fixed QTCREATORBUG-29662 but had issues with dlls having different names than the link libraries provided by CMake. By only allowing "bin" directories from paths not from the build directory we allow Qt, or OpenSSL dependencies, but not just existing "bin" directories. Amends 0d8a542b4f7d8a7b4d27f42ff16d309fba6cbf22 Amends 8713919f31f2aecc7e7c15f1fc9ce7906b8fefa0 Amends ac97ab1abf9bf073088925755a46f08f38721090 Amends 2ce6255a7d4fd33e54139a9615b3037e83587887 Task-number: QTCREATORBUG-29662 Change-Id: If0b162f25ae1e5bfc1e1ff313720c54c5ae936c5 Reviewed-by: Alessandro Portale --- src/plugins/cmakeprojectmanager/fileapidataextractor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 66748345ced..9d55e2e692b 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -336,7 +336,7 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t, // actual dll files in ../bin on windows. Qt is one example of that. if (tmp.fileName() == "lib") { const FilePath path = tmp.parentDir().pathAppended("bin"); - if (path.isDir()) + if (path.isDir() && !isChildOf(path, {buildDir})) librarySeachPaths.append(path); } } From 1543ead1b641b4ae5e60b3e65663da16ec44e618 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 30 May 2024 10:19:58 +0200 Subject: [PATCH 09/69] Add change log for 13.0.2 Change-Id: I6f80bc14b472ec9ec402a40e850ae86d46a29a5a Reviewed-by: Leena Miettinen --- dist/changelog/changes-13.0.2.md | 86 ++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 dist/changelog/changes-13.0.2.md diff --git a/dist/changelog/changes-13.0.2.md b/dist/changelog/changes-13.0.2.md new file mode 100644 index 00000000000..3e1b67b3784 --- /dev/null +++ b/dist/changelog/changes-13.0.2.md @@ -0,0 +1,86 @@ +Qt Creator 13.0.2 +================= + +Qt Creator version 13.0.2 contains bug fixes. + +The most important changes are listed in this document. For a complete list of +changes, see the Git log for the Qt Creator sources that you can check out from +the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline v13.0.1..v13.0.2 + +General +------- + +* Fixed that the `-client` option could start a new Qt Creator instance instead + of using a running one (which affects for example version control operations) + ([QTCREATORBUG-30624](https://bugreports.qt.io/browse/QTCREATORBUG-30624)) + +Editing +------- + +* Fixed that closing files with the tool button didn't add an entry to the + navigation history + +### Widget Designer + +* Fixed that `Use Qt module name in #include-directive` used Qt 4 module names + ([QTCREATORBUG-30751](https://bugreports.qt.io/browse/QTCREATORBUG-30751)) + +Projects +-------- + +### Meson + +* Fixed a crash when selecting kits + ([QTCREATORBUG-30698](https://bugreports.qt.io/browse/QTCREATORBUG-30698)) + +Terminal +-------- + +* Fixed the handling of environment variables with an equal sign `=` in the + value + ([QTCREATORBUG-30844](https://bugreports.qt.io/browse/QTCREATORBUG-30844)) + +Version Control Systems +----------------------- + +### Git + +* Fixed a crash in `Instant Blame` when reloading externally modified files + ([QTCREATORBUG-30824](https://bugreports.qt.io/browse/QTCREATORBUG-30824)) + +Platforms +--------- + +### Windows + +* Fixed missing paths with `Add build library search path to PATH` for CMake + projects + ([QTCREATORBUG-30556](https://bugreports.qt.io/browse/QTCREATORBUG-30556), + [QTCREATORBUG-30827](https://bugreports.qt.io/browse/QTCREATORBUG-30827), + [QTCREATORBUG-30932](https://bugreports.qt.io/browse/QTCREATORBUG-30932)) + +### Android + +* Fixed a crash when re-connecting devices + ([QTCREATORBUG-30645](https://bugreports.qt.io/browse/QTCREATORBUG-30645), + [QTCREATORBUG-30770](https://bugreports.qt.io/browse/QTCREATORBUG-30770)) + +### Remote Linux + +* Fixed passing more than one argument to `rsync` + ([QTCREATORBUG-30795](https://bugreports.qt.io/browse/QTCREATORBUG-30795)) + +Credits for these changes go to: +-------------------------------- +Alessandro Portale +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Eike Ziller +Leena Miettinen +Marcus Tillmanns +Robert Löhning From a726e5d3494e86534e4d66d69efb2630d8ca1536 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 3 Jun 2024 11:13:05 +0200 Subject: [PATCH 10/69] Utils: Let all FilePath::is*{Dir,File} return false when empty Less guessing on the user side what effectively happens. Change-Id: I6ace4645d40346ee942ae7390575f28992dd0c26 Reviewed-by: Marcus Tillmanns Reviewed-by: Christian Stenger Reviewed-by: Orgad Shaneh --- src/libs/utils/filepath.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 0965a52b1f3..c631034eba2 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1230,6 +1230,9 @@ FilePathInfo FilePath::filePathInfo() const */ bool FilePath::exists() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1242,6 +1245,9 @@ bool FilePath::exists() const */ bool FilePath::isExecutableFile() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1254,6 +1260,9 @@ bool FilePath::isExecutableFile() const */ bool FilePath::isWritableDir() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1266,6 +1275,9 @@ bool FilePath::isWritableDir() const */ bool FilePath::isWritableFile() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1273,9 +1285,11 @@ bool FilePath::isWritableFile() const return (*access)->isWritableFile(*this); } - bool FilePath::isReadableFile() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1285,6 +1299,9 @@ bool FilePath::isReadableFile() const bool FilePath::isReadableDir() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1294,6 +1311,9 @@ bool FilePath::isReadableDir() const bool FilePath::isFile() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1303,6 +1323,9 @@ bool FilePath::isFile() const bool FilePath::isDir() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; @@ -1312,6 +1335,9 @@ bool FilePath::isDir() const bool FilePath::isSymLink() const { + if (isEmpty()) + return false; + const expected_str access = getFileAccess(*this); if (!access) return false; From 4a4eda1b097ab0e7fa7102911b4490af4140f3a1 Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 3 Jun 2024 11:39:42 +0200 Subject: [PATCH 11/69] Debugger: Simplify some redundant file property checks Change-Id: Ie6d638d0ece7b08dc28058f74f4c37910ffb6d83 Reviewed-by: David Schulz --- src/plugins/debugger/cdb/cdbengine.cpp | 2 +- src/plugins/debugger/console/consoleview.cpp | 3 +-- src/plugins/debugger/gdb/gdbengine.cpp | 2 +- src/plugins/debugger/lldb/lldbengine.cpp | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 432205ff027..840f78dfc93 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -2800,7 +2800,7 @@ void CdbEngine::setupScripting(const DebuggerResponse &response) } const FilePath path = settings().extraDumperFile(); - if (!path.isEmpty() && path.isReadableFile()) { + if (path.isReadableFile()) { DebuggerCommand cmd("theDumper.addDumperModule", ScriptCommand); cmd.arg("path", path.path()); runCommand(cmd); diff --git a/src/plugins/debugger/console/consoleview.cpp b/src/plugins/debugger/console/consoleview.cpp index d42f4ae9185..de68944e8f4 100644 --- a/src/plugins/debugger/console/consoleview.cpp +++ b/src/plugins/debugger/console/consoleview.cpp @@ -162,9 +162,8 @@ void ConsoleView::onRowActivated(const QModelIndex &index) const Utils::FilePath fp = m_finder.findFile(model()->data(index, ConsoleItem::FileRole).toString()).constFirst(); - if (fp.exists() && fp.isFile() && fp.isReadableFile()) { + if (fp.isReadableFile()) Core::EditorManager::openEditorAt({fp, model()->data(index, ConsoleItem::LineRole).toInt()}); - } } void ConsoleView::copyToClipboard(const QModelIndex &index) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 3abaea75a46..1079fb24eaf 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -4044,7 +4044,7 @@ void GdbEngine::handleGdbStarted() } const FilePath path = settings().extraDumperFile(); - if (!path.isEmpty() && path.isReadableFile()) { + if (path.isReadableFile()) { DebuggerCommand cmd("addDumperModule"); cmd.arg("path", path.path()); runCommand(cmd); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index d7994be0070..e234aefc216 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -238,7 +238,7 @@ void LldbEngine::handleLldbStarted() executeCommand(commands); const FilePath path = settings().extraDumperFile(); - if (!path.isEmpty() && path.isReadableFile()) { + if (path.isReadableFile()) { DebuggerCommand cmd("addDumperModule"); cmd.arg("path", path.path()); runCommand(cmd); From 8965e12d4f7f29660cb32a2f3b48a2708ed99ab1 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 4 Jun 2024 09:09:38 +0200 Subject: [PATCH 12/69] ProjectExplorer: Add readOnly handling to runconfig aspects Change-Id: If8ebd2d13c22bf60f50bb55a7b61f15ba81e481b Reviewed-by: hjk --- src/plugins/projectexplorer/runconfigurationaspects.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 80e82b9f826..a383438c8b5 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -203,6 +203,9 @@ void WorkingDirectoryAspect::addToLayout(Layout &builder) m_chooser->setEnvironment(m_envAspect->environment()); } + m_chooser->setReadOnly(isReadOnly()); + m_resetButton->setEnabled(!isReadOnly()); + builder.addItems({Tr::tr("Working directory:"), m_chooser.data(), m_resetButton.data()}); } @@ -437,6 +440,7 @@ QWidget *ArgumentsAspect::setupChooser() this, [this] { setArguments(m_multiLineChooser->toPlainText()); }); } m_multiLineChooser->setPlainText(m_arguments); + m_multiLineChooser->setReadOnly(isReadOnly()); return m_multiLineChooser.data(); } if (!m_chooser) { @@ -445,6 +449,8 @@ QWidget *ArgumentsAspect::setupChooser() connect(m_chooser.data(), &QLineEdit::textChanged, this, &ArgumentsAspect::setArguments); } m_chooser->setText(m_arguments); + m_chooser->setReadOnly(isReadOnly()); + return m_chooser.data(); } @@ -597,6 +603,7 @@ void ExecutableAspect::setEnvironment(const Environment &env) void ExecutableAspect::setReadOnly(bool readOnly) { + BaseAspect::setReadOnly(readOnly); m_executable.setReadOnly(readOnly); } From 249312acd08a0124dbb81c4a1f2ac18b4a5de877 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Thu, 30 May 2024 14:32:34 +0200 Subject: [PATCH 13/69] Lua: Add -loadluaplugin startup argument Change-Id: Ia0a57bbb94d42534857889a0d08ec41c75708dca Reviewed-by: hjk --- src/plugins/lua/Lua.json.in | 8 +++++++ src/plugins/lua/luaplugin.cpp | 45 ++++++++++++++++++++++++++++++++--- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/plugins/lua/Lua.json.in b/src/plugins/lua/Lua.json.in index c41ce7bc88c..68b54fd1add 100644 --- a/src/plugins/lua/Lua.json.in +++ b/src/plugins/lua/Lua.json.in @@ -17,5 +17,13 @@ "Category" : "Scripting", "Description" : "Support for Lua based plugins.", "Url" : "https://www.qt.io", + "JsonWizardPaths" : [":/lua/wizards"], + "Arguments" : [ + { + "Name" : "-loadluaplugin", + "Parameter" : "path", + "Description" : "Load an additional lua plugin from the specified path" + } + ], ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index 93d48ff6cf8..60505ade850 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -49,6 +49,24 @@ public: } }; +struct Arguments +{ + std::optional loadPlugin; +}; + +Arguments parseArguments(const QStringList &arguments) +{ + Arguments args; + for (int i = 0; i < arguments.size() - 1; ++i) { + if (arguments.at(i) == QLatin1String("-loadluaplugin")) { + const QString path(arguments.at(i + 1)); + args.loadPlugin = FilePath::fromUserInput(path); + i++; // skip the argument + } + } + return args; +} + class LuaPlugin : public IPlugin { Q_OBJECT @@ -56,15 +74,18 @@ class LuaPlugin : public IPlugin private: std::unique_ptr m_luaEngine; + Arguments m_arguments; public: LuaPlugin() {} ~LuaPlugin() override = default; - void initialize() final + bool initialize(const QStringList &arguments, QString *) final { m_luaEngine.reset(new LuaEngine()); + m_arguments = parseArguments(arguments); + addAsyncModule(); addFetchModule(); addActionModule(); @@ -79,6 +100,8 @@ public: addInstallModule(); Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); }); + + return true; } bool delayedInitialize() final @@ -94,8 +117,7 @@ public: { QSet plugins; for (const FilePath &path : paths) { - const FilePaths folders = path.dirEntries( - FileFilter({}, QDir::Dirs | QDir::NoDotAndDotDot)); + FilePaths folders = path.dirEntries(FileFilter({}, QDir::Dirs | QDir::NoDotAndDotDot)); for (const FilePath &folder : folders) { const FilePath script = folder / (folder.baseName() + ".lua"); @@ -113,6 +135,23 @@ public: } } + if (m_arguments.loadPlugin) { + const FilePath folder = *m_arguments.loadPlugin; + const FilePath script = folder / (folder.baseName() + ".lua"); + const expected_str result = m_luaEngine->loadPlugin(script); + + if (!result) { + qWarning() << "Failed to load plugin" << script << ":" << result.error(); + MessageManager::writeFlashing(tr("Failed to load plugin %1: %2") + .arg(script.toUserOutput()) + .arg(result.error())); + + } else { + (*result)->setEnabledBySettings(true); + plugins.insert(*result); + } + } + PluginManager::addPlugins({plugins.begin(), plugins.end()}); PluginManager::loadPluginsAtRuntime(plugins); } From a30eb2ccc49a50828f24fd69b308c122092fbfff Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 4 Jun 2024 09:09:07 +0200 Subject: [PATCH 14/69] Utils: Fix terminal process environment The TerminalInterface did not fallback to the system environment if no environment was set by the user. Instead it had platform specific code that saved over the path. Instead we just apply the user changes to the system environment. Change-Id: I011f1a9d935c958265b2bda8257ad8611f06e578 Reviewed-by: Eike Ziller --- src/libs/utils/terminalinterface.cpp | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp index 0d5a3be0203..45bdd8a4f9d 100644 --- a/src/libs/utils/terminalinterface.cpp +++ b/src/libs/utils/terminalinterface.cpp @@ -334,22 +334,12 @@ void TerminalInterface::start() Environment finalEnv = m_setup.m_environment; - if (HostOsInfo::isWindowsHost()) { - if (!finalEnv.hasKey("PATH")) { - const QString path = qtcEnvironmentVariable("PATH"); - if (!path.isEmpty()) - finalEnv.set("PATH", path); - } - if (!finalEnv.hasKey("SystemRoot")) { - const QString systemRoot = qtcEnvironmentVariable("SystemRoot"); - if (!systemRoot.isEmpty()) - finalEnv.set("SystemRoot", systemRoot); - } - } else if (HostOsInfo::isMacHost()) { + if (HostOsInfo::isMacHost()) finalEnv.set("TERM", "xterm-256color"); - } if (finalEnv.hasChanges()) { + finalEnv = finalEnv.appliedToEnvironment(Environment::systemEnvironment()); + d->envListFile = std::make_unique(this); if (!d->envListFile->open()) { cleanupAfterStartFailure(msgCannotCreateTempFile(d->envListFile->errorString())); From bfd3b88bb890460a5b0213258f12e0ab05b84351 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 3 Jun 2024 13:03:00 +0200 Subject: [PATCH 15/69] RemoteLinux: Do not announce connection attempts from non-UI threads This can lead to a deadlock. Example scenario: - Have a kit with an unreachable device as the build device. - Open a project with a .user file not matching the current QtC. - The target setup page will open, showing a candidate widget for each kit. These widgets contains path choosers, which nowadays start a dedicated thread to validate their input. - The path validation thread for the Linux kit arrives at setupShell() and tries to announce the connection attempt. To that end, it invokes a method of Core::ICore::infoBar, using Qt::BlockingQueuedConnection, as otherwise the call would either not be tread-safe or arrive too late. - As the InfoBar lives in the main thread, control is now passed back to that one, and the target setup page continues its work, which is to gather candidates for importing a build from. This involves perusing potential build directories for all kits. In case of the Linux kit, QDirIterator is backed by a remote call to find(), which triggers a call to runInShell(), which tries to acquire the mutex that is already held by the path validation thread, which in turn is waiting for the UI thread. - Qt Creator now hangs indefinitely. Skipping the announcements for non-UI threads limits the usefulness of this feature, but I don't see a better way. Change-Id: I816c83358f543aa9a6e6e97eee7fa8ad95e66ea8 Reviewed-by: Marcus Tillmanns --- src/plugins/remotelinux/linuxdevice.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index d2779e2ebb8..97cb7f9ae72 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -1231,25 +1231,20 @@ RunResult LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArra void LinuxDevicePrivate::announceConnectionAttempt() { - const auto announce = [id = announceId(), name = q->displayName()] { - Core::ICore::infoBar()->addInfo( - InfoBarEntry(id, - Tr::tr("Establishing initial connection to device \"%1\". " - "This might take a moment.") - .arg(name))); + const QString message = Tr::tr("Establishing initial connection to device \"%1\". " + "This might take a moment.").arg(q->displayName()); + qCDebug(linuxDeviceLog) << message; + if (isMainThread()) { + Core::ICore::infoBar()->addInfo(InfoBarEntry(announceId(), message)); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // Yes, twice. - }; - if (QThread::currentThread() == qApp->thread()) - announce(); - else - QMetaObject::invokeMethod(Core::ICore::infoBar(), announce, Qt::BlockingQueuedConnection); + } } void LinuxDevicePrivate::unannounceConnectionAttempt() { - QMetaObject::invokeMethod(Core::ICore::infoBar(), - [id = announceId()] { Core::ICore::infoBar()->removeInfo(id); }); + if (isMainThread()) + Core::ICore::infoBar()->removeInfo(announceId()); } bool LinuxDevicePrivate::checkDisconnectedWithWarning() From 725cf0637fefb17e434b44aba7ff9ac1462d8aa7 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 3 Jun 2024 22:13:36 +0200 Subject: [PATCH 16/69] CMakePM: Skip .obj/.o for static qml plugins in PATH Amends 5d159e6185a9973ceff2dd91a78302f48b6be715 This commit reverts to Qt Creator 12 behavior, plus skipping .obj/.o parts which are needed for linking but not for finding out library paths that need to be added to PATH environment. Tested with https://code.qt.io/cgit/qt/qtdeclarative.git/tree/examples/ qml/tutorials/extending-qml/chapter6-plugins Without the patch (Qt Creator 12 behavior): "C:/Projects/chapter6-plugins/build/Desktop_Qt_6_7_1_MinGW_64_bit-Debug/ Charts/CMakeFiles/chartsplugin_resources_1.dir/.qt/rcc", "C:/Projects/chapter6-plugins/build/Desktop_Qt_6_7_1_MinGW_64_bit-Debug/ Charts/CMakeFiles/chartsplugin_init.dir/chartsplugin_init_autogen", "C:/Projects/chapter6-plugins/build/Desktop_Qt_6_7_1_MinGW_64_bit-Debug/ Charts/CMakeFiles/chartsplugin_init.dir", "C:/Projects/chapter6-plugins/build/Desktop_Qt_6_7_1_MinGW_64_bit-Debug/ Charts", "C:/Qt/6.7.1/mingw_64/lib", "C:/Qt/6.7.1/mingw_64/bin" With the patch: "C:/Projects/chapter6-plugins/build/Desktop_Qt_6_7_1_MinGW_64_bit-Debug/ Charts", "C:/Qt/6.7.1/mingw_64/lib", "C:/Qt/6.7.1/mingw_64/bin" As you can see above, only skipping the .obj/.o files will bring a lot. The change also doesn't come with the side effects of the Qt Creator 13.0.0/1 releases. Task-number: QTCREATORBUG-29662 Change-Id: I264a0ef579177b7129471919b77cd22a46e7d511 Reviewed-by: Eike Ziller --- .../fileapidataextractor.cpp | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 9d55e2e692b..bb45b5224ae 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -209,12 +209,9 @@ static bool isChildOf(const FilePath &path, const FilePaths &prefixes) static CMakeBuildTarget toBuildTarget(const TargetDetails &t, const FilePath &sourceDirectory, const FilePath &buildDirectory, - bool relativeLibs, - const QSet &sharedLibraryArtifacts) + bool relativeLibs) { const FilePath currentBuildDir = buildDirectory.resolvePath(t.buildDir); - const QSet sharedLibraryArtifactsPaths - = transform(sharedLibraryArtifacts, &FilePath::parentDir); CMakeBuildTarget ct; ct.title = t.name; @@ -323,20 +320,20 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t, // "/usr/local/lib" since these are usually in the standard search // paths. There probably are more, but the naming schemes are arbitrary // so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR"). - if (buildDir.osType() != OsTypeWindows - && !isChildOf(tmp, - {"/lib", "/lib64", "/usr/lib", "/usr/lib64", "/usr/local/lib"})) + if (buildDir.osType() == OsTypeWindows + || !isChildOf(tmp, + {"/lib", + "/lib64", + "/usr/lib", + "/usr/lib64", + "/usr/local/lib"})) { librarySeachPaths.append(tmp); - if (buildDir.osType() == OsTypeWindows) { - if (sharedLibraryArtifactsPaths.contains(tmp)) - librarySeachPaths.append(tmp); - // Libraries often have their import libs in ../lib and the // actual dll files in ../bin on windows. Qt is one example of that. - if (tmp.fileName() == "lib") { + if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) { const FilePath path = tmp.parentDir().pathAppended("bin"); - if (path.isDir() && !isChildOf(path, {buildDir})) + if (path.isDir()) librarySeachPaths.append(path); } } @@ -355,19 +352,12 @@ static QList generateBuildTargets(const QFuture &cancelF const FilePath &buildDirectory, bool relativeLibs) { - QSet sharedLibraryArtifacts; - for (const TargetDetails &t : input.targetDetails) - if (t.type == "MODULE_LIBRARY" || t.type == "SHARED_LIBRARY") - for (const FilePath &p : t.artifacts) - sharedLibraryArtifacts.insert(buildDirectory.resolvePath(p)); - QList result; result.reserve(input.targetDetails.size()); for (const TargetDetails &t : input.targetDetails) { if (cancelFuture.isCanceled()) return {}; - result.append( - toBuildTarget(t, sourceDirectory, buildDirectory, relativeLibs, sharedLibraryArtifacts)); + result.append(toBuildTarget(t, sourceDirectory, buildDirectory, relativeLibs)); } return result; } From 173aed9b8e0399e7a0b6eca6c6e937e03ea4b540 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Tue, 4 Jun 2024 07:51:23 +0300 Subject: [PATCH 17/69] C++: Fix compiler warning warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data Change-Id: I3a8203c1662cfba36ebe28ad3bd1b5ff46b55dfe Reviewed-by: Christian Kandeler --- src/libs/3rdparty/cplusplus/TranslationUnit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp index 8505deb0bdc..3e51352b147 100644 --- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp +++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp @@ -307,7 +307,7 @@ recognize: if (currentExpanded) { QTC_ASSERT(macroOffset != -1 && macroLength != -1, continue); - _expansionPositions[_tokens->size() - 1] = std::make_pair(macroOffset, macroLength); + _expansionPositions[int(_tokens->size()) - 1] = std::make_pair(macroOffset, macroLength); } } while (tk.kind()); From 408e986f2b2409624b8a329f7a2b9e0ba9b91d7b Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 3 Jun 2024 13:59:46 +0200 Subject: [PATCH 18/69] ClangCodeModel: Fix potential crash ... when removing from the list of FollowSymbol operations. The done() callback of ClangdFollowSymbol messes with the list, so call cancel() after removing the corresponding iterator. Change-Id: Ibb72ffa436598692e48119d93d430bb79bcb0f5e Reviewed-by: hjk --- src/plugins/clangcodemodel/clangdclient.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index f3600352642..19cd4bd5a91 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -1011,9 +1011,10 @@ void ClangdClient::followSymbol(TextDocument *document, : ClangdFollowSymbol::Origin::Code; if (origin == ClangdFollowSymbol::Origin::User) { for (auto it = d->followSymbolOps.begin(); it != d->followSymbolOps.end(); ) { - if ((*it)->isInteractive()) { - (*it)->cancel(); + ClangdFollowSymbol * const followSymbol = *it; + if (followSymbol->isInteractive()) { it = d->followSymbolOps.erase(it); + followSymbol->cancel(); } else { ++it; } From 3c71998e34d887bd4a4cb3c191fea50f163b6a6f Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 4 Jun 2024 11:44:08 +0200 Subject: [PATCH 19/69] PluginManager: Fix -load all -noload Plugin Setting a plugin forceDisabled must also remove the forceEnabled flag and vice versa. Broke in b39b1925189416585933c017e189d04d6e4c478d Change-Id: I9c2b84de0753a1b283c301b9868650e62c088144 Reviewed-by: Christian Stenger --- src/libs/extensionsystem/pluginspec.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 2158b99ac27..18c8d11e78f 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -627,10 +627,14 @@ void PluginSpec::setEnabledIndirectly(bool value) } void PluginSpec::setForceDisabled(bool value) { + if (value) + d->forceEnabled = false; d->forceDisabled = value; } void PluginSpec::setForceEnabled(bool value) { + if (value) + d->forceDisabled = false; d->forceEnabled = value; } From 440c579ec0cf549c9bc4187524c619a5bf678b05 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 31 May 2024 13:29:51 +0200 Subject: [PATCH 20/69] Doc: Remove references to Qt 4 and < Qt 5.15 Leave only mentions to things visible in the UI and to compatibility functions. Fixes: QTCREATORBUG-30883 Change-Id: Id63d791f79363c2f4c2602bb8c0ed656a9ceab29 Reviewed-by: Ulf Hermann --- .../src/analyze/creator-ctf-visualizer.qdoc | 2 +- doc/qtcreator/src/android/androiddev.qdoc | 16 ++-- .../src/debugger/qtquick-debugging.qdoc | 12 +-- .../creator-coding-edit-mode.qdoc | 14 +-- .../howto/creator-only/creator-autotest.qdoc | 3 +- .../src/howto/creator-only/qtcreator-faq.qdoc | 10 +- doc/qtcreator/src/ios/creator-ios-dev.qdoc | 4 +- .../creator-desktop-platforms.qdoc | 3 +- .../creator-only/qtquick-creating.qdoc | 3 +- .../src/qtquick/qtquick-profiler.qdoc | 91 ++----------------- 10 files changed, 32 insertions(+), 126 deletions(-) diff --git a/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc b/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc index 16031b8f6ae..8f02ee0607e 100644 --- a/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc +++ b/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc @@ -113,7 +113,7 @@ LTTng is a tracing toolkit for Linux that you can apply on embedded Linux systems to find out how to optimize the startup time of an application. - Since Qt 5.13, Qt has a set of kernel trace points and a tracing + Qt has a set of kernel trace points and a tracing subsystem for custom user space trace points. \section2 Configuring the Kernel diff --git a/doc/qtcreator/src/android/androiddev.qdoc b/doc/qtcreator/src/android/androiddev.qdoc index 5d1e49de6c4..ab6ae2d9092 100644 --- a/doc/qtcreator/src/android/androiddev.qdoc +++ b/doc/qtcreator/src/android/androiddev.qdoc @@ -131,22 +131,24 @@ C:\Users\Username\AppData\Local\QtProject\qtcreator\android\sdk_definitions.json \endcode - For example, the SDK configuration file sets the NDK version 19.2.5345600 - for use with Qt 5.12.0 to 5.12.5 and Qt 5.13.0 to 5.13.1: + For example, the SDK configuration file sets the NDK version 22.1.7171670 + for use with Qt 6.3, Qt 6.2, and Qt 5.15.9 to 5.15.20: \badcode "specific_qt_versions": [ - { - "versions": ["5.12.[0-5]", "5.13.[0-1]"], - "sdk_essential_packages": ["build-tools;28.0.2", "ndk;19.2.5345600"], - "ndk_path": "ndk/19.2.5345600" - } + { "versions": ["6.3", "6.2", "5.15.[9-20]"], + "sdk_essential_packages": ["build-tools;31.0.0", "ndk;22.1.7171670"] + }, ] \endcode You can view the latest version of the configuration file that is up-to-date with the Android SDK and NDK changes, \l{sdk_definitions.json}, in Git. + \note For Qt 6.5 or later, \QC reads the NDK version that was used for + building Qt from \c modules/Core.json and uses it instead of the version + in \c sdk_definitions.json. + \sa {Android}{How To: Develop for Android}, {Developing for Android} */ diff --git a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc index f027ec0f960..1d4f41f789e 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc @@ -25,10 +25,6 @@ information about the state of the UI, and inspect QML properties and JavaScript variables, as well as change their values temporarily at runtime. - \if defined(qtcreator) - \note You need Qt 5.0 or later to debug Qt Quick projects. - \endif - For an example of how to debug Qt Quick Projects, see \l{Debugging a Qt Quick Application}. @@ -86,7 +82,7 @@ rebuild the project. \li To debug applications on \l{glossary-device}{devices}, check that - Qt 5.0, or later, libraries are installed on the device and + Qt libraries are installed on the device and \l{Run on many platforms}{select the corresponding kit for the device} before you start debugging. @@ -250,12 +246,6 @@ application to jump to their definitions in the code. The properties of the selected item are displayed in the \uicontrol {Locals} view. - \if defined(qtcreator) - The \uicontrol Select tool will be enabled either if your application is - using Qt 5.7 or later, or if your application is using an earlier version - of Qt and is based on the \c QQuickView class. - \endif - You can also view the item hierarchy in the running application: Double-click an item in the running application to cycle through the item diff --git a/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc b/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc index c90a21f80e7..9bbfe729d93 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc @@ -417,18 +417,18 @@ \image qtcreator-options-texteditor-behavior-file-encodings.png {File encoding preferences} - Qt 5 and Qt 6 require UTF-8 encoded source files, and therefore the default + Qt requires UTF-8 encoded source files, and therefore the default encoding is set to \uicontrol UTF-8. - Detecting the correct encoding is tricky, so \QC will not try to do so. - Instead, it displays the following error message when you try to edit a file - that is not UTF-8 encoded: \uicontrol {Error: Could not decode "filename" with + + If you try to edit a file that is not UTF-8 encoded, you see the following + error message: \uicontrol {Error: Could not decode "filename" with "UTF-8"-encoding. Editing not possible.} To resolve the issue, use a file conversion tool to convert the file - encoding to UTF-8 when developing Qt 5 applications. Otherwise, conversion - of string constants to QString might not work as expected. + encoding to UTF-8. Otherwise, conversion of string constants to + QString might not work as expected. - If you develop only Qt 4 applications or other than Qt applications, you + If you do not develop Qt applications, you can set other encoding options as the default encoding. Select the \uicontrol System option to use the file encoding used by your system. diff --git a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc index a678e094636..f6006e8eded 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc @@ -849,8 +849,7 @@ \section1 Blacklisting Tests - Since Qt 5.4, you can add a BLACKLIST file for tests. It is mainly used - internally by the Qt CI system. + A BLACKLIST file for tests is mainly used internally by the Qt CI system. \table \header diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc index 4302cd8e438..967031f69be 100644 --- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc +++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc @@ -121,14 +121,10 @@ \b {The Qt API Reference Documentation is missing and context help does not find topics. What can I do?} - \QC comes fully integrated with Qt documentation and examples using - the Qt Help plugin. The integrated Qt Reference Documentation is available - for Qt 4.4 and later. \QC and other Qt deliverables have - documentation as .qch files. All the documentation is accessible in the - \uicontrol Help mode. + Install a Qt version and Qt documentation with \QOI. - To view the documentation that is available and to add documentation, - select \preferences > \uicontrol Help > + To view the installed documentation (.qch files) and to add documentation, + go to \preferences > \uicontrol Help > \uicontrol Documentation. For more information, see \l{Add external documentation}. diff --git a/doc/qtcreator/src/ios/creator-ios-dev.qdoc b/doc/qtcreator/src/ios/creator-ios-dev.qdoc index 5dccfd1dccc..f02397f149f 100644 --- a/doc/qtcreator/src/ios/creator-ios-dev.qdoc +++ b/doc/qtcreator/src/ios/creator-ios-dev.qdoc @@ -16,8 +16,8 @@ the necessary \l{Kits}{kits} to build applications for and run them on configured iOS devices. - You only need Qt libraries that are built for iOS. You can install them as - part of Qt 5.2, or later. + You only need Qt libraries that are built for iOS. You can install Qt for iOS + with \QOI. \section1 iOS 17 Devices diff --git a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc index 67605429f86..6a2602b34d0 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc @@ -45,8 +45,7 @@ \li libxft-dev \li libxi-dev \li libxrandr-dev - \li libgl-dev and libglu-dev if you use Qt OpenGL (deprecated - in Qt 5) or Qt GUI OpenGL functions + \li libgl-dev and libglu-dev if you use Qt GUI OpenGL functions \endlist \section1 macOS diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc index 0ff290f0d0d..0fbb4fdea07 100644 --- a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc @@ -149,8 +149,7 @@ imports that are used in the QML files. You can add imports later to combine Qt Quick basic types with - Qt Quick Controls, Qt Quick Dialogs, and Qt Quick Layouts (available - since Qt 5.1). + Qt Quick Controls, Qt Quick Dialogs, and Qt Quick Layouts. \li Select the \uicontrol {Use Qt Virtual Keyboard} check box to add support for \l{Qt Virtual Keyboard} to the application. diff --git a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc index bd0985e3121..b22bd5be5e7 100644 --- a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc @@ -261,33 +261,24 @@ of the code the event is associated with. The following types of events are displayed in the timeline view on one or - several rows. The availability of event types depends on the Qt version that - you build the application with and the Qt Quick version you use. + several rows. \table \header \li Event Category \li Description - \li Minimum Qt Version - \li Qt Quick Version - \row \li \uicontrol {Pixmap Cache} \li Displays the general amount of pixmap data cached, in pixels. In addition, displays a separate event for each picture being loaded, with specifics about its file name and size. - \li Qt 5.1 - \li Qt Quick 2 \row \li \uicontrol {Scene Graph} \li Displays the time when scene graph frames are rendered and some additional timing information for the various stages executed to do so. - \li Qt 5.1 - \li Qt Quick 2 - \row \li \uicontrol {Memory Usage} \li Displays block allocations of the JavaScript memory manager. @@ -300,82 +291,49 @@ The second row displays the actual usage of the allocated memory. This is the amount of JavaScript heap the application has actually requested. - \li Qt 5.4 - \li Qt Quick 2 - \row \li \uicontrol {Input Events} \li Displays mouse and keyboard events. - \li Qt 4.7.4 - \li Qt Quick 1 or Qt Quick 2 - \row \li \uicontrol Painting - \li Displays the time spent painting the scene for each frame. - \li Qt 4.7.4 - \li Qt Quick 1 - + \li Not used. \row \li \uicontrol Animations \li Displays the amount of animations that are active and the frame rate that they are running at. - Information about render thread animations is displayed for - applications that are built with Qt 5.3 or later. Render thread - animations are shown in a separate row then. - \li Qt 5.0 (Qt 5.3) - \li Qt Quick 2 - + Render thread animations are shown on a separate row. \row \li \uicontrol Compiling \li Displays the time spent compiling the QML files. - \li Qt 4.7.4 - \li Qt Quick 1 or Qt Quick 2 - \row \li \uicontrol Creating - \li Displays the time spent creating the elements in the scene. In Qt - Quick 2, creation of elements takes place in two stages. The first + \li Displays the time spent creating the elements in the scene. + The creation of elements takes place in two stages. The first stage is for the creation of the data structures, including child elements. The second stage represents the completion callbacks. Not all elements trigger completion callbacks, though. The stages are shown as separate events in the timeline. - For Qt Quick 2 applications compiled with versions of Qt before - 5.2.1 only the creation of top-level elements is shown, as single - events. - \li Qt 4.7.4 (Qt 5.2.1) - \li Qt Quick 1 or Qt Quick 2 - \row \li \uicontrol Binding \li Displays the time when a binding is evaluated and how long the evaluation takes. - \li Qt 4.7.4 - \li Qt Quick 1 or Qt Quick 2 - \row \li \uicontrol {Handling Signal} \li Displays the time when a signal is handled and how long the handling takes. - \li Qt 4.7.4 - \li Qt Quick 1 or Qt Quick 2 - \row \li \uicontrol JavaScript \li Displays the time spent executing the actual JavaScript behind bindings and signal handlers. It lists all the JavaScript functions you may be using to evaluate bindings or handle signals. - \li Qt 5.3 - \li Qt Quick 2 - \row \li \uicontrol Quick3D \li Displays the time spent rendering Qt Quick 3D frames, timing information for frame preparation and synchronization, particle system update times and particle update count, as well as texture and mesh memory allocations and memory consumption. - \li Qt 6.3 - \li Qt Quick 3D + This event type is available since Qt 6.3. \endtable \section2 Analyzing Scene Graph Events @@ -399,7 +357,6 @@ \li Render Loop Types \li Label in output of QSG_RENDER_TIMING \li Description - \li Caveats \row \li \uicontrol {Polish} \li GUI @@ -407,8 +364,6 @@ \li polish \li Final touch-up of items before they are rendered using QQuickItem::updatePolish(). - \li Versions of Qt prior to Qt 5.4 record no polish times for the basic - render loop and incorrect ones for the windows render loop. \row \li \uicontrol {GUI Thread Wait} \li GUI @@ -419,14 +374,12 @@ the same mutex at \uicontrol {GUI Thread Sync}. If this starts long before \uicontrol {Render Thread Sync}, there is \e free time in the GUI thread you could be using for running additional QML or JavaScript. - \li None \row \li \uicontrol {GUI Thread Sync} \li GUI \li Threaded \li blockedForSync \li The time the GUI thread is blocked, waiting for the render thread. - \li None \row \li \uicontrol {Animations} \li GUI @@ -437,7 +390,6 @@ animation events will be shown when using the basic render loop. Watch the \uicontrol {Animations} category to see animation timing in this case. - \li None \row \li \uicontrol {Render Thread Sync} \li Render @@ -445,7 +397,6 @@ \li Frame rendered ... sync \li Synchronizing the QML state into the scene graph using QQuickItem::updatePaintNode(). - \li None \row \li \uicontrol {Render} \li Render @@ -455,20 +406,12 @@ uploading all the necessary data to the GPU. This is the \e gross render time. Do not confuse it with the \e net \uicontrol{Render Render} time below. - \li With versions of Qt prior to Qt 5.5, the gross render time and the - below breakup of render times may be misaligned by some - microseconds due to different, unsynchronized timers being used to - measure them. For example \uicontrol {Render Preprocess} might seem to - start before \uicontrol {Render Thread Sync} is finished. \row \li \uicontrol {Swap} \li Render \li Threaded, Basic, Windows \li Frame rendered ... swap \li Swapping frames after rendering. - \li The output of swap times triggered by setting QSG_RENDER_TIMING is - incorrect for the basic render loop and versions of Qt prior to - Qt 5.4. QML Profiler shows the correct swap times. \row \li \uicontrol {Render Preprocess} \li Render @@ -476,8 +419,6 @@ \li time in renderer ... preprocess \li Calling QSGNode::preprocess() on all nodes that need to be preprocessed. This is part of the gross \uicontrol {Render} step. - \li May not be properly aligned with \uicontrol {Render} with versions of Qt - prior to Qt 5.5. \row \li \uicontrol {Render Update} \li Render @@ -489,8 +430,6 @@ with state from the GUI thread. In \uicontrol {Render Update}, all the nodes are combined to create the final scene. This is part of the gross \uicontrol {Render} step. - \li May not be properly aligned with \uicontrol {Render} with versions of Qt - prior to Qt 5.5. \row \li \uicontrol {Render Bind} \li Render @@ -498,8 +437,6 @@ \li time in renderer ... binding \li Binding the correct framebuffer for OpenGL rendering. This is part of the gross \uicontrol {Render} step. - \li May not be properly aligned with \uicontrol {Render} with versions of Qt - prior to Qt 5.5. \row \li \uicontrol {Render Render} \li Render @@ -507,38 +444,30 @@ \li time in renderer ... rendering \li The actual process of sending all the data to the GPU via OpenGL. This is part of the gross \uicontrol {Render} step. - \li May not be properly aligned with \uicontrol {Render} with versions of Qt - prior to Qt 5.5. \row \li \uicontrol {Material Compile} \li Render \li Threaded, Basic, Windows \li shader compiled \li Compiling GLSL shader programs. - \li None \row \li \uicontrol {Glyph Render} \li Render \li Threaded, Basic, Windows \li glyphs ... rendering \li Rendering of font glyphs into textures. - \li Versions of Qt prior to Qt 5.4 report incorrect times for these - events. \row \li \uicontrol {Glyph Upload} \li Render \li Threaded, Basic, Windows \li glyphs ... upload \li Uploading of glyph textures to the GPU. - \li Versions of Qt prior to Qt 5.4 report incorrect times for these - events. \row \li \uicontrol {Texture Bind} \li Render \li Threaded, Basic, Windows \li plain texture ... bind \li Binding a texture in the OpenGL context using glBindTextures. - \li None \row \li \uicontrol {Texture Convert} \li Render @@ -546,35 +475,30 @@ \li plain texture ... convert \li Converting the format and downscaling an image to make it suitable for usage as a texture. - \li None \row \li \uicontrol {Texture Swizzle} \li Render \li Threaded, Basic, Windows \li plain texture ... swizzle \li Swizzling the texture data on the CPU if necessary. - \li None \row \li \uicontrol {Texture Upload} \li Render \li Threaded, Basic, Windows \li plain texture ... upload / atlastexture uploaded \li Uploading the texture data to the GPU. - \li None \row \li \uicontrol {Texture Mipmap} \li Render \li Threaded, Basic, Windows \li plain texture ... mipmap \li Mipmapping a texture on the GPU. - \li None \row \li \uicontrol {Texture Delete} \li Render \li Threaded, Basic, Windows \li plain texture deleted \li Deleting a texture from the GPU that became unnecessary. - \li None \endtable \section2 Analyzing Qt Quick 3D Events @@ -676,9 +600,6 @@ To copy the contents of one view or row to the clipboard, select \uicontrol {Copy Table} or \uicontrol {Copy Row} in the context menu. - JavaScript events are shown in the \uicontrol Statistics view only for applications - that use Qt Quick 2 and are built with Qt 5.3 or later. - \section2 Visualizing Statistics as Flame Graphs The \uicontrol {Flame Graph} view shows a more concise statistical overview From f1d0aee486b5a436b4dc8c654884f380257ec7b2 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 4 Jun 2024 12:06:34 +0200 Subject: [PATCH 21/69] ProjectExplorer: Add projectnodeshelper.h into project Otherwise this file is exlcuded from project search results. Change-Id: I65ce5121ac105f5565c46e4b5a5fbd74bee966d6 Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/CMakeLists.txt | 1 + src/plugins/projectexplorer/projectexplorer.qbs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 0c62d2177a1..ea7a079d908 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -140,6 +140,7 @@ add_qtc_plugin(ProjectExplorer projectmanager.cpp projectmanager.h projectmodels.cpp projectmodels.h projectnodes.cpp projectnodes.h + projectnodeshelper.h projectpanelfactory.cpp projectpanelfactory.h projectsettingswidget.cpp projectsettingswidget.h projecttree.cpp projecttree.h diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index bce24dc1361..19b9221de2b 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -115,6 +115,7 @@ QtcPlugin { "projectmanager.cpp", "projectmanager.h", "projectmodels.cpp", "projectmodels.h", "projectnodes.cpp", "projectnodes.h", + "projectnodeshelper.h", "projectpanelfactory.cpp", "projectpanelfactory.h", "projectsettingswidget.cpp", "projectsettingswidget.h", "projecttree.cpp", From 968e47194cca131afa8caab4abca9e6766ed7f59 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 3 Jun 2024 09:50:42 +0200 Subject: [PATCH 22/69] VCS: Fix handling of default executable We set the executable by default to a single command which should be used as found in PATH. Although listed valid in settings some commands refuse to run when we validate the command before running without taking their full path into account. Use the full path of vcs binaries internally to avoid problems but still display single command if set. Change-Id: If8677b531c5534d27c19557ed36fa780a248558f Reviewed-by: Marcus Tillmanns Reviewed-by: Orgad Shaneh --- src/plugins/bazaar/bazaarplugin.cpp | 2 +- src/plugins/cvs/cvsplugin.cpp | 2 +- src/plugins/fossil/fossilclient.cpp | 2 +- src/plugins/mercurial/mercurialplugin.cpp | 2 +- src/plugins/subversion/subversionplugin.cpp | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp index 47c4bd8f8c5..31d9c243906 100644 --- a/src/plugins/bazaar/bazaarplugin.cpp +++ b/src/plugins/bazaar/bazaarplugin.cpp @@ -884,7 +884,7 @@ bool BazaarPluginPrivate::managesFile(const FilePath &workingDirectory, const QS bool BazaarPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); return !binary.isEmpty() && binary.isExecutableFile(); } diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index 01c47b1646f..6a25b29fa04 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -339,7 +339,7 @@ bool CvsPluginPrivate::isVcsFileOrDirectory(const Utils::FilePath &filePath) con bool CvsPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); if (binary.isEmpty()) return false; QFileInfo fi = binary.toFileInfo(); diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp index 662785930d4..2c077a3b591 100644 --- a/src/plugins/fossil/fossilclient.cpp +++ b/src/plugins/fossil/fossilclient.cpp @@ -766,7 +766,7 @@ unsigned int FossilClient::binaryVersion() const static unsigned int cachedBinaryVersion = 0; static FilePath cachedBinaryPath; - const FilePath currentBinaryPath = settings().binaryPath(); + const FilePath currentBinaryPath = settings().binaryPath.effectiveBinary(); if (currentBinaryPath.isEmpty()) return 0; diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index 39b3fe44320..6d7a1ef7302 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -669,7 +669,7 @@ bool MercurialPluginPrivate::managesFile(const FilePath &workingDirectory, const bool MercurialPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); if (binary.isEmpty()) return false; QFileInfo fi = binary.toFileInfo(); diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index b28224a8667..36a5455a629 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -1067,7 +1067,7 @@ bool SubversionPluginPrivate::isVcsFileOrDirectory(const FilePath &filePath) con bool SubversionPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); if (binary.isEmpty()) return false; QFileInfo fi = binary.toFileInfo(); From b10d5581e428e0977a1c56c6a402f9e9fcaf1ec1 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 31 May 2024 13:46:04 +0200 Subject: [PATCH 23/69] Utils: Make LayoutBuilder setter setup less repetitive Change-Id: I9113f7a97566c21cf83dcb95ce8e75e9707360b4 Reviewed-by: Marcus Tillmanns --- src/libs/utils/layoutbuilder.cpp | 52 ++++----- src/libs/utils/layoutbuilder.h | 107 +++++++----------- .../cmakeprojectmanager/cmakebuildstep.cpp | 2 +- .../debuggerrunconfigurationaspect.cpp | 2 +- src/plugins/gitlab/gitlaboptionspage.cpp | 2 +- src/plugins/lua/bindings/gui.cpp | 4 +- .../projectexplorer/buildconfiguration.cpp | 2 +- src/plugins/projectexplorer/buildstep.cpp | 2 +- src/plugins/projectexplorer/makestep.cpp | 2 +- .../projectexplorer/runconfiguration.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakestep.cpp | 2 +- 11 files changed, 75 insertions(+), 104 deletions(-) diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 09e3a83520e..059e7294aea 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -339,17 +339,17 @@ void Layout::span(int cols, int rows) pendingItems.back().spanRows = rows; } -void Layout::noMargin() +void Layout::setNoMargins() { - customMargin({}); + setContentMargins({}); } -void Layout::normalMargin() +void Layout::setNormalMargins() { - customMargin({9, 9, 9, 9}); + setContentMargins({9, 9, 9, 9}); } -void Layout::customMargin(const QMargins &margin) +void Layout::setContentMargins(const QMargins &margin) { access(this)->setContentsMargins(margin); } @@ -467,11 +467,11 @@ void addToLayout(Layout *layout, const QString &inner) layout->addLayoutItem(item); } -void empty(Layout *iface) +void empty(Layout *layout) { LayoutItem item; item.empty = true; - iface->addLayoutItem(item); + layout->addLayoutItem(item); } void hr(Layout *layout) @@ -479,26 +479,26 @@ void hr(Layout *layout) layout->addLayoutItem(createHr()); } -void br(Layout *iface) +void br(Layout *layout) { - iface->flush(); + layout->flush(); } -void st(Layout *iface) +void st(Layout *layout) { LayoutItem item; item.stretch = 1; - iface->addLayoutItem(item); + layout->addLayoutItem(item); } -void noMargin(Layout *iface) +void noMargin(Layout *layout) { - iface->noMargin(); + layout->setNoMargins(); } -void normalMargin(Layout *iface) +void normalMargin(Layout *layout) { - iface->normalMargin(); + layout->setNormalMargins(); } QFormLayout *Layout::asForm() @@ -612,9 +612,9 @@ void Layout::flush_() const const_cast(this)->flush(); } -void withFormAlignment(Layout *iface) +void withFormAlignment(Layout *layout) { - iface->useFormAlignment = true; + layout->useFormAlignment = true; } // Flow @@ -672,7 +672,7 @@ Form::Form(std::initializer_list ps) flush(); } -void Layout::fieldGrowthPolicy(int policy) +void Layout::setFieldGrowthPolicy(int policy) { if (auto lt = asForm()) lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy)); @@ -699,7 +699,7 @@ Widget::Widget(std::initializer_list ps) apply(this, ps); } -void Widget::resize(int w, int h) +void Widget::setSize(int w, int h) { access(this)->resize(w, h); } @@ -724,19 +724,19 @@ void Widget::show() access(this)->show(); } -void Widget::noMargin(int) +void Widget::setNoMargins(int) { - customMargin({}); + setContentMargins({}); } -void Widget::normalMargin(int) +void Widget::setNormalMargins(int) { - customMargin({9, 9, 9, 9}); + setContentMargins({9, 9, 9, 9}); } -void Widget::customMargin(const QMargins &margin) +void Widget::setContentMargins(const QMargins &margins) { - access(this)->setContentsMargins(margin); + access(this)->setContentsMargins(margins); } QWidget *Widget::emerge() const @@ -979,7 +979,7 @@ void addToLayout(Layout *layout, const Span &inner) LayoutModifier spacing(int space) { - return [space](Layout *iface) { iface->setSpacing(space); }; + return [space](Layout *layout) { layout->setSpacing(space); }; } void addToLayout(Layout *layout, const Space &inner) diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 07a6b9478ef..3240ea85117 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -139,13 +139,16 @@ public: Layout(Implementation *w) { ptr = w; } void span(int cols, int rows); - void noMargin(); - void normalMargin(); - void customMargin(const QMargins &margin); + + void setNoMargins(); + void setNormalMargins(); + void setContentMargins(const QMargins &margin); void setColumnStretch(int cols, int rows); void setSpacing(int space); + void setFieldGrowthPolicy(int policy); void attachTo(QWidget *); + void addItem(I item); void addItems(std::initializer_list items); void addRow(std::initializer_list items); @@ -153,7 +156,6 @@ public: void flush(); void flush_() const; - void fieldGrowthPolicy(int policy); QWidget *emerge() const; void show() const; @@ -258,15 +260,15 @@ public: Widget(Implementation *w) { ptr = w; } QWidget *emerge() const; - void show(); - void resize(int, int); + void setLayout(const Layout &layout); + void setSize(int, int); void setWindowTitle(const QString &); void setToolTip(const QString &); - void noMargin(int = 0); - void normalMargin(int = 0); - void customMargin(const QMargins &margin); + void setNoMargins(int = 0); + void setNormalMargins(int = 0); + void setContentMargins(const QMargins &); }; class QTCREATOR_UTILS_EXPORT Label : public Widget @@ -439,69 +441,38 @@ void doit(Interface *x, IdId, auto p) // Setter dispatchers -class SizeId {}; -auto size(auto w, auto h) { return IdAndArg{SizeId{}, std::pair{w, h}}; } -void doit(auto x, SizeId, auto p) { x->resize(p->first, p->second); } +#define QTCREATOR_SETTER(name, setter) \ + class name##_TAG {}; \ + inline auto name(auto p) { return IdAndArg{name##_TAG{}, p}; } \ + inline void doit(auto x, name##_TAG, auto p) { x->setter(p); } -class TextId {}; -auto text(auto p) { return IdAndArg{TextId{}, p}; } -void doit(auto x, TextId, auto p) { x->setText(p); } +#define QTCREATOR_SETTER2(name, setter) \ + class name##_TAG {}; \ + inline auto name(auto p1, auto p2) { return IdAndArg{name##_TAG{}, std::pair{p1, p2}}; } \ + inline void doit(auto x, name##_TAG, auto p) { x->setter(p.first, p.second); } -class TitleId {}; -auto title(auto p) { return IdAndArg{TitleId{}, p}; } -void doit(auto x, TitleId, auto p) { x->setTitle(p); } +#define QTCREATOR_TYPED_SETTER(name, setter, type) \ + class name##_TAG {}; \ + inline auto name(type p) { return IdAndArg{name##_TAG{}, p}; } \ + inline void doit(auto x, name##_TAG, auto p) { x->setter(p); } -class TextFormatId {}; -auto textFormat(auto p) { return IdAndArg{TextFormatId{}, p}; } -void doit(auto x, TextFormatId, auto p) { x->setTextFormat(p); } +QTCREATOR_SETTER(fieldGrowthPolicy, setFieldGrowthPolicy); +QTCREATOR_SETTER(groupChecker, setGroupChecker); +QTCREATOR_SETTER(openExternalLinks, setOpenExternalLinks); +QTCREATOR_SETTER2(size, setSize) +QTCREATOR_SETTER(text, setText) +QTCREATOR_SETTER(textFormat, setTextFormat); +QTCREATOR_SETTER(textInteractionFlags, setTextInteractionFlags); +QTCREATOR_SETTER(title, setTitle) +QTCREATOR_SETTER(toolTip, setToolTip); +QTCREATOR_SETTER(windowTitle, setWindowTitle); +QTCREATOR_SETTER(wordWrap, setWordWrap); +QTCREATOR_SETTER2(columnStretch, setColumnStretch); +QTCREATOR_SETTER2(onClicked, onClicked); +QTCREATOR_SETTER2(onLinkHovered, onLinkHovered); +QTCREATOR_SETTER2(onTextChanged, onTextChanged); -class WordWrapId {}; -auto wordWrap(auto p) { return IdAndArg{WordWrapId{}, p}; } -void doit(auto x, WordWrapId, auto p) { x->setWordWrap(p); } - -class TextInteractionFlagId {}; -auto textInteractionFlags(auto p) { return IdAndArg{TextInteractionFlagId{}, p}; } -void doit(auto x, TextInteractionFlagId, auto p) { x->setTextInteractionFlags(p); } - -class OpenExternalLinksId {}; -auto openExternalLinks(auto p) { return IdAndArg{OpenExternalLinksId{}, p}; } -void doit(auto x, OpenExternalLinksId, auto p) { x->setOpenExternalLinks(p); } - -class OnLinkHoveredId {}; -auto onLinkHovered(auto p, QObject *guard) { return IdAndArg{OnLinkHoveredId{}, std::pair{p, guard}}; } -void doit(auto x, OnLinkHoveredId, auto p) { x->onLinkHovered(p.first, p.second); } - -class GroupCheckerId {}; -auto groupChecker(auto p) { return IdAndArg{GroupCheckerId{}, p}; } -void doit(auto x, GroupCheckerId, auto p) { x->setGroupChecker(p); } - -class ToolTipId {}; -auto toolTip(auto p) { return IdAndArg{ToolTipId{}, p}; } -void doit(auto x, ToolTipId, auto p) { x->setToolTip(p); } - -class WindowTitleId {}; -auto windowTitle(auto p) { return IdAndArg{WindowTitleId{}, p}; } -void doit(auto x, WindowTitleId, auto p) { x->setWindowTitle(p); } - -class OnTextChangedId {}; -auto onTextChanged(auto p) { return IdAndArg{OnTextChangedId{}, p}; } -void doit(auto x, OnTextChangedId, auto p) { x->onTextChanged(p); } - -class OnClickedId {}; -auto onClicked(auto p, auto guard) { return IdAndArg{OnClickedId{}, std::pair{p, guard}}; } -void doit(auto x, OnClickedId, auto p) { x->onClicked(p.first, p.second); } - -class CustomMarginId {}; -inline auto customMargin(const QMargins &p) { return IdAndArg{CustomMarginId{}, p}; } -void doit(auto x, CustomMarginId, auto p) { x->customMargin(p); } - -class FieldGrowthPolicyId {}; -inline auto fieldGrowthPolicy(auto p) { return IdAndArg{FieldGrowthPolicyId{}, p}; } -void doit(auto x, FieldGrowthPolicyId, auto p) { x->fieldGrowthPolicy(p); } - -class ColumnStretchId {}; -inline auto columnStretch(int column, int stretch) { return IdAndArg{ColumnStretchId{}, std::pair{column, stretch}}; } -void doit(auto x, ColumnStretchId, auto p) { x->setColumnStretch(p.first, p.second); } +QTCREATOR_TYPED_SETTER(customMargin, setContentMargins, const QMargins &); // Nesting dispatchers diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index e992dc8e9ea..aacd273f63e 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -605,7 +605,7 @@ QWidget *CMakeBuildStep::createConfigWidget() if (!isCleanStep() && !m_buildPreset.isEmpty()) createAndAddEnvironmentWidgets(builder); - builder.noMargin(); + builder.setNoMargins(); auto widget = builder.emerge(); updateDetails(); diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp index dc1919ce8b0..4bd1dec9675 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp @@ -72,7 +72,7 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target) details->setState(DetailsWidget::Expanded); auto innerPane = new QWidget; details->setWidget(innerPane); - builder.noMargin(); + builder.setNoMargins(); builder.attachTo(innerPane); const auto setSummaryText = [this, details] { diff --git a/src/plugins/gitlab/gitlaboptionspage.cpp b/src/plugins/gitlab/gitlaboptionspage.cpp index 03d078ba14e..181d5fba829 100644 --- a/src/plugins/gitlab/gitlaboptionspage.cpp +++ b/src/plugins/gitlab/gitlaboptionspage.cpp @@ -102,7 +102,7 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent) m_token, br, m_port, br, m_secure, - m == Edit ? &Layout::normalMargin : &Layout::noMargin + m == Edit ? &Layout::setNormalMargins : &Layout::setNoMargins }, }.attachTo(this); } diff --git a/src/plugins/lua/bindings/gui.cpp b/src/plugins/lua/bindings/gui.cpp index 7e236972330..aaba86f461c 100644 --- a/src/plugins/lua/bindings/gui.cpp +++ b/src/plugins/lua/bindings/gui.cpp @@ -306,8 +306,8 @@ void addGuiModule() }), "show", &Widget::show, - "resize", - &Widget::resize, + "setSize", + &Widget::setSize, sol::base_classes, sol::bases()); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 6b60b659215..a69133b2190 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -326,7 +326,7 @@ NamedWidget *BuildConfiguration::createConfigWidget() } Layouting::Form form; - form.noMargin(); + form.setNoMargins(); for (BaseAspect *aspect : aspects()) { if (aspect->isVisible()) { form.addItem(aspect); diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index 9b28cd08b23..a290fc0c69a 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -113,7 +113,7 @@ QWidget *BuildStep::doCreateConfigWidget() QWidget *BuildStep::createConfigWidget() { Layouting::Form form; - form.noMargin(); + form.setNoMargins(); for (BaseAspect *aspect : std::as_const(*this)) { if (aspect->isVisible()) { form.addItem(aspect); diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index 16e6f2c4e72..7bf7a1f6df4 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -313,7 +313,7 @@ QWidget *MakeStep::createConfigWidget() if (m_disablingForSubDirsSupported) builder.addRow({m_disabledForSubdirsAspect}); builder.addRow({m_buildTargetsAspect}); - builder.noMargin(); + builder.setNoMargins(); auto widget = builder.emerge(); diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 3aa2457ef95..a0c3aa2e6dd 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -201,7 +201,7 @@ bool RunConfiguration::isEnabled(Utils::Id) const QWidget *RunConfiguration::createConfigurationWidget() { Layouting::Form form; - form.noMargin(); + form.setNoMargins(); for (BaseAspect *aspect : std::as_const(*this)) { if (aspect->isVisible()) { form.addItem(aspect); diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index d1de44d1f57..65b79a7238a 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -428,7 +428,7 @@ QWidget *QMakeStep::createConfigWidget() builder.addRow({userArguments}); builder.addRow({effectiveCall}); builder.addRow({abisLabel, abisListWidget}); - builder.noMargin(); + builder.setNoMargins(); auto widget = builder.emerge(); qmakeBuildConfigChanged(); From 6231213aa3dad4652b46507183793180cd5bb7ee Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 31 May 2024 15:14:27 +0200 Subject: [PATCH 24/69] Utils: Make contents margins handling less special Change-Id: I985419bbb213aba899c83efb49b376e0f3338bd5 Reviewed-by: Marcus Tillmanns --- .../qmt/model_widgets_ui/modeltreefilter.cpp | 6 ++-- src/libs/utils/layoutbuilder.cpp | 16 ++++----- src/libs/utils/layoutbuilder.h | 33 ++++++++++++------- .../compilerexplorereditor.cpp | 4 +-- src/plugins/coreplugin/outputpanemanager.cpp | 2 +- src/plugins/coreplugin/welcomepagehelper.cpp | 4 +-- .../marketplace/qtmarketplacewelcomepage.cpp | 4 +-- .../projectexplorer/projectwelcomepage.cpp | 8 ++--- .../qtsupport/gettingstartedwelcomepage.cpp | 4 +-- src/plugins/welcome/welcomeplugin.cpp | 8 ++--- 10 files changed, 50 insertions(+), 39 deletions(-) diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp index 3fa671283af..f02b4668300 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp @@ -73,7 +73,7 @@ ModelTreeFilter::ModelTreeFilter(QWidget *parent) : }, d->relationsCheckBox, d->diagramElementsCheckBox, - customMargin({margin, 0, margin, 0}), + customMargins(margin, 0, margin, 0), }, Space(10), line(), @@ -88,11 +88,11 @@ ModelTreeFilter::ModelTreeFilter(QWidget *parent) : Tr::tr("Name:"), d->nameLineEdit, br, Tr::tr("Direction:"), d->directionComboBox, br, }, - customMargin({margin, 0, margin, 0}), + customMargins(margin, 0, margin, 0), }, st, line(), - customMargin({0, margin, 0, 0}), + customMargins(0, margin, 0, 0), }.attachTo(this); connect(d->resetViewButton, &QPushButton::clicked, this, &ModelTreeFilter::resetView); diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 059e7294aea..e285ed98f6c 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -341,17 +341,17 @@ void Layout::span(int cols, int rows) void Layout::setNoMargins() { - setContentMargins({}); + setContentsMargins(0, 0, 0, 0); } void Layout::setNormalMargins() { - setContentMargins({9, 9, 9, 9}); + setContentsMargins(9, 9, 9, 9); } -void Layout::setContentMargins(const QMargins &margin) +void Layout::setContentsMargins(int left, int top, int right, int bottom) { - access(this)->setContentsMargins(margin); + access(this)->setContentsMargins(left, top, right, bottom); } /*! @@ -726,17 +726,17 @@ void Widget::show() void Widget::setNoMargins(int) { - setContentMargins({}); + setContentsMargins(0, 0, 0, 0); } void Widget::setNormalMargins(int) { - setContentMargins({9, 9, 9, 9}); + setContentsMargins(9, 9, 9, 9); } -void Widget::setContentMargins(const QMargins &margins) +void Widget::setContentsMargins(int left, int top, int right, int bottom) { - access(this)->setContentsMargins(margins); + access(this)->setContentsMargins(left, top, right, bottom); } QWidget *Widget::emerge() const diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 3240ea85117..17fa24e0f42 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include @@ -26,7 +25,6 @@ class QGroupBox; class QHBoxLayout; class QLabel; class QLayout; -class QMargins; class QObject; class QPushButton; class QSpinBox; @@ -52,6 +50,15 @@ public: const T2 arg; // FIXME: Could be const &, but this would currently break bindTo(). }; +template +struct Arg2 { const T1 p1; const T2 p2; }; + +template +struct Arg3 { const T1 p1; const T2 p2; const T3 p3; }; + +template +struct Arg4 { const T1 p1; const T2 p2; const T3 p3; const T4 p4; }; + // The main dispatcher void doit(auto x, auto id, auto p); @@ -142,7 +149,7 @@ public: void setNoMargins(); void setNormalMargins(); - void setContentMargins(const QMargins &margin); + void setContentsMargins(int left, int top, int right, int bottom); void setColumnStretch(int cols, int rows); void setSpacing(int space); void setFieldGrowthPolicy(int policy); @@ -268,7 +275,7 @@ public: void setToolTip(const QString &); void setNoMargins(int = 0); void setNormalMargins(int = 0); - void setContentMargins(const QMargins &); + void setContentsMargins(int left, int top, int right, int bottom); }; class QTCREATOR_UTILS_EXPORT Label : public Widget @@ -448,13 +455,18 @@ void doit(Interface *x, IdId, auto p) #define QTCREATOR_SETTER2(name, setter) \ class name##_TAG {}; \ - inline auto name(auto p1, auto p2) { return IdAndArg{name##_TAG{}, std::pair{p1, p2}}; } \ - inline void doit(auto x, name##_TAG, auto p) { x->setter(p.first, p.second); } + inline auto name(auto p1, auto p2) { return IdAndArg{name##_TAG{}, Arg2{p1, p2}}; } \ + inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2); } -#define QTCREATOR_TYPED_SETTER(name, setter, type) \ +#define QTCREATOR_SETTER3(name, setter) \ class name##_TAG {}; \ - inline auto name(type p) { return IdAndArg{name##_TAG{}, p}; } \ - inline void doit(auto x, name##_TAG, auto p) { x->setter(p); } + inline auto name(auto p1, auto p2, auto p3) { return IdAndArg{name##_TAG{}, Arg4{p1, p2, p3}}; } \ + inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3); } + +#define QTCREATOR_SETTER4(name, setter) \ + class name##_TAG {}; \ + inline auto name(auto p1, auto p2, auto p3, auto p4) { return IdAndArg{name##_TAG{}, Arg4{p1, p2, p3, p4}}; } \ + inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3, p.p4); } QTCREATOR_SETTER(fieldGrowthPolicy, setFieldGrowthPolicy); QTCREATOR_SETTER(groupChecker, setGroupChecker); @@ -471,8 +483,7 @@ QTCREATOR_SETTER2(columnStretch, setColumnStretch); QTCREATOR_SETTER2(onClicked, onClicked); QTCREATOR_SETTER2(onLinkHovered, onLinkHovered); QTCREATOR_SETTER2(onTextChanged, onTextChanged); - -QTCREATOR_TYPED_SETTER(customMargin, setContentMargins, const QMargins &); +QTCREATOR_SETTER4(customMargins, setContentsMargins); // Nesting dispatchers diff --git a/src/plugins/compilerexplorer/compilerexplorereditor.cpp b/src/plugins/compilerexplorer/compilerexplorereditor.cpp index 340cc7ba2ce..1d00f8cb95c 100644 --- a/src/plugins/compilerexplorer/compilerexplorereditor.cpp +++ b/src/plugins/compilerexplorer/compilerexplorereditor.cpp @@ -258,7 +258,7 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr &se settings->languageId, addCompilerButton, removeSourceButton, - customMargin({6, 0, 0, 0}), spacing(0), + customMargins(6, 0, 0, 0), spacing(0), }.attachTo(toolBar); Column { @@ -403,7 +403,7 @@ CompilerWidget::CompilerWidget(const std::shared_ptr &sourceSett m_compilerSettings->compiler, advButton, removeCompilerBtn, - customMargin({6, 0, 0, 0}), spacing(0), + customMargins(6, 0, 0, 0), spacing(0), }.attachTo(toolBar); Column { diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index a5495612d5c..a36b55a9021 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -421,7 +421,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) : }.attachTo(this); Row { - spacing(creatorTheme()->flag(Theme::FlatToolBars) ? 9 : 4), customMargin({5, 0, 0, 0}), + spacing(creatorTheme()->flag(Theme::FlatToolBars) ? 9 : 4), customMargins(5, 0, 0, 0), }.attachTo(m_buttonsWidget); StatusBarManager::addStatusBarWidget(m_buttonsWidget, StatusBarManager::Second); diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index 63ec6745b5e..3fc432f7d01 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -1203,7 +1203,7 @@ ListModel *SectionedGridView::addSection(const Section §ion, const QList(widget(0)); @@ -1274,7 +1274,7 @@ void SectionedGridView::zoomInSection(const Section §ion) st, backLink, Space(ExVPaddingGapXl), - customMargin({0, ExPaddingGapL, 0, VPaddingL}), + customMargins(0, ExPaddingGapL, 0, VPaddingL), }.emerge(); auto gridView = new GridView(zoomedInWidget); diff --git a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp index e315ce7efb9..f26a618a10a 100644 --- a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp +++ b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp @@ -62,11 +62,11 @@ public: Column { Row { m_searcher, - customMargin({0, 0, ExVPaddingGapXl, 0}), + customMargins(0, 0, ExVPaddingGapXl, 0), }, m_sectionedProducts, spacing(VPaddingL), - customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0), }.attachTo(this); connect(m_sectionedProducts, &SectionedProducts::toggleProgressIndicator, diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 7597f88f06e..34ffab0b18a 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -799,11 +799,11 @@ public: sessionsLabel, st, manageSessionsButton, - customMargin({HPaddingS, 0, sessionScrollBarGap, 0}), + customMargins(HPaddingS, 0, sessionScrollBarGap, 0), }, sessionsList, spacing(ExPaddingGapL), - customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0), }.attachTo(sessions); connect(manageSessionsButton, &Button::clicked, this, &SessionManager::showSessionManager); @@ -823,11 +823,11 @@ public: Column { Row { projectsLabel, - customMargin({HPaddingS, 0, 0, 0}), + customMargins(HPaddingS, 0, 0, 0), }, projectsList, spacing(ExPaddingGapL), - customMargin({ExVPaddingGapXl - sessionScrollBarGap, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl - sessionScrollBarGap, ExVPaddingGapXl, 0, 0), }.attachTo(projects); } diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index f02a433fbe7..1240c9bbd33 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -279,7 +279,7 @@ public: using namespace Layouting; Row titleRow { - customMargin({0, 0, ExVPaddingGapXl, 0}), + customMargins(0, 0, ExVPaddingGapXl, 0), spacing(ExVPaddingGapXl), }; @@ -317,7 +317,7 @@ public: titleRow, gridView, spacing(ExVPaddingGapXl), - customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0), }.attachTo(this); connect(&m_exampleDelegate, &ExampleDelegate::tagClicked, diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp index 68e6d549f81..432ce59cb96 100644 --- a/src/plugins/welcome/welcomeplugin.cpp +++ b/src/plugins/welcome/welcomeplugin.cpp @@ -162,7 +162,7 @@ public: welcomeLabel, st, spacing(ExVPaddingGapXl), - customMargin({HPaddingM, VPaddingM, HPaddingM, VPaddingM}), + customMargins(HPaddingM, VPaddingM, HPaddingM, VPaddingM), }, createRule(Qt::Horizontal), noMargin, spacing(0), @@ -188,7 +188,7 @@ public: Column mainColumn { spacing(0), - customMargin({ExVPaddingGapXl, 0, ExVPaddingGapXl, 0}), + customMargins(ExVPaddingGapXl, 0, ExVPaddingGapXl, 0), }; m_essentials = new QWidget; @@ -205,7 +205,7 @@ public: newButton, openButton, spacing(ExPaddingGapL), - customMargin({0, ExVPaddingGapXl, 0, ExVPaddingGapXl}), + customMargins(0, ExVPaddingGapXl, 0, ExVPaddingGapXl), }; essentials.addItem(projectButtons); @@ -238,7 +238,7 @@ public: Column linksLayout { label, spacing(VGapS), - customMargin({0, VGapL, 0, ExVPaddingGapXl}), + customMargins(0, VGapL, 0, ExVPaddingGapXl), }; const struct { From 3be0b263a87c4289f26ad778829c2b38643178a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sivert=20Kr=C3=B8vel?= Date: Thu, 30 May 2024 17:19:10 +0200 Subject: [PATCH 25/69] McuSupport: Read and write user settings with non-versioned key The settings keys are versioned to avoid the situation where the installer removes a settings key which may still be in use. See QTCREATORBUG-29194 for details. When reading from user settings, it makes more sense to use the non-versioned keys, especially in the case of the main Qt for MCUs SDK package path. With this patch, the plain settings key is used to read and write except for when determining the default value of a path in the McuPackage constructor, where we want to also look in the system scope settings for the values written by the installer, which may be version specific Task-number: QTCREATORBUG-30810 Change-Id: Ib1e2be170cb7da24b6e4534b59f702b894556d8c Reviewed-by: Yasser Grimes Reviewed-by: Eike Ziller --- src/plugins/mcusupport/mcupackage.cpp | 18 ++++++++++++++---- src/plugins/mcusupport/mcupackage.h | 2 +- src/plugins/mcusupport/settingshandler.cpp | 16 ++++++++++++++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index 2ff913c680e..efcff77352b 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -43,11 +43,11 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, const McuPackageVersionDetector *versionDetector, const bool addToSystemPath, const Utils::PathChooser::Kind &valueType, - const bool useNewestVersionKey) + const bool allowNewerVersionKey) : settingsHandler(settingsHandler) , m_label(label) , m_detectionPaths(detectionPaths) - , m_settingsKey(settingsHandler->getVersionedKey(settingsKey, QSettings::SystemScope, versions, useNewestVersionKey)) + , m_settingsKey(settingsKey) , m_versionDetector(versionDetector) , m_versions(versions) , m_cmakeVariableName(cmakeVarName) @@ -56,8 +56,18 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, , m_addToSystemPath(addToSystemPath) , m_valueType(valueType) { - m_defaultPath = settingsHandler->getPath(m_settingsKey, QSettings::SystemScope, defaultPath); - m_path = settingsHandler->getPath(m_settingsKey, QSettings::UserScope, m_defaultPath); + // The installer writes versioned keys as well as the plain key as found in the kits. + // Use the versioned key in case the plain key was removed by an uninstall operation + const Utils::Key versionedKey = settingsHandler->getVersionedKey(settingsKey, + QSettings::SystemScope, + versions, + allowNewerVersionKey); + m_defaultPath = settingsHandler->getPath(versionedKey, QSettings::SystemScope, defaultPath); + m_path = settingsHandler->getPath(m_settingsKey, QSettings::UserScope, ""); + // The user settings may have been written with a versioned key in older versions of QtCreator + if (m_path.isEmpty()) { + m_path = settingsHandler->getPath(versionedKey, QSettings::UserScope, m_defaultPath); + } if (m_path.isEmpty()) { m_path = FilePath::fromUserInput(qtcEnvironmentVariable(m_environmentVariableName)); } diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index a74c08bcf2d..7b9ef646506 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -41,7 +41,7 @@ public: const bool addToPath = false, const Utils::PathChooser::Kind &valueType = Utils::PathChooser::Kind::ExistingDirectory, - const bool useNewestVersionKey = false); + const bool allowNewerVersionKey = false); ~McuPackage() override = default; diff --git a/src/plugins/mcusupport/settingshandler.cpp b/src/plugins/mcusupport/settingshandler.cpp index 1ecec030b45..939ba125d1c 100644 --- a/src/plugins/mcusupport/settingshandler.cpp +++ b/src/plugins/mcusupport/settingshandler.cpp @@ -105,12 +105,24 @@ FilePath SettingsHandler::getPath(const Key &settingsKey, bool SettingsHandler::write(const Key &settingsKey, const FilePath &path, - const FilePath &defaultPath) const + const FilePath &maybeDefaultPath) const { const FilePath savedPath = packagePathFromSettings(settingsKey, *Core::ICore::settings(QSettings::UserScope), - defaultPath); + maybeDefaultPath); const Key key = Key(Constants::SETTINGS_GROUP) + '/' + Constants::SETTINGS_KEY_PACKAGE_PREFIX + settingsKey; + + FilePath defaultPath = maybeDefaultPath; + if (path == maybeDefaultPath) { + // If the installer has overwritten the non-versioned key with an older version than the + // newest versioned key, and the user wants to manually return to the newest installed + // version, the defaultPath will match the desired path, and the settings object will + // assume it can simply remove the key instead of writing a new value to it. + // To work around this, pretend like the default value is the value found from the global scope + defaultPath = packagePathFromSettings(settingsKey, + *Core::ICore::settings(QSettings::SystemScope), + maybeDefaultPath);; + } Core::ICore::settings()->setValueWithDefault(key, path.toUserOutput(), defaultPath.toUserOutput()); From 0e80f63a1ae675692afdfc9085bbaf2d228b6a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sivert=20Kr=C3=B8vel?= Date: Fri, 31 May 2024 13:41:43 +0200 Subject: [PATCH 26/69] McuSupport: Use arm-none-eabi-gdb as a backup debugger We normally use arm-none-eabi-gdb-py, but this doesn't come with the new arm gcc version which ships with Qt for MCUs 2.8.0 To make sure the kit works, use this as a fallback. Task-number: QTCREATORBUG-30699 Change-Id: I85c6c3ea1f7aae504e0aa1afb8a344d9bc3067d5 Reviewed-by: Kwangsub Kim Reviewed-by: Eike Ziller --- src/plugins/mcusupport/mcupackage.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index efcff77352b..2ccc4bcc1a0 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -569,6 +569,10 @@ QVariant McuToolchainPackage::debuggerId() const switch (m_type) { case ToolchainType::ArmGcc: { sub = QString::fromLatin1("bin/arm-none-eabi-gdb-py"); + const FilePath command = (path() / sub).withExecutableSuffix(); + if (!command.exists()) { + sub = QString::fromLatin1("bin/arm-none-eabi-gdb"); + } displayName = Tr::tr("Arm GDB at %1"); engineType = Debugger::GdbEngineType; break; From f512bbff895f387f7fe03529a9fa09ca50175ebe Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 3 Jun 2024 08:55:10 +0200 Subject: [PATCH 27/69] Copilot: Adapt to agent.js => language-server.js rename Copilot has changed the name of agent.js to language-server.js. This patch adds the new name and changes the installation note without changing the translation. Change-Id: I585fe54c86029de32de806447ec3356999a99540 Reviewed-by: Eike Ziller --- src/plugins/copilot/copilotsettings.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp index 9ddfa4edfbb..21737c3a926 100644 --- a/src/plugins/copilot/copilotsettings.cpp +++ b/src/plugins/copilot/copilotsettings.cpp @@ -50,18 +50,22 @@ CopilotSettings::CopilotSettings() // Vim, Linux/macOS: FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"), FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"), + FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/language-server.js"), // Neovim, Linux/macOS: FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/dist/agent.js"), FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"), + FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/dist/language-server.js"), // Vim, Windows (PowerShell command): FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/dist/agent.js"), FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/copilot/dist/agent.js"), + FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/dist/language-server.js"), // Neovim, Windows (PowerShell command): FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/dist/agent.js"), - FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js") + FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"), + FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/dist/language-server.js") }; // clang-format on @@ -202,7 +206,7 @@ CopilotSettings::CopilotSettings() "file from the Copilot neovim plugin.", "Markdown text for the copilot instruction label") .arg("[README.md](https://github.com/github/copilot.vim)") - .arg("[agent.js](https://github.com/github/copilot.vim/tree/release/dist)")); + .arg("[language-server.js](https://github.com/github/copilot.vim/tree/release/dist)")); return Column { Group { From 99ce8b522f6910e8a94550c9944561e142cbbedf Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 4 Jun 2024 11:37:21 +0200 Subject: [PATCH 28/69] Core: compress shortcut settings widget updates reduces the number of calls to ShortcutSettingsWidget::initialize while the shortcut settings widget is open and actions get registered. For example when searching the preferences dialog the shortcut settings widget is alive and some setting page widgets create a text editor, which again registers some actions. Change-Id: Ieb3c3d1c7ce317c3407a9c97514f6cc4a4ce76c4 Reviewed-by: hjk --- src/plugins/coreplugin/dialogs/shortcutsettings.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp index 8649adc1679..4c7de88fd9b 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -420,6 +421,7 @@ private: QGridLayout *m_shortcutLayout; std::vector> m_shortcutInputs; QPointer m_addButton = nullptr; + QTimer m_updateTimer; }; ShortcutSettingsWidget::ShortcutSettingsWidget() @@ -428,7 +430,12 @@ ShortcutSettingsWidget::ShortcutSettingsWidget() setTargetHeader(Tr::tr("Shortcut")); setResetVisible(true); + m_updateTimer.setSingleShot(true); + m_updateTimer.setInterval(100); + connect(ActionManager::instance(), &ActionManager::commandListChanged, + &m_updateTimer, qOverload<>(&QTimer::start)); + connect(&m_updateTimer, &QTimer::timeout, this, &ShortcutSettingsWidget::initialize); connect(this, &ShortcutSettingsWidget::currentCommandChanged, this, &ShortcutSettingsWidget::handleCurrentCommandChanged); From 87a7451c2a9e0e5cf9334f2a77272ffda4632f56 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 4 Jun 2024 15:25:25 +0200 Subject: [PATCH 29/69] LayoutBuilder: Fix build with Apple Clang It needs an explicit constructor, otherwise it complains that it cannot find a match to the Arg2{p1, p2} call (requires 1 argument, but 2 were provided) etc. Amends 6231213aa3dad4652b46507183793180cd5bb7ee Change-Id: Ibe3b27b334b8abff5028a77372cf208bfda9d8c1 Reviewed-by: hjk --- src/libs/utils/layoutbuilder.h | 44 ++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 17fa24e0f42..eb516cecc88 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -50,14 +50,44 @@ public: const T2 arg; // FIXME: Could be const &, but this would currently break bindTo(). }; -template -struct Arg2 { const T1 p1; const T2 p2; }; +template +struct Arg2 +{ + Arg2(const T1 &a1, const T2 &a2) + : p1(a1) + , p2(a2) + {} + const T1 p1; + const T2 p2; +}; -template -struct Arg3 { const T1 p1; const T2 p2; const T3 p3; }; +template +struct Arg3 +{ + Arg3(const T1 &a1, const T2 &a2, const T3 &a3) + : p1(a1) + , p2(a2) + , p3(a3) + {} + const T1 p1; + const T2 p2; + const T3 p3; +}; -template -struct Arg4 { const T1 p1; const T2 p2; const T3 p3; const T4 p4; }; +template +struct Arg4 +{ + Arg4(const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4) + : p1(a1) + , p2(a2) + , p3(a3) + , p4(a4) + {} + const T1 p1; + const T2 p2; + const T3 p3; + const T4 p4; +}; // The main dispatcher @@ -460,7 +490,7 @@ void doit(Interface *x, IdId, auto p) #define QTCREATOR_SETTER3(name, setter) \ class name##_TAG {}; \ - inline auto name(auto p1, auto p2, auto p3) { return IdAndArg{name##_TAG{}, Arg4{p1, p2, p3}}; } \ + inline auto name(auto p1, auto p2, auto p3) { return IdAndArg{name##_TAG{}, Arg3{p1, p2, p3}}; } \ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3); } #define QTCREATOR_SETTER4(name, setter) \ From 00174e493f8a20d5955015b5521c3e6d860ff671 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Fri, 31 May 2024 16:09:02 +0200 Subject: [PATCH 30/69] ProjectExplorer: Add WorkspaceProjectRunConfig Change-Id: I66f25165f652f371130555279eddc0437b011887 Reviewed-by: hjk --- .../projectexplorer/workspaceproject.cpp | 129 +++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/workspaceproject.cpp b/src/plugins/projectexplorer/workspaceproject.cpp index d380f7cea88..2a22c65c4be 100644 --- a/src/plugins/projectexplorer/workspaceproject.cpp +++ b/src/plugins/projectexplorer/workspaceproject.cpp @@ -9,6 +9,9 @@ #include "projectexplorertr.h" #include "projectmanager.h" #include "projecttree.h" +#include "runconfiguration.h" +#include "runconfigurationaspects.h" +#include "runcontrol.h" #include "target.h" #include "treescanner.h" @@ -24,6 +27,7 @@ const QLatin1StringView FOLDER_MIMETYPE{"inode/directory"}; const QLatin1StringView WORKSPACE_MIMETYPE{"text/x-workspace-project"}; const QLatin1StringView WORKSPACE_PROJECT_ID{"ProjectExplorer.WorkspaceProject"}; +const QLatin1StringView WORKSPACE_PROJECT_RUNCONFIG_ID{"WorkspaceProject.RunConfiguration:"}; const QLatin1StringView PROJECT_NAME_KEY{"project.name"}; const QLatin1StringView FILES_EXCLUDE_KEY{"files.exclude"}; @@ -100,7 +104,7 @@ public: if (projectNameValue.isString()) project()->setDisplayName(projectNameValue.toString()); const QJsonArray excludesJson = json.value(FILES_EXCLUDE_KEY).toArray(); - for (QJsonValue excludeJson : excludesJson) { + for (const QJsonValue &excludeJson : excludesJson) { if (excludeJson.isString()) { FilePath absolute = projectPath.pathAppended(excludeJson.toString()); if (absolute.isDir()) @@ -111,6 +115,46 @@ public: } } + QList targetInfos; + + const QJsonArray targets = json.value("targets").toArray(); + int i = 0; + for (const QJsonValue &target : targets) { + i++; + QTC_ASSERT(target.isObject(), continue); + const QJsonObject targetObject = target.toObject(); + + QJsonArray args = targetObject["arguments"].toArray(); + QStringList arguments = Utils::transform(args, [](const QJsonValue &arg) { + return arg.toString(); + }); + FilePath workingDirectory = FilePath::fromUserInput( + targetObject["workingDirectory"].toString()); + + if (!workingDirectory.isDir()) + workingDirectory = FilePath::currentWorkingPath(); + + const QString name = targetObject["name"].toString(); + const FilePath executable = FilePath::fromUserInput( + targetObject["executable"].toString()); + + if (name.isEmpty() || executable.isEmpty()) + continue; + + BuildTargetInfo bti; + bti.buildKey = name + QString::number(i); + bti.displayName = name; + bti.displayNameUniquifier = QString(" (%1)").arg(i); + bti.targetFilePath = executable; + bti.projectFilePath = projectPath; + bti.workingDirectory = workingDirectory; + bti.additionalData = QVariantMap{{"arguments", arguments}}; + + targetInfos << bti; + } + + setApplicationTargets(targetInfos); + m_parseGuard = guardParsingRun(); m_scanner.asyncScanForFiles(target()->project()->projectDirectory()); } @@ -123,6 +167,86 @@ private: TreeScanner m_scanner; }; +class WorkspaceRunConfiguration : public RunConfiguration +{ +public: + WorkspaceRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) + { + hint.setText( + Tr::tr("You can edit this configuration inside the .qtcreator/project.json file.")); + + const BuildTargetInfo bti = buildTargetInfo(); + executable.setLabelText(Tr::tr("Executable:")); + executable.setReadOnly(true); + executable.setValue(bti.targetFilePath); + executable.setMacroExpanderProvider( + [this]() -> MacroExpander * { return const_cast(macroExpander()); }); + + auto argumentsAsString = [this]() { + return CommandLine{ + "", buildTargetInfo().additionalData.toMap()["arguments"].toStringList()} + .arguments(); + }; + + arguments.setLabelText(Tr::tr("Arguments:")); + arguments.setReadOnly(true); + arguments.setMacroExpander(macroExpander()); + arguments.setArguments(argumentsAsString()); + + workingDirectory.setLabelText(Tr::tr("Working directory:")); + workingDirectory.setReadOnly(true); + workingDirectory.setDefaultWorkingDirectory(bti.workingDirectory); + + setCommandLineGetter([this] { + const BuildTargetInfo bti = buildTargetInfo(); + CommandLine cmdLine{ + macroExpander()->expand(bti.targetFilePath), + Utils::transform( + bti.additionalData.toMap()["arguments"].toStringList(), + [this](const QString &arg) { return macroExpander()->expand(arg); })}; + + return cmdLine; + }); + + setUpdater([this, argumentsAsString] { + const BuildTargetInfo bti = buildTargetInfo(); + executable.setValue(bti.targetFilePath); + arguments.setArguments(argumentsAsString()); + workingDirectory.setDefaultWorkingDirectory(bti.workingDirectory); + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + } + + TextDisplay hint{this}; + FilePathAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDirectory{this}; +}; + +class WorkspaceProjectRunConfigurationFactory : public RunConfigurationFactory +{ +public: + WorkspaceProjectRunConfigurationFactory() + { + registerRunConfiguration( + Id::fromString(WORKSPACE_PROJECT_RUNCONFIG_ID)); + addSupportedProjectType(Id::fromString(WORKSPACE_PROJECT_ID)); + } +}; + +class WorkspaceProjectRunWorkerFactory : public RunWorkerFactory +{ +public: + WorkspaceProjectRunWorkerFactory() + { + setProduct(); + addSupportedRunMode(Constants::NORMAL_RUN_MODE); + addSupportedRunConfig(Id::fromString(WORKSPACE_PROJECT_RUNCONFIG_ID)); + } +}; + class WorkspaceProject : public Project { Q_OBJECT @@ -203,6 +327,9 @@ void setupWorkspaceProject(QObject *guard) QTC_ASSERT(project, return); project->excludeNode(node); }); + + static WorkspaceProjectRunConfigurationFactory theRunConfigurationFactory; + static WorkspaceProjectRunWorkerFactory theRunWorkerFactory; } } // namespace ProjectExplorer From ba017c03c2b4e87dbbbfc6d771d6077fc033e814 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 4 Jun 2024 14:05:31 +0200 Subject: [PATCH 31/69] RemoteLinux: Lower the timeout when retrieving the environment Otherwise, a non-responsive device will freeze Qt Creator for ten seconds (with no announcement, because the env call does not go through the device shell). Change-Id: I92ab847dd441494770dbe29cde1c726dd31f32fb Reviewed-by: Marcus Tillmanns --- src/plugins/remotelinux/linuxdevice.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 97cb7f9ae72..c971c8caa2e 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -366,7 +366,8 @@ Environment LinuxDevicePrivate::getEnvironment() Process getEnvProc; getEnvProc.setCommand(CommandLine{q->filePath("env")}); - getEnvProc.runBlocking(); + using namespace std::chrono; + getEnvProc.runBlocking(5s); const QString remoteOutput = getEnvProc.cleanedStdOut(); m_environmentCache = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType()); From a9995e23b1fc41960b23042102150a088cf00f38 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Tue, 4 Jun 2024 16:54:56 +0200 Subject: [PATCH 32/69] CMakePM: Allow CMake custom build types CMake can have custom build types. Qt Creator can rename existing build types. This changeset allows having a custom build type like for example: * CMAKE_BUILD_TYPE=MyRelease * CMAKE_CONFIGURATION_TYPES=MyDebug;MyRelease Fixes: QTCREATORBUG-30014 Change-Id: I3d81d9ff867bfaab29aaf1741606f9c586da82e0 Reviewed-by: Alessandro Portale --- src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 5189c7934fd..998ccbf7ca8 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -1124,6 +1124,11 @@ const QList CMakeProjectImporter::buildInfoList(void *directoryData) && data->hasQmlDebugging) buildType = CMakeBuildConfigurationFactory::BuildTypeProfile; BuildInfo info = CMakeBuildConfigurationFactory::createBuildInfo(buildType); + + // For CMake Presets use the provided build type if is not mapped to a known type + if (!data->cmakePreset.isEmpty() && info.buildType == BuildConfiguration::Unknown) + info.typeName = info.displayName = QString::fromUtf8(data->cmakeBuildType); + info.buildDirectory = data->buildDirectory; QVariantMap config = info.extraInfo.toMap(); // new empty, or existing one from createBuildInfo From 831b93905bf291d315c76e0692445cf5d39b9b08 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Tue, 4 Jun 2024 19:33:57 +0200 Subject: [PATCH 33/69] CMakePM: Set platform codegen and linker flags for GCC compilers If a GCC compiler has platform codegen flags they will be set into CMAKE_C_FLAGS_INIT or CMAKE_CXX_FLAGS_INIT initial CMake parameters. The linker flags are set they will be set into CMAKE_EXE_LINKER_FLAGS_INIT, CMAKE_MODULE_LINKER_FLAGS_INIT and CMAKE_SHARED_LINKER_FLAGS_INIT initial CMake parameters. If the kit has a Qt the CMAKE_CXX_FLAGS_INIT value will merge both GCC C++ platform codegen flags and the QML Debugging flags. Fixes: QTCREATORBUG-24420 Change-Id: I066d30b816ae8575f615654bb85bd82a394f9737 Reviewed-by: Alessandro Portale --- .../cmakebuildconfiguration.cpp | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index ee2658076e2..e727fc68f88 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -1176,6 +1177,30 @@ static CommandLine defaultInitialCMakeCommand( } } + // GCC compiler and linker specific flags + for (Toolchain *tc : ToolchainKitAspect::toolChains(k)) { + if (auto *gccTc = tc->asGccToolchain()) { + const QStringList compilerFlags = gccTc->platformCodeGenFlags(); + + QString language; + if (gccTc->language() == ProjectExplorer::Constants::C_LANGUAGE_ID) + language = "C"; + else if (gccTc->language() == ProjectExplorer::Constants::CXX_LANGUAGE_ID) + language = "CXX"; + + if (!language.isEmpty() && !compilerFlags.isEmpty()) + cmd.addArg("-DCMAKE_" + language + "_FLAGS_INIT:STRING=" + compilerFlags.join(" ")); + + const QStringList linkerFlags = gccTc->platformLinkerFlags(); + if (!linkerFlags.isEmpty()) { + const QString joinedLinkerFlags = linkerFlags.join(" "); + cmd.addArg("-DCMAKE_EXE_LINKER_FLAGS_INIT:STRING=" + joinedLinkerFlags); + cmd.addArg("-DCMAKE_MODULE_LINKER_FLAGS_INIT:STRING=" + joinedLinkerFlags); + cmd.addArg("-DCMAKE_SHARED_LINKER_FLAGS_INIT:STRING=" + joinedLinkerFlags); + } + } + } + cmd.addArgs(CMakeConfigurationKitAspect::toArgumentsList(k)); cmd.addArgs(CMakeConfigurationKitAspect::additionalConfiguration(k), CommandLine::Raw); @@ -2163,9 +2188,20 @@ void InitialCMakeArgumentsAspect::setAllValues(const QString &values, QStringLis if (!cmakeGenerator.isEmpty()) arguments.append(cmakeGenerator); - m_cmakeConfiguration = CMakeConfig::fromArguments(arguments, additionalOptions); - for (CMakeConfigItem &ci : m_cmakeConfiguration) + CMakeConfig config = CMakeConfig::fromArguments(arguments, additionalOptions); + // Join CMAKE_CXX_FLAGS_INIT values if more entries are present, or skip the same + // values like CMAKE_EXE_LINKER_FLAGS_INIT coming from both C and CXX compilers + QHash uniqueConfig; + for (CMakeConfigItem &ci : config) { ci.isInitial = true; + if (uniqueConfig.contains(ci.key)) { + if (uniqueConfig[ci.key].value != ci.value) + uniqueConfig[ci.key].value = uniqueConfig[ci.key].value + " " + ci.value; + } else { + uniqueConfig.insert(ci.key, ci); + } + } + m_cmakeConfiguration = uniqueConfig.values(); // Display the unknown arguments in "Additional CMake Options" const QString additionalOptionsValue = ProcessArgs::joinArgs(additionalOptions); From dfef293aa4217ed4044a1d566ad04c208195ea27 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Thu, 30 May 2024 13:20:56 +0200 Subject: [PATCH 34/69] Android: Fix find process and user PID recipe Before, the loop got finished after the very first failure of the pid command. Fix it so that the loop stops on the first successful execution of any child. Make timeout task return error, so that the loop continues executing after the timeout. Simplify some commands' constructions. Amends a7ece15f6ef9d74a575babb9da74e46332778ea1 Change-Id: I637002f0248ec69e61e058c7246471396aac1142 Reviewed-by: Alessandro Portale --- src/plugins/android/androidrunnerworker.cpp | 23 ++++++++++----------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index 7202c93f4cf..b5041917b09 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -628,17 +628,15 @@ void AndroidRunnerWorker::asyncStart() using namespace Tasking; const Storage pidStorage; - const LoopUntil iterator([pidStorage](int) { return pidStorage->first <= 0; }); const FilePath adbPath = AndroidConfig::adbToolPath(); const QStringList args = selector(); + const QString pidScript = m_isPreNougat + ? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done") + : QString("pidof -s '%1'").arg(m_packageName); - const auto onPidSetup = [adbPath, args, packageName = m_packageName, - isPreNougat = m_isPreNougat](Process &process) { - const QString pidScript = isPreNougat - ? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done") - : QString("pidof -s '%1'").arg(packageName); - process.setCommand({adbPath, args + QStringList{"shell", pidScript}}); + const auto onPidSetup = [adbPath, args, pidScript](Process &process) { + process.setCommand({adbPath, {args, "shell", pidScript}}); }; const auto onPidDone = [pidStorage, packageName = m_packageName, isPreNougat = m_isPreNougat](const Process &process) { @@ -650,8 +648,8 @@ void AndroidRunnerWorker::asyncStart() }; const auto onUserSetup = [pidStorage, adbPath, args](Process &process) { - process.setCommand({adbPath, args - + QStringList{"shell", "ps", "-o", "user", "-p", QString::number(pidStorage->first)}}); + process.setCommand({adbPath, {args, "shell", "ps", "-o", "user", "-p", + QString::number(pidStorage->first)}}); }; const auto onUserDone = [pidStorage](const Process &process) { const QString out = process.allOutput(); @@ -674,10 +672,11 @@ void AndroidRunnerWorker::asyncStart() const Group root { pidStorage, onGroupSetup([pidStorage] { *pidStorage = {-1, 0}; }), - Group { - iterator, + Forever { + stopOnSuccess, ProcessTask(onPidSetup, onPidDone, CallDoneIf::Success), - TimeoutTask([](std::chrono::milliseconds &timeout) { timeout = 200ms; }) + TimeoutTask([](std::chrono::milliseconds &timeout) { timeout = 200ms; }, + [] { return DoneResult::Error; }) }.withTimeout(45s), ProcessTask(onUserSetup, onUserDone, CallDoneIf::Success), onGroupDone([pidStorage, this] { onProcessIdChanged(*pidStorage); }) From 6621c68ca970c6760fbf10deae0a175202c6a8e1 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 4 Jun 2024 17:16:05 +0200 Subject: [PATCH 35/69] TaskTree: Qt-ify the code Make it more conforming to the current Qt style: 1. Remove trailing semicolons after inlined functions. 2. Use Q_SIGNALS / Q_EMIT in headers. 3. Use QString::fromLatin1() where needed (as otherwise the string c'tor is deprecated). 4. Use module names in Qt includes. 5. Simplify local asserts in barrier.cpp. Change-Id: I13cadee6ff61a24d792efcf3b613f7cf5563076b Reviewed-by: hjk --- src/libs/solutions/tasking/barrier.cpp | 17 ++++++------- src/libs/solutions/tasking/barrier.h | 2 +- src/libs/solutions/tasking/concurrentcall.h | 4 +-- src/libs/solutions/tasking/networkquery.cpp | 2 +- src/libs/solutions/tasking/networkquery.h | 6 ++--- src/libs/solutions/tasking/qprocesstask.cpp | 14 +++++------ src/libs/solutions/tasking/qprocesstask.h | 8 +++--- src/libs/solutions/tasking/tasking_global.h | 2 +- src/libs/solutions/tasking/tasktree.cpp | 28 ++++++++++----------- src/libs/solutions/tasking/tasktree.h | 18 ++++++------- src/libs/solutions/tasking/tasktreerunner.h | 4 +-- 11 files changed, 51 insertions(+), 54 deletions(-) diff --git a/src/libs/solutions/tasking/barrier.cpp b/src/libs/solutions/tasking/barrier.cpp index e160dbac769..74c11f50de5 100644 --- a/src/libs/solutions/tasking/barrier.cpp +++ b/src/libs/solutions/tasking/barrier.cpp @@ -6,23 +6,20 @@ namespace Tasking { // That's cut down qtcassert.{c,h} to avoid the dependency. -#define QTC_STRINGIFY_HELPER(x) #x -#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x) -#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__)) -#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0) -#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0) +#define QT_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QT_STRINGIFY(__LINE__)) +#define QT_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QT_STRING(#cond); action; } do {} while (0) void Barrier::setLimit(int value) { - QTC_ASSERT(!isRunning(), return); - QTC_ASSERT(value > 0, return); + QT_ASSERT(!isRunning(), return); + QT_ASSERT(value > 0, return); m_limit = value; } void Barrier::start() { - QTC_ASSERT(!isRunning(), return); + QT_ASSERT(!isRunning(), return); m_current = 0; m_result = {}; } @@ -30,7 +27,7 @@ void Barrier::start() void Barrier::advance() { // Calling advance on finished is OK - QTC_ASSERT(isRunning() || m_result, return); + QT_ASSERT(isRunning() || m_result, return); if (!isRunning()) // no-op return; ++m_current; @@ -41,7 +38,7 @@ void Barrier::advance() void Barrier::stopWithResult(DoneResult result) { // Calling stopWithResult on finished is OK when the same success is passed - QTC_ASSERT(isRunning() || (m_result && *m_result == result), return); + QT_ASSERT(isRunning() || (m_result && *m_result == result), return); if (!isRunning()) // no-op return; m_current = -1; diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h index a9ed9949f06..88ef5f9a96e 100644 --- a/src/libs/solutions/tasking/barrier.h +++ b/src/libs/solutions/tasking/barrier.h @@ -25,7 +25,7 @@ public: int current() const { return m_current; } std::optional result() const { return m_result; } -signals: +Q_SIGNALS: void done(DoneResult success); private: diff --git a/src/libs/solutions/tasking/concurrentcall.h b/src/libs/solutions/tasking/concurrentcall.h index 6bd44601318..a3721fca0cf 100644 --- a/src/libs/solutions/tasking/concurrentcall.h +++ b/src/libs/solutions/tasking/concurrentcall.h @@ -5,7 +5,7 @@ #include "tasktree.h" -#include +#include namespace Tasking { @@ -76,7 +76,7 @@ public: } } - void start() { + void start() final { if (!this->task()->m_startHandler) { emit this->done(DoneResult::Error); // TODO: Add runtime assert return; diff --git a/src/libs/solutions/tasking/networkquery.cpp b/src/libs/solutions/tasking/networkquery.cpp index 335f79afd94..c3de4455eb6 100644 --- a/src/libs/solutions/tasking/networkquery.cpp +++ b/src/libs/solutions/tasking/networkquery.cpp @@ -3,7 +3,7 @@ #include "networkquery.h" -#include +#include namespace Tasking { diff --git a/src/libs/solutions/tasking/networkquery.h b/src/libs/solutions/tasking/networkquery.h index dd099dc7a88..b8ad13bdfae 100644 --- a/src/libs/solutions/tasking/networkquery.h +++ b/src/libs/solutions/tasking/networkquery.h @@ -7,8 +7,8 @@ #include "tasktree.h" -#include -#include +#include +#include #include @@ -37,7 +37,7 @@ public: QNetworkReply *reply() const { return m_reply.get(); } void start(); -signals: +Q_SIGNALS: void started(); void done(DoneResult result); diff --git a/src/libs/solutions/tasking/qprocesstask.cpp b/src/libs/solutions/tasking/qprocesstask.cpp index 7d63e658478..70f9cc2ad8e 100644 --- a/src/libs/solutions/tasking/qprocesstask.cpp +++ b/src/libs/solutions/tasking/qprocesstask.cpp @@ -3,12 +3,12 @@ #include "qprocesstask.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include namespace Tasking { @@ -60,7 +60,7 @@ public: terminate(); } -signals: +Q_SIGNALS: void finished(); private: diff --git a/src/libs/solutions/tasking/qprocesstask.h b/src/libs/solutions/tasking/qprocesstask.h index cc0b83698b8..fb71d159aab 100644 --- a/src/libs/solutions/tasking/qprocesstask.h +++ b/src/libs/solutions/tasking/qprocesstask.h @@ -7,7 +7,7 @@ #include "tasktree.h" -#include +#include namespace Tasking { @@ -45,17 +45,17 @@ public: class TASKING_EXPORT QProcessAdapter : public TaskAdapter { private: - void start() override { + void start() final { connect(task(), &QProcess::finished, this, [this] { const bool success = task()->exitStatus() == QProcess::NormalExit && task()->error() == QProcess::UnknownError && task()->exitCode() == 0; - emit done(toDoneResult(success)); + Q_EMIT done(toDoneResult(success)); }); connect(task(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) { if (error != QProcess::FailedToStart) return; - emit done(DoneResult::Error); + Q_EMIT done(DoneResult::Error); }); task()->start(); } diff --git a/src/libs/solutions/tasking/tasking_global.h b/src/libs/solutions/tasking/tasking_global.h index d7e76fa9e6f..9d9e183b9a3 100644 --- a/src/libs/solutions/tasking/tasking_global.h +++ b/src/libs/solutions/tasking/tasking_global.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #if defined(TASKING_LIBRARY) # define TASKING_EXPORT Q_DECL_EXPORT diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 107559d6a53..c5a6ed6a70e 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -5,17 +5,17 @@ #include "barrier.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace std::chrono; @@ -1461,7 +1461,7 @@ static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateW ExecutableItem ExecutableItem::withLog(const QString &logName) const { const auto header = [logName] { - return QString("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName); + return QString::fromLatin1("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName); }; struct LogStorage { @@ -1482,8 +1482,8 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const const int asyncCountDiff = activeTaskTree()->asyncCount() - storage->asyncCount; QT_CHECK(asyncCountDiff >= 0); const QMetaEnum doneWithEnum = QMetaEnum::fromType(); - const QString syncType = asyncCountDiff ? QString("asynchronously") - : QString("synchronously"); + const QString syncType = asyncCountDiff ? QString::fromLatin1("asynchronously") + : QString::fromLatin1("synchronously"); qDebug().noquote().nospace() << header() << " finished " << syncType << " with " << doneWithEnum.valueToKey(int(result)) << " within " << elapsed.count() << "ms."; }) diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 9b1e4b9eafd..06a3d4ea79c 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -5,8 +5,8 @@ #include "tasking_global.h" -#include -#include +#include +#include #include @@ -84,7 +84,7 @@ class TASKING_EXPORT TaskInterface : public QObject { Q_OBJECT -signals: +Q_SIGNALS: void done(DoneResult result); private: @@ -345,7 +345,7 @@ private: std::invoke(handler); return SetupResult::Continue; }; - }; + } template static GroupDoneHandler wrapGroupDone(Handler &&handler) { @@ -368,7 +368,7 @@ private: std::invoke(handler); return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error; }; - }; + } }; template @@ -433,7 +433,7 @@ private: std::invoke(handler); return SetupResult::StopWithSuccess; }; - }; + } }; template > @@ -490,7 +490,7 @@ private: std::invoke(handler, *adapter.task()); return SetupResult::Continue; }; - }; + } template static InterfaceDoneHandler wrapDone(Handler &&handler) { @@ -529,7 +529,7 @@ private: std::invoke(handler); return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error; }; - }; + } }; class TASKING_EXPORT TaskTree final : public QObject @@ -579,7 +579,7 @@ public: wrapHandler(std::forward(handler))); } -signals: +Q_SIGNALS: void started(); void done(DoneWith result); void asyncCountChanged(int count); diff --git a/src/libs/solutions/tasking/tasktreerunner.h b/src/libs/solutions/tasking/tasktreerunner.h index 92d9f9c0b45..766ea074f50 100644 --- a/src/libs/solutions/tasking/tasktreerunner.h +++ b/src/libs/solutions/tasking/tasktreerunner.h @@ -6,7 +6,7 @@ #include "tasking_global.h" #include "tasktree.h" -#include +#include namespace Tasking { @@ -33,7 +33,7 @@ public: // No done() signal is emitted. void reset(); -signals: +Q_SIGNALS: void aboutToStart(TaskTree *taskTree); void done(DoneWith result); From f17c0fe19cc117ae9c8bc8cdf348603e51f61c29 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Wed, 5 Jun 2024 00:50:37 +0200 Subject: [PATCH 36/69] CMakePM: Fix Presets macro expanding of environment Amends a25bbf23c64c912b39bf2020df73c0db97b5bc40 The commit broke evironment variable usage in CMake presets. For example "$env{HOME}" would have been expanded to "/home/user:/home/user" because of code thinking it deals with paths. Change-Id: I3ef34179e2ebaf55b25a42dcce87438c1a72b73e Reviewed-by: Eike Ziller --- .../cmakeprojectmanager/presetsmacros.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.cpp b/src/plugins/cmakeprojectmanager/presetsmacros.cpp index b7496d0c412..cfc161b417e 100644 --- a/src/plugins/cmakeprojectmanager/presetsmacros.cpp +++ b/src/plugins/cmakeprojectmanager/presetsmacros.cpp @@ -139,11 +139,13 @@ void expand(const PresetType &preset, Environment &env, const FilePath &sourceDi return presetEnv.value(macroName); }); - bool append = true; + enum Operation { set, appendOrSet, prependOrSet }; + Operation op = set; if (key.compare("PATH", Qt::CaseInsensitive) == 0) { + op = appendOrSet; const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive); if (index != 0) - append = false; + op = prependOrSet; value.replace("$penv{PATH}", "", Qt::CaseInsensitive); } @@ -154,10 +156,17 @@ void expand(const PresetType &preset, Environment &env, const FilePath &sourceDi // Make sure to expand the CMake macros also for environment variables expandAllButEnv(preset, sourceDirectory, value); - if (append) + switch (op) { + case set: + env.set(key, value); + break; + case appendOrSet: env.appendOrSet(key, value); - else + break; + case prependOrSet: env.prependOrSet(key, value); + break; + } }); } From 1ad2c04e0ea5aae60ae7236229f4588846c40229 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 5 Jun 2024 09:02:10 +0200 Subject: [PATCH 37/69] Update change log for 13.0.2 Change-Id: I542d2f53d957471660ae0c210c4825b5a9c42a57 Reviewed-by: Eike Ziller --- dist/changelog/changes-13.0.2.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dist/changelog/changes-13.0.2.md b/dist/changelog/changes-13.0.2.md index 3e1b67b3784..629efc3d1b0 100644 --- a/dist/changelog/changes-13.0.2.md +++ b/dist/changelog/changes-13.0.2.md @@ -28,9 +28,17 @@ Editing * Fixed that `Use Qt module name in #include-directive` used Qt 4 module names ([QTCREATORBUG-30751](https://bugreports.qt.io/browse/QTCREATORBUG-30751)) +### Copilot + +* Adapted to changes in the Copilot neovim plugin + Projects -------- +### CMake + +* Fixed the environment macro expansion for Presets + ### Meson * Fixed a crash when selecting kits From 9b2bd223dd31a5ef05627e19d250beb20fa08305 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Fri, 19 Apr 2024 15:37:32 +0200 Subject: [PATCH 38/69] AppStatisticMonitor: Use QCharts instead of self made Change-Id: I4de07dd104dec9e1c68e5daf00a55ab7f8a3f72e Reviewed-by: hjk --- .../appstatisticsmonitor/CMakeLists.txt | 4 + .../appstatisticsmonitor.qbs | 3 + src/plugins/appstatisticsmonitor/chart.cpp | 118 ++++++++++++++++-- src/plugins/appstatisticsmonitor/chart.h | 32 ++++- src/plugins/appstatisticsmonitor/manager.cpp | 53 ++++---- 5 files changed, 175 insertions(+), 35 deletions(-) diff --git a/src/plugins/appstatisticsmonitor/CMakeLists.txt b/src/plugins/appstatisticsmonitor/CMakeLists.txt index 891da1f812a..292eb2a3eb2 100644 --- a/src/plugins/appstatisticsmonitor/CMakeLists.txt +++ b/src/plugins/appstatisticsmonitor/CMakeLists.txt @@ -1,4 +1,8 @@ +find_package(Qt6 COMPONENTS Charts QUIET) + add_qtc_plugin(AppStatisticsMonitor + CONDITION TARGET Qt6::Charts + DEPENDS Qt6::Charts SKIP_TRANSLATION PLUGIN_DEPENDS Core ProjectExplorer SOURCES diff --git a/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs b/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs index 3905a41a0aa..177dd9d0a28 100644 --- a/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs +++ b/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs @@ -3,6 +3,9 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "ProjectExplorer" } + Depends { name: "Qt.charts"; required: false } + + condition: Qt.charts.present files: [ "appstatisticsmonitorplugin.cpp", diff --git a/src/plugins/appstatisticsmonitor/chart.cpp b/src/plugins/appstatisticsmonitor/chart.cpp index 2abf6d1ceb6..260282b39a4 100644 --- a/src/plugins/appstatisticsmonitor/chart.cpp +++ b/src/plugins/appstatisticsmonitor/chart.cpp @@ -5,11 +5,19 @@ #include +#include +#include +#include #include #include #include #include +#include +#include #include +#include + +using namespace Utils; namespace AppStatisticsMonitor::Internal { @@ -17,6 +25,100 @@ static const int padding = 40; static const int numPadding = 10; static const QRectF dataRangeDefault = QRectF(0, 0, 5, 1); +AppStatisticsMonitorChart::AppStatisticsMonitorChart( + const QString &name, QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QChart(QChart::ChartTypeCartesian, parent, wFlags) + , m_series(new QLineSeries(this)) + , m_axisX(new QValueAxis()) + , m_axisY(new QValueAxis()) + , m_point(0, 0) + , m_chartView(new QChartView(this)) + , m_name(name) +{ + m_chartView->setMinimumHeight(200); + m_chartView->setMinimumWidth(400); + const QBrush brushTitle(creatorColor(Theme::Token_Text_Muted)); + const QBrush brush(creatorColor(Theme::Token_Background_Default)); + const QPen penBack(creatorColor(Theme::Token_Text_Muted)); + const QPen penAxis(creatorColor(Theme::Token_Text_Muted)); + + setTitleBrush(brushTitle); + setBackgroundBrush(brush); + setBackgroundPen(penBack); + m_axisX->setLinePen(penAxis); + m_axisY->setLinePen(penAxis); + m_axisX->setLabelsColor(creatorColor(Theme::Token_Text_Muted)); + m_axisY->setLabelsColor(creatorColor(Theme::Token_Text_Muted)); + QPen pen(creatorColor(Theme::Token_Accent_Default)); + pen.setWidth(2); + m_series->setPen(pen); + + setTitle(m_name + " " + QString::number(m_point.y(), 'g', 4) + "%"); + m_chartView->setRenderHint(QPainter::Antialiasing); + m_chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + addSeries(m_series); + + addAxis(m_axisX, Qt::AlignBottom); + addAxis(m_axisY, Qt::AlignLeft); + m_series->attachAxis(m_axisX); + m_series->attachAxis(m_axisY); + m_axisX->applyNiceNumbers(); + m_axisY->applyNiceNumbers(); + legend()->hide(); + + clear(); +} + +QChartView *AppStatisticsMonitorChart::chartView() +{ + return m_chartView; +} + +void AppStatisticsMonitorChart::addNewPoint(const QPointF &point) +{ + m_point = point; + if (m_axisY->max() < m_point.y()) + m_axisY->setRange(0, qRound(m_point.y()) + 1); + m_axisX->setRange(0, qRound(m_point.x()) + 1); + + setTitle(m_name + " " + QString::number(m_point.y(), 'g', 4) + "%"); + m_series->append(m_point); +} + +void AppStatisticsMonitorChart::loadNewProcessData(const QList &data) +{ + clear(); + QList points{{0, 0}}; + int i = 0; + double max_y = 0; + + for (double e : qAsConst(data)) { + points.push_back({double(++i), e}); + max_y = qMax(max_y, e); + } + + m_axisY->setRange(0, qRound(max_y) + 1); + m_axisX->setRange(0, data.size() + 1); + + m_series->clear(); + m_series->append(points); +} + +void AppStatisticsMonitorChart::clear() +{ + m_axisX->setRange(0, 5); + m_axisY->setRange(0, 1); + m_series->clear(); + m_series->append(0, 0); +} + +double AppStatisticsMonitorChart::lastPointX() const +{ + return m_point.x(); +} + +//---------------------- Chart ----------------------- + Chart::Chart(const QString &name, QWidget *parent) : QWidget(parent) , m_name(name) @@ -31,7 +133,7 @@ void Chart::addNewPoint(const QPointF &point) update(); } -void Chart::loadNewProcessData(QList data) +void Chart::loadNewProcessData(const QList &data) { clear(); for (long i = 0; i < data.size(); ++i) { @@ -59,7 +161,7 @@ void Chart::paintEvent(QPaintEvent *event) Q_UNUSED(event); QPainter painter(this); - painter.fillRect(rect(), Utils::creatorColor(Utils::Theme::Token_Background_Default)); + painter.fillRect(rect(), creatorColor(Theme::Token_Background_Default)); // add the name of the chart in the middle of the widget width and on the top painter.drawText( @@ -74,10 +176,10 @@ void Chart::paintEvent(QPaintEvent *event) double xPos = padding + (x - dataRange.left()) * m_xScale; if (xPos < padding || xPos > width() - padding) continue; - painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default)); + painter.setPen(creatorColor(Theme::Token_Foreground_Default)); painter.drawLine(xPos, padding, xPos, height() - padding); - painter.setPen(Utils::creatorColor(Utils::Theme::Token_Text_Muted)); + painter.setPen(creatorColor(Theme::Token_Text_Muted)); painter.drawText(xPos, height() - numPadding, QString::number(x)); } @@ -86,18 +188,18 @@ void Chart::paintEvent(QPaintEvent *event) if (yPos < padding || yPos > height() - padding) continue; - painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default)); + painter.setPen(creatorColor(Theme::Token_Foreground_Default)); painter.drawLine(padding, yPos, width() - padding, yPos); - painter.setPen(Utils::creatorColor(Utils::Theme::Token_Text_Muted)); + painter.setPen(creatorColor(Theme::Token_Text_Muted)); painter.drawText(numPadding, yPos, QString::number(y)); } - painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default)); + painter.setPen(creatorColor(Theme::Token_Foreground_Default)); painter.drawLine(padding, height() - padding, width() - padding, height() - padding); // X axis painter.drawLine(padding, height() - padding, padding, padding); // Y axis - QPen pen(Utils::creatorColor(Utils::Theme::Token_Accent_Default)); + QPen pen(creatorColor(Theme::Token_Accent_Default)); pen.setWidth(2); painter.setPen(pen); painter.setRenderHint(QPainter::Antialiasing); diff --git a/src/plugins/appstatisticsmonitor/chart.h b/src/plugins/appstatisticsmonitor/chart.h index 0a9ed927512..ed478061ce7 100644 --- a/src/plugins/appstatisticsmonitor/chart.h +++ b/src/plugins/appstatisticsmonitor/chart.h @@ -2,21 +2,51 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 #pragma once +#include #include #include #include #include #include +QT_BEGIN_NAMESPACE +class QChartView; +class QLineSeries; +class QValueAxis; +QT_END_NAMESPACE + namespace AppStatisticsMonitor::Internal { +class AppStatisticsMonitorChart : public QChart +{ +public: + AppStatisticsMonitorChart( + const QString &name, QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {}); + + void addNewPoint(const QPointF &point); + void loadNewProcessData(const QList &data); + double lastPointX() const; + void clear(); + QChartView *chartView(); + +private: + QLineSeries *m_series; + QStringList m_titles; + QValueAxis *m_axisX; + QValueAxis *m_axisY; + QPointF m_point; + + QChartView *m_chartView; + QString m_name; +}; + class Chart : public QWidget { public: Chart(const QString &name, QWidget *parent = nullptr); void addNewPoint(const QPointF &point); - void loadNewProcessData(QList data); + void loadNewProcessData(const QList &data); double lastPointX() const; void clear(); diff --git a/src/plugins/appstatisticsmonitor/manager.cpp b/src/plugins/appstatisticsmonitor/manager.cpp index ad17b80dd78..a0a08fd36a0 100644 --- a/src/plugins/appstatisticsmonitor/manager.cpp +++ b/src/plugins/appstatisticsmonitor/manager.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -24,15 +25,15 @@ class AppStatisticsMonitorView : public QWidget { public: explicit AppStatisticsMonitorView( - AppStatisticsMonitorManager *appStatisticManager, QWidget *parent = nullptr); + AppStatisticsMonitorManager *appStatisticManager); ~AppStatisticsMonitorView() override; private: QComboBox *m_comboBox; - Chart *m_memChart; - Chart *m_cpuChart; + std::unique_ptr m_chartMem; + std::unique_ptr m_chartCpu; AppStatisticsMonitorManager *m_manager; }; @@ -114,25 +115,21 @@ QHash AppStatisticsMonitorManager::pidNameMap() const } // AppStatisticsMonitorView -AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *dapManager, QWidget *) - : m_manager(dapManager) +AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *appManager) + : m_manager(appManager) { auto layout = new QVBoxLayout; auto form = new QFormLayout; setLayout(layout); - m_comboBox = new QComboBox(); + m_comboBox = new QComboBox(this); form->addRow(m_comboBox); - m_memChart = new Chart("Memory consumption "); - m_memChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_memChart->clear(); - form->addRow(m_memChart); + m_chartMem = std::make_unique(tr("Memory consumption")); + m_chartCpu = std::make_unique(tr("CPU consumption")); - m_cpuChart = new Chart("CPU consumption "); - m_cpuChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - m_cpuChart->clear(); - form->addRow(m_cpuChart); + form->addRow(m_chartMem->chartView()); + form->addRow(m_chartCpu->chartView()); layout->addLayout(form); @@ -142,16 +139,19 @@ AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager * } m_comboBox->setCurrentIndex(m_comboBox->count() - 1); - m_memChart->clear(); - m_cpuChart->clear(); + m_chartCpu->clear(); + m_chartMem->clear(); auto updateCharts = [this](int index) { m_manager->setCurrentDataProvider(m_comboBox->itemData(index).toLongLong()); if (m_manager->currentDataProvider() != nullptr) { - m_memChart->loadNewProcessData( - m_manager->currentDataProvider()->memoryConsumptionHistory()); - m_cpuChart->loadNewProcessData( - m_manager->currentDataProvider()->cpuConsumptionHistory()); + const QList &memConsumptionHistory + = m_manager->currentDataProvider()->memoryConsumptionHistory(); + const QList &cpuConsumptionHistory + = m_manager->currentDataProvider()->cpuConsumptionHistory(); + + m_chartMem->loadNewProcessData(memConsumptionHistory); + m_chartCpu->loadNewProcessData(cpuConsumptionHistory); } }; @@ -170,16 +170,17 @@ AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager * if (pid != m_comboBox->currentData()) { m_comboBox->addItem(name + " : " + QString::number(pid), pid); - m_memChart->clear(); - m_cpuChart->clear(); + m_chartMem->clear(); + m_chartCpu->clear(); m_comboBox->setCurrentIndex(m_comboBox->count() - 1); } }); connect(m_manager, &AppStatisticsMonitorManager::appStoped, this, [this](qint64 pid) { - m_memChart->addNewPoint({m_memChart->lastPointX() + 1, 0}); - m_cpuChart->addNewPoint({m_cpuChart->lastPointX() + 1, 0}); + m_chartMem->addNewPoint({m_chartMem->lastPointX() + 1, 0}); + m_chartCpu->addNewPoint({m_chartCpu->lastPointX() + 1, 0}); + const int indx = m_comboBox->findData(pid); if (indx != -1) m_comboBox->removeItem(indx); @@ -188,10 +189,10 @@ AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager * connect(m_manager, &AppStatisticsMonitorManager::newDataAvailable, this, [this] { const IDataProvider *currentDataProvider = m_manager->currentDataProvider(); if (currentDataProvider != nullptr) { - m_memChart->addNewPoint( + m_chartMem->addNewPoint( {(double) currentDataProvider->memoryConsumptionHistory().size(), currentDataProvider->memoryConsumptionLast()}); - m_cpuChart->addNewPoint( + m_chartCpu->addNewPoint( {(double) currentDataProvider->cpuConsumptionHistory().size(), currentDataProvider->cpuConsumptionLast()}); } From e8938acca96f636d545506e45830a61c6d9ca943 Mon Sep 17 00:00:00 2001 From: Artem Sokolovskii Date: Mon, 3 Jun 2024 15:45:39 +0200 Subject: [PATCH 39/69] ClangFormat: Fix indenting 'return' after key words Change-Id: I9e11b4d299c13ffada897b009fb70c3447213500 Reviewed-by: Christian Kandeler --- .../clangformat/clangformatbaseindenter.cpp | 20 +++++++++-- .../clangformat/tests/clangformat-test.cpp | 36 ++++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 4d89ea8aa2e..295c86fcb6f 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -210,6 +210,21 @@ static bool comesDirectlyAfterIf(const QTextDocument *doc, int pos) return pos > 0 && doc->characterAt(pos) == 'f' && doc->characterAt(pos - 1) == 'i'; } +static bool startsWithKeyWord(const QString &keyWord, const QString &text) +{ + if (text.size() <= keyWord.size()) + return false; + + const QChar chAfter = text.at(keyWord.size()); + return text.startsWith(keyWord) && !chAfter.isDigit() && !chAfter.isLetter() && chAfter != '_'; +} + +static bool startsWithKeyWords(const QString &text) +{ + return startsWithKeyWord("if", text) || startsWithKeyWord("while", text) + || startsWithKeyWord("for", text); +} + static CharacterContext characterContext(const QTextBlock ¤tBlock) { QTextBlock previousNonEmptyBlock = reverseFindLastEmptyBlock(currentBlock); @@ -220,8 +235,9 @@ static CharacterContext characterContext(const QTextBlock ¤tBlock) if (prevLineText.isEmpty()) return CharacterContext::NewStatementOrContinuation; - if ((currentBlock.text().trimmed().isEmpty() || currentBlock.text().trimmed().endsWith(")")) - && prevLineText.endsWith("{")) + const QString currentBlockText = currentBlock.text().trimmed(); + if ((currentBlockText.isEmpty() || currentBlockText.endsWith(")")) + && prevLineText.endsWith("{") && !startsWithKeyWords(currentBlockText)) return CharacterContext::BracketAfterFunctionCall; const QChar firstNonWhitespaceChar = findFirstNonWhitespaceCharacter(currentBlock); diff --git a/src/plugins/clangformat/tests/clangformat-test.cpp b/src/plugins/clangformat/tests/clangformat-test.cpp index c52767baa27..6e7cdd367be 100644 --- a/src/plugins/clangformat/tests/clangformat-test.cpp +++ b/src/plugins/clangformat/tests/clangformat-test.cpp @@ -114,8 +114,10 @@ private slots: void testFunctionCallClosingParenthesis(); void testFunctionCallClosingParenthesisEmptyLine(); void testNoIndentationInMiddleOfLine(); - void testIndentationInTheBegginingOfLine(); void testIndentationInMiddleOfLine(); + void testIndentationInTheBegginingOfLine(); + void testIndentationReturnAfterIf(); + void testIndentationReturnAfterIfSomthingFunction(); private: void insertLines(const std::vector &lines); @@ -965,6 +967,38 @@ void ClangFormatTest::testIndentationInTheBegginingOfLine() "}"})); } +void ClangFormatTest::testIndentationReturnAfterIf() +{ + insertLines({"int main()", + "{", + " if (true)", + " return 0;", + "}"}); + m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettings()); + QCOMPARE(documentLines(), + (std::vector{"int main()", + "{", + " if (true)", + " return 0;", + "}"})); +} + +void ClangFormatTest::testIndentationReturnAfterIfSomthingFunction() +{ + insertLines({"int main()", + "{", + " if_somthing()", + " return 0;", + "}"}); + m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettings()); + QCOMPARE(documentLines(), + (std::vector{"int main()", + "{", + " if_somthing()", + " return 0;", + "}"})); +} + QObject *createClangFormatTest() { return new ClangFormatTest; From 6692462dcb0e1fa23888e96bff4137f03cc935f9 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 5 Jun 2024 11:46:45 +0200 Subject: [PATCH 40/69] Wizard: Fix size calculation of outline titles On macOS we now (with Qt 6.7) get nice, high resolution pixmaps from QIcon::fromTheme. That means that our wrong assumption that the pixmap size == UI size leads to huge layout sizes for the indicator of the current wizard page. We need to take devicePixelRatio into account for determining the UI size. Fixes: QTCREATORBUG-31015 Change-Id: I6e9c77cf2f37fce60735e75c1fa694e4b4208b98 Reviewed-by: Marcus Tillmanns --- src/libs/utils/wizard.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp index 71069e60f66..29bfb787a6a 100644 --- a/src/libs/utils/wizard.cpp +++ b/src/libs/utils/wizard.cpp @@ -45,7 +45,9 @@ public: m_indicatorPixmap(indicatorPixmap) { m_indicatorLabel = new QLabel(this); - m_indicatorLabel->setFixedSize(m_indicatorPixmap.size()); + const QSizeF indicatorSize = m_indicatorPixmap.deviceIndependentSize(); + m_indicatorLabel->setFixedSize( + {qCeil(indicatorSize.width()), qCeil(indicatorSize.height())}); m_titleLabel = new QLabel(title, this); auto l = new QHBoxLayout(this); l->setContentsMargins(0, 0, 0, 0); From 5b93e34c000e26b38514cf6cbfc00f02f709eec9 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 3 Jun 2024 14:01:06 +0200 Subject: [PATCH 41/69] Utils: Allow comments in environment items This patch allows commenting environment changes or adding comments to the batch edit widget. To mark a line as comment prefix it with '##'. Modifying the environment by using the batch edit mode allows using '#' to disable variables. Mis-using this to disable statements of the environment items widget is tempting and other tools explicitly allow it this way. But when doing so, the environment may get some unforeseen modifications. So, explicitly provide a mechanism for comments and be more clear about this inside the documentation. Change-Id: I6a58d0d00e996a3f886ec30e826cade324321818 Reviewed-by: Christian Kandeler --- .../creator-projects-settings-environment.qdoc | 8 ++++++-- src/libs/utils/environmentmodel.cpp | 3 ++- src/libs/utils/namevalueitem.cpp | 11 +++++++++++ src/libs/utils/namevalueitem.h | 2 +- src/libs/utils/namevaluesdialog.cpp | 3 ++- src/plugins/projectexplorer/environmentwidget.cpp | 7 ++++++- tests/auto/environment/tst_environment.cpp | 13 +++++++++++-- 7 files changed, 39 insertions(+), 8 deletions(-) diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc index ce809fc536d..db6aefeb64c 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc @@ -141,6 +141,10 @@ Use the following syntax to enter environment variable names and values: \c {=}. + To temporarily disable a variable, add a hash character (#) to the beginning + of the line. + \note Using this approach for a different statement (append, prepend, unset) + may result in unexpected changes of the environment. To remove a variable value from the environment, enter the variable name. For example, \c TEST sets the value of the \c TEST variable empty when @@ -160,8 +164,8 @@ following lines. However, you can remove a value after you have referred to it on an earlier line. - To temporarily disable a variable, add a hash character (#) to the beginning - of the line. + To add a comment or disable any of the above actions, prefix it with two hash + characters (##). \sa {Specify the environment for projects}, {Configure projects for building}, {Configure projects for running}, {Use Qt Creator variables} diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp index 6ac9b356406..e81ee520d81 100644 --- a/src/libs/utils/environmentmodel.cpp +++ b/src/libs/utils/environmentmodel.cpp @@ -397,7 +397,8 @@ EnvironmentItems EnvironmentModel::userChanges() const void EnvironmentModel::setUserChanges(const EnvironmentItems &items) { EnvironmentItems filtered = Utils::filtered(items, [](const EnvironmentItem &i) { - return i.name != "export " && !i.name.contains('='); + return i.operation == EnvironmentItem::Comment + || (i.name != "export " && !i.name.contains('=')); }); // We assume nobody is reordering the items here. if (filtered == d->m_items) diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp index 3f4cdbd4b2c..cb34f8a5505 100644 --- a/src/libs/utils/namevalueitem.cpp +++ b/src/libs/utils/namevalueitem.cpp @@ -19,6 +19,10 @@ EnvironmentItems EnvironmentItem::fromStringList(const QStringList &list) { EnvironmentItems result; for (const QString &string : list) { + if (string.startsWith("##")) { + result.append({string.mid(2), {}, EnvironmentItem::Comment}); + continue; + } int pos = string.indexOf("+="); if (pos != -1) { result.append({string.left(pos), string.mid(pos + 2), EnvironmentItem::Append}); @@ -59,6 +63,8 @@ QStringList EnvironmentItem::toStringList(const EnvironmentItems &list) return QString('#' + item.name + '=' + item.value); case EnvironmentItem::SetEnabled: return QString(item.name + '=' + item.value); + case EnvironmentItem::Comment: + return QString("##" + item.name); } return QString(); }); @@ -170,6 +176,8 @@ void EnvironmentItem::apply(NameValueDictionary *dictionary, Operation op) const apply(dictionary, SetEnabled); } } break; + case Comment: // ignore comments when applying to environment + break; } } @@ -195,6 +203,9 @@ QDebug operator<<(QDebug debug, const EnvironmentItem &i) case EnvironmentItem::Append: debug << "append to \"" << i.name << "\":\"" << i.value << '"'; break; + case EnvironmentItem::Comment: + debug << "comment:" << i.name; + break; } debug << ')'; return debug; diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h index 6ad5ed03791..c518b17e667 100644 --- a/src/libs/utils/namevalueitem.h +++ b/src/libs/utils/namevalueitem.h @@ -16,7 +16,7 @@ namespace Utils { class QTCREATOR_UTILS_EXPORT EnvironmentItem { public: - enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled }; + enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled, Comment }; EnvironmentItem() = default; EnvironmentItem(const QString &key, const QString &value, Operation operation = SetEnabled) : name(key) diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp index 0b009c7568e..89482b99105 100644 --- a/src/libs/utils/namevaluesdialog.cpp +++ b/src/libs/utils/namevaluesdialog.cpp @@ -60,11 +60,12 @@ NameValueItemsWidget::NameValueItemsWidget(QWidget *parent) const QString helpText = Tr::tr( "Enter one environment variable per line.\n" "To set or change a variable, use VARIABLE=VALUE.\n" + "To disable a variable, prefix this line with \"#\".\n" "To append to a variable, use VARIABLE+=VALUE.\n" "To prepend to a variable, use VARIABLE=+VALUE.\n" "Existing variables can be referenced in a VALUE with ${OTHER}.\n" "To clear a variable, put its name on a line with nothing else on it.\n" - "To disable a variable, prefix the line with \"#\"."); + "Lines starting with \"##\" will be treated as comments."); m_editor = new Internal::TextEditHelper(this); auto layout = new QVBoxLayout(this); diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index a34f8bc256e..973a499ced9 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -363,7 +363,10 @@ void EnvironmentWidget::updateSummaryText() return; } - Utils::EnvironmentItems list = d->m_model->userChanges(); + Utils::EnvironmentItems list + = Utils::filtered(d->m_model->userChanges(), [](const EnvironmentItem &it) { + return it.operation != Utils::EnvironmentItem::Comment; + }); Utils::EnvironmentItem::sort(&list); QString text; @@ -387,6 +390,8 @@ void EnvironmentWidget::updateSummaryText() case Utils::EnvironmentItem::SetDisabled: text.append(Tr::tr("Set %1 to %2 [disabled]").arg(item.name.toHtmlEscaped(), item.value.toHtmlEscaped())); break; + case Utils::EnvironmentItem::Comment: + break; } } } diff --git a/tests/auto/environment/tst_environment.cpp b/tests/auto/environment/tst_environment.cpp index 3cf9c127a22..251ad4d496a 100644 --- a/tests/auto/environment/tst_environment.cpp +++ b/tests/auto/environment/tst_environment.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include #include #include @@ -263,10 +264,18 @@ void tst_Environment::incrementalChanges() { const Environment origEnv({{"VAR1", "VALUE1"}, {"VAR2", "VALUE2"}, {"PATH", "/usr/bin"}}); const EnvironmentItems changes({ + {"VAR1", QString(), EnvironmentItem::Comment}, {"VAR1", QString(), EnvironmentItem::Unset}, {"VAR2", "VALUE2", EnvironmentItem::SetDisabled}, + {"PATH+=/bin", QString(), EnvironmentItem::Comment}, {"PATH", "/usr/local/bin", EnvironmentItem::Append}, - {"PATH", "/tmp", EnvironmentItem::Prepend}}); + {"PATH", "/tmp", EnvironmentItem::Prepend}, + {"PATH=/opt/bin", QString(), EnvironmentItem::Comment}}); + + const QStringList changesStringList = EnvironmentItem::toStringList(changes); + const int comments = Utils::count(changesStringList, + [](const QString &line) { return line.startsWith("##"); }); + QCOMPARE(comments, 3); // Check values after change application. Environment newEnv = origEnv; @@ -290,7 +299,7 @@ void tst_Environment::incrementalChanges() QCOMPARE(newEnv2, origEnv); // Check conversion round-trips. - QCOMPARE(EnvironmentItem::fromStringList(EnvironmentItem::toStringList(changes)), changes); + QCOMPARE(EnvironmentItem::fromStringList(changesStringList), changes); QCOMPARE(EnvironmentItem::fromStringList(EnvironmentItem::toStringList(diff)), diff); QCOMPARE(EnvironmentItem::fromStringList(EnvironmentItem::toStringList(reverseDiff)), reverseDiff); QCOMPARE(EnvironmentItem::itemsFromVariantList(EnvironmentItem::toVariantList(changes)), changes); From f3e164af4f8e1cd86821fa937233cfbc2b059b3b Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 5 Jun 2024 10:50:43 +0200 Subject: [PATCH 42/69] ProjectExplorer: prepare for parallelization of recursive file scan Change-Id: Ia2db3ef0fe619907c1ed30b8f984de4de04cc477 Reviewed-by: Jarek Kobus --- .../cmakeprojectmanager/cmakebuildsystem.cpp | 11 +++++++--- .../cmakeprojectmanager/cmakebuildsystem.h | 3 ++- .../compilationdatabaseproject.cpp | 6 +++++- .../compilationdatabaseutils.h | 3 ++- .../compilationdbparser.cpp | 20 ++++++++++++++++--- .../compilationdbparser.h | 1 + src/plugins/nim/project/nimbuildsystem.cpp | 15 +++++++------- 7 files changed, 42 insertions(+), 17 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 7b2520cc903..730cdaed4cf 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -83,12 +83,17 @@ CMakeBuildSystem::CMakeBuildSystem(CMakeBuildConfiguration *bc) // Cache mime check result for speed up if (!isIgnored) { - auto it = m_mimeBinaryCache.find(mimeType.name()); - if (it != m_mimeBinaryCache.end()) { + if (auto it = m_mimeBinaryCache.get>( + [mimeType](const QHash &cache) -> std::optional { + auto it = cache.find(mimeType.name()); + if (it != cache.end()) + return *it; + return {}; + })) { isIgnored = *it; } else { isIgnored = TreeScanner::isMimeBinary(mimeType, fn); - m_mimeBinaryCache[mimeType.name()] = isIgnored; + m_mimeBinaryCache.writeLocked()->insert(mimeType.name(), isIgnored); } } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index dd85a52e228..32966c64bbb 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace ProjectExplorer { @@ -221,7 +222,7 @@ private: ProjectExplorer::TreeScanner m_treeScanner; std::shared_ptr m_allFiles; - QHash m_mimeBinaryCache; + Utils::SynchronizedValue> m_mimeBinaryCache; bool m_waitingForParse = false; bool m_combinedScanAndParseResult = false; diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index ebe7ff44753..af22b135203 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -332,7 +332,11 @@ CompilationDatabaseBuildSystem::CompilationDatabaseBuildSystem(Target *target) this, &CompilationDatabaseBuildSystem::updateDeploymentData); } -CompilationDatabaseBuildSystem::~CompilationDatabaseBuildSystem() = default; +CompilationDatabaseBuildSystem::~CompilationDatabaseBuildSystem() +{ + if (m_parser) + delete m_parser; +} void CompilationDatabaseBuildSystem::triggerParsing() { diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h index df0d4c9c9fe..dd202efbf39 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include @@ -29,7 +30,7 @@ public: QStringList extras; }; -using MimeBinaryCache = QHash; +using MimeBinaryCache = Utils::SynchronizedValue>; QStringList filterFromFileName(const QStringList &flags, const QString &fileName); diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp index 1ba5a9a1f1b..d613c81faf5 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp @@ -130,6 +130,15 @@ CompilationDbParser::CompilationDbParser(const QString &projectName, }); } +CompilationDbParser::~CompilationDbParser() +{ + if (m_treeScanner && !m_treeScanner->isFinished()) { + auto future = m_treeScanner->future(); + future.cancel(); + future.waitForFinished(); + } +} + void CompilationDbParser::start() { // Check hash first. @@ -158,12 +167,17 @@ void CompilationDbParser::start() // Cache mime check result for speed up if (!isIgnored) { - auto it = m_mimeBinaryCache.find(mimeType.name()); - if (it != m_mimeBinaryCache.end()) { + if (auto it = m_mimeBinaryCache.get>( + [mimeType](const QHash &cache) -> std::optional { + auto it = cache.find(mimeType.name()); + if (it != cache.end()) + return *it; + return {}; + })) { isIgnored = *it; } else { isIgnored = TreeScanner::isMimeBinary(mimeType, fn); - m_mimeBinaryCache[mimeType.name()] = isIgnored; + m_mimeBinaryCache.writeLocked()->insert(mimeType.name(), isIgnored); } } diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h index a06ba82581e..065dfbe129a 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h @@ -32,6 +32,7 @@ public: MimeBinaryCache &mimeBinaryCache, ProjectExplorer::BuildSystem::ParseGuard &&guard, QObject *parent = nullptr); + ~CompilationDbParser(); void setPreviousProjectFileHash(const QByteArray &fileHash) { m_projectFileHash = fileHash; } diff --git a/src/plugins/nim/project/nimbuildsystem.cpp b/src/plugins/nim/project/nimbuildsystem.cpp index ef7d6c47320..373a263ba27 100644 --- a/src/plugins/nim/project/nimbuildsystem.cpp +++ b/src/plugins/nim/project/nimbuildsystem.cpp @@ -25,14 +25,6 @@ const char EXCLUDED_FILES_KEY[] = "ExcludedFiles"; NimProjectScanner::NimProjectScanner(Project *project) : m_project(project) { - m_scanner.setFilter([this](const Utils::MimeType &, const FilePath &fp) { - const QString path = fp.toString(); - return excludedFiles().contains(path) - || path.endsWith(".nimproject") - || path.contains(".nimproject.user") - || path.contains(".nimble.user"); - }); - connect(&m_directoryWatcher, &FileSystemWatcher::directoryChanged, this, &NimProjectScanner::directoryChanged); connect(&m_directoryWatcher, &FileSystemWatcher::fileChanged, @@ -91,6 +83,13 @@ void NimProjectScanner::saveSettings() void NimProjectScanner::startScan() { + m_scanner.setFilter( + [excludedFiles = excludedFiles()](const Utils::MimeType &, const FilePath &fp) { + const QString path = fp.toString(); + return excludedFiles.contains(path) || path.endsWith(".nimproject") + || path.contains(".nimproject.user") || path.contains(".nimble.user"); + }); + m_scanner.asyncScanForFiles(m_project->projectDirectory()); } From 32322801f564eb5debafa1a343bd81c48308f35a Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 4 Jun 2024 11:18:03 +0200 Subject: [PATCH 43/69] Shared: Add project.json schema for workspace projects Change-Id: I16f2944316805ecbd3cd3dfc763c457e5ad6142f Reviewed-by: David Schulz --- share/qtcreator/CMakeLists.txt | 1 + share/qtcreator/jsonschemas/project.json | 57 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 share/qtcreator/jsonschemas/project.json diff --git a/share/qtcreator/CMakeLists.txt b/share/qtcreator/CMakeLists.txt index f8be01a7a7b..73a0837ede2 100644 --- a/share/qtcreator/CMakeLists.txt +++ b/share/qtcreator/CMakeLists.txt @@ -3,6 +3,7 @@ set(resource_directories cplusplus glsl indexer_preincludes + jsonschemas modeleditor qmldesigner qmlicons diff --git a/share/qtcreator/jsonschemas/project.json b/share/qtcreator/jsonschemas/project.json new file mode 100644 index 00000000000..df55dbd693a --- /dev/null +++ b/share/qtcreator/jsonschemas/project.json @@ -0,0 +1,57 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Qt Creator workspace project definition", + "description": "A Qt Creator workspace project definition", + "type": "object", + "properties": { + "project.name": { + "type": "string", + "description": "The name of the project" + }, + "files.exclude": { + "type": "array", + "items": [ + { + "type": "string" + } + ], + "description": "Files to exclude from the project" + }, + "targets": { + "type": "array", + "description": "A list of targets", + "items": [ + { + "type": "object", + "properties": { + "arguments": { + "type": "array", + "items": [ + { + "type": "string" + } + ], + "description": "Arguments to pass to the executable" + }, + "executable": { + "type": "string", + "description": "The executable to run" + }, + "name": { + "type": "string", + "description": "The name of the target" + }, + "workingDirectory": { + "type": "string", + "description": "The working directory to run the executable in" + } + }, + "required": [ + "executable", + "name" + ] + } + ] + } + } +} From 4a9b2deb3ef708ea543dac558c12e73291cd8f81 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 3 Jun 2024 15:42:47 +0200 Subject: [PATCH 44/69] Docs: Describe changes on Axivion plugin side Change-Id: Iefdd4d33fbb5b29f8afec8899d8a4373cc0f2436 Reviewed-by: Leena Miettinen --- doc/qtcreator/src/analyze/creator-axivion.qdoc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/doc/qtcreator/src/analyze/creator-axivion.qdoc b/doc/qtcreator/src/analyze/creator-axivion.qdoc index 9a827f7bb4d..53735a9c31a 100644 --- a/doc/qtcreator/src/analyze/creator-axivion.qdoc +++ b/doc/qtcreator/src/analyze/creator-axivion.qdoc @@ -140,12 +140,18 @@ To connect to an Axivion dashboard server: - \list 1 - \li Select \uicontrol Edit to create a connection to the Axivion + \list + \li Select \uicontrol Add to add a new connection to an Axivion dashboard server. + \li Select \uicontrol Edit to modify an existing connection to + an Axivion dashboard server. \image qtcreator-edit-dashboard-configuration.webp {Edit Dashboard Configuration dialog} - \li In \uicontrol {Dashboard URL}, enter the URL of the server. - \li In \uicontrol Username, enter the username to access the server. + \list + \li In \uicontrol {Dashboard URL}, enter the URL of the server. + \li In \uicontrol Username, enter the username to access the server. + \endlist + \li Select \uicontrol Remove to remove the current selected connection + to an Axivion dashboard server. \endlist The first time you access the server, you must enter the password that @@ -173,6 +179,8 @@ \li Go to \uicontrol Projects > \uicontrol {Project Settings} > \uicontrol Axivion. \image qtcreator-preferences-axivion-project.webp {Axivion settings in Project Settings} + \li From \uicontrol {Dashboard} select one of the configured Axivion + dashboard configurations. \li Select \uicontrol {Fetch Projects} to list projects from Axivion. \li Select a project, and then select \uicontrol {Link Project} to link to it. From c33decf1b0a0c52a56282bfc734bed86c835b7f3 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 5 Jun 2024 11:20:23 +0200 Subject: [PATCH 45/69] Utils: Fix editing in environment widget In the edge case that the name of one variable starts with the name of another followed by an underscore, we mistakenly stopped at the former when searching for the latter in the text widget. Fixes: QTCREATORBUG-30855 Change-Id: I6decf7805a937d52d25e6c62899ae062f636a2a1 Reviewed-by: Christian Stenger --- src/libs/utils/namevaluesdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp index 89482b99105..5a45c54a75e 100644 --- a/src/libs/utils/namevaluesdialog.cpp +++ b/src/libs/utils/namevaluesdialog.cpp @@ -142,7 +142,7 @@ bool NameValueItemsWidget::editVariable(const QString &name, Selection selection skipWhiteSpace(); if (offset < line.length()) { QChar nextChar = line.at(offset); - if (nextChar.isLetterOrNumber()) + if (nextChar.isLetterOrNumber() || nextChar == '_') continue; if (nextChar == '=') { if (++offset < line.length() && line.at(offset) == '+') From 7f08529a60eda30909b977137d81d4ad75b174ff Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 5 Jun 2024 13:07:18 +0200 Subject: [PATCH 46/69] PE: Remove left-over connect The item view was made read-only and this connect only results in a warning on the command line when double clicking an item. Amends 8ef85e481aa3095667be33b5db34d180cd18be07. Change-Id: I32d6b9d38e2c554fb91f5cbd1f1f5fd1e6c1a394 Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/environmentwidget.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index 973a499ced9..c438b7b1f2c 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -197,10 +197,7 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, Type type, QWidget *additi auto horizontalLayout = new QHBoxLayout(); horizontalLayout->setContentsMargins(0, 0, 0, 0); - auto tree = new Utils::TreeView(this); - connect(tree, &QAbstractItemView::activated, - tree, [tree](const QModelIndex &idx) { tree->edit(idx); }); - d->m_environmentView = tree; + d->m_environmentView = new Utils::TreeView(this); d->m_environmentView->setModel(d->m_model); d->m_environmentView->setMinimumHeight(400); d->m_environmentView->setRootIsDecorated(false); From 9a4cae42c00ad1f845e1ab8fb650a7c2c08e6ee1 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 5 Jun 2024 13:20:35 +0200 Subject: [PATCH 47/69] Tests: Make environment test less verbose Only print out additional information if the environment does not match the expected one. Change-Id: I9be1a90400a180a4cbd0381f54eea79409c007c7 Reviewed-by: Christian Kandeler --- tests/auto/environment/tst_environment.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/auto/environment/tst_environment.cpp b/tests/auto/environment/tst_environment.cpp index 251ad4d496a..58ee3a72a51 100644 --- a/tests/auto/environment/tst_environment.cpp +++ b/tests/auto/environment/tst_environment.cpp @@ -377,9 +377,12 @@ void tst_Environment::pathChanges() else environment.appendOrSet(variable, value); - qDebug() << "Actual :" << environment.toStringList(); - qDebug() << "Expected:" << expected.toStringList(); - QCOMPARE(environment, expected); + const bool envEqualsExpected = environment == expected; + if (!envEqualsExpected) { + qDebug() << "Actual :" << environment.toStringList(); + qDebug() << "Expected:" << expected.toStringList(); + } + QVERIFY(envEqualsExpected); } void tst_Environment::find_data() From 2d1d4d9ba3a08de6c27b2e41bdb429d6b96b5401 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 21 May 2024 18:58:18 +0200 Subject: [PATCH 48/69] Android: Optimize isConnected() Before, the implementation used connectedDevices() function which constructs the detailed info about each device found, including running 3 extra processes for each device in a loop. For the purpose of isConnected() - the detailed info isn't really necessary - it's enough to parse the output of the {adbToolPath(), {"devices"}} command and detect if the requested serialNumber appears there. We skip executing 3 additional processes for each device found. Change-Id: I34f79fb56b4aa4d52a284bc2c67ae01a3467b505 Reviewed-by: Alessandro Portale --- src/plugins/android/androidconfigurations.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 8a4a0465c3b..db284270804 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -731,9 +731,18 @@ QList connectedDevices(QString *error) bool isConnected(const QString &serialNumber) { - const QList devices = connectedDevices(); - for (const AndroidDeviceInfo &device : devices) { - if (device.serialNumber == serialNumber) + Process adbProcess; + adbProcess.setCommand({adbToolPath(), {"devices"}}); + adbProcess.runBlocking(); + if (adbProcess.result() != ProcessResult::FinishedWithSuccess) + return false; + + // mid(1) - remove "List of devices attached" header line. + // Example output: "List of devices attached\nemulator-5554\tdevice\n\n". + const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + for (const QString &line : lines) { + // skip the daemon logs + if (!line.startsWith("* daemon") && line.left(line.indexOf('\t')).trimmed() == serialNumber) return true; } return false; From ffc3c9b7f30914610a23c846655d9689d2cd44b3 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 21 May 2024 20:59:47 +0200 Subject: [PATCH 49/69] Android: Avoid using connectedDevices() in findAvd() The connectedDevices() constructs the full list of AndroidDeviceInfo with details not needed by findAvd(). These details were generated by using 2 excessive process runs. Get rid of them, and deliver result as soon as the matching device if found. Make getAvdName() public. Change-Id: Ic58e2ee55f275e230c07631853225ca3fdefd730 Reviewed-by: Alessandro Portale --- src/plugins/android/androidavdmanager.cpp | 23 ++++++-- src/plugins/android/androidconfigurations.cpp | 54 +++++++++---------- src/plugins/android/androidconfigurations.h | 1 + 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 38cfa2cdbf8..ded1c5fe160 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -89,12 +89,25 @@ bool startAvdAsync(const QString &avdName) QString findAvd(const QString &avdName) { - const QList devices = AndroidConfig::connectedDevices(); - for (const AndroidDeviceInfo &device : devices) { - if (device.type != ProjectExplorer::IDevice::Emulator) + Process adbProcess; + adbProcess.setCommand({AndroidConfig::adbToolPath(), {"devices"}}); + adbProcess.runBlocking(); + if (adbProcess.result() != ProcessResult::FinishedWithSuccess) + return {}; + + // mid(1) - remove "List of devices attached" header line + const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + for (const QString &line : lines) { + // skip the daemon logs + if (line.startsWith("* daemon")) continue; - if (device.avdName == avdName) - return device.serialNumber; + + const QString serialNumber = line.left(line.indexOf('\t')).trimmed(); + if (!serialNumber.startsWith("emulator")) + continue; + + if (AndroidConfig::getAvdName(serialNumber) == avdName) + return serialNumber; } return {}; } diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index db284270804..97f31550c50 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -141,33 +141,6 @@ static QString buildToolsPackageMarker() return QLatin1String(Constants::buildToolsPackageName) + ";"; } -static QString getAvdName(const QString &serialnumber) -{ - const int index = serialnumber.indexOf(QLatin1String("-")); - if (index == -1) - return {}; - bool ok; - const int port = serialnumber.mid(index + 1).toInt(&ok); - if (!ok) - return {}; - - QTcpSocket tcpSocket; - tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port); - if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection - return {}; - - tcpSocket.write("avd name\nexit\n"); - tcpSocket.waitForDisconnected(500); - - const QByteArrayList response = tcpSocket.readAll().split('\n'); - // The input "avd name" might not be echoed as-is, but contain ASCII control sequences. - for (int i = response.size() - 1; i > 1; --i) { - if (response.at(i).startsWith("OK")) - return QString::fromLatin1(response.at(i - 1)).trimmed(); - } - return {}; -} - static QString getDeviceProperty(const QString &device, const QString &property) { // workaround for '????????????' serial numbers @@ -311,6 +284,33 @@ static FilePath ndkSubPathFromQtVersion(const QtVersion &version) // AndroidConfig ////////////////////////////////// +QString getAvdName(const QString &serialnumber) +{ + const int index = serialnumber.indexOf(QLatin1String("-")); + if (index == -1) + return {}; + bool ok; + const int port = serialnumber.mid(index + 1).toInt(&ok); + if (!ok) + return {}; + + QTcpSocket tcpSocket; + tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port); + if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection + return {}; + + tcpSocket.write("avd name\nexit\n"); + tcpSocket.waitForDisconnected(500); + + const QByteArrayList response = tcpSocket.readAll().split('\n'); + // The input "avd name" might not be echoed as-is, but contain ASCII control sequences. + for (int i = response.size() - 1; i > 1; --i) { + if (response.at(i).startsWith("OK")) + return QString::fromLatin1(response.at(i - 1)).trimmed(); + } + return {}; +} + QLatin1String displayName(const Abi &abi) { switch (abi.architecture()) { diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index ca7825cf03e..ec3a441ed32 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -38,6 +38,7 @@ public: namespace AndroidConfig { +QString getAvdName(const QString &serialnumber); QStringList apiLevelNamesFor(const SdkPlatformList &platforms); QString apiLevelNameFor(const SdkPlatform *platform); From e04c7270435edd04779c612bad62534cb18c518a Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Tue, 4 Jun 2024 23:18:22 +0200 Subject: [PATCH 50/69] CMakePM: Don't remove QML debugging flag with the preset value If a CMake preset has the CMAKE_CXX_FLAGS_INIT parameter defined, it would override the QML debugging flag set before. This patchset makes sure that both are present. Change-Id: I2012567af04c2fa0b0097331c05d96770dd86503 Reviewed-by: Alessandro Portale --- .../cmakeprojectmanager/cmakebuildconfiguration.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index e727fc68f88..92746dce305 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -1349,6 +1349,14 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume if (argFilePath != presetFilePath) arg = presetItem.toArgument(); + } else if (argItem.key == "CMAKE_CXX_FLAGS_INIT") { + // Append the preset value with at the initial parameters value (e.g. QML Debugging) + if (argItem.expandedValue(k) != QString::fromUtf8(presetItem.value)) { + argItem.value.append(" "); + argItem.value.append(presetItem.value); + + arg = argItem.toArgument(); + } } else if (argItem.expandedValue(k) != QString::fromUtf8(presetItem.value)) { arg = presetItem.toArgument(); } From 0fa8047f12353fcd89098f32052fdc878997a593 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Wed, 5 Jun 2024 13:39:27 +0200 Subject: [PATCH 51/69] CMakePM: Transform a few raw C strings into constant literals This way you can have code completion for the constant literals and not do any accidental typos. Change-Id: I1f9dc36327052fcda9575dddec909db93c4b225c Reviewed-by: Alessandro Portale --- .../cmakebuildconfiguration.cpp | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 92746dce305..274ebd8f83f 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -97,6 +97,13 @@ const char USER_ENVIRONMENT_CHANGES_KEY[] = "CMake.Configure.UserEnvironmentChan const char BASE_ENVIRONMENT_KEY[] = "CMake.Configure.BaseEnvironment"; const char GENERATE_QMLLS_INI_SETTING[] = "J.QtQuick/QmlJSEditor.GenerateQmllsIniFiles"; +const char CMAKE_TOOLCHAIN_FILE[] = "CMAKE_TOOLCHAIN_FILE"; +const char CMAKE_C_FLAGS_INIT[] = "CMAKE_C_FLAGS_INIT"; +const char CMAKE_CXX_FLAGS_INIT[] = "CMAKE_CXX_FLAGS_INIT"; +const char CMAKE_CXX_FLAGS[] = "CMAKE_CXX_FLAGS"; +const char CMAKE_CXX_FLAGS_DEBUG[] = "CMAKE_CXX_FLAGS_DEBUG"; +const char CMAKE_CXX_FLAGS_RELWITHDEBINFO[] = "CMAKE_CXX_FLAGS_RELWITHDEBINFO"; + namespace Internal { class CMakeBuildSettingsWidget : public NamedWidget @@ -889,11 +896,11 @@ CMakeConfig CMakeBuildSettingsWidget::getQmlDebugCxxFlags() const bool enable = m_buildConfig->qmlDebugging() == TriState::Enabled; const CMakeConfig configList = m_buildConfig->cmakeBuildSystem()->configurationFromCMake(); - const QByteArrayList cxxFlagsPrev{"CMAKE_CXX_FLAGS", - "CMAKE_CXX_FLAGS_DEBUG", - "CMAKE_CXX_FLAGS_RELWITHDEBINFO", - "CMAKE_CXX_FLAGS_INIT"}; - const QByteArrayList cxxFlags{"CMAKE_CXX_FLAGS_INIT", "CMAKE_CXX_FLAGS"}; + const QByteArrayList cxxFlagsPrev{CMAKE_CXX_FLAGS, + CMAKE_CXX_FLAGS_DEBUG, + CMAKE_CXX_FLAGS_RELWITHDEBINFO, + CMAKE_CXX_FLAGS_INIT}; + const QByteArrayList cxxFlags{CMAKE_CXX_FLAGS_INIT, CMAKE_CXX_FLAGS}; const QByteArray qmlDebug(QT_QML_DEBUG_PARAM); CMakeConfig changedConfig; @@ -1182,14 +1189,14 @@ static CommandLine defaultInitialCMakeCommand( if (auto *gccTc = tc->asGccToolchain()) { const QStringList compilerFlags = gccTc->platformCodeGenFlags(); - QString language; + QLatin1String languageFlagsInit; if (gccTc->language() == ProjectExplorer::Constants::C_LANGUAGE_ID) - language = "C"; + languageFlagsInit = QLatin1String(CMAKE_C_FLAGS_INIT); else if (gccTc->language() == ProjectExplorer::Constants::CXX_LANGUAGE_ID) - language = "CXX"; + languageFlagsInit = QLatin1String(CMAKE_CXX_FLAGS_INIT); - if (!language.isEmpty() && !compilerFlags.isEmpty()) - cmd.addArg("-DCMAKE_" + language + "_FLAGS_INIT:STRING=" + compilerFlags.join(" ")); + if (!languageFlagsInit.isEmpty() && !compilerFlags.isEmpty()) + cmd.addArg("-D" + languageFlagsInit + ":STRING=" + compilerFlags.join(" ")); const QStringList linkerFlags = gccTc->platformLinkerFlags(); if (!linkerFlags.isEmpty()) { @@ -1342,14 +1349,14 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume } arg = argItem.toArgument(); - } else if (argItem.key == "CMAKE_TOOLCHAIN_FILE") { + } else if (argItem.key == CMAKE_TOOLCHAIN_FILE) { const FilePath argFilePath = FilePath::fromString(argItem.expandedValue(k)); const FilePath presetFilePath = FilePath::fromUserInput( QString::fromUtf8(presetItem.value)); if (argFilePath != presetFilePath) arg = presetItem.toArgument(); - } else if (argItem.key == "CMAKE_CXX_FLAGS_INIT") { + } else if (argItem.key == CMAKE_CXX_FLAGS_INIT) { // Append the preset value with at the initial parameters value (e.g. QML Debugging) if (argItem.expandedValue(k) != QString::fromUtf8(presetItem.value)) { argItem.value.append(" "); @@ -1618,7 +1625,8 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id) : TriState::Default); if (qt && qt->isQmlDebuggingSupported()) - cmd.addArg("-DCMAKE_CXX_FLAGS_INIT:STRING=%{" + QLatin1String(QT_QML_DEBUG_FLAG) + "}"); + cmd.addArg( + QLatin1String("-D") + CMAKE_CXX_FLAGS_INIT + ":STRING=%{" + QT_QML_DEBUG_FLAG + "}"); // QT_QML_GENERATE_QMLLS_INI, if enabled via the settings checkbox: if (Core::ICore::settings()->value(GENERATE_QMLLS_INI_SETTING).toBool()) { @@ -1679,8 +1687,8 @@ bool CMakeBuildConfiguration::hasQmlDebugging(const CMakeConfig &config) // Determine QML debugging flags. This must match what we do in // CMakeBuildSettingsWidget::getQmlDebugCxxFlags() // such that in doubt we leave the QML Debugging setting at "Leave at default" - const QString cxxFlagsInit = config.stringValueOf("CMAKE_CXX_FLAGS_INIT"); - const QString cxxFlags = config.stringValueOf("CMAKE_CXX_FLAGS"); + const QString cxxFlagsInit = config.stringValueOf(CMAKE_CXX_FLAGS_INIT); + const QString cxxFlags = config.stringValueOf(CMAKE_CXX_FLAGS); return cxxFlagsInit.contains(QT_QML_DEBUG_PARAM) && cxxFlags.contains(QT_QML_DEBUG_PARAM); } From 345ae8e4d4e44917f57b3961e4895368b80a3a44 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 5 Jun 2024 13:22:51 +0200 Subject: [PATCH 52/69] ProjectExplorer: Merge CustomExecutableRunConfiguration::runnable() ... into parent class implementation. This way, the executable expansion will be done for all sub-classes, the remote custom run configurations being the most relevant ones. Task-number: QTCREATORBUG-30925 Change-Id: Id7715d9f60338767c0623fa33749ef18338ae479 Reviewed-by: hjk --- .../customexecutablerunconfiguration.cpp | 15 --------------- .../customexecutablerunconfiguration.h | 1 - src/plugins/projectexplorer/runconfiguration.cpp | 7 +++++++ 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 8639bc00cee..12bc39a7c4d 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -49,21 +49,6 @@ bool CustomExecutableRunConfiguration::isEnabled(Id) const return true; } -ProcessRunData CustomExecutableRunConfiguration::runnable() const -{ - ProcessRunData r; - r.command = commandLine(); - r.environment = environment.environment(); - r.workingDirectory = workingDir(); - - if (!r.command.isEmpty()) { - const FilePath expanded = macroExpander()->expand(r.command.executable()); - r.command.setExecutable(expanded); - } - - return r; -} - QString CustomExecutableRunConfiguration::defaultDisplayName() const { if (executable().isEmpty()) diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.h b/src/plugins/projectexplorer/customexecutablerunconfiguration.h index ced9892c016..3b9e8996b8a 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.h +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.h @@ -20,7 +20,6 @@ public: QString defaultDisplayName() const; private: - Utils::ProcessRunData runnable() const override; bool isEnabled(Utils::Id) const override; Tasks checkForIssues() const override; diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index a0c3aa2e6dd..97d467fbc75 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -425,6 +425,13 @@ ProcessRunData RunConfiguration::runnable() const r.environment = environmentAspect->environment(); if (m_runnableModifier) m_runnableModifier(r); + + // TODO: Do expansion in commandLine()? + if (!r.command.isEmpty()) { + const FilePath expanded = macroExpander()->expand(r.command.executable()); + r.command.setExecutable(expanded); + } + return r; } From 5221d53a6d42e656feb922b83650edde34949a43 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 5 Jun 2024 13:23:50 +0200 Subject: [PATCH 53/69] BareMetal: Use runnable() to get executable and arguments ... rather than accessing the aspects directly. Task-number: QTCREATORBUG-30925 Change-Id: Id39b6226f6bbee75b93905747373513294deb29e Reviewed-by: hjk --- .../baremetal/debugservers/gdb/gdbserverprovider.cpp | 10 +++------- .../baremetal/debugservers/uvsc/uvscserverprovider.cpp | 6 +----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp index 885e20fac42..773a2ac577f 100644 --- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp @@ -138,11 +138,8 @@ bool GdbServerProvider::isValid() const bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessage) const { QTC_ASSERT(runTool, return false); - const RunControl *runControl = runTool->runControl(); - const auto exeAspect = runControl->aspectData(); - QTC_ASSERT(exeAspect, return false); - - const FilePath bin = FilePath::fromString(exeAspect->executable.path()); + const ProcessRunData runnable = runTool->runControl()->runnable(); + const FilePath bin = FilePath::fromString(runnable.command.executable().path()); if (bin.isEmpty()) { errorMessage = Tr::tr("Cannot debug: Local executable is not set."); return false; @@ -155,8 +152,7 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessa ProcessRunData inferior; inferior.command.setExecutable(bin); - if (const auto argAspect = runControl->aspectData()) - inferior.command.setArguments(argAspect->arguments); + inferior.command.setArguments(runnable.command.arguments()); runTool->setInferior(inferior); runTool->setSymbolFile(bin); runTool->setStartMode(AttachToRemoteServer); diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp index 3709736d842..4dac9b654c4 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp @@ -168,11 +168,7 @@ QString UvscServerProvider::channelString() const bool UvscServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessage) const { QTC_ASSERT(runTool, return false); - const RunControl *runControl = runTool->runControl(); - const auto exeAspect = runControl->aspectData(); - QTC_ASSERT(exeAspect, return false); - - const FilePath bin = exeAspect->executable; + const FilePath bin = runTool->runControl()->runnable().command.executable(); if (bin.isEmpty()) { errorMessage = Tr::tr("Cannot debug: Local executable is not set."); return false; From 152307a7580934586b9a35724ad14d6fef0a0b86 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 5 Jun 2024 13:48:21 +0200 Subject: [PATCH 54/69] Themes: Update Figma color tokens This adds color tokens for gradients 01 and 02. Change-Id: I05ae55ff4fd9f496ff1e465e6905398825078b52 Reviewed-by: hjk --- share/qtcreator/themes/dark.figmatokens | 5 +++++ share/qtcreator/themes/light.figmatokens | 5 +++++ src/libs/utils/theme/theme.h | 4 ++++ 3 files changed, 14 insertions(+) diff --git a/share/qtcreator/themes/dark.figmatokens b/share/qtcreator/themes/dark.figmatokens index 194ce7fab25..d65ae21fa3f 100644 --- a/share/qtcreator/themes/dark.figmatokens +++ b/share/qtcreator/themes/dark.figmatokens @@ -36,3 +36,8 @@ Token_Notification_Alert=ffc98014 Token_Notification_Success=ff1f9b5d Token_Notification_Neutral=ff016876 Token_Notification_Danger=ffb22245 + +Token_Gradient01_Start=ff016876 +Token_Gradient01_End=ff1F9B5D +Token_Gradient02_Start=ff3A3A3A +Token_Gradient02_End=ff838383 diff --git a/share/qtcreator/themes/light.figmatokens b/share/qtcreator/themes/light.figmatokens index 138ad74afdf..8bbe56319d3 100644 --- a/share/qtcreator/themes/light.figmatokens +++ b/share/qtcreator/themes/light.figmatokens @@ -35,3 +35,8 @@ Token_Notification_Alert=ffeb991f Token_Notification_Success=ff23b26a Token_Notification_Neutral=ff0e7887 Token_Notification_Danger=ffdc1343 + +Token_Gradient01_Start=ff23B26A +Token_Gradient01_End=ff0E7887 +Token_Gradient02_Start=ff949494 +Token_Gradient02_End=ff474747 diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 59475aff820..8d7e922e79d 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -247,6 +247,10 @@ public: Token_Notification_Success, Token_Notification_Neutral, Token_Notification_Danger, + Token_Gradient01_Start, + Token_Gradient01_End, + Token_Gradient02_Start, + Token_Gradient02_End, /* Timeline Library */ Timeline_TextColor, From 0650fe14e524f9643dce0105b79983e9b5baa4c8 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 5 Jun 2024 13:50:19 +0200 Subject: [PATCH 55/69] Utils: Update Figma text tokens This adds UiElementLabelMedium and UiElementLabelSmall. Change-Id: I592f2bdf7a4d0a98e38ea69c33cf1b27040f7eea Reviewed-by: hjk --- src/libs/utils/stylehelper.cpp | 2 ++ src/libs/utils/stylehelper.h | 4 ++++ src/plugins/coreplugin/icore.cpp | 2 ++ tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp | 2 ++ 4 files changed, 10 insertions(+) diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index 617dac09a8e..4472c43b558 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -950,6 +950,8 @@ static const UiFontMetrics& uiFontMetrics(StyleHelper::UiElement element) {StyleHelper::UiElementBody2, {12, 20, QFont::Light}}, {StyleHelper::UiElementButtonMedium, {12, 16, QFont::Bold}}, {StyleHelper::UiElementButtonSmall, {10, 12, QFont::Bold}}, + {StyleHelper::UiElementLabelMedium, {12, 16, QFont::DemiBold}}, + {StyleHelper::UiElementLabelSmall, {10, 12, QFont::DemiBold}}, {StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}}, {StyleHelper::UiElementCaption, {10, 12, QFont::Normal}}, {StyleHelper::UiElementIconStandard, {12, 16, QFont::Medium}}, diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 7d434925e66..ecc2eea2b96 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -79,6 +79,8 @@ enum ToolbarStyle { }; constexpr ToolbarStyle defaultToolbarStyle = ToolbarStyleCompact; +// Keep in sync with: +// SyleHelper::uiFontMetrics, ICore::uiConfigInformation, tst_manual_widgets_uifonts::main enum UiElement { UiElementH1, UiElementH2, @@ -91,6 +93,8 @@ enum UiElement { UiElementBody2, UiElementButtonMedium, UiElementButtonSmall, + UiElementLabelMedium, + UiElementLabelSmall, UiElementCaptionStrong, UiElementCaption, UiElementIconStandard, diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index da47d44b6e0..a4098963a60 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -1068,6 +1068,8 @@ QString uiConfigInformation() QTC_ADD_UIELEMENT_FONT(Body2); QTC_ADD_UIELEMENT_FONT(ButtonMedium); QTC_ADD_UIELEMENT_FONT(ButtonSmall); + QTC_ADD_UIELEMENT_FONT(LabelMedium); + QTC_ADD_UIELEMENT_FONT(LabelSmall); QTC_ADD_UIELEMENT_FONT(CaptionStrong); QTC_ADD_UIELEMENT_FONT(Caption); QTC_ADD_UIELEMENT_FONT(IconStandard); diff --git a/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp b/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp index 247bba67728..ecbf4b274b8 100644 --- a/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp +++ b/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp @@ -29,6 +29,8 @@ int main(int argc, char *argv[]) { StyleHelper::UiElementBody2, "Body-02" }, { StyleHelper::UiElementButtonMedium, "Button Medium" }, { StyleHelper::UiElementButtonSmall, "Button Small" }, + { StyleHelper::UiElementLabelMedium, "Label Medium" }, + { StyleHelper::UiElementLabelSmall, "Label Small" }, { StyleHelper::UiElementCaptionStrong, "Caption strong" }, { StyleHelper::UiElementCaption, "Caption" }, { StyleHelper::UiElementIconStandard, "Icon Standard" }, From bbfb7542a74648eb049f646fb4789525820f9afa Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 5 Jun 2024 14:03:15 +0200 Subject: [PATCH 56/69] ExtensionManager: Read more fields from service response This adds reading of "download_count", "id" and "is_internal". Change-Id: I45382d7c70bbc5f44f9391bf3a87ee354d1809e4 Reviewed-by: hjk --- src/plugins/extensionmanager/extensionsmodel.cpp | 10 ++++++++++ src/plugins/extensionmanager/extensionsmodel.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/plugins/extensionmanager/extensionsmodel.cpp b/src/plugins/extensionmanager/extensionsmodel.cpp index 881125555df..56fcc54ad8b 100644 --- a/src/plugins/extensionmanager/extensionsmodel.cpp +++ b/src/plugins/extensionmanager/extensionsmodel.cpp @@ -38,6 +38,7 @@ struct Plugin { Dependencies dependencies; QString copyright; + bool isInternal = false; QString name; QString packageUrl; QString vendor; @@ -54,6 +55,8 @@ struct Description { struct Extension { QString copyright; Description description; + int downloadCount = -1; + QString id; QString license; QString name; QStringList platforms; @@ -83,6 +86,7 @@ static Plugin pluginFromJson(const QJsonObject &obj) return { .dependencies = dependencies, .copyright = metaDataObj.value("Copyright").toString(), + .isInternal = obj.value("is_internal").toBool(false), .name = metaDataObj.value("Name").toString(), .packageUrl = obj.value("url").toString(), .vendor = metaDataObj.value("Vendor").toString(), @@ -158,6 +162,8 @@ static Extension extensionFromJson(const QJsonObject &obj) const Extension extension = { .copyright = obj.value("copyright").toString(), .description = description, + .downloadCount = obj.value("download_count").toInt(-1), + .id = obj.value("id").toString(), .license = obj.value("license").toString(), .name = obj.value("name").toString(), .platforms = platforms, @@ -318,6 +324,10 @@ static QVariant dataFromExtension(const Extension &extension, int role) return QVariant::fromValue(extension.description.links); case RoleDescriptionText: return QVariant::fromValue(extension.description.text); + case RoleDownloadCount: + return extension.downloadCount; + case RoleId: + return extension.id; case RoleItemType: return extension.type; case RoleLicense: diff --git a/src/plugins/extensionmanager/extensionsmodel.h b/src/plugins/extensionmanager/extensionsmodel.h index d426f443533..1fc86d3afde 100644 --- a/src/plugins/extensionmanager/extensionsmodel.h +++ b/src/plugins/extensionmanager/extensionsmodel.h @@ -30,6 +30,8 @@ enum Role { RoleDescriptionImages, RoleDescriptionLinks, RoleDescriptionText, + RoleDownloadCount, + RoleId, RoleItemType, RoleLicense, RoleLocation, From 74e4e1053aef7fac82cc793fdaaa45655b7b864f Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Fri, 31 May 2024 21:31:09 +0200 Subject: [PATCH 57/69] ExtensionManager: Update ExtensionManager list items design This updates the list delegate for ExtensionManager items to match the latest Figma design. Change-Id: I769026caa1e08feea4f71d901d1bda01d74ab0a2 Reviewed-by: hjk --- .../extensionmanager/extensionmanager.qrc | 2 + .../extensionmanager/extensionsbrowser.cpp | 237 ++++++++++++------ .../extensionmanager/images/download.png | Bin 0 -> 137 bytes .../extensionmanager/images/download@2x.png | Bin 0 -> 207 bytes .../images/extensionsmall.png | Bin 164 -> 314 bytes .../images/extensionsmall@2x.png | Bin 196 -> 504 bytes .../extensionmanager/images/packsmall.png | Bin 271 -> 260 bytes .../extensionmanager/images/packsmall@2x.png | Bin 454 -> 418 bytes src/tools/icons/qtcreatoricons.svg | 32 ++- 9 files changed, 179 insertions(+), 92 deletions(-) create mode 100644 src/plugins/extensionmanager/images/download.png create mode 100644 src/plugins/extensionmanager/images/download@2x.png diff --git a/src/plugins/extensionmanager/extensionmanager.qrc b/src/plugins/extensionmanager/extensionmanager.qrc index cff16c156ff..b6a3554cb1f 100644 --- a/src/plugins/extensionmanager/extensionmanager.qrc +++ b/src/plugins/extensionmanager/extensionmanager.qrc @@ -1,5 +1,7 @@ + images/download.png + images/download@2x.png images/extensionsmall.png images/extensionsmall@2x.png images/mode_extensionmanager_mask.png diff --git a/src/plugins/extensionmanager/extensionsbrowser.cpp b/src/plugins/extensionmanager/extensionsbrowser.cpp index b5d42ec4f95..ccf814cd7c8 100644 --- a/src/plugins/extensionmanager/extensionsbrowser.cpp +++ b/src/plugins/extensionmanager/extensionsbrowser.cpp @@ -40,27 +40,37 @@ #include #include -using namespace ExtensionSystem; using namespace Core; +using namespace ExtensionSystem; using namespace Utils; +using namespace StyleHelper; +using namespace SpacingTokens; +using namespace WelcomePageHelpers; namespace ExtensionManager::Internal { Q_LOGGING_CATEGORY(browserLog, "qtc.extensionmanager.browser", QtWarningMsg) -constexpr QSize itemSize = {330, 86}; -constexpr int gapSize = StyleHelper::SpacingTokens::ExVPaddingGapXl; -constexpr QSize cellSize = {itemSize.width() + gapSize, itemSize.height() + gapSize}; - -static QColor colorForExtensionName(const QString &name) -{ - const size_t hash = qHash(name); - return QColor::fromHsv(hash % 360, 180, 110); -} +constexpr int gapSize = ExVPaddingGapXl; +constexpr int itemWidth = 330; +constexpr int cellWidth = itemWidth + HPaddingL; class ExtensionItemDelegate : public QItemDelegate { public: + constexpr static QSize dividerS{1, 16}; + constexpr static QSize iconBgS{50, 50}; + constexpr static TextFormat itemNameTF + {Theme::Token_Text_Default, UiElement::UiElementH6}; + constexpr static TextFormat countTF + {Theme::Token_Text_Default, UiElement::UiElementLabelSmall, + Qt::AlignCenter | Qt::TextDontClip}; + constexpr static TextFormat vendorTF + {Theme::Token_Text_Muted, UiElement::UiElementLabelSmall, + Qt::AlignVCenter | Qt::TextDontClip}; + constexpr static TextFormat tagsTF + {Theme::Token_Text_Default, UiElement::UiElementCaption}; + explicit ExtensionItemDelegate(QObject *parent = nullptr) : QItemDelegate(parent) { @@ -69,12 +79,54 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { + // +---------------+-------+---------------+----------------------------------------------------------------------+---------------+-----------+ + // | | | | (ExPaddingGapL) | | | + // | | | +-------------------------------------------------------------+--------+ | | + // | | | | || | | + // | | | +-------------------------------------------------------------+--------+ | | + // | | | | (VGapXxs) | | | + // | | | +--------+--------+--------------+--------+--------+---------+---------+ | | + // |(ExPaddingGapL)| |(ExPaddingGapL)||(HGapXs)|(h16)|(HGapXs)||(HGapXxs)||(ExPaddingGapL)|(HPaddingL)| + // | |(50x50)| +--------+--------+--------------+--------+--------+---------+---------+ | | + // | | | | (VGapXxs) | | | + // | | | +----------------------------------------------------------------------+ | | + // | | | | | | | + // | | | +----------------------------------------------------------------------+ | | + // | | | | (ExPaddingGapL) | | | + // +---------------+-------+---------------+----------------------------------------------------------------------+---------------+-----------+ + // | (ExVPaddingGapXl) | + // +------------------------------------------------------------------------------------------------------------------------------------------+ + + const QRect bgRGlobal = option.rect.adjusted(0, 0, -HPaddingL, -gapSize); + const QRect bgR = bgRGlobal.translated(-option.rect.topLeft()); + + const int middleColumnW = bgR.width() - ExPaddingGapL - iconBgS.width() - ExPaddingGapL + - ExPaddingGapL; + + int x = bgR.x(); + int y = bgR.y(); + x += ExPaddingGapL; + const QRect iconBgR(x, y + (bgR.height() - iconBgS.height()) / 2, + iconBgS.width(), iconBgS.height()); + x += iconBgS.width() + ExPaddingGapL; + y += ExPaddingGapL; + const QRect itemNameR(x, y, middleColumnW, itemNameTF.lineHeight()); + const QString itemName = index.data().toString(); + + y += itemNameR.height() + VGapXxs; + const QRect vendorRowR(x, y, middleColumnW, vendorRowHeight()); + QRect vendorR = vendorRowR; + + y += vendorRowR.height() + VGapXxs; + const QRect tagsR(x, y, middleColumnW, tagsTF.lineHeight()); + + QTC_CHECK(option.rect.height() - 1 == tagsR.bottom() + ExPaddingGapL + gapSize); + painter->save(); painter->setRenderHint(QPainter::Antialiasing); + painter->translate(bgRGlobal.topLeft()); - const QString itemName = index.data().toString(); const bool isPack = index.data(RoleItemType) == ItemTypePack; - const QRectF itemRect(option.rect.topLeft(), itemSize); { const bool selected = option.state & QStyle::State_Selected; const bool hovered = option.state & QStyle::State_MouseOver; @@ -85,94 +137,114 @@ public: creatorColor(selected ? Theme::Token_Stroke_Strong : hovered ? WelcomePageHelpers::cardHoverStroke : WelcomePageHelpers::cardDefaultStroke); - WelcomePageHelpers::drawCardBackground(painter, itemRect, fillColor, strokeColor); + WelcomePageHelpers::drawCardBackground(painter, bgR, fillColor, strokeColor); } { - constexpr QRectF bigCircle(16, 16, 48, 48); - constexpr double gradientMargin = 0.14645; - const QRectF bigCircleLocal = bigCircle.translated(itemRect.topLeft()); - QPainterPath bigCirclePath; - bigCirclePath.addEllipse(bigCircleLocal); - QLinearGradient gradient(bigCircleLocal.topLeft(), bigCircleLocal.bottomRight()); - const QColor startColor = isPack ? qRgb(0x1e, 0x99, 0x6e) - : colorForExtensionName(itemName); - const QColor endColor = isPack ? qRgb(0x07, 0x6b, 0x6d) : startColor.lighter(150); - gradient.setColorAt(gradientMargin, startColor); - gradient.setColorAt(1 - gradientMargin, endColor); - painter->fillPath(bigCirclePath, gradient); + QLinearGradient gradient(iconBgR.topRight(), iconBgR.bottomLeft()); + const QColor startColor = creatorColor(Utils::Theme::Token_Gradient01_Start); + const QColor endColor = creatorColor(Utils::Theme::Token_Gradient01_End); + gradient.setColorAt(0, startColor); + gradient.setColorAt(1, endColor); + constexpr int iconRectRounding = 4; + drawCardBackground(painter, iconBgR, gradient, Qt::NoPen, iconRectRounding); - static const QIcon packIcon = - Icon({{":/extensionmanager/images/packsmall.png", - Theme::Token_Text_Default}}, Icon::Tint).icon(); - static const QIcon extensionIcon = - Icon({{":/extensionmanager/images/extensionsmall.png", - Theme::Token_Text_Default}}, Icon::Tint).icon(); - QRectF iconRect(0, 0, 32, 32); - iconRect.moveCenter(bigCircleLocal.center()); - (isPack ? packIcon : extensionIcon).paint(painter, iconRect.toRect()); + // Icon + constexpr Theme::Color color = Theme::Token_Basic_White; + static const QIcon pack = Icon({{":/extensionmanager/images/packsmall.png", color}}, + Icon::Tint).icon(); + static const QIcon extension = Icon({{":/extensionmanager/images/extensionsmall.png", + color}}, Icon::Tint).icon(); + (isPack ? pack : extension).paint(painter, iconBgR); } if (isPack) { - constexpr QRectF smallCircle(47, 50, 18, 18); - constexpr qreal strokeWidth = 1; - constexpr qreal shrink = strokeWidth / 2; - constexpr QRectF smallCircleAdjusted = smallCircle.adjusted(shrink, shrink, - -shrink, -shrink); - const QRectF smallCircleLocal = smallCircleAdjusted.translated(itemRect.topLeft()); + constexpr int circleSize = 18; + constexpr int circleOverlap = 3; // Protrusion from lower right corner of iconRect + const QRect smallCircle(iconBgR.right() + 1 + circleOverlap - circleSize, + iconBgR.bottom() + 1 + circleOverlap - circleSize, + circleSize, circleSize); const QColor fillColor = creatorColor(Theme::Token_Foreground_Muted); const QColor strokeColor = creatorColor(Theme::Token_Stroke_Subtle); - painter->setBrush(fillColor); - painter->setPen(strokeColor); - painter->drawEllipse(smallCircleLocal); + drawCardBackground(painter, smallCircle, fillColor, strokeColor, circleSize / 2); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong)); - const QColor textColor = creatorColor(Theme::Token_Text_Default); - painter->setPen(textColor); + painter->setFont(countTF.font()); + painter->setPen(countTF.color()); const PluginsData plugins = index.data(RolePlugins).value(); - painter->drawText( - smallCircleLocal, - QString::number(plugins.count()), - QTextOption(Qt::AlignCenter)); + painter->drawText(smallCircle, countTF.drawTextFlags, QString::number(plugins.count())); } { - constexpr int textX = 80; - constexpr int rightMargin = StyleHelper::SpacingTokens::ExVPaddingGapXl; - constexpr int maxTextWidth = itemSize.width() - textX - rightMargin; - constexpr Qt::TextElideMode elideMode = Qt::ElideRight; - - constexpr int titleY = 30; - const QPointF titleOrigin(itemRect.topLeft() + QPointF(textX, titleY)); - painter->setPen(creatorColor(Theme::Token_Text_Default)); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementH6)); + painter->setPen(itemNameTF.color()); + painter->setFont(itemNameTF.font()); const QString titleElided - = painter->fontMetrics().elidedText(itemName, elideMode, maxTextWidth); - painter->drawText(titleOrigin, titleElided); + = painter->fontMetrics().elidedText(itemName, Qt::ElideRight, itemNameR.width()); + painter->drawText(itemNameR, itemNameTF.drawTextFlags, titleElided); + } + { + const QString vendor = index.data(RoleVendor).toString(); + const QFontMetrics fm(vendorTF.font()); + painter->setPen(vendorTF.color()); + painter->setFont(vendorTF.font()); - constexpr int copyrightY = 52; - const QPointF copyrightOrigin(itemRect.topLeft() + QPointF(textX, copyrightY)); - painter->setPen(creatorColor(Theme::Token_Text_Muted)); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong)); - const QString copyright = index.data(RoleCopyright).toString(); - const QString copyrightElided - = painter->fontMetrics().elidedText(copyright, elideMode, maxTextWidth); - painter->drawText(copyrightOrigin, copyrightElided); + if (const int dlCount = index.data(RoleDownloadCount).toInt(); dlCount > 0) { + constexpr QSize dlIconS(16, 16); + const QString dlCountString = QString::number(dlCount); + const int dlCountW = fm.horizontalAdvance(dlCountString); + const int dlItemsW = HGapXs + dividerS.width() + HGapXs + dlIconS.width() + + HGapXxs + dlCountW; + const int vendorW = fm.horizontalAdvance(vendor); + vendorR.setWidth(qMin(middleColumnW - dlItemsW, vendorW)); - constexpr int tagsY = 70; - const QPointF tagsOrigin(itemRect.topLeft() + QPointF(textX, tagsY)); - const QString tags = index.data(RoleTags).toStringList().join(", "); - painter->setPen(creatorColor(Theme::Token_Text_Default)); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaption)); - const QString tagsElided = painter->fontMetrics().elidedText( - tags, elideMode, maxTextWidth); - painter->drawText(tagsOrigin, tagsElided); + QRect dividerR = vendorRowR; + dividerR.setLeft(vendorR.right() + HGapXs); + dividerR.setWidth(dividerS.width()); + painter->fillRect(dividerR, vendorTF.color()); + + QRect dlIconR = vendorRowR; + dlIconR.setLeft(dividerR.right() + HGapXs); + dlIconR.setWidth(dlIconS.width()); + static const QIcon dlIcon = Icon({{":/extensionmanager/images/download.png", + vendorTF.themeColor}}, Icon::Tint).icon(); + dlIcon.paint(painter, dlIconR); + + QRect dlCountR = vendorRowR; + dlCountR.setLeft(dlIconR.right() + HGapXxs); + painter->drawText(dlCountR, vendorTF.drawTextFlags, dlCountString); + } + + const QString vendorElided = fm.elidedText(vendor, Qt::ElideRight, vendorR.width()); + painter->drawText(vendorR, vendorTF.drawTextFlags, vendorElided); + } + { + const QStringList tagList = index.data(RoleTags).toStringList(); + const QString tags = tagList.join(", "); + painter->setPen(tagsTF.color()); + painter->setFont(tagsTF.font()); + const QString tagsElided + = painter->fontMetrics().elidedText(tags, Qt::ElideRight, tagsR.width()); + painter->drawText(tagsR, tagsTF.drawTextFlags, tagsElided); } painter->restore(); } + static int vendorRowHeight() + { + return qMax(vendorTF.lineHeight(), dividerS.height()); + } + QSize sizeHint([[maybe_unused]] const QStyleOptionViewItem &option, [[maybe_unused]] const QModelIndex &index) const override { - return cellSize; + const int middleColumnH = + itemNameTF.lineHeight() + + VGapXxs + + vendorRowHeight() + + VGapXxs + + tagsTF.lineHeight(); + const int height = + ExPaddingGapL + + qMax(iconBgS.height(), middleColumnH) + + ExPaddingGapL; + return {cellWidth, height + gapSize}; } }; @@ -196,11 +268,10 @@ ExtensionsBrowser::ExtensionsBrowser(QWidget *parent) setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); auto manageLabel = new QLabel(Tr::tr("Manage Extensions")); - manageLabel->setFont(StyleHelper::uiFont(StyleHelper::UiElementH1)); + manageLabel->setFont(uiFont(UiElementH1)); d->searchBox = new SearchBox; - d->searchBox->setFixedWidth(itemSize.width()); - + d->searchBox->setFixedWidth(itemWidth); d->updateButton = new Button(Tr::tr("Install..."), Button::MediumPrimary); d->model = new ExtensionsModel(this); @@ -267,14 +338,14 @@ ExtensionsBrowser::~ExtensionsBrowser() void ExtensionsBrowser::adjustToWidth(const int width) { const int widthForItems = width - extraListViewWidth(); - d->columnsCount = qMax(1, qFloor(widthForItems / cellSize.width())); + d->columnsCount = qMax(1, qFloor(widthForItems / cellWidth)); d->updateButton->setVisible(d->columnsCount > 1); updateGeometry(); } QSize ExtensionsBrowser::sizeHint() const { - const int columsWidth = d->columnsCount * cellSize.width(); + const int columsWidth = d->columnsCount * cellWidth; return { columsWidth + extraListViewWidth(), 0}; } diff --git a/src/plugins/extensionmanager/images/download.png b/src/plugins/extensionmanager/images/download.png new file mode 100644 index 0000000000000000000000000000000000000000..d328e062fcc28df0bee443154bf9e6a851a73ee4 GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4h9AW2CEqh_A)Rq1bDhQhDc0J{?X4Sv0zIt z?<@vp=2oWLX3Z?y@>{q+82hCr%s6}GAHSNx+=&w#8z(N5PdpYf;Q&jD^Kv62Kc}s! ouR=_+C;mw~+`y8vfpY;P!&Ih&xe>3LUV!ZIboFyt=akR{0Flov_y7O^ literal 0 HcmV?d00001 diff --git a/src/plugins/extensionmanager/images/download@2x.png b/src/plugins/extensionmanager/images/download@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..404df01da06f06b3ccb6cd54ba7799bc6e1c106e GIT binary patch literal 207 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4h9AWhA=@@4F(2=X`U{QAr`0KUfRfa$biT7 zVsz5f&WT4Oe`LD3w6a88U~O%zPv&sGqUReF5Pq*_$q!4lb1m|Y0lc9P4u&%xkPmST zc+39bR<6GA14o_4=Tc_xc^r5iT%5$bmx1X5)7e6+C$SQj-xTZSh^%(r_e>!s{FbxO zyO6jkoI8qhC#w~$jo*4#Y+kJx%TfVe!%1}u1^&yzopr00+EFy8r+H literal 0 HcmV?d00001 diff --git a/src/plugins/extensionmanager/images/extensionsmall.png b/src/plugins/extensionmanager/images/extensionsmall.png index 6cdb8df12c9e14b43bdccf8cc21716e2a99a5e86..b3d270c51468701034c0ecc766275427fdfb4825 100644 GIT binary patch delta 299 zcmZ3&xQl6mL_G%s0|P^tpsEG~10$oSi(`nz>Es{&3X_>U}MYM!NfD&@lJN{5{9#>0^k3+1~t?_G1^frZ`-V$km9m~ z=heOYuWe@>dB|mCv_MZ=Bqc#WXV1s~o_kF+PHZ@G)SRs?zkWOW1_Of!0SrZbqRc!A z5sZNc8dfsA@+g>5m=JuRVME4kGbS#Dhg~ZlNY@El9GG{!fSG;U3%5_LN8NHb*_Jh! zKeqX%!X9$q$bkfdzryESR8D`|Z}rw=n+f3*Mr delta 148 zcmdnRw1jbjL_G^L0|P^2NcwRG1_rGFpAc6D1_owkW-&1_Sy@>lBO_yDV;dWr;^N|| zQ>SjhNeZENw7h?sNJYsTY;PnJTAxm*HLyx5Bqpp14Q6KTOUxNWZzk zl-Ha&|3i0@A;bRI74Ht2Jg`>0uhf2~+=tn$TZk+F$qvE0J&E-VCN6Hf{&DUr`MyMM z8Q)=sj*2(sPMQC!bs`kS`It4z7+Dh=rsSOLW}U#|`jSD#JdSF0aknc5if0>q7x73m zs4y>)>@Z+377%1J5J+aIGMSXa5apzFm*L8io{$Tk!6^sQG*_M#k!%RO!Bfqg7bw-S zp~pMSBx(Jv0yULPCGGGSwvIfdO;yM98a|#`f5gmSgT#UPwKH4P_}UmFj&0sQ;mQdo z(+1@d^Oa|9IvCd^L{)!c|0Ff*|9^HvgAMNTY8zK_ua9WRotVEleZSh`jl63kmT2a@ k^UjrfYuqd4Ai=<}^SCvC6X)b81_lNOPgg&ebxsLQ0I7w=-T(jq delta 169 zcmeyte1vg=ay>)0r;B4q#Np&0{)!0_rU_RX0-4=-B}@}sBtB$rYB$klW+{kVqja;u zH;G3h!LE}nCn4s2DU~%2;Bd`1k$+r27?RH2Uqi{F6|4q5S8glNoQdCcEb6FNX?8zXAc~B zvpsjVyfJh0w<-J?Tt~*zm+?M|EJL=z-X_0{vY~6Harn zN=mSBFMM}&(S!g0^LcpgKX7dEWmGE`(EQQ1@`3auCccvM&P^@M(Iv|4uheol*;J!8 os9J96P*uGsH2Dp?Rz4HMqfYHcM%T@D3=9kmp00i_>zopr09qzs!~g&Q delta 243 zcmZo+>SvmuQh&|U#WBR27?Q+2di|~eytFhvmudzX|}M1$Q{n)6oJ`hzCDeG zZ}`Mm^H1ND+;PhO&;S2Ro2NILzEz#oG+&rSVuq8vgN6U)8Il#R_A6>fXdgfDpprB3 zovJ?@+s>^XTow`%A^~&CoEjS&nLQbqnY%6@IG38>5ZLgFVdQ&MBb@0QE6w A@Bjb+ diff --git a/src/plugins/extensionmanager/images/packsmall@2x.png b/src/plugins/extensionmanager/images/packsmall@2x.png index a58632c756c7b602a11861a1ba697b40352faa82..281ccd8207f07bd85d797bb29b6fec8afff041b3 100644 GIT binary patch delta 392 zcmX@cyoh;%N`0oMi(^Q{;p89wiU|^?2|uI@R!2B3`XRQH?a$q}hfgptU0Yo@G4a4= zK1()>2^#edvl#2k*)|z0V3>AbZhfcOgVO0-KJ%Y0a-C+d&FgyhA|8v$$5^i(oLwfT zQm{8hKTRrz`V7TA15%^tq*n5 zmUOO(DHJV<&y^?oYZ72H5)Li9V=XUL)MA?j3NpXiOtvqovb|>{s zx^+f5;_0Pj3q3a^E%Z;G&2{$gMZ3!!MhpzLwrh(Mbd}i|7#J8lUHx3vIVCVQ0RVLw Br;Y#s delta 428 zcmZ3)e2jU5N`0@Vi(^Q{;p89wiU|^?2|uJi?2YzX_1pR;+n?yAM;KU)jQ>waKM<$P zrg7n(R~zH>y%G}|uBQs|R?IrYbL~V}zyY>2gXK1=2OIQd_Xu>?zWek4zri%YGtadC zdn{qxpYG+bC0eG9V@^@S2Un{)q2vR3u^mNULuVvqt^Oxy^k7Q8ZP;ytnAXD#Hve2( zPUp^H+jCIzxG6V+DKI9(a|Wc*(Gz@kX$$*DIA2hG}~mW?6AcHB5VD5Tq{1 z_CTM%!-yfi(`5*J=pabCl?!z=X>FT*u%1;Z~@jUvn)0@9`l7E&MPZeYCs zQL4fIrqU$yc}cSI#~RZOG8bu!y@`}@KlbP0l+XkKd>g+I diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 7d78434c75a..9c62d2a0ef3 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -3772,7 +3772,7 @@ height="100%" /> @@ -3787,15 +3787,29 @@ width="100%" height="100%" /> + id="path6795-3" + style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-linecap:round" + d="m 48,422.5 v 4 m 0,4 v 4 M 59,418 c 0,0 -1.6471,0.674 -3.6667,1.5 M 51.6667,421 C 49.6472,421.826 48,422.5 48,422.5 c 0,0 -1.6471,-0.674 -3.6667,-1.5 m -3.6666,-1.5 C 38.6472,418.674 37,418 37,418 m 7.3333,15.5 C 46.3528,434.326 48,435 48,435 m 11,-17.5 c 0,0 -1.6471,-0.674 -3.6667,-1.5 m -3.6666,-1.5 C 49.6472,413.674 48,413 48,413 c 0,0 -1.6471,0.674 -3.6667,1.5 M 40.6667,416 C 38.6472,416.826 37,417.5 37,417.5 v 4.333 m 0,4.334 v 4.333 c 0,0 1.6471,0.674 3.6667,1.5" /> + style="fill:#000000;stroke:#000000;stroke-width:2;stroke-linejoin:round" + d="M 59,421.75 V 430.5 l -8,3.25 V 425 Z" + id="path32048" + sodipodi:nodetypes="ccccc" /> + + + + From 5f0b9cd5fffd6d5cfcc8bd56a655dc04c28cd55c Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 5 Jun 2024 09:37:55 +0200 Subject: [PATCH 58/69] QmlPuppet: Fix some deprecation warnings Crc/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp Change-Id: I83342da2a9015ac1a8ba15d2fa10626ef00f5e0c Reviewed-by: Thomas Hartmann --- .../instances/nodeinstanceclientproxy.cpp | 56 +++++++++---------- .../instances/objectnodeinstance.cpp | 8 +-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp index a7ab2bf8737..6ba2fa7fcaa 100644 --- a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp @@ -541,34 +541,34 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command) { NANOTRACE_SCOPE_ARGS("Update", "dispatchCommand", {"name", command.typeName()}); - static const int createInstancesCommandType = QMetaType::type("CreateInstancesCommand"); - static const int update3dViewStateCommand = QMetaType::type("Update3dViewStateCommand"); - static const int changeFileUrlCommandType = QMetaType::type("ChangeFileUrlCommand"); - static const int createSceneCommandType = QMetaType::type("CreateSceneCommand"); - static const int clearSceneCommandType = QMetaType::type("ClearSceneCommand"); - static const int removeInstancesCommandType = QMetaType::type("RemoveInstancesCommand"); - static const int removePropertiesCommandType = QMetaType::type("RemovePropertiesCommand"); - static const int changeBindingsCommandType = QMetaType::type("ChangeBindingsCommand"); - static const int changeValuesCommandType = QMetaType::type("ChangeValuesCommand"); - static const int changeAuxiliaryCommandType = QMetaType::type("ChangeAuxiliaryCommand"); - static const int reparentInstancesCommandType = QMetaType::type("ReparentInstancesCommand"); - static const int changeIdsCommandType = QMetaType::type("ChangeIdsCommand"); - static const int changeStateCommandType = QMetaType::type("ChangeStateCommand"); - static const int completeComponentCommandType = QMetaType::type("CompleteComponentCommand"); - static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand"); - static const int changeNodeSourceCommandType = QMetaType::type("ChangeNodeSourceCommand"); - static const int removeSharedMemoryCommandType = QMetaType::type("RemoveSharedMemoryCommand"); - static const int tokenCommandType = QMetaType::type("TokenCommand"); - static const int endPuppetCommandType = QMetaType::type("EndPuppetCommand"); - static const int changeSelectionCommandType = QMetaType::type("ChangeSelectionCommand"); - static const int inputEventCommandType = QMetaType::type("InputEventCommand"); - static const int view3DActionCommandType = QMetaType::type("View3DActionCommand"); - static const int requestModelNodePreviewImageCommandType = QMetaType::type("RequestModelNodePreviewImageCommand"); - static const int changeLanguageCommand = QMetaType::type("ChangeLanguageCommand"); - static const int changePreviewImageSizeCommand = QMetaType::type( - "ChangePreviewImageSizeCommand"); - static const int startNanotraceCommandType = QMetaType::type("StartNanotraceCommand"); - static const int endNanotraceCommandType = QMetaType::type("EndNanotraceCommand"); + static const int createInstancesCommandType = QMetaType::fromName("CreateInstancesCommand").id(); + static const int update3dViewStateCommand = QMetaType::fromName("Update3dViewStateCommand").id(); + static const int changeFileUrlCommandType = QMetaType::fromName("ChangeFileUrlCommand").id(); + static const int createSceneCommandType = QMetaType::fromName("CreateSceneCommand").id(); + static const int clearSceneCommandType = QMetaType::fromName("ClearSceneCommand").id(); + static const int removeInstancesCommandType = QMetaType::fromName("RemoveInstancesCommand").id(); + static const int removePropertiesCommandType = QMetaType::fromName("RemovePropertiesCommand").id(); + static const int changeBindingsCommandType = QMetaType::fromName("ChangeBindingsCommand").id(); + static const int changeValuesCommandType = QMetaType::fromName("ChangeValuesCommand").id(); + static const int changeAuxiliaryCommandType = QMetaType::fromName("ChangeAuxiliaryCommand").id(); + static const int reparentInstancesCommandType = QMetaType::fromName("ReparentInstancesCommand").id(); + static const int changeIdsCommandType = QMetaType::fromName("ChangeIdsCommand").id(); + static const int changeStateCommandType = QMetaType::fromName("ChangeStateCommand").id(); + static const int completeComponentCommandType = QMetaType::fromName("CompleteComponentCommand").id(); + static const int synchronizeCommandType = QMetaType::fromName("SynchronizeCommand").id(); + static const int changeNodeSourceCommandType = QMetaType::fromName("ChangeNodeSourceCommand").id(); + static const int removeSharedMemoryCommandType = QMetaType::fromName("RemoveSharedMemoryCommand").id(); + static const int tokenCommandType = QMetaType::fromName("TokenCommand").id(); + static const int endPuppetCommandType = QMetaType::fromName("EndPuppetCommand").id(); + static const int changeSelectionCommandType = QMetaType::fromName("ChangeSelectionCommand").id(); + static const int inputEventCommandType = QMetaType::fromName("InputEventCommand").id(); + static const int view3DActionCommandType = QMetaType::fromName("View3DActionCommand").id(); + static const int requestModelNodePreviewImageCommandType = QMetaType::fromName("RequestModelNodePreviewImageCommand").id(); + static const int changeLanguageCommand = QMetaType::fromName("ChangeLanguageCommand").id(); + static const int changePreviewImageSizeCommand = QMetaType::fromName( + "ChangePreviewImageSizeCommand").id(); + static const int startNanotraceCommandType = QMetaType::fromName("StartNanotraceCommand").id(); + static const int endNanotraceCommandType = QMetaType::fromName("EndNanotraceCommand").id(); const int commandType = command.typeId(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index e2529f42af7..2a673bb2e9e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -471,7 +471,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar QVariant oldValue = property.read(); - if (oldValue.typeId() == QVariant::Url) { + if (oldValue.typeId() == QMetaType::QUrl) { QUrl url = oldValue.toUrl(); QString path = url.toLocalFile(); if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty()) @@ -488,7 +488,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar qDebug() << "ObjectNodeInstance.setPropertyVariant: Cannot be written: " << object() << name << adjustedValue; QVariant newValue = property.read(); - if (newValue.typeId() == QVariant::Url) { + if (newValue.typeId() == QMetaType::QUrl) { QUrl url = newValue.toUrl(); QString path = url.toLocalFile(); if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty()) @@ -578,7 +578,7 @@ void ObjectNodeInstance::refreshProperty(const PropertyName &name) else property.write(resetValue(name)); - if (oldValue.typeId() == QVariant::Url) { + if (oldValue.typeId() == QMetaType::QUrl) { QByteArray key = oldValue.toUrl().toEncoded(QUrl::UrlFormattingOption(0x100)); QString pixmapKey = QString::fromUtf8(key.constData(), key.size()); QPixmapCache::remove(pixmapKey); @@ -623,7 +623,7 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const return QVariant::fromValue(Enumeration(me.scope(), me.valueToKey(value.toInt()))); } - if (property.propertyType() == QVariant::Url) { + if (property.propertyType() == QMetaType::QUrl) { QUrl url = property.read().toUrl(); if (url.isEmpty()) return QVariant(); From 05d942d488dcc8070758f311a35628499423ad4c Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 5 Jun 2024 09:44:46 +0200 Subject: [PATCH 59/69] Modeling: Fix some Qt 6.6 deprecation warnings Change-Id: I850bff960601595d3273f3a8dedd3f7e6830e9ba Reviewed-by: Jochen Becher Reviewed-by: hjk --- .../qmt/config/stereotypedefinitionparser.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp index 981641e0ad0..d81f71c5000 100644 --- a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp +++ b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp @@ -1157,11 +1157,8 @@ QColor StereotypeDefinitionParser::parseColorExpression() Token token = d->m_scanner->read(); if (token.type() == Token::TokenIdentifier || token.type() == Token::TokenColor) { QString value = token.text().toLower(); - QColor color; - if (QColor::isValidColor(value)) { - color.setNamedColor(value); - return color; - } + if (QColor::isValidColorName(value)) + return QColor::fromString(value); } throw StereotypeDefinitionParserError("Expected color name.", token.sourcePos()); } @@ -1191,9 +1188,8 @@ StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseExpression() return Value(Float, QVariant(value)); } else if (token.type() == Token::TokenColor) { QString value = token.text().toLower(); - QColor color; - if (QColor::isValidColor(value)) { - color.setNamedColor(value); + if (QColor::isValidColorName(value)) { + const QColor color = QColor::fromString(value); return Value(Color, QVariant(color)); } else { throw StereotypeDefinitionParserError("Invalid color.", token.sourcePos()); From 4ca1edc0804315b2661ea81e829b58aeb24797cc Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 21 May 2024 21:11:05 +0200 Subject: [PATCH 60/69] Android: Avoid using connectedDevices() in getRunningAvdsSerialNumber() The connectedDevices() constructs the full list of AndroidDeviceInfo with details not needed by getRunningAvdsSerialNumber(). These details were generated by using 2 excessive process runs. Get rid of them, and deliver result as soon as the matching device if found. Change-Id: Idc3d9d3321e471f8eaa0d69287f5e748813fc427 Reviewed-by: Alessandro Portale --- src/plugins/android/androiddevice.cpp | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index c5b7b78a80f..6a83548644e 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -633,17 +633,30 @@ void AndroidDeviceManager::setEmulatorArguments(QWidget *parent) QString AndroidDeviceManager::getRunningAvdsSerialNumber(const QString &name) const { - for (const AndroidDeviceInfo &dev : AndroidConfig::connectedDevices()) { - if (!dev.serialNumber.startsWith("emulator")) + Process adbProcess; + adbProcess.setCommand({AndroidConfig::adbToolPath(), {"devices"}}); + adbProcess.runBlocking(); + if (adbProcess.result() != ProcessResult::FinishedWithSuccess) + return {}; + + // mid(1) - remove "List of devices attached" header line + const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + for (const QString &line : lines) { + // skip the daemon logs + if (line.startsWith("* daemon")) continue; - const QString stdOut = emulatorName(dev.serialNumber); + + const QString serialNumber = line.left(line.indexOf('\t')).trimmed(); + if (!serialNumber.startsWith("emulator")) + continue; + + const QString stdOut = emulatorName(serialNumber); if (stdOut.isEmpty()) continue; // Not an avd - const QStringList outputLines = stdOut.split('\n'); - if (outputLines.size() > 1 && outputLines.first() == name) - return dev.serialNumber; - } + if (stdOut.left(stdOut.indexOf('\n')) == name) + return serialNumber; + } return {}; } From 5a12b3618f5c3af3b200a842cd6f972b7a8906c8 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Tue, 21 May 2024 21:15:08 +0200 Subject: [PATCH 61/69] Android: Get rid of connectedDevices() Change-Id: Ib803c2f7cfb6db71a9b35716aca1663a7ce0176a Reviewed-by: Alessandro Portale --- src/plugins/android/androidconfigurations.cpp | 55 ------------------- src/plugins/android/androidconfigurations.h | 2 - 2 files changed, 57 deletions(-) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 97f31550c50..86e1d57b234 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -674,61 +674,6 @@ FilePath keytoolPath() return openJDKBinPath().pathAppended(keytoolName).withExecutableSuffix(); } -QList connectedDevices(QString *error) -{ - QList devices; - Process adbProc; - CommandLine cmd{adbToolPath(), {"devices"}}; - adbProc.setCommand(cmd); - using namespace std::chrono_literals; - adbProc.runBlocking(30s); - if (adbProc.result() != ProcessResult::FinishedWithSuccess) { - if (error) - *error = Tr::tr("Could not run: %1").arg(cmd.toUserOutput()); - return devices; - } - QStringList adbDevs = adbProc.allOutput().split('\n', Qt::SkipEmptyParts); - if (adbDevs.empty()) - return devices; - - for (const QString &line : adbDevs) // remove the daemon logs - if (line.startsWith("* daemon")) - adbDevs.removeOne(line); - adbDevs.removeFirst(); // remove "List of devices attached" header line - - // workaround for '????????????' serial numbers: - // can use "adb -d" when only one usb device attached - for (const QString &device : std::as_const(adbDevs)) { - const QString serialNo = device.left(device.indexOf('\t')).trimmed(); - const QString deviceType = device.mid(device.indexOf('\t')).trimmed(); - AndroidDeviceInfo dev; - dev.serialNumber = serialNo; - dev.type = serialNo.startsWith(QLatin1String("emulator")) ? IDevice::Emulator - : IDevice::Hardware; - dev.sdk = getSDKVersion(dev.serialNumber); - dev.cpuAbi = getAbis(dev.serialNumber); - if (deviceType == QLatin1String("unauthorized")) - dev.state = IDevice::DeviceConnected; - else if (deviceType == QLatin1String("offline")) - dev.state = IDevice::DeviceDisconnected; - else - dev.state = IDevice::DeviceReadyToUse; - - if (dev.type == IDevice::Emulator) { - dev.avdName = getAvdName(dev.serialNumber); - if (dev.avdName.isEmpty()) - dev.avdName = serialNo; - } - - devices.push_back(dev); - } - - Utils::sort(devices); - if (devices.isEmpty() && error) - *error = Tr::tr("No devices found in output of: %1").arg(cmd.toUserOutput()); - return devices; -} - bool isConnected(const QString &serialNumber) { Process adbProcess; diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index ec3a441ed32..5e50dab0cd0 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -89,8 +89,6 @@ Utils::FilePath makePathFromNdk(const Utils::FilePath &ndkLocation); Utils::FilePath keytoolPath(); -QList connectedDevices(QString *error = nullptr); - QString bestNdkPlatformMatch(int target, const QtSupport::QtVersion *qtVersion); QLatin1String displayName(const ProjectExplorer::Abi &abi); From ccd68726085a820ecb2cf4af62cea01ede900d4b Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 22 May 2024 14:50:42 +0200 Subject: [PATCH 62/69] Android: Hide AndroidDeviceManager methods in cpp Change-Id: I871b1126d36367b3716ed988f581a8b5bca68693 Reviewed-by: Alessandro Portale --- src/plugins/android/androiddevice.cpp | 640 +++++++++++++------------- src/plugins/android/androiddevice.h | 15 - 2 files changed, 317 insertions(+), 338 deletions(-) diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 6a83548644e..1b69456bbd9 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -57,6 +57,133 @@ static QString displayNameFromInfo(const AndroidDeviceInfo &info) : info.avdName; } +static IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) +{ + const QStringList args = AndroidDeviceInfo::adbSelector(serial) << "shell" << "echo 1"; + const SdkToolResult result = AndroidManager::runAdbCommand(args); + if (result.success()) + return IDevice::DeviceReadyToUse; + else if (type == IDevice::Emulator || result.stdErr().contains("unauthorized")) + return IDevice::DeviceConnected; + return IDevice::DeviceDisconnected; +} + +static void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device) +{ + const AndroidDevice *dev = static_cast(device.get()); + const QString serial = dev->serialNumber(); + DeviceManager *const devMgr = DeviceManager::instance(); + const Id id = dev->id(); + if (!serial.isEmpty()) + devMgr->setDeviceState(id, getDeviceState(serial, dev->machineType())); + else if (dev->machineType() == IDevice::Emulator) + devMgr->setDeviceState(id, IDevice::DeviceConnected); +} + +static void startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent) +{ + Q_UNUSED(parent) + const AndroidDevice *androidDev = static_cast(device.get()); + const QString name = androidDev->avdName(); + qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name)); + auto future = Utils::asyncRun([name, device] { + const QString serialNumber = AndroidAvdManager::startAvd(name); + // Mark the AVD as ReadyToUse once we know it's started + if (!serialNumber.isEmpty()) { + DeviceManager *const devMgr = DeviceManager::instance(); + devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse); + } + }); + // TODO: use future! +} + +static void setEmulatorArguments(QWidget *parent) +{ + const QString helpUrl = + "https://developer.android.com/studio/run/emulator-commandline#startup-options"; + + QInputDialog dialog(parent ? parent : Core::ICore::dialogParent()); + dialog.setWindowTitle(Tr::tr("Emulator Command-line Startup Options")); + dialog.setLabelText(Tr::tr("Emulator command-line startup options " + "(Help Web Page):") + .arg(helpUrl)); + dialog.setTextValue(AndroidConfig::emulatorArgs()); + + if (auto label = dialog.findChild()) { + label->setOpenExternalLinks(true); + label->setMinimumWidth(500); + } + + if (dialog.exec() == QDialog::Accepted) + AndroidConfig::setEmulatorArgs(dialog.textValue()); +} + +static QString emulatorName(const QString &serialNumber) +{ + const QStringList args = AndroidDeviceInfo::adbSelector(serialNumber) << "emu" << "avd" << "name"; + return AndroidManager::runAdbCommand(args).stdOut(); +} + +static QString getRunningAvdsSerialNumber(const QString &name) +{ + Process adbProcess; + adbProcess.setCommand({AndroidConfig::adbToolPath(), {"devices"}}); + adbProcess.runBlocking(); + if (adbProcess.result() != ProcessResult::FinishedWithSuccess) + return {}; + + // mid(1) - remove "List of devices attached" header line + const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + for (const QString &line : lines) { + // skip the daemon logs + if (line.startsWith("* daemon")) + continue; + + const QString serialNumber = line.left(line.indexOf('\t')).trimmed(); + if (!serialNumber.startsWith("emulator")) + continue; + + const QString stdOut = emulatorName(serialNumber); + if (stdOut.isEmpty()) + continue; // Not an avd + + if (stdOut.left(stdOut.indexOf('\n')) == name) + return serialNumber; + } + return {}; +} + +static FilePath avdFilePath() +{ + QString avdEnvVar = qtcEnvironmentVariable("ANDROID_AVD_HOME"); + if (avdEnvVar.isEmpty()) { + avdEnvVar = qtcEnvironmentVariable("ANDROID_SDK_HOME"); + if (avdEnvVar.isEmpty()) + avdEnvVar = qtcEnvironmentVariable("HOME"); + avdEnvVar.append("/.android/avd"); + } + return FilePath::fromUserInput(avdEnvVar); +} + +static IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info) +{ + if (info.apiLevel < 0) { + qCWarning(androidDeviceLog) << "System image of the created AVD is nullptr"; + return IDevice::Ptr(); + } + AndroidDevice *dev = new AndroidDevice; + const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(info); + dev->setupId(IDevice::AutoDetected, deviceId); + dev->setMachineType(IDevice::Emulator); + dev->settings()->displayName.setValue(info.name); + dev->setDeviceState(IDevice::DeviceConnected); + dev->setAvdPath(avdFilePath() / (info.name + ".avd")); + dev->setExtraData(Constants::AndroidAvdName, info.name); + dev->setExtraData(Constants::AndroidCpuAbi, {info.abi}); + dev->setExtraData(Constants::AndroidSdk, info.apiLevel); + return IDevice::Ptr(dev); +} + class AndroidDeviceWidget : public IDeviceWidget { public: @@ -70,6 +197,66 @@ public: static bool questionDialog(const QString &question, QWidget *parent = nullptr); }; +static void setupWifiForDevice(const IDevice::Ptr &device, QWidget *parent) +{ + if (device->deviceState() != IDevice::DeviceReadyToUse) { + AndroidDeviceWidget::infoDialog( + Tr::tr("The device has to be connected with ADB debugging " + "enabled to use this feature."), parent); + return; + } + + const auto androidDev = static_cast(device.get()); + const QStringList adbSelector = AndroidDeviceInfo::adbSelector(androidDev->serialNumber()); + // prepare port + QStringList args = adbSelector; + args.append({"tcpip", wifiDevicePort}); + const SdkToolResult result = AndroidManager::runAdbCommand(args); + if (!result.success()) { + AndroidDeviceWidget::criticalDialog( + Tr::tr("Opening connection port %1 failed.").arg(wifiDevicePort), + parent); + return; + } + + QTimer::singleShot(2000, parent, [adbSelector, parent] { + // Get device IP address + QStringList args = adbSelector; + args.append({"shell", "ip", "route"}); + const SdkToolResult ipRes = AndroidManager::runAdbCommand(args); + if (!ipRes.success()) { + AndroidDeviceWidget::criticalDialog( + Tr::tr("Retrieving the device IP address failed."), parent); + return; + } + + // Expected output from "ip route" is: + // 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.190 + // where the ip of interest is at the end of the line + const QStringList ipParts = ipRes.stdOut().split(" "); + QString ip; + if (!ipParts.isEmpty()) { + ip = ipParts.last(); + } + if (!ipRegex.match(ipParts.last()).hasMatch()) { + AndroidDeviceWidget::criticalDialog( + Tr::tr("The retrieved IP address is invalid."), parent); + return; + } + + // Connect to device + args = adbSelector; + args.append({"connect", QString("%1:%2").arg(ip).arg(wifiDevicePort)}); + const SdkToolResult connectRes = AndroidManager::runAdbCommand(args); + if (!connectRes.success()) { + AndroidDeviceWidget::criticalDialog( + Tr::tr("Connecting to the device IP \"%1\" failed.").arg(ip), + parent); + return; + } + }); +} + AndroidDeviceWidget::AndroidDeviceWidget(const IDevice::Ptr &device) : IDeviceWidget(device) { @@ -168,7 +355,7 @@ AndroidDevice::AndroidDevice() addDeviceAction({Tr::tr("Refresh"), [](const IDevice::Ptr &device, QWidget *parent) { Q_UNUSED(parent) - AndroidDeviceManager::instance()->updateDeviceState(device); + updateDeviceState(device); }}); } @@ -198,7 +385,7 @@ void AndroidDevice::addActionsIfNotFound() if (machineType() == Emulator) { if (!hasStartAction) { addDeviceAction({startAvdAction, [](const IDevice::Ptr &device, QWidget *parent) { - AndroidDeviceManager::instance()->startAvd(device, parent); + startAvd(device, parent); }}); } @@ -211,13 +398,13 @@ void AndroidDevice::addActionsIfNotFound() if (!hasAvdArgumentsAction) { addDeviceAction({avdArgumentsAction, [](const IDevice::Ptr &device, QWidget *parent) { Q_UNUSED(device) - AndroidDeviceManager::instance()->setEmulatorArguments(parent); + setEmulatorArguments(parent); }}); } } else if (machineType() == Hardware && !ipRegex.match(id().toString()).hasMatch()) { if (!hasSetupWifi) { addDeviceAction({setupWifi, [](const IDevice::Ptr &device, QWidget *parent) { - AndroidDeviceManager::instance()->setupWifiForDevice(device, parent); + setupWifiForDevice(device, parent); }}); } } @@ -318,8 +505,7 @@ QString AndroidDevice::serialNumber() const const QString serialNumber = extraData(Constants::AndroidSerialNumber).toString(); if (machineType() == Hardware) return serialNumber; - - return AndroidDeviceManager::instance()->getRunningAvdsSerialNumber(avdName()); + return getRunningAvdsSerialNumber(avdName()); } QString AndroidDevice::avdName() const @@ -419,31 +605,6 @@ void AndroidDeviceManager::updateAvdList() m_avdListRunner.start(m_avdListRecipe); } -IDevice::DeviceState AndroidDeviceManager::getDeviceState(const QString &serial, - IDevice::MachineType type) const -{ - const QStringList args = AndroidDeviceInfo::adbSelector(serial) << "shell" << "echo 1"; - const SdkToolResult result = AndroidManager::runAdbCommand(args); - if (result.success()) - return IDevice::DeviceReadyToUse; - else if (type == IDevice::Emulator || result.stdErr().contains("unauthorized")) - return IDevice::DeviceConnected; - - return IDevice::DeviceDisconnected; -} - -void AndroidDeviceManager::updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device) -{ - const AndroidDevice *dev = static_cast(device.get()); - const QString serial = dev->serialNumber(); - DeviceManager *const devMgr = DeviceManager::instance(); - const Id id = dev->id(); - if (!serial.isEmpty()) - devMgr->setDeviceState(id, getDeviceState(serial, dev->machineType())); - else if (dev->machineType() == IDevice::Emulator) - devMgr->setDeviceState(id, IDevice::DeviceConnected); -} - expected_str AndroidDeviceManager::createAvd(const CreateAvdInfo &info, bool force) { CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name}); @@ -488,23 +649,6 @@ expected_str AndroidDeviceManager::createAvd(const CreateAvdInfo &info, bo return {}; } -void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent) -{ - Q_UNUSED(parent) - const AndroidDevice *androidDev = static_cast(device.get()); - const QString name = androidDev->avdName(); - qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name)); - auto future = Utils::asyncRun([name, device] { - const QString serialNumber = AndroidAvdManager::startAvd(name); - // Mark the AVD as ReadyToUse once we know it's started - if (!serialNumber.isEmpty()) { - DeviceManager *const devMgr = DeviceManager::instance(); - devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse); - } - }); - // TODO: use future! -} - void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent) { if (!device) @@ -541,280 +685,7 @@ void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent) m_removeAvdProcess->start(); } -void AndroidDeviceManager::setupWifiForDevice(const IDevice::Ptr &device, QWidget *parent) -{ - if (device->deviceState() != IDevice::DeviceReadyToUse) { - AndroidDeviceWidget::infoDialog( - Tr::tr("The device has to be connected with ADB debugging " - "enabled to use this feature."), parent); - return; - } - - const auto androidDev = static_cast(device.get()); - const QStringList adbSelector = AndroidDeviceInfo::adbSelector(androidDev->serialNumber()); - // prepare port - QStringList args = adbSelector; - args.append({"tcpip", wifiDevicePort}); - const SdkToolResult result = AndroidManager::runAdbCommand(args); - if (!result.success()) { - AndroidDeviceWidget::criticalDialog( - Tr::tr("Opening connection port %1 failed.").arg(wifiDevicePort), - parent); - return; - } - - QTimer::singleShot(2000, parent, [adbSelector, parent] { - // Get device IP address - QStringList args = adbSelector; - args.append({"shell", "ip", "route"}); - const SdkToolResult ipRes = AndroidManager::runAdbCommand(args); - if (!ipRes.success()) { - AndroidDeviceWidget::criticalDialog( - Tr::tr("Retrieving the device IP address failed."), parent); - return; - } - - // Expected output from "ip route" is: - // 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.190 - // where the ip of interest is at the end of the line - const QStringList ipParts = ipRes.stdOut().split(" "); - QString ip; - if (!ipParts.isEmpty()) { - ip = ipParts.last(); - } - if (!ipRegex.match(ipParts.last()).hasMatch()) { - AndroidDeviceWidget::criticalDialog( - Tr::tr("The retrieved IP address is invalid."), parent); - return; - } - - // Connect to device - args = adbSelector; - args.append({"connect", QString("%1:%2").arg(ip).arg(wifiDevicePort)}); - const SdkToolResult connectRes = AndroidManager::runAdbCommand(args); - if (!connectRes.success()) { - AndroidDeviceWidget::criticalDialog( - Tr::tr("Connecting to the device IP \"%1\" failed.").arg(ip), - parent); - return; - } - }); -} - -QString AndroidDeviceManager::emulatorName(const QString &serialNumber) const -{ - QStringList args = AndroidDeviceInfo::adbSelector(serialNumber); - args.append({"emu", "avd", "name"}); - return AndroidManager::runAdbCommand(args).stdOut(); -} - -void AndroidDeviceManager::setEmulatorArguments(QWidget *parent) -{ - const QString helpUrl = - "https://developer.android.com/studio/run/emulator-commandline#startup-options"; - - QInputDialog dialog(parent ? parent : Core::ICore::dialogParent()); - dialog.setWindowTitle(Tr::tr("Emulator Command-line Startup Options")); - dialog.setLabelText(Tr::tr("Emulator command-line startup options " - "(Help Web Page):") - .arg(helpUrl)); - dialog.setTextValue(AndroidConfig::emulatorArgs()); - - if (auto label = dialog.findChild()) { - label->setOpenExternalLinks(true); - label->setMinimumWidth(500); - } - - if (dialog.exec() != QDialog::Accepted) - return; - - AndroidConfig::setEmulatorArgs(dialog.textValue()); -} - -QString AndroidDeviceManager::getRunningAvdsSerialNumber(const QString &name) const -{ - Process adbProcess; - adbProcess.setCommand({AndroidConfig::adbToolPath(), {"devices"}}); - adbProcess.runBlocking(); - if (adbProcess.result() != ProcessResult::FinishedWithSuccess) - return {}; - - // mid(1) - remove "List of devices attached" header line - const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); - for (const QString &line : lines) { - // skip the daemon logs - if (line.startsWith("* daemon")) - continue; - - const QString serialNumber = line.left(line.indexOf('\t')).trimmed(); - if (!serialNumber.startsWith("emulator")) - continue; - - const QString stdOut = emulatorName(serialNumber); - if (stdOut.isEmpty()) - continue; // Not an avd - - if (stdOut.left(stdOut.indexOf('\n')) == name) - return serialNumber; - } - return {}; -} - -static FilePath avdFilePath() -{ - QString avdEnvVar = qtcEnvironmentVariable("ANDROID_AVD_HOME"); - if (avdEnvVar.isEmpty()) { - avdEnvVar = qtcEnvironmentVariable("ANDROID_SDK_HOME"); - if (avdEnvVar.isEmpty()) - avdEnvVar = qtcEnvironmentVariable("HOME"); - avdEnvVar.append("/.android/avd"); - } - return FilePath::fromUserInput(avdEnvVar); -} - -void AndroidDeviceManager::setupDevicesWatcher() -{ - if (!AndroidConfig::adbToolPath().exists()) { - qCDebug(androidDeviceLog) << "Cannot start ADB device watcher" - << "because adb path does not exist."; - return; - } - - if (!m_adbDeviceWatcherProcess) - m_adbDeviceWatcherProcess.reset(new Process(this)); - - if (m_adbDeviceWatcherProcess->isRunning()) { - qCDebug(androidDeviceLog) << "ADB device watcher is already running."; - return; - } - - connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] { - if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) { - qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:" - << m_adbDeviceWatcherProcess->errorString(); - if (!m_adbDeviceWatcherProcess->isRunning()) { - qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now."; - QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start); - } - } - qCDebug(androidDeviceLog) << "ADB device watcher finished."; - }); - - m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) { - qCDebug(androidDeviceLog) << "ADB device watcher error" << error; }); - m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) { - handleDevicesListChange(output); - }); - - const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}}; - m_adbDeviceWatcherProcess->setCommand(command); - m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir()); - m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment()); - m_adbDeviceWatcherProcess->start(); - - // Setup AVD filesystem watcher to listen for changes when an avd is created/deleted, - // or started/stopped - m_avdFileSystemWatcher.addPath(avdFilePath().toString()); - connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] { - if (!m_avdPathGuard.isLocked()) - updateAvdList(); - }); - // Call initial update - updateAvdList(); -} - -IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &info) -{ - if (info.apiLevel < 0) { - qCWarning(androidDeviceLog) << "System image of the created AVD is nullptr"; - return IDevice::Ptr(); - } - AndroidDevice *dev = new AndroidDevice; - const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(info); - dev->setupId(IDevice::AutoDetected, deviceId); - dev->setMachineType(IDevice::Emulator); - dev->settings()->displayName.setValue(info.name); - dev->setDeviceState(IDevice::DeviceConnected); - dev->setAvdPath(avdFilePath() / (info.name + ".avd")); - dev->setExtraData(Constants::AndroidAvdName, info.name); - dev->setExtraData(Constants::AndroidCpuAbi, {info.abi}); - dev->setExtraData(Constants::AndroidSdk, info.apiLevel); - return IDevice::Ptr(dev); -} - -void AndroidDeviceManager::handleAvdListChange(const AndroidDeviceInfoList &avdList) -{ - DeviceManager *const devMgr = DeviceManager::instance(); - - QList existingAvds; - for (int i = 0; i < devMgr->deviceCount(); ++i) { - const IDevice::ConstPtr dev = devMgr->deviceAt(i); - const bool isEmulator = dev->machineType() == IDevice::Emulator; - if (isEmulator && dev->type() == Constants::ANDROID_DEVICE_TYPE) - existingAvds.append(dev->id()); - } - - QList connectedDevs; - for (const AndroidDeviceInfo &item : avdList) { - const Id deviceId = AndroidDevice::idFromDeviceInfo(item); - const QString displayName = displayNameFromInfo(item); - IDevice::ConstPtr dev = devMgr->find(deviceId); - if (dev) { - const auto androidDev = static_cast(dev.get()); - // DeviceManager doens't seem to have a way to directly update the name, if the name - // of the device has changed, remove it and register it again with the new name. - // Also account for the case of an AVD registered through old QC which might have - // invalid data by checking if the avdPath is not empty. - if (dev->displayName() != displayName || androidDev->avdPath().toString().isEmpty()) { - devMgr->removeDevice(dev->id()); - } else { - // Find the state of the AVD retrieved from the AVD watcher - const QString serial = getRunningAvdsSerialNumber(item.avdName); - if (!serial.isEmpty()) { - const IDevice::DeviceState state = getDeviceState(serial, IDevice::Emulator); - if (dev->deviceState() != state) { - devMgr->setDeviceState(dev->id(), state); - qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.", - dev->id().toString().toUtf8().data()); - } - } else { - devMgr->setDeviceState(dev->id(), IDevice::DeviceConnected); - } - connectedDevs.append(dev->id()); - continue; - } - } - - AndroidDevice *newDev = new AndroidDevice(); - newDev->setupId(IDevice::AutoDetected, deviceId); - newDev->settings()->displayName.setValue(displayName); - newDev->setMachineType(item.type); - newDev->setDeviceState(item.state); - - newDev->setExtraData(Constants::AndroidAvdName, item.avdName); - newDev->setExtraData(Constants::AndroidSerialNumber, item.serialNumber); - newDev->setExtraData(Constants::AndroidCpuAbi, item.cpuAbi); - newDev->setExtraData(Constants::AndroidSdk, item.sdk); - newDev->setAvdPath(item.avdPath); - - qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".", - newDev->id().toString().toUtf8().data()); - const IDevice::ConstPtr constNewDev = IDevice::ConstPtr(newDev); - devMgr->addDevice(IDevice::ConstPtr(constNewDev)); - connectedDevs.append(constNewDev->id()); - } - - // Set devices no longer connected to disconnected state. - for (const Id &id : existingAvds) { - if (!connectedDevs.contains(id)) { - qCDebug(androidDeviceLog, "Removing AVD id \"%s\" because it no longer exists.", - id.toString().toUtf8().data()); - devMgr->removeDevice(id); - } - } -} - -void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber) +static void handleDevicesListChange(const QString &serialNumber) { DeviceManager *const devMgr = DeviceManager::instance(); const QStringList serialBits = serialNumber.split('\t'); @@ -883,6 +754,57 @@ void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber) } } +void AndroidDeviceManager::setupDevicesWatcher() +{ + if (!AndroidConfig::adbToolPath().exists()) { + qCDebug(androidDeviceLog) << "Cannot start ADB device watcher" + << "because adb path does not exist."; + return; + } + + if (!m_adbDeviceWatcherProcess) + m_adbDeviceWatcherProcess.reset(new Process(this)); + + if (m_adbDeviceWatcherProcess->isRunning()) { + qCDebug(androidDeviceLog) << "ADB device watcher is already running."; + return; + } + + connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] { + if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) { + qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:" + << m_adbDeviceWatcherProcess->errorString(); + if (!m_adbDeviceWatcherProcess->isRunning()) { + qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now."; + QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start); + } + } + qCDebug(androidDeviceLog) << "ADB device watcher finished."; + }); + + m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) { + qCDebug(androidDeviceLog) << "ADB device watcher error" << error; }); + m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) { + handleDevicesListChange(output); + }); + + const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}}; + m_adbDeviceWatcherProcess->setCommand(command); + m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir()); + m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment()); + m_adbDeviceWatcherProcess->start(); + + // Setup AVD filesystem watcher to listen for changes when an avd is created/deleted, + // or started/stopped + m_avdFileSystemWatcher.addPath(avdFilePath().toString()); + connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] { + if (!m_avdPathGuard.isLocked()) + updateAvdList(); + }); + // Call initial update + updateAvdList(); +} + static AndroidDeviceManager *s_instance = nullptr; AndroidDeviceManager *AndroidDeviceManager::instance() @@ -918,6 +840,78 @@ static void modifyManufacturerTag(const FilePath &avdPath, TagModification modif saver.finalize(); } +static void handleAvdListChange(const AndroidDeviceInfoList &avdList) +{ + DeviceManager *const devMgr = DeviceManager::instance(); + + QList existingAvds; + for (int i = 0; i < devMgr->deviceCount(); ++i) { + const IDevice::ConstPtr dev = devMgr->deviceAt(i); + const bool isEmulator = dev->machineType() == IDevice::Emulator; + if (isEmulator && dev->type() == Constants::ANDROID_DEVICE_TYPE) + existingAvds.append(dev->id()); + } + + QList connectedDevs; + for (const AndroidDeviceInfo &item : avdList) { + const Id deviceId = AndroidDevice::idFromDeviceInfo(item); + const QString displayName = displayNameFromInfo(item); + IDevice::ConstPtr dev = devMgr->find(deviceId); + if (dev) { + const auto androidDev = static_cast(dev.get()); + // DeviceManager doens't seem to have a way to directly update the name, if the name + // of the device has changed, remove it and register it again with the new name. + // Also account for the case of an AVD registered through old QC which might have + // invalid data by checking if the avdPath is not empty. + if (dev->displayName() != displayName || androidDev->avdPath().toString().isEmpty()) { + devMgr->removeDevice(dev->id()); + } else { + // Find the state of the AVD retrieved from the AVD watcher + const QString serial = getRunningAvdsSerialNumber(item.avdName); + if (!serial.isEmpty()) { + const IDevice::DeviceState state = getDeviceState(serial, IDevice::Emulator); + if (dev->deviceState() != state) { + devMgr->setDeviceState(dev->id(), state); + qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.", + dev->id().toString().toUtf8().data()); + } + } else { + devMgr->setDeviceState(dev->id(), IDevice::DeviceConnected); + } + connectedDevs.append(dev->id()); + continue; + } + } + + AndroidDevice *newDev = new AndroidDevice; + newDev->setupId(IDevice::AutoDetected, deviceId); + newDev->settings()->displayName.setValue(displayName); + newDev->setMachineType(item.type); + newDev->setDeviceState(item.state); + + newDev->setExtraData(Constants::AndroidAvdName, item.avdName); + newDev->setExtraData(Constants::AndroidSerialNumber, item.serialNumber); + newDev->setExtraData(Constants::AndroidCpuAbi, item.cpuAbi); + newDev->setExtraData(Constants::AndroidSdk, item.sdk); + newDev->setAvdPath(item.avdPath); + + qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".", + newDev->id().toString().toUtf8().data()); + const IDevice::ConstPtr constNewDev = IDevice::ConstPtr(newDev); + devMgr->addDevice(IDevice::ConstPtr(constNewDev)); + connectedDevs.append(constNewDev->id()); + } + + // Set devices no longer connected to disconnected state. + for (const Id &id : existingAvds) { + if (!connectedDevs.contains(id)) { + qCDebug(androidDeviceLog, "Removing AVD id \"%s\" because it no longer exists.", + id.toString().toUtf8().data()); + devMgr->removeDevice(id); + } + } +} + AndroidDeviceManager::AndroidDeviceManager(QObject *parent) : QObject(parent) , m_avdListRecipe{} @@ -1003,7 +997,7 @@ public: if (dialog.exec() != QDialog::Accepted) return IDevice::Ptr(); - const IDevice::Ptr dev = AndroidDeviceManager::createDeviceFromInfo(dialog.avdInfo()); + const IDevice::Ptr dev = createDeviceFromInfo(dialog.avdInfo()); if (const auto androidDev = static_cast(dev.get())) { qCDebug(androidDeviceLog, "Created new Android AVD id \"%s\".", qPrintable(androidDev->avdName())); diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 789fa75ff12..7e2b4960f9f 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -76,29 +76,14 @@ public: static AndroidDeviceManager *instance(); void setupDevicesWatcher(); void updateAvdList(); - IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) const; - void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device); Utils::expected_str createAvd(const CreateAvdInfo &info, bool force); - void startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr); void eraseAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr); - void setupWifiForDevice(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr); - - void setEmulatorArguments(QWidget *parent = nullptr); - - QString getRunningAvdsSerialNumber(const QString &name) const; - - static ProjectExplorer::IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info); private: explicit AndroidDeviceManager(QObject *parent); ~AndroidDeviceManager(); - void handleDevicesListChange(const QString &serialNumber); - void handleAvdListChange(const AndroidDeviceInfoList &avdList); - - QString emulatorName(const QString &serialNumber) const; - Tasking::Group m_avdListRecipe; Tasking::TaskTreeRunner m_avdListRunner; std::unique_ptr m_removeAvdProcess; From f450610a6feb5b1e342b838a47aa203d5f86fd8a Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 22 May 2024 15:20:58 +0200 Subject: [PATCH 63/69] Android: Transform AndroidDeviceManager into namespace Change-Id: I92f5ea325dfcb2a11b0fde14b797c5d40bf47d0b Reviewed-by: Alessandro Portale --- src/plugins/android/androidconfigurations.cpp | 3 +- src/plugins/android/androiddevice.cpp | 338 +++++++++--------- src/plugins/android/androiddevice.h | 26 +- src/plugins/android/avddialog.cpp | 6 +- 4 files changed, 185 insertions(+), 188 deletions(-) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 86e1d57b234..fda6b646117 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -1542,8 +1542,7 @@ void AndroidConfigurations::updateAndroidDevice() IDevice::ConstPtr dev = devMgr->find(Constants::ANDROID_DEVICE_ID); if (dev) devMgr->removeDevice(dev->id()); - - AndroidDeviceManager::instance()->setupDevicesWatcher(); + AndroidDeviceManager::setupDevicesWatcher(); } #ifdef WITH_TESTS diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 1b69456bbd9..eeab8924d5a 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -39,6 +39,7 @@ #include using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; namespace { @@ -51,6 +52,30 @@ static constexpr char ipRegexStr[] = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} static const QRegularExpression ipRegex = QRegularExpression(ipRegexStr); static constexpr char wifiDevicePort[] = "5555"; +enum TagModification { CommentOut, Uncomment }; +static class AndroidDeviceManagerInstance *s_instance = nullptr; + +class AndroidDeviceManagerInstance : public QObject +{ +public: + AndroidDeviceManagerInstance(QObject *parent); + ~AndroidDeviceManagerInstance() + { + QTC_ASSERT(s_instance == this, return); + s_instance = nullptr; + } + + void setupDevicesWatcher(); + void eraseAvd(const IDevice::Ptr &device, QWidget *parent); + + Group m_avdListRecipe; + TaskTreeRunner m_avdListRunner; + std::unique_ptr m_removeAvdProcess; + QFileSystemWatcher m_avdFileSystemWatcher; + Guard m_avdPathGuard; + std::unique_ptr m_adbDeviceWatcherProcess; +}; + static QString displayNameFromInfo(const AndroidDeviceInfo &info) { return info.type == IDevice::Hardware ? AndroidConfig::getProductModel(info.serialNumber) @@ -68,7 +93,7 @@ static IDevice::DeviceState getDeviceState(const QString &serial, IDevice::Machi return IDevice::DeviceDisconnected; } -static void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device) +static void updateDeviceState(const IDevice::ConstPtr &device) { const AndroidDevice *dev = static_cast(device.get()); const QString serial = dev->serialNumber(); @@ -80,7 +105,7 @@ static void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device) devMgr->setDeviceState(id, IDevice::DeviceConnected); } -static void startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent) +static void startAvd(const IDevice::Ptr &device, QWidget *parent) { Q_UNUSED(parent) const AndroidDevice *androidDev = static_cast(device.get()); @@ -172,7 +197,7 @@ static IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info) return IDevice::Ptr(); } AndroidDevice *dev = new AndroidDevice; - const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(info); + const Id deviceId = AndroidDevice::idFromAvdInfo(info); dev->setupId(IDevice::AutoDetected, deviceId); dev->setMachineType(IDevice::Emulator); dev->settings()->displayName.setValue(info.name); @@ -187,7 +212,7 @@ static IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info) class AndroidDeviceWidget : public IDeviceWidget { public: - AndroidDeviceWidget(const ProjectExplorer::IDevice::Ptr &device); + AndroidDeviceWidget(const IDevice::Ptr &device); void updateDeviceFromUi() final {} static QString dialogTitle(); @@ -391,7 +416,7 @@ void AndroidDevice::addActionsIfNotFound() if (!hasEraseAction) { addDeviceAction({eraseAvdAction, [](const IDevice::Ptr &device, QWidget *parent) { - AndroidDeviceManager::instance()->eraseAvd(device, parent); + s_instance->eraseAvd(device, parent); }}); } @@ -434,7 +459,6 @@ AndroidDeviceInfo AndroidDevice::androidDeviceInfoFromIDevice(const IDevice *dev info.avdPath = FilePath::fromSettings(dev->extraData(Constants::AndroidAvdPath)); info.sdk = dev->extraData(Constants::AndroidSdk).toInt(); info.type = dev->machineType(); - return info; } @@ -446,7 +470,7 @@ Id AndroidDevice::idFromDeviceInfo(const AndroidDeviceInfo &info) Id AndroidDevice::idFromAvdInfo(const CreateAvdInfo &info) { - return Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name); + return Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name); } QStringList AndroidDevice::supportedAbis() const @@ -599,92 +623,6 @@ void AndroidDevice::initAvdSettings() m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat)); } -void AndroidDeviceManager::updateAvdList() -{ - if (AndroidConfig::adbToolPath().exists()) - m_avdListRunner.start(m_avdListRecipe); -} - -expected_str AndroidDeviceManager::createAvd(const CreateAvdInfo &info, bool force) -{ - CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name}); - cmd.addArgs({"-k", info.sdkStylePath}); - if (info.sdcardSize > 0) - cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)}); - - const QString deviceDef = info.deviceDefinition; - if (!deviceDef.isEmpty() && deviceDef != "Custom") - cmd.addArgs({"-d", deviceDef}); - - if (force) - cmd.addArg("-f"); - - Process process; - process.setProcessMode(ProcessMode::Writer); - process.setEnvironment(AndroidConfig::toolsEnvironment()); - process.setCommand(cmd); - process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile" - - QByteArray buffer; - QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] { - // This interaction is needed only if there is no "-d" arg for the avdmanager command. - buffer += process.readAllRawStandardOutput(); - if (buffer.endsWith(QByteArray("]:"))) { - // truncate to last line - const int index = buffer.lastIndexOf('\n'); - if (index != -1) - buffer = buffer.mid(index); - if (buffer.contains("hw.gpu.enabled")) - process.write("yes\n"); - else - process.write("\n"); - buffer.clear(); - } - }); - - GuardLocker locker(m_avdPathGuard); - process.runBlocking(); - if (process.result() != ProcessResult::FinishedWithSuccess) - return Utils::make_unexpected(process.exitMessage()); - return {}; -} - -void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent) -{ - if (!device) - return; - - if (device->machineType() == IDevice::Hardware) - return; - - const QString name = static_cast(device.get())->avdName(); - const QString question - = Tr::tr("Erase the Android AVD \"%1\"?\nThis cannot be undone.").arg(name); - if (!AndroidDeviceWidget::questionDialog(question, parent)) - return; - - qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name); - m_removeAvdProcess.reset(new Process); - const CommandLine command(AndroidConfig::avdManagerToolPath(), {"delete", "avd", "-n", name}); - qCDebug(androidDeviceLog).noquote() << "Running command (removeAvd):" << command.toUserOutput(); - m_removeAvdProcess->setEnvironment(AndroidConfig::toolsEnvironment()); - m_removeAvdProcess->setCommand(command); - connect(m_removeAvdProcess.get(), &Process::done, this, [this, device] { - const QString name = device->displayName(); - if (m_removeAvdProcess->result() == ProcessResult::FinishedWithSuccess) { - qCDebug(androidDeviceLog, "Android AVD id \"%s\" removed from the system.", - qPrintable(name)); - // Remove the device from QtC after it's been removed using avdmanager. - DeviceManager::instance()->removeDevice(device->id()); - } else { - AndroidDeviceWidget::criticalDialog(Tr::tr("An error occurred while removing the " - "Android AVD \"%1\" using avdmanager tool.").arg(name)); - } - m_removeAvdProcess.release()->deleteLater(); - }); - m_removeAvdProcess->start(); -} - static void handleDevicesListChange(const QString &serialNumber) { DeviceManager *const devMgr = DeviceManager::instance(); @@ -754,66 +692,6 @@ static void handleDevicesListChange(const QString &serialNumber) } } -void AndroidDeviceManager::setupDevicesWatcher() -{ - if (!AndroidConfig::adbToolPath().exists()) { - qCDebug(androidDeviceLog) << "Cannot start ADB device watcher" - << "because adb path does not exist."; - return; - } - - if (!m_adbDeviceWatcherProcess) - m_adbDeviceWatcherProcess.reset(new Process(this)); - - if (m_adbDeviceWatcherProcess->isRunning()) { - qCDebug(androidDeviceLog) << "ADB device watcher is already running."; - return; - } - - connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] { - if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) { - qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:" - << m_adbDeviceWatcherProcess->errorString(); - if (!m_adbDeviceWatcherProcess->isRunning()) { - qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now."; - QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start); - } - } - qCDebug(androidDeviceLog) << "ADB device watcher finished."; - }); - - m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) { - qCDebug(androidDeviceLog) << "ADB device watcher error" << error; }); - m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) { - handleDevicesListChange(output); - }); - - const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}}; - m_adbDeviceWatcherProcess->setCommand(command); - m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir()); - m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment()); - m_adbDeviceWatcherProcess->start(); - - // Setup AVD filesystem watcher to listen for changes when an avd is created/deleted, - // or started/stopped - m_avdFileSystemWatcher.addPath(avdFilePath().toString()); - connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] { - if (!m_avdPathGuard.isLocked()) - updateAvdList(); - }); - // Call initial update - updateAvdList(); -} - -static AndroidDeviceManager *s_instance = nullptr; - -AndroidDeviceManager *AndroidDeviceManager::instance() -{ - return s_instance; -} - -enum TagModification { CommentOut, Uncomment }; - static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification) { if (!avdPath.exists()) @@ -912,15 +790,13 @@ static void handleAvdListChange(const AndroidDeviceInfoList &avdList) } } -AndroidDeviceManager::AndroidDeviceManager(QObject *parent) +AndroidDeviceManagerInstance::AndroidDeviceManagerInstance(QObject *parent) : QObject(parent) , m_avdListRecipe{} { QTC_ASSERT(!s_instance, return); s_instance = this; - using namespace Tasking; - const Storage storage; const LoopUntil iterator([storage](int iteration) { @@ -933,7 +809,7 @@ AndroidDeviceManager::AndroidDeviceManager(QObject *parent) process.setEnvironment(AndroidConfig::toolsEnvironment()); process.setCommand(cmd); }; - const auto onProcessDone = [this, storage](const Process &process, DoneWith result) { + const auto onProcessDone = [storage](const Process &process, DoneWith result) { const QString output = process.allOutput(); if (result != DoneWith::Success) { qCDebug(androidDeviceLog) @@ -969,15 +845,153 @@ AndroidDeviceManager::AndroidDeviceManager(QObject *parent) }; } -AndroidDeviceManager::~AndroidDeviceManager() +void AndroidDeviceManagerInstance::setupDevicesWatcher() { - QTC_ASSERT(s_instance == this, return); - s_instance = nullptr; + if (!AndroidConfig::adbToolPath().exists()) { + qCDebug(androidDeviceLog) << "Cannot start ADB device watcher" + << "because adb path does not exist."; + return; + } + + if (!m_adbDeviceWatcherProcess) + m_adbDeviceWatcherProcess.reset(new Process(this)); + + if (m_adbDeviceWatcherProcess->isRunning()) { + qCDebug(androidDeviceLog) << "ADB device watcher is already running."; + return; + } + + connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] { + if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) { + qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:" + << m_adbDeviceWatcherProcess->errorString(); + if (!m_adbDeviceWatcherProcess->isRunning()) { + qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now."; + QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start); + } + } + qCDebug(androidDeviceLog) << "ADB device watcher finished."; + }); + + m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) { + qCDebug(androidDeviceLog) << "ADB device watcher error" << error; }); + m_adbDeviceWatcherProcess->setStdOutLineCallback([](const QString &output) { + handleDevicesListChange(output); + }); + + const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}}; + m_adbDeviceWatcherProcess->setCommand(command); + m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir()); + m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment()); + m_adbDeviceWatcherProcess->start(); + + // Setup AVD filesystem watcher to listen for changes when an avd is created/deleted, + // or started/stopped + m_avdFileSystemWatcher.addPath(avdFilePath().toString()); + connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] { + if (!m_avdPathGuard.isLocked()) + AndroidDeviceManager::updateAvdList(); + }); + // Call initial update + AndroidDeviceManager::updateAvdList(); } +void AndroidDeviceManagerInstance::eraseAvd(const IDevice::Ptr &device, QWidget *parent) +{ + if (!device) + return; + + if (device->machineType() == IDevice::Hardware) + return; + + const QString name = static_cast(device.get())->avdName(); + const QString question + = Tr::tr("Erase the Android AVD \"%1\"?\nThis cannot be undone.").arg(name); + if (!AndroidDeviceWidget::questionDialog(question, parent)) + return; + + qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name); + m_removeAvdProcess.reset(new Process); + const CommandLine command(AndroidConfig::avdManagerToolPath(), {"delete", "avd", "-n", name}); + qCDebug(androidDeviceLog).noquote() << "Running command (removeAvd):" << command.toUserOutput(); + m_removeAvdProcess->setEnvironment(AndroidConfig::toolsEnvironment()); + m_removeAvdProcess->setCommand(command); + connect(m_removeAvdProcess.get(), &Process::done, this, [this, device] { + const QString name = device->displayName(); + if (m_removeAvdProcess->result() == ProcessResult::FinishedWithSuccess) { + qCDebug(androidDeviceLog, "Android AVD id \"%s\" removed from the system.", + qPrintable(name)); + // Remove the device from QtC after it's been removed using avdmanager. + DeviceManager::instance()->removeDevice(device->id()); + } else { + AndroidDeviceWidget::criticalDialog(Tr::tr("An error occurred while removing the " + "Android AVD \"%1\" using avdmanager tool.").arg(name)); + } + m_removeAvdProcess.release()->deleteLater(); + }); + m_removeAvdProcess->start(); +} + +namespace AndroidDeviceManager { + +void setupDevicesWatcher() { s_instance->setupDevicesWatcher(); } + +void updateAvdList() +{ + if (AndroidConfig::adbToolPath().exists()) + s_instance->m_avdListRunner.start(s_instance->m_avdListRecipe); +} + +expected_str createAvd(const CreateAvdInfo &info, bool force) +{ + CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name}); + cmd.addArgs({"-k", info.sdkStylePath}); + if (info.sdcardSize > 0) + cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)}); + + const QString deviceDef = info.deviceDefinition; + if (!deviceDef.isEmpty() && deviceDef != "Custom") + cmd.addArgs({"-d", deviceDef}); + + if (force) + cmd.addArg("-f"); + + Process process; + process.setProcessMode(ProcessMode::Writer); + process.setEnvironment(AndroidConfig::toolsEnvironment()); + process.setCommand(cmd); + process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile" + + QByteArray buffer; + QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] { + // This interaction is needed only if there is no "-d" arg for the avdmanager command. + buffer += process.readAllRawStandardOutput(); + if (buffer.endsWith(QByteArray("]:"))) { + // truncate to last line + const int index = buffer.lastIndexOf('\n'); + if (index != -1) + buffer = buffer.mid(index); + if (buffer.contains("hw.gpu.enabled")) + process.write("yes\n"); + else + process.write("\n"); + buffer.clear(); + } + }); + + GuardLocker locker(s_instance->m_avdPathGuard); + process.runBlocking(); + if (process.result() != ProcessResult::FinishedWithSuccess) + return Utils::make_unexpected(process.exitMessage()); + return {}; +} + + +} // namespace AndroidDeviceManager + // Factory -class AndroidDeviceFactory final : public ProjectExplorer::IDeviceFactory +class AndroidDeviceFactory final : public IDeviceFactory { public: AndroidDeviceFactory() @@ -1017,7 +1031,7 @@ void setupAndroidDevice() void setupAndroidDeviceManager(QObject *guard) { - (void) new AndroidDeviceManager(guard); + (void) new AndroidDeviceManagerInstance(guard); } } // Android::Internal diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 7e2b4960f9f..fbb7e0bccb6 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -70,29 +70,13 @@ private: std::unique_ptr m_avdSettings; }; -class AndroidDeviceManager : public QObject -{ -public: - static AndroidDeviceManager *instance(); - void setupDevicesWatcher(); - void updateAvdList(); +namespace AndroidDeviceManager { - Utils::expected_str createAvd(const CreateAvdInfo &info, bool force); - void eraseAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr); +void setupDevicesWatcher(); +void updateAvdList(); +Utils::expected_str createAvd(const CreateAvdInfo &info, bool force); -private: - explicit AndroidDeviceManager(QObject *parent); - ~AndroidDeviceManager(); - - Tasking::Group m_avdListRecipe; - Tasking::TaskTreeRunner m_avdListRunner; - std::unique_ptr m_removeAvdProcess; - QFileSystemWatcher m_avdFileSystemWatcher; - Utils::Guard m_avdPathGuard; - std::unique_ptr m_adbDeviceWatcherProcess; - - friend void setupAndroidDeviceManager(QObject *guard); -}; +} // namespace AndroidDeviceManager void setupAndroidDevice(); void setupAndroidDeviceManager(QObject *guard); diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index a6c08ffb054..4ae894a1920 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -131,15 +131,15 @@ int AvdDialog::exec() const CreateAvdInfo avdInfo{si->sdkStylePath(), si->apiLevel(), name(), abi(), deviceDefinition(), sdcardSize()}; - const auto result = AndroidDeviceManager::instance()->createAvd( - avdInfo, m_overwriteCheckBox->isChecked()); + const auto result = AndroidDeviceManager::createAvd(avdInfo, + m_overwriteCheckBox->isChecked()); if (!result) { QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Create new AVD"), result.error()); return QDialog::Rejected; } m_createdAvdInfo = avdInfo; - AndroidDeviceManager::instance()->updateAvdList(); + AndroidDeviceManager::updateAvdList(); } return execResult; } From c40dceced70b657d5d1cc611011c7a3b731d0480 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Wed, 29 May 2024 12:35:23 +0200 Subject: [PATCH 64/69] Android: Introduce devicesCommandOutput() helper and reuse it Change-Id: I098aa17328efcf66a3fb80416e65a2f82d190edc Reviewed-by: Alessandro Portale --- src/plugins/android/androidavdmanager.cpp | 9 +-------- src/plugins/android/androidconfigurations.cpp | 11 ++++++++--- src/plugins/android/androidconfigurations.h | 2 ++ src/plugins/android/androiddevice.cpp | 9 +-------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index ded1c5fe160..3b5c6ccf916 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -89,14 +89,7 @@ bool startAvdAsync(const QString &avdName) QString findAvd(const QString &avdName) { - Process adbProcess; - adbProcess.setCommand({AndroidConfig::adbToolPath(), {"devices"}}); - adbProcess.runBlocking(); - if (adbProcess.result() != ProcessResult::FinishedWithSuccess) - return {}; - - // mid(1) - remove "List of devices attached" header line - const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + const QStringList lines = AndroidConfig::devicesCommandOutput(); for (const QString &line : lines) { // skip the daemon logs if (line.startsWith("* daemon")) diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index fda6b646117..feadec6111e 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -674,17 +674,22 @@ FilePath keytoolPath() return openJDKBinPath().pathAppended(keytoolName).withExecutableSuffix(); } -bool isConnected(const QString &serialNumber) +QStringList devicesCommandOutput() { Process adbProcess; adbProcess.setCommand({adbToolPath(), {"devices"}}); adbProcess.runBlocking(); if (adbProcess.result() != ProcessResult::FinishedWithSuccess) - return false; + return {}; // mid(1) - remove "List of devices attached" header line. // Example output: "List of devices attached\nemulator-5554\tdevice\n\n". - const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + return adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); +} + +bool isConnected(const QString &serialNumber) +{ + const QStringList lines = devicesCommandOutput(); for (const QString &line : lines) { // skip the daemon logs if (!line.startsWith("* daemon") && line.left(line.indexOf('\t')).trimmed() == serialNumber) diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 5e50dab0cd0..81477b408a8 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -89,6 +89,8 @@ Utils::FilePath makePathFromNdk(const Utils::FilePath &ndkLocation); Utils::FilePath keytoolPath(); +QStringList devicesCommandOutput(); + QString bestNdkPlatformMatch(int target, const QtSupport::QtVersion *qtVersion); QLatin1String displayName(const ProjectExplorer::Abi &abi); diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index eeab8924d5a..7e6e715dc86 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -151,14 +151,7 @@ static QString emulatorName(const QString &serialNumber) static QString getRunningAvdsSerialNumber(const QString &name) { - Process adbProcess; - adbProcess.setCommand({AndroidConfig::adbToolPath(), {"devices"}}); - adbProcess.runBlocking(); - if (adbProcess.result() != ProcessResult::FinishedWithSuccess) - return {}; - - // mid(1) - remove "List of devices attached" header line - const QStringList lines = adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1); + const QStringList lines = AndroidConfig::devicesCommandOutput(); for (const QString &line : lines) { // skip the daemon logs if (line.startsWith("* daemon")) From 355b8e4d1a3b7dc4b69773781480673c3d9aa0fd Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 5 Jun 2024 17:58:59 +0200 Subject: [PATCH 65/69] Fix QIcon::pixmap() related deprecation warnings QPixmap pixmap(const QSize &size, qreal devicePixelRatio, Mode mode = Normal, State state = Off) const; QT_DEPRECATED_VERSION_X_6_0("Use pixmap(size, devicePixelRatio) instead") QPixmap pixmap(QWindow *window, const QSize &size, Mode mode = Normal, State state = Off) const; Change-Id: I8b08bec86014809887c1915b72c2776bae4a87fb Reviewed-by: Alessandro Portale --- src/libs/utils/historycompleter.cpp | 6 +++--- src/libs/utils/icon.cpp | 4 ++-- src/libs/utils/infolabel.cpp | 3 +-- src/libs/utils/stylehelper.cpp | 5 ++--- src/plugins/autotest/testresultdelegate.cpp | 9 ++++----- src/plugins/coreplugin/manhattanstyle.cpp | 5 ++--- .../components/navigator/iconcheckboxitemdelegate.cpp | 5 +---- 7 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/libs/utils/historycompleter.cpp b/src/libs/utils/historycompleter.cpp index 8eff3fdad82..8dc9ec3887e 100644 --- a/src/libs/utils/historycompleter.cpp +++ b/src/libs/utils/historycompleter.cpp @@ -54,11 +54,11 @@ public: optCopy.state |= QStyle::State_HasFocus; QItemDelegate::paint(painter,option,index); // add remove button - QWindow *window = view->window()->windowHandle(); - const QPixmap iconPixmap = icon.pixmap(window, option.rect.size()); + const qreal devicePixelRatio = painter->device()->devicePixelRatio(); + const QPixmap iconPixmap = icon.pixmap(option.rect.size(), devicePixelRatio); QRect pixmapRect = QStyle::alignedRect(option.direction, Qt::AlignRight | Qt::AlignVCenter, - iconPixmap.size() / window->devicePixelRatio(), + iconPixmap.size() / devicePixelRatio, option.rect); if (!clearIconSize.isValid()) clearIconSize = pixmapRect.size(); diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp index 132494e8252..df880ac67ec 100644 --- a/src/libs/utils/icon.cpp +++ b/src/libs/utils/icon.cpp @@ -221,11 +221,11 @@ QIcon Icon::modeIcon(const Icon &classic, const Icon &flat, const Icon &flatActi QIcon Icon::combinedIcon(const QList &icons) { QIcon result; - QWindow *window = QApplication::allWidgets().constFirst()->windowHandle(); + const qreal devicePixelRatio = QApplication::allWidgets().constFirst()->devicePixelRatio(); for (const QIcon &icon: icons) for (const QIcon::Mode mode: {QIcon::Disabled, QIcon::Normal}) for (const QSize &size: icon.availableSizes(mode)) - result.addPixmap(icon.pixmap(window, size, mode), mode); + result.addPixmap(icon.pixmap(size, devicePixelRatio, mode), mode); return result; } diff --git a/src/libs/utils/infolabel.cpp b/src/libs/utils/infolabel.cpp index 9eb9f801060..e05c093ca57 100644 --- a/src/libs/utils/infolabel.cpp +++ b/src/libs/utils/infolabel.cpp @@ -118,10 +118,9 @@ void InfoLabel::paintEvent(QPaintEvent *event) p.restore(); } const QIcon &icon = iconForType(m_type); - QWindow *window = this->window()->windowHandle(); const QIcon::Mode mode = !this->isEnabled() ? QIcon::Disabled : QIcon::Normal; const QPixmap iconPx = - icon.pixmap(window, QSize(iconSize, iconSize) * devicePixelRatio(), mode); + icon.pixmap(QSize(iconSize, iconSize) * devicePixelRatio(), devicePixelRatio(), mode); p.drawPixmap(iconRect, iconPx); ElidingLabel::paintEvent(event); } diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index 4472c43b558..080bb8a3820 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -551,8 +551,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect, // return a high-dpi pixmap, which will in that case have a devicePixelRatio // different than 1. The shadow drawing caluculations are done in device // pixels. - QWindow *window = dynamic_cast(p->device())->window()->windowHandle(); - QPixmap px = icon.pixmap(window, rect.size(), iconMode); + QPixmap px = icon.pixmap(rect.size(), devicePixelRatio, iconMode); int radius = int(dipRadius * devicePixelRatio); QPoint offset = dipOffset * devicePixelRatio; cache = QPixmap(px.size() + QSize(radius * 2, radius * 2)); @@ -563,7 +562,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect, const bool hasDisabledState = icon.availableSizes().count() == icon.availableSizes(QIcon::Disabled).count(); if (!hasDisabledState) - px = disabledSideBarIcon(icon.pixmap(window, rect.size())); + px = disabledSideBarIcon(icon.pixmap(rect.size(), devicePixelRatio)); } else if (creatorTheme()->flag(Theme::ToolBarIconShadow)) { // Draw shadow QImage tmp(px.size() + QSize(radius * 2, radius * 2 + 1), QImage::Format_ARGB32_Premultiplied); diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp index 14fe1f646f9..8c9f11b2c79 100644 --- a/src/plugins/autotest/testresultdelegate.cpp +++ b/src/plugins/autotest/testresultdelegate.cpp @@ -53,13 +53,12 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op const TestResult testResult = resultFilterModel->testResult(index); QTC_ASSERT(testResult.isValid(), painter->restore(); return); - const QWidget *widget = dynamic_cast(painter->device()); - QWindow *window = widget ? widget->window()->windowHandle() : nullptr; - QIcon icon = index.data(Qt::DecorationRole).value(); - if (!icon.isNull()) + if (!icon.isNull()) { painter->drawPixmap(positions.left(), positions.top(), - icon.pixmap(window, QSize(positions.iconSize(), positions.iconSize()))); + icon.pixmap(QSize(positions.iconSize(), positions.iconSize()), + painter->device()->devicePixelRatio())); + } TestResultItem *item = resultFilterModel->itemForIndex(index); QTC_ASSERT(item, painter->restore(); return); diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 390970eb84e..7a51c633782 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -563,16 +563,15 @@ static void drawPrimitiveTweakedForDarkTheme(QStyle::PrimitiveElement element, break; } case QStyle::PE_IndicatorTabClose: { - QWindow *window = widget ? widget->window()->windowHandle() : nullptr; + const qreal devicePixelRatio = painter->device()->devicePixelRatio(); QRect iconRect = QRect(0, 0, 16, 16); iconRect.moveCenter(option->rect.center()); const QIcon::Mode mode = !isEnabled ? QIcon::Disabled : QIcon::Normal; const static QIcon closeIcon = Utils::Icons::CLOSE_FOREGROUND.icon(); if (option->state & QStyle::State_MouseOver && widget) widget->style()->drawPrimitive(QStyle::PE_PanelButtonCommand, option, painter, widget); - const int devicePixelRatio = widget ? widget->devicePixelRatio() : 1; const QPixmap iconPx = - closeIcon.pixmap(window, iconRect.size() * devicePixelRatio, mode); + closeIcon.pixmap(iconRect.size() * devicePixelRatio, devicePixelRatio, mode); painter->drawPixmap(iconRect, iconPx); break; } diff --git a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp index 5b36bee7f97..09cf5945e82 100644 --- a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp @@ -79,15 +79,12 @@ void IconCheckboxItemDelegate::paint(QPainter *painter, if (rowIsPropertyRole(modelIndex.model(), modelIndex) || getModelNode(modelIndex).isRootNode()) return; // Do not paint icons for property rows or root node - QWindow *window = dynamic_cast(painter->device())->window()->windowHandle(); - QTC_ASSERT(window, return); - const QSize iconSize(16, 16); QPoint iconPosition(styleOption.rect.left() + (styleOption.rect.width() - iconSize.width()) / 2, styleOption.rect.top() + 2 + delegateMargin); const QIcon::State state = isChecked(modelIndex) ? QIcon::State::On : QIcon::State::Off; - const QPixmap iconPixmap = m_icon.pixmap(window, iconSize, mode, state); + const QPixmap iconPixmap = m_icon.pixmap(iconSize, painter->device()->devicePixelRatio(), mode, state); // Shift the lock icon (last column) slightly to the left due to vertical scrollbar width if (modelIndex.column() == NavigatorTreeModel::ColumnType::Lock) From 7996c5afac0dd7d14f931baf9886268e8029a973 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 15 May 2024 14:45:17 +0200 Subject: [PATCH 66/69] Editors: Improve the highlight of the current view Make the overlay widget as small as possible, paint it opaque, and paint only on the editor tool bar. Change-Id: If48f8f7c4dd221cb605548449f5bbb1a30c25a76 Reviewed-by: Alessandro Portale --- src/libs/utils/overlaywidget.cpp | 27 ++++++++++++++++--- src/libs/utils/overlaywidget.h | 10 ++++++- .../coreplugin/editormanager/editorview.cpp | 14 +++++----- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/libs/utils/overlaywidget.cpp b/src/libs/utils/overlaywidget.cpp index 720c5a90e18..950dc4b5dc3 100644 --- a/src/libs/utils/overlaywidget.cpp +++ b/src/libs/utils/overlaywidget.cpp @@ -8,16 +8,34 @@ #include #include +namespace Utils::Internal { +class OverlayWidgetPrivate +{ +public: + OverlayWidget::PaintFunction m_paint; + OverlayWidget::ResizeFunction m_resize; +}; +} // namespace Utils::Internal + Utils::OverlayWidget::OverlayWidget(QWidget *parent) + : d(new Internal::OverlayWidgetPrivate) { setAttribute(Qt::WA_TransparentForMouseEvents); if (parent) attachToWidget(parent); + d->m_resize = [](QWidget *w, const QSize &size) { w->setGeometry(QRect(QPoint(0, 0), size)); }; } +Utils::OverlayWidget::~OverlayWidget() = default; + void Utils::OverlayWidget::setPaintFunction(const Utils::OverlayWidget::PaintFunction &paint) { - m_paint = paint; + d->m_paint = paint; +} + +void Utils::OverlayWidget::setResizeFunction(const ResizeFunction &resize) +{ + d->m_resize = resize; } bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev) @@ -29,9 +47,9 @@ bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev) void Utils::OverlayWidget::paintEvent(QPaintEvent *ev) { - if (m_paint) { + if (d->m_paint) { QPainter p(this); - m_paint(this, p, ev); + d->m_paint(this, p, ev); } } @@ -50,5 +68,6 @@ void Utils::OverlayWidget::attachToWidget(QWidget *parent) void Utils::OverlayWidget::resizeToParent() { QTC_ASSERT(parentWidget(), return ); - setGeometry(QRect(QPoint(0, 0), parentWidget()->size())); + if (d->m_resize) + d->m_resize(this, parentWidget()->size()); } diff --git a/src/libs/utils/overlaywidget.h b/src/libs/utils/overlaywidget.h index 8bc5b7d1eb5..c5686ad171c 100644 --- a/src/libs/utils/overlaywidget.h +++ b/src/libs/utils/overlaywidget.h @@ -8,18 +8,26 @@ #include #include +#include namespace Utils { +namespace Internal { +class OverlayWidgetPrivate; +} + class QTCREATOR_UTILS_EXPORT OverlayWidget : public QWidget { public: using PaintFunction = std::function; + using ResizeFunction = std::function; explicit OverlayWidget(QWidget *parent = nullptr); + ~OverlayWidget(); void attachToWidget(QWidget *parent); void setPaintFunction(const PaintFunction &paint); + void setResizeFunction(const ResizeFunction &resize); protected: bool eventFilter(QObject *obj, QEvent *ev) override; @@ -28,7 +36,7 @@ protected: private: void resizeToParent(); - PaintFunction m_paint; + std::unique_ptr d; }; } // namespace Utils diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp index fd9d375d81d..42ad07d2498 100644 --- a/src/plugins/coreplugin/editormanager/editorview.cpp +++ b/src/plugins/coreplugin/editormanager/editorview.cpp @@ -133,13 +133,13 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent) auto currentViewOverlay = new OverlayWidget; currentViewOverlay->attachToWidget(this); - currentViewOverlay->setPaintFunction([this](QWidget *w, QPainter &p, QPaintEvent *) { - const int width = 2; - const QPoint margin{0, width}; - p.setPen({w->palette().color(QPalette::Highlight), width}); - p.drawLine( - m_toolBar->geometry().bottomLeft() + margin, - m_toolBar->geometry().bottomRight() + margin); + currentViewOverlay->setAttribute(Qt::WA_OpaquePaintEvent); + currentViewOverlay->setResizeFunction([this](QWidget *w, const QSize &) { + const QRect toolbarRect = m_toolBar->geometry(); + w->setGeometry(toolbarRect.x(), toolbarRect.bottom() - 1, toolbarRect.width(), 2); + }); + currentViewOverlay->setPaintFunction([](QWidget *w, QPainter &p, QPaintEvent *) { + p.fillRect(w->rect(), w->palette().color(QPalette::Highlight)); }); currentViewOverlay->setVisible(false); const auto updateCurrentViewOverlay = [this, currentViewOverlay] { From b0e1ea7da772bfd84d3341f75a996aa1a0ff50bf Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 5 Jun 2024 14:58:21 +0200 Subject: [PATCH 67/69] Fix recent projects menu directly after startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We missed to initialize the recent projects menu when initializing. Amends 77d7106b3a889079cc8338d8d6474830799e8f3d Change-Id: Icd394d8500c5853a6195ab1868e20caed3f5f323 Reviewed-by: André Hartmann --- src/plugins/projectexplorer/projectexplorer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index c3d827eb28c..3b1f3f19841 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -796,6 +796,7 @@ static void restoreRecentProjects(QtcSettings *s) {FilePath::fromUserInput(filePaths.at(i)), displayNames.at(i), exists}); } } + dd->updateRecentProjectMenu(); dd->checkRecentProjectsAsync(); } From 42eb44f622af20137e828ddf074be607bce77218 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 5 Jun 2024 16:03:59 +0200 Subject: [PATCH 68/69] ProjectExplorer: Workspace projects were missing from recent projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we checked for validity of recent projects, we used `isFile`, but the path that is used for opening a workspace project is a directory. Change-Id: Ie22c13c06c22d4584d1e198d5ab133025ea536c8 Reviewed-by: hjk Reviewed-by: André Hartmann --- src/plugins/projectexplorer/projectexplorer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 3b1f3f19841..b9da6f33a87 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -2185,7 +2185,7 @@ void ProjectExplorerPluginPrivate::checkRecentProjectsAsync() m_recentProjectsFuture = QtConcurrent::mapped(&m_recentProjectsPool, m_recentProjects, [](RecentProjectsEntry p) { // check if project is available, but avoid querying devices - p.exists = p.filePath.needsDevice() || p.filePath.isFile(); + p.exists = p.filePath.needsDevice() || p.filePath.exists(); return p; }); Utils::futureSynchronizer()->addFuture(m_recentProjectsFuture); From 15056578eb8a405332be03cec4ee480abc8e9088 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Wed, 5 Jun 2024 15:52:28 +0200 Subject: [PATCH 69/69] Shared: Add json file to qbs build as well Amends 32322801f564eb5debafa1a343bd81c48308f35a. Change-Id: I1078f532598e98ce4f12f28cad1ef512f27be52b Reviewed-by: Marcus Tillmanns --- share/share.qbs | 1 + 1 file changed, 1 insertion(+) diff --git a/share/share.qbs b/share/share.qbs index a2114dad2d2..2a1c8d8fce4 100644 --- a/share/share.qbs +++ b/share/share.qbs @@ -16,6 +16,7 @@ Product { "debugger/**/*", "designer/**/*", "glsl/**/*", + "jsonschemas/**/*", "modeleditor/**/*", "qml/**/*", "qmldesigner/**/*",