From dc99f5ad6fb12b855fc5bf88cfb280b828aa8bb7 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 2 Jun 2023 16:43:48 +0200 Subject: [PATCH 01/57] Doc: Describe dragging and pinning the progress bar Task-number: QTCREATORBUG-28996 Change-Id: Iba9eb4b1f53645763c637221b9f11c3ff54d2855 Reviewed-by: Eike Ziller --- .../src/projects/creator-only/creator-projects-opening.qdoc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc index d06989e66fc..85d04903921 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-opening.qdoc @@ -99,4 +99,7 @@ \image qtcreator-toggle-progress-bar.webp {Toggle Progress Details button} + You can drag the progress bar to another position. The position is saved for + later. Select the \inlineimage icons/pin.png + (\uicontrol Pin) button to pin the progress bar back to the toggle button. */ From e92a131644a1082b2e6cef832167ad7a81c444bc Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Fri, 2 Jun 2023 17:10:58 +0300 Subject: [PATCH 02/57] Terminal: Fix MSVC warning warning C4267: 'argument': conversion from 'size_t' to 'int', possible loss of data Change-Id: I5d57843eba79f0eb023ea7e3c4149aa515430189 Reviewed-by: Marcus Tillmanns Reviewed-by: Qt CI Patch Build Bot --- src/plugins/terminal/terminalwidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 5027d390294..12a0521857c 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -72,7 +72,7 @@ static constexpr std::chrono::milliseconds minRefreshInterval = 1s / 30; TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters) : QAbstractScrollArea(parent) - , m_context(Utils::Id("TerminalWidget_").withSuffix((size_t) this)) + , m_context(Utils::Id("TerminalWidget_").withSuffix(QString::number((uintptr_t) this))) , m_openParameters(openParameters) , m_lastFlush(std::chrono::system_clock::now()) , m_lastDoubleClick(std::chrono::system_clock::now()) From 980a95f7d6ae8ee53831aa4c8c9a7f517299ff21 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 5 Jun 2023 09:39:32 +0200 Subject: [PATCH 03/57] TextEditor: Fix missing include Amends 8da3575d7290be5002f06a363a622fd4ba70b803. Change-Id: I5696d83731cd25cc3abc58d7dc5359859918ac8e Reviewed-by: Orgad Shaneh --- src/plugins/texteditor/basefilefind.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 99f37d05eac..285d42fd3d9 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include From 8b3f246285bd022b1a87673997ba571892c30da1 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 5 Jun 2023 08:03:11 +0200 Subject: [PATCH 04/57] AutoTest: Tweak postponing parse once more Postpone the parsing also if the build system is waiting for a parse. Change-Id: I56aaecfca05ea28a21c13747847b518d1eb8732f Reviewed-by: David Schulz --- src/plugins/autotest/testcodeparser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index 8553702432f..66bbb256059 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -35,7 +35,7 @@ using namespace ProjectExplorer; static bool isProjectParsing() { const BuildSystem *bs = ProjectManager::startupBuildSystem(); - return bs && bs->isParsing(); + return bs && (bs->isParsing() || bs->isWaitingForParse()); } TestCodeParser::TestCodeParser() From e376355b189ea0c9b33bbeb606dda403713a0091 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 12:44:28 +0200 Subject: [PATCH 05/57] CorePlugin: Reuse qScopeGuard instead of ExecuteOnDestruction Change-Id: I2b6f6319d82512fec9a407649d7514f93283770d Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Eike Ziller Reviewed-by: Qt CI Bot --- src/plugins/coreplugin/editormanager/editormanager.cpp | 6 ++---- src/plugins/coreplugin/loggingviewer.cpp | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index e7147e94cea..81262f5ebfe 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -70,6 +69,7 @@ #include #include #include +#include #include #include #include @@ -3325,9 +3325,7 @@ IEditor *EditorManager::openEditorWithContents(Id editorId, EditorManager::gotoOtherSplit(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); - Utils::ExecuteOnDestruction appRestoreCursor(&QApplication::restoreOverrideCursor); - Q_UNUSED(appRestoreCursor) - + const auto cleanup = qScopeGuard(&QApplication::restoreOverrideCursor); const QString title = makeTitleUnique(titlePattern); diff --git a/src/plugins/coreplugin/loggingviewer.cpp b/src/plugins/coreplugin/loggingviewer.cpp index a81ed5936db..63fba41a4df 100644 --- a/src/plugins/coreplugin/loggingviewer.cpp +++ b/src/plugins/coreplugin/loggingviewer.cpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -33,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -591,7 +591,7 @@ void LoggingViewManagerWidget::saveLoggingsToFile() const { // should we just let it continue without temporarily disabling? const bool enabled = m_manager->isEnabled(); - Utils::ExecuteOnDestruction exec([this, enabled] { m_manager->setEnabled(enabled); }); + const auto cleanup = qScopeGuard([this, enabled] { m_manager->setEnabled(enabled); }); if (enabled) m_manager->setEnabled(false); const Utils::FilePath fp = Utils::FileUtils::getSaveFilePath(ICore::dialogParent(), From b14c07eed60b90309b017a0f1f2b2a5de4e1d9e7 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 13:30:22 +0200 Subject: [PATCH 06/57] TextEditor: Reuse qScopeGuard instead of ExecuteOnDestruction Change-Id: I2ca7d4676bb4f34fbf59fd45bcd01d7857cb7e4e Reviewed-by: Qt CI Patch Build Bot Reviewed-by: David Schulz Reviewed-by: Qt CI Bot Reviewed-by: --- src/plugins/texteditor/basehoverhandler.cpp | 4 ++-- src/plugins/texteditor/codeassist/codeassistant.cpp | 6 +++--- src/plugins/texteditor/colorpreviewhoverhandler.cpp | 9 ++++----- src/plugins/texteditor/texteditor.cpp | 8 ++++---- 4 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/plugins/texteditor/basehoverhandler.cpp b/src/plugins/texteditor/basehoverhandler.cpp index 9bb3d97f7d0..203ecc36e20 100644 --- a/src/plugins/texteditor/basehoverhandler.cpp +++ b/src/plugins/texteditor/basehoverhandler.cpp @@ -4,10 +4,10 @@ #include "basehoverhandler.h" #include "texteditor.h" -#include #include #include +#include #include namespace TextEditor { @@ -121,7 +121,7 @@ void BaseHoverHandler::process(TextEditorWidget *widget, int pos, ReportPriority void BaseHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos, ReportPriority report) { - Utils::ExecuteOnDestruction reportPriority([this, report](){ report(priority()); }); + const auto cleanup = qScopeGuard([this, report] { report(priority()); }); QString tooltip = editorWidget->extraSelectionTooltip(pos); if (!tooltip.isEmpty()) diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index 430b7252cdf..0da6571da8f 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -18,13 +18,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include using namespace TextEditor::Internal; @@ -158,7 +158,7 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, bool isUpdate) { // make sure to cleanup old proposals if we cannot find a new assistant - Utils::ExecuteOnDestruction earlyReturnContextClear([this] { destroyContext(); }); + auto cleanup = qScopeGuard([this] { destroyContext(); }); if (isWaitingForProposal()) cancelCurrentRequest(); @@ -179,7 +179,7 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, QTC_ASSERT(assistInterface, return); // We got an assist provider and interface so no need to reset the current context anymore - earlyReturnContextClear.reset({}); + cleanup.dismiss(); m_assistKind = kind; m_requestProvider = provider; diff --git a/src/plugins/texteditor/colorpreviewhoverhandler.cpp b/src/plugins/texteditor/colorpreviewhoverhandler.cpp index 181ba28e96f..1a66392c550 100644 --- a/src/plugins/texteditor/colorpreviewhoverhandler.cpp +++ b/src/plugins/texteditor/colorpreviewhoverhandler.cpp @@ -5,12 +5,12 @@ #include "texteditor.h" #include -#include #include #include -#include #include +#include +#include #include using namespace Core; @@ -333,10 +333,9 @@ static QColor colorFromFuncAndArgs(const QString &func, const QStringList &args) } void ColorPreviewHoverHandler::identifyMatch(TextEditorWidget *editorWidget, - int pos, - ReportPriority report) + int pos, ReportPriority report) { - Utils::ExecuteOnDestruction reportPriority([this, report](){ report(priority()); }); + const auto cleanup = qScopeGuard([this, report] { report(priority()); }); if (editorWidget->extraSelectionTooltip(pos).isEmpty()) { const QTextBlock tb = editorWidget->document()->findBlock(pos); diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index ecb617aed70..2c8ff597015 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -50,7 +50,6 @@ #include #include #include -#include #include #include #include @@ -91,9 +90,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -1435,7 +1435,7 @@ void TextEditorWidgetPrivate::print(QPrinter *printer) return; doc = doc->clone(doc); - Utils::ExecuteOnDestruction docDeleter([doc]() { delete doc; }); + const auto cleanup = qScopeGuard([doc] { delete doc; }); QTextOption opt = doc->defaultTextOption(); opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); @@ -5284,7 +5284,7 @@ void TextEditorWidgetPrivate::paintTextMarks(QPainter &painter, const ExtraAreaP int yoffset = blockBoundingRect.top(); painter.save(); - Utils::ExecuteOnDestruction eod([&painter, size, yoffset, xoffset, overrideIcon]() { + const auto cleanup = qScopeGuard([&painter, size, yoffset, xoffset, overrideIcon] { if (!overrideIcon.isNull()) { const QRect r(xoffset, yoffset, size, size); overrideIcon.paint(&painter, r, Qt::AlignCenter); From 8d02d961278a3d0a31be758e43b211789f58527a Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Mon, 5 Jun 2023 09:13:03 +0200 Subject: [PATCH 07/57] Terminal: Fix esc button update Change-Id: I4862de654ab920faa46742b4a4433dff07ed0387 Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/terminal/terminalpane.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp index 7e9cae7539e..965e3ba651c 100644 --- a/src/plugins/terminal/terminalpane.cpp +++ b/src/plugins/terminal/terminalpane.cpp @@ -87,9 +87,10 @@ TerminalPane::TerminalPane(QObject *parent) updateEscButton(); - connect(m_escSettingButton, &QToolButton::toggled, this, [this] { + connect(m_escSettingButton, &QToolButton::toggled, this, [this, updateEscButton] { TerminalSettings::instance().sendEscapeToTerminal.setValue(m_escSettingButton->isChecked()); TerminalSettings::instance().writeSettings(ICore::settings()); + updateEscButton(); }); connect(&TerminalSettings::instance(), &TerminalSettings::applied, this, updateEscButton); From b7bd30aeb859b4ce271e1de7f027afe13208264d Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 09:36:39 +0200 Subject: [PATCH 08/57] Tasking: Introduce ConcurrentCall It substitutes Utils::Async, but is Utils independent. The ConcurrentCall class is prepared to work with TaskTree. Provide the task tree adapter for the ConcurrentCall class. Register the task inside the Tasking namespace under the ConcurrentCallTask name. This class introduces the dependency to Qt::Concurrent, otherwise Tasking namespace is independent on Qt::Concurrent. Possibly, may be added into Qt::Concurrent lib, as a wrapper around the QtConcurrent::run() call. Change-Id: I4511ff0430e78346aa6a4fae1a9d5370fdd08506 Reviewed-by: Qt CI Bot Reviewed-by: Marcus Tillmanns Reviewed-by: --- src/libs/solutions/tasking/CMakeLists.txt | 3 +- src/libs/solutions/tasking/barrier.h | 2 +- src/libs/solutions/tasking/concurrentcall.h | 100 ++++++++++++++++++++ src/libs/solutions/tasking/tasking.qbs | 3 +- 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 src/libs/solutions/tasking/concurrentcall.h diff --git a/src/libs/solutions/tasking/CMakeLists.txt b/src/libs/solutions/tasking/CMakeLists.txt index 5beed2fe5b4..52c994de8db 100644 --- a/src/libs/solutions/tasking/CMakeLists.txt +++ b/src/libs/solutions/tasking/CMakeLists.txt @@ -1,9 +1,10 @@ add_qtc_library(Tasking OBJECT # Never add dependencies to non-Qt libraries for this library - DEPENDS Qt::Core + DEPENDS Qt::Concurrent Qt::Core PUBLIC_DEFINES TASKING_LIBRARY SOURCES barrier.cpp barrier.h + concurrentcall.h tasking_global.h tasktree.cpp tasktree.h ) diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h index 6939da5b365..6f1afe39b09 100644 --- a/src/libs/solutions/tasking/barrier.h +++ b/src/libs/solutions/tasking/barrier.h @@ -34,7 +34,7 @@ private: int m_current = -1; }; -class TASKING_EXPORT BarrierTaskAdapter : public Tasking::TaskAdapter +class TASKING_EXPORT BarrierTaskAdapter : public TaskAdapter { public: BarrierTaskAdapter() { connect(task(), &Barrier::done, this, &TaskInterface::done); } diff --git a/src/libs/solutions/tasking/concurrentcall.h b/src/libs/solutions/tasking/concurrentcall.h new file mode 100644 index 00000000000..d7799159447 --- /dev/null +++ b/src/libs/solutions/tasking/concurrentcall.h @@ -0,0 +1,100 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include "tasking_global.h" + +#include "tasktree.h" + +#include + +namespace Tasking { + +// This class introduces the dependency to Qt::Concurrent, otherwise Tasking namespace +// is independent on Qt::Concurrent. +// Possibly, it could be placed inside Qt::Concurrent library, as a wrapper around +// QtConcurrent::run() call. + +template +class ConcurrentCall +{ + Q_DISABLE_COPY_MOVE(ConcurrentCall) + +public: + ConcurrentCall() = default; + template + void setConcurrentCallData(Function &&function, Args &&...args) + { + return wrapConcurrent(std::forward(function), std::forward(args)...); + } + void setThreadPool(QThreadPool *pool) { m_threadPool = pool; } + ResultType result() const + { + return m_future.resultCount() ? m_future.result() : ResultType(); + } + QFuture future() const { return m_future; } + +private: + template + void wrapConcurrent(Function &&function, Args &&...args) + { + m_startHandler = [=] { + if (m_threadPool) + return QtConcurrent::run(m_threadPool, function, args...); + return QtConcurrent::run(function, args...); + }; + } + + template + void wrapConcurrent(std::reference_wrapper &&wrapper, Args &&...args) + { + m_startHandler = [=] { + if (m_threadPool) { + return QtConcurrent::run(m_threadPool, + std::forward(wrapper.get()), args...); + } + return QtConcurrent::run(std::forward(wrapper.get()), args...); + }; + } + + template + friend class ConcurrentCallTaskAdapter; + + std::function()> m_startHandler; + QThreadPool *m_threadPool = nullptr; + QFuture m_future; +}; + +template +class ConcurrentCallTaskAdapter : public TaskAdapter> +{ +public: + ~ConcurrentCallTaskAdapter() { + if (m_watcher) { + m_watcher->cancel(); + m_watcher->waitForFinished(); + } + } + + void start() { + if (!this->task()->m_startHandler) { + emit this->done(false); // TODO: Add runtime assert + return; + } + m_watcher.reset(new QFutureWatcher); + this->connect(m_watcher.get(), &QFutureWatcherBase::finished, this, [this] { + emit this->done(!m_watcher->isCanceled()); + m_watcher.release()->deleteLater(); + }); + this->task()->m_future = this->task()->m_startHandler(); + m_watcher->setFuture(this->task()->m_future); + } + +private: + std::unique_ptr> m_watcher; +}; + +} // namespace Tasking + +TASKING_DECLARE_TEMPLATE_TASK(ConcurrentCallTask, Tasking::ConcurrentCallTaskAdapter); diff --git a/src/libs/solutions/tasking/tasking.qbs b/src/libs/solutions/tasking/tasking.qbs index 8697b9c009b..0e78ed74f0a 100644 --- a/src/libs/solutions/tasking/tasking.qbs +++ b/src/libs/solutions/tasking/tasking.qbs @@ -1,11 +1,12 @@ QtcLibrary { name: "Tasking" - Depends { name: "Qt"; submodules: ["core"] } + Depends { name: "Qt"; submodules: ["concurrent", "core"] } cpp.defines: base.concat("TASKING_LIBRARY") files: [ "barrier.cpp", "barrier.h", + "concurrentcall.h", "tasking_global.h", "tasktree.cpp", "tasktree.h", From ebc185b1bb3759bb8a11606bb98335533e414fe0 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 1 Jun 2023 09:54:03 +0200 Subject: [PATCH 09/57] QmlProjectManager: Use FilePathAspect for device QML viewer overide Cosmetical. Change-Id: I480fd3163b45c3b2a19b7636cb79d6cb9b166825 Reviewed-by: Qt CI Bot Reviewed-by: Tim Jenssen --- src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 1cce1650ce0..756a0707d4a 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -61,7 +61,7 @@ private: FilePath qmlRuntimeFilePath() const; void createQtVersionAspect(); - StringAspect *m_qmlViewerAspect = nullptr; + FilePathAspect *m_qmlViewerAspect = nullptr; QmlMainFileAspect *m_qmlMainFileAspect = nullptr; QmlMultiLanguageAspect *m_multiLanguageAspect = nullptr; SelectionAspect *m_qtversionAspect = nullptr; @@ -71,10 +71,9 @@ private: QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) : RunConfiguration(target, id) { - m_qmlViewerAspect = addAspect(); + m_qmlViewerAspect = addAspect(); m_qmlViewerAspect->setLabelText(Tr::tr("Override device QML viewer:")); m_qmlViewerAspect->setPlaceHolderText(qmlRuntimeFilePath().toUserOutput()); - m_qmlViewerAspect->setDisplayStyle(StringAspect::PathChooserDisplay); m_qmlViewerAspect->setHistoryCompleter("QmlProjectManager.viewer.history"); m_qmlViewerAspect->setSettingsKey(Constants::QML_VIEWER_KEY); From 7b3c2c13a1aa6b66f5d383804b556a66ca857950 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 6 Jun 2023 07:59:09 +0200 Subject: [PATCH 10/57] Terminal: Decode percent encoding Fixes: QTCREATORBUG-29254 Change-Id: I0d1712753f488dff9252d2fcf099c9aefb06ed0b Reviewed-by: Christian Stenger --- src/plugins/terminal/shellintegration.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/terminal/shellintegration.cpp b/src/plugins/terminal/shellintegration.cpp index d8e26f94ce5..d981dc7269e 100644 --- a/src/plugins/terminal/shellintegration.cpp +++ b/src/plugins/terminal/shellintegration.cpp @@ -85,7 +85,8 @@ void ShellIntegration::onOsc(int cmd, const VTermStringFragment &fragment) emit currentDirChanged(FilePath::fromUserInput(value.toString()).path()); } else if (cmd == 7) { - emit currentDirChanged(FilePath::fromUserInput(d).path()); + const QString decoded = QUrl::fromPercentEncoding(d.toUtf8()); + emit currentDirChanged(FilePath::fromUserInput(decoded).path()); } else if (cmd == 133) { qCDebug(integrationLog) << "OSC 133:" << data; } else if (cmd == 633 && command.length() == 1) { From 82966bc7baf480da14e6b5b03cc95a4a00cb0ec0 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 08:14:45 +0200 Subject: [PATCH 11/57] Revert "ProjectExplorer: Remove 4.11 compatibility code in buildsteplist" This reverts commit c8e1333f89ae10b3e2c571faeaf5792a077ffac9. Contrary to the comment in the code this is apparently still in active use in the user file accessor. Change-Id: I5e5dddbe2a95c70bda92ce732688041787145055 Reviewed-by: Christian Stenger --- src/plugins/projectexplorer/buildsteplist.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugins/projectexplorer/buildsteplist.cpp b/src/plugins/projectexplorer/buildsteplist.cpp index 0094bf0b683..8d6159541ef 100644 --- a/src/plugins/projectexplorer/buildsteplist.cpp +++ b/src/plugins/projectexplorer/buildsteplist.cpp @@ -43,6 +43,16 @@ QVariantMap BuildStepList::toMap() const { QVariantMap map; + { + // Only written for compatibility reasons within the 4.11 cycle + const char CONFIGURATION_ID_KEY[] = "ProjectExplorer.ProjectConfiguration.Id"; + const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DisplayName"; + const char DEFAULT_DISPLAY_NAME_KEY[] = "ProjectExplorer.ProjectConfiguration.DefaultDisplayName"; + map.insert(QLatin1String(CONFIGURATION_ID_KEY), m_id.toSetting()); + map.insert(QLatin1String(DISPLAY_NAME_KEY), displayName()); + map.insert(QLatin1String(DEFAULT_DISPLAY_NAME_KEY), displayName()); + } + // Save build steps map.insert(QString::fromLatin1(STEPS_COUNT_KEY), m_steps.count()); for (int i = 0; i < m_steps.count(); ++i) From cfa88ac1692a49a560a8e6c5b05b5e57767002cf Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 09:00:27 +0200 Subject: [PATCH 12/57] QmlJS: Avoid deprecation warning de18b3ff370543b5b99bd068b871a2cd677cf9f3 deprecated the QCryptographicHash::addData(const char *data, qsizetype length) overload. Change-Id: I86444a9d7de3cafc596f508fc08c3a4c1f25142f Reviewed-by: Eike Ziller --- src/libs/qmljs/qmljsdocument.cpp | 53 ++++++++++++++++---------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/libs/qmljs/qmljsdocument.cpp b/src/libs/qmljs/qmljsdocument.cpp index 16b17b0faee..4b79bdafccc 100644 --- a/src/libs/qmljs/qmljsdocument.cpp +++ b/src/libs/qmljs/qmljsdocument.cpp @@ -369,61 +369,62 @@ LibraryInfo::LibraryInfo(const QmlDirParser &parser, const QByteArray &fingerpri QByteArray LibraryInfo::calculateFingerprint() const { QCryptographicHash hash(QCryptographicHash::Sha1); - hash.addData(reinterpret_cast(&_status), sizeof(_status)); + auto addData = [&hash](auto p, size_t len) { + hash.addData(QByteArrayView(reinterpret_cast(p), len)); + }; + + addData(&_status, sizeof(_status)); int len = _components.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); + addData(&len, sizeof(len)); for (const QmlDirParser::Component &component : _components) { len = component.fileName.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); - hash.addData(reinterpret_cast(component.fileName.constData()), - len * sizeofQChar); - hash.addData(reinterpret_cast(&component.majorVersion), sizeof(component.majorVersion)); - hash.addData(reinterpret_cast(&component.minorVersion), sizeof(component.minorVersion)); + addData(&len, sizeof(len)); + addData(component.fileName.constData(), len * sizeofQChar); + addData(&component.majorVersion, sizeof(component.majorVersion)); + addData(&component.minorVersion, sizeof(component.minorVersion)); len = component.typeName.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); - hash.addData(reinterpret_cast(component.typeName.constData()), - component.typeName.size() * sizeofQChar); + addData(&len, sizeof(len)); + addData(component.typeName.constData(), component.typeName.size() * sizeofQChar); int flags = (component.singleton ? (1 << 0) : 0) + (component.internal ? (1 << 1) : 0); - hash.addData(reinterpret_cast(&flags), sizeof(flags)); + addData(&flags, sizeof(flags)); } len = _plugins.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); + addData(&len, sizeof(len)); for (const QmlDirParser::Plugin &plugin : _plugins) { len = plugin.path.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); - hash.addData(reinterpret_cast(plugin.path.constData()), len * sizeofQChar); + addData(&len, sizeof(len)); + addData(plugin.path.constData(), len * sizeofQChar); len = plugin.name.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); - hash.addData(reinterpret_cast(plugin.name.constData()), len * sizeofQChar); + addData(&len, sizeof(len)); + addData(plugin.name.constData(), len * sizeofQChar); } len = _typeinfos.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); + addData(&len, sizeof(len)); for (const QString &typeinfo : _typeinfos) { len = typeinfo.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); - hash.addData(reinterpret_cast(typeinfo.constData()), - len * sizeofQChar); + addData(&len, sizeof(len)); + addData(typeinfo.constData(), len * sizeofQChar); } len = _metaObjects.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); + addData(&len, sizeof(len)); QList metaFingerprints; for (const LanguageUtils::FakeMetaObject::ConstPtr &metaObject : _metaObjects) metaFingerprints.append(metaObject->fingerprint()); std::sort(metaFingerprints.begin(), metaFingerprints.end()); for (const QByteArray &fp : std::as_const(metaFingerprints)) hash.addData(fp); - hash.addData(reinterpret_cast(&_dumpStatus), sizeof(_dumpStatus)); + addData(&_dumpStatus, sizeof(_dumpStatus)); len = _dumpError.size(); // localization dependent (avoid?) - hash.addData(reinterpret_cast(&len), sizeof(len)); - hash.addData(reinterpret_cast(_dumpError.constData()), len * sizeofQChar); + addData(&len, sizeof(len)); + addData(_dumpError.constData(), len * sizeofQChar); len = _moduleApis.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); + addData(&len, sizeof(len)); for (const ModuleApiInfo &moduleInfo : _moduleApis) moduleInfo.addToHash(hash); // make it order independent? len = _imports.size(); - hash.addData(reinterpret_cast(&len), sizeof(len)); + addData(&len, sizeof(len)); for (const QmlDirParser::Import &import : _imports) hash.addData(import.module.toUtf8()); // import order matters, keep order-dependent From f5d02f4bcb291b299408094f0ddbe6a298563520 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 08:28:06 +0200 Subject: [PATCH 13/57] Utils: Fix compilation with recent Qt dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After an update of Qt dev the following code in Creator fails to compile with /data/dev/creator-out/src/libs/utils/aspects.cpp: In member function ‘void Utils::IntegersAspect::setValue(const QList&) /data/dev/creator-out/src/libs/utils/aspects.cpp:2323:35: error: no matching function for call to ‘transform(const QList&, )’ 2323 | BaseAspect::setValue(transform(value, &QVariant::fromValue)); Work around by using a lambda. Change-Id: Id5a844e7e5fe1846c4904dbad21472743439c4da Reviewed-by: Fabian Kosmale --- src/libs/utils/aspects.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index 57d358a7c1b..999385e1ca9 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -2320,7 +2320,7 @@ QList IntegersAspect::value() const void IntegersAspect::setValue(const QList &value) { - BaseAspect::setValue(transform(value, &QVariant::fromValue)); + BaseAspect::setValue(transform(value, [](int i) { return QVariant::fromValue(i); })); } QList IntegersAspect::defaultValue() const @@ -2331,7 +2331,7 @@ QList IntegersAspect::defaultValue() const void IntegersAspect::setDefaultValue(const QList &value) { - BaseAspect::setDefaultValue(transform(value, &QVariant::fromValue)); + BaseAspect::setDefaultValue(transform(value, [](int i) { return QVariant::fromValue(i); })); } From 73791080d43d79af1d88937d10ebbd6b3d9de8ad Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 1 Jun 2023 12:40:59 +0200 Subject: [PATCH 14/57] Core: Use LayoutBuilder in CommandMappingsPrivate Change-Id: I327e3eae887c0e4e1dfe600685e71c194f26ff75 Reviewed-by: Qt CI Patch Build Bot Reviewed-by: hjk Reviewed-by: --- .../actionmanager/commandmappings.cpp | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/src/plugins/coreplugin/actionmanager/commandmappings.cpp b/src/plugins/coreplugin/actionmanager/commandmappings.cpp index bfc62c3d184..15765698cbd 100644 --- a/src/plugins/coreplugin/actionmanager/commandmappings.cpp +++ b/src/plugins/coreplugin/actionmanager/commandmappings.cpp @@ -6,18 +6,17 @@ #include #include -#include #include +#include +#include #include #include #include -#include #include #include #include #include -#include Q_DECLARE_METATYPE(Core::Internal::ShortcutItem*) @@ -32,13 +31,10 @@ public: CommandMappingsPrivate(CommandMappings *parent) : q(parent) { - groupBox = new QGroupBox(parent); - groupBox->setTitle(::Core::Tr::tr("Command Mappings")); - - filterEdit = new FancyLineEdit(groupBox); + filterEdit = new FancyLineEdit; filterEdit->setFiltering(true); - commandList = new QTreeWidget(groupBox); + commandList = new QTreeWidget; commandList->setRootIsDecorated(false); commandList->setUniformRowHeights(true); commandList->setSortingEnabled(true); @@ -49,33 +45,28 @@ public: item->setText(1, ::Core::Tr::tr("Label")); item->setText(0, ::Core::Tr::tr("Command")); - defaultButton = new QPushButton(::Core::Tr::tr("Reset All"), groupBox); + defaultButton = new QPushButton(::Core::Tr::tr("Reset All")); defaultButton->setToolTip(::Core::Tr::tr("Reset all to default.")); - resetButton = new QPushButton(::Core::Tr::tr("Reset"), groupBox); + resetButton = new QPushButton(::Core::Tr::tr("Reset")); resetButton->setToolTip(::Core::Tr::tr("Reset to default.")); resetButton->setVisible(false); - importButton = new QPushButton(::Core::Tr::tr("Import..."), groupBox); - exportButton = new QPushButton(::Core::Tr::tr("Export..."), groupBox); + importButton = new QPushButton(::Core::Tr::tr("Import...")); + exportButton = new QPushButton(::Core::Tr::tr("Export...")); - auto hboxLayout1 = new QHBoxLayout(); - hboxLayout1->addWidget(defaultButton); - hboxLayout1->addWidget(resetButton); - hboxLayout1->addStretch(); - hboxLayout1->addWidget(importButton); - hboxLayout1->addWidget(exportButton); - - auto hboxLayout = new QHBoxLayout(); - hboxLayout->addWidget(filterEdit); - - auto vboxLayout1 = new QVBoxLayout(groupBox); - vboxLayout1->addLayout(hboxLayout); - vboxLayout1->addWidget(commandList); - vboxLayout1->addLayout(hboxLayout1); - - auto vboxLayout = new QVBoxLayout(parent); - vboxLayout->addWidget(groupBox); + using namespace Layouting; + Column { + Group { + title(::Core::Tr::tr("Command Mappings")), + bindTo(&groupBox), + Column { + filterEdit, + commandList, + Row { defaultButton, resetButton, st, importButton, exportButton }, + }, + }, + }.attachTo(parent); q->connect(exportButton, &QPushButton::clicked, q, &CommandMappings::exportAction); From 9694d9c6f6e8871841414b8616efeca592dbf491 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 2 Jun 2023 23:56:50 +0200 Subject: [PATCH 15/57] Tasking: Introduce NetworkQuery The NetworkQuery class is prepared to work with TaskTree. Provide the task tree adapter for the NetworkQuery class. Register the task inside the Tasking namespace under the NetworkQueryTask name. This class introduces the dependency to Qt::Network, otherwise Tasking namespace is independent on Qt::Network. Possibly, may be added into Qt::Network lib, as a wrapper around the QNetworkReply. Change-Id: I29f160c0bf128c567ce20f044540b2dd6f1e17c4 Reviewed-by: Qt CI Bot Reviewed-by: Marcus Tillmanns --- src/libs/solutions/tasking/CMakeLists.txt | 3 +- src/libs/solutions/tasking/networkquery.cpp | 38 ++++++++++++++ src/libs/solutions/tasking/networkquery.h | 55 +++++++++++++++++++++ src/libs/solutions/tasking/tasking.qbs | 4 +- tests/auto/solutions/tasking/CMakeLists.txt | 2 +- tests/auto/solutions/tasking/tasking.qbs | 1 + tests/manual/tasking/demo/CMakeLists.txt | 2 +- tests/manual/tasking/demo/demo.qbs | 2 +- 8 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 src/libs/solutions/tasking/networkquery.cpp create mode 100644 src/libs/solutions/tasking/networkquery.h diff --git a/src/libs/solutions/tasking/CMakeLists.txt b/src/libs/solutions/tasking/CMakeLists.txt index 52c994de8db..f70c910e042 100644 --- a/src/libs/solutions/tasking/CMakeLists.txt +++ b/src/libs/solutions/tasking/CMakeLists.txt @@ -1,10 +1,11 @@ add_qtc_library(Tasking OBJECT # Never add dependencies to non-Qt libraries for this library - DEPENDS Qt::Concurrent Qt::Core + DEPENDS Qt::Concurrent Qt::Core Qt::Network PUBLIC_DEFINES TASKING_LIBRARY SOURCES barrier.cpp barrier.h concurrentcall.h + networkquery.cpp networkquery.h tasking_global.h tasktree.cpp tasktree.h ) diff --git a/src/libs/solutions/tasking/networkquery.cpp b/src/libs/solutions/tasking/networkquery.cpp new file mode 100644 index 00000000000..292d3c7d4aa --- /dev/null +++ b/src/libs/solutions/tasking/networkquery.cpp @@ -0,0 +1,38 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "networkquery.h" + +#include + +namespace Tasking { + +void NetworkQuery::start() +{ + if (m_reply) { + qWarning("The NetworkQuery is already running. Ignoring the call to start()."); + return; + } + if (!m_manager) { + qWarning("Can't start the NetworkQuery without the QNetworkAccessManager. " + "Stopping with an error."); + emit done(false); + return; + } + m_reply.reset(m_manager->get(m_request)); + connect(m_reply.get(), &QNetworkReply::finished, this, [this] { + disconnect(m_reply.get(), nullptr, this, nullptr); + emit done(m_reply->error() == QNetworkReply::NoError); + m_reply.release()->deleteLater(); + }); + if (m_reply->isRunning()) + emit started(); +} + +NetworkQuery::~NetworkQuery() +{ + if (m_reply) + m_reply->abort(); +} + +} // namespace Tasking diff --git a/src/libs/solutions/tasking/networkquery.h b/src/libs/solutions/tasking/networkquery.h new file mode 100644 index 00000000000..faf482df90d --- /dev/null +++ b/src/libs/solutions/tasking/networkquery.h @@ -0,0 +1,55 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#pragma once + +#include "tasking_global.h" + +#include "tasktree.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QNetworkAccessManager; +QT_END_NAMESPACE + +namespace Tasking { + +// This class introduces the dependency to Qt::Network, otherwise Tasking namespace +// is independent on Qt::Network. +// Possibly, it could be placed inside Qt::Network library, as a wrapper around QNetworkReply. + +class TASKING_EXPORT NetworkQuery final : public QObject +{ + Q_OBJECT + +public: + ~NetworkQuery(); + void setRequest(const QNetworkRequest &request) { m_request = request; } + void setNetworkAccessManager(QNetworkAccessManager *manager) { m_manager = manager; } + QNetworkReply *reply() const { return m_reply.get(); } + void start(); + +signals: + void started(); + void done(bool success); + +private: + QNetworkRequest m_request; + QNetworkAccessManager *m_manager = nullptr; + std::unique_ptr m_reply; +}; + +class TASKING_EXPORT NetworkQueryTaskAdapter : public TaskAdapter +{ +public: + NetworkQueryTaskAdapter() { connect(task(), &NetworkQuery::done, this, &TaskInterface::done); } + void start() final { task()->start(); } +}; + +} // namespace Tasking + +TASKING_DECLARE_TASK(NetworkQueryTask, Tasking::NetworkQueryTaskAdapter); diff --git a/src/libs/solutions/tasking/tasking.qbs b/src/libs/solutions/tasking/tasking.qbs index 0e78ed74f0a..fa0a5ebacc9 100644 --- a/src/libs/solutions/tasking/tasking.qbs +++ b/src/libs/solutions/tasking/tasking.qbs @@ -1,12 +1,14 @@ QtcLibrary { name: "Tasking" - Depends { name: "Qt"; submodules: ["concurrent", "core"] } + Depends { name: "Qt"; submodules: ["concurrent", "core", "network"] } cpp.defines: base.concat("TASKING_LIBRARY") files: [ "barrier.cpp", "barrier.h", "concurrentcall.h", + "networkquery.cpp", + "networkquery.h", "tasking_global.h", "tasktree.cpp", "tasktree.h", diff --git a/tests/auto/solutions/tasking/CMakeLists.txt b/tests/auto/solutions/tasking/CMakeLists.txt index a425250a5a9..f55c3b1c7b2 100644 --- a/tests/auto/solutions/tasking/CMakeLists.txt +++ b/tests/auto/solutions/tasking/CMakeLists.txt @@ -1,4 +1,4 @@ add_qtc_test(tst_solutions_tasking - DEPENDS Tasking + DEPENDS Tasking Qt::Network SOURCES tst_tasking.cpp ) diff --git a/tests/auto/solutions/tasking/tasking.qbs b/tests/auto/solutions/tasking/tasking.qbs index f099edb370c..d6ffa884920 100644 --- a/tests/auto/solutions/tasking/tasking.qbs +++ b/tests/auto/solutions/tasking/tasking.qbs @@ -1,6 +1,7 @@ QtcAutotest { name: "Tasking autotest" + Depends { name: "Qt"; submodules: ["network"] } Depends { name: "Tasking" } files: "tst_tasking.cpp" diff --git a/tests/manual/tasking/demo/CMakeLists.txt b/tests/manual/tasking/demo/CMakeLists.txt index bdd790cd898..7597e74d608 100644 --- a/tests/manual/tasking/demo/CMakeLists.txt +++ b/tests/manual/tasking/demo/CMakeLists.txt @@ -1,6 +1,6 @@ add_qtc_test(tst_tasking_demo MANUALTEST - DEPENDS Tasking Qt::Widgets + DEPENDS Tasking Qt::Widgets Qt::Network SOURCES demo.qrc main.cpp diff --git a/tests/manual/tasking/demo/demo.qbs b/tests/manual/tasking/demo/demo.qbs index 2a54143c415..9cf856bca21 100644 --- a/tests/manual/tasking/demo/demo.qbs +++ b/tests/manual/tasking/demo/demo.qbs @@ -4,7 +4,7 @@ QtcManualtest { name: "Tasking demo" type: ["application"] - Depends { name: "Qt"; submodules: ["widgets"] } + Depends { name: "Qt"; submodules: ["network", "widgets"] } Depends { name: "Tasking" } files: [ From c3344c740af03c892dab506afd629d3e5e266271 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Fri, 2 Jun 2023 22:56:30 +0200 Subject: [PATCH 16/57] Tasking: Import imagescaling example It's an initial copy of the qtbase/examples/qtconcurrent/imagescaling. The goal is to implement it using TaskTree and compare both implementations. Change-Id: I92953a70a3330ac679823060e75e5f39971ae444 Reviewed-by: Qt CI Bot Reviewed-by: Reviewed-by: Marcus Tillmanns Reviewed-by: Leena Miettinen Reviewed-by: hjk --- tests/manual/manual.qbs | 1 + tests/manual/tasking/CMakeLists.txt | 1 + .../tasking/imagescaling/CMakeLists.txt | 11 + .../tasking/imagescaling/downloaddialog.cpp | 41 +++ .../tasking/imagescaling/downloaddialog.h | 29 +++ .../tasking/imagescaling/downloaddialog.ui | 119 +++++++++ .../tasking/imagescaling/imagescaling.cpp | 236 ++++++++++++++++++ .../tasking/imagescaling/imagescaling.h | 54 ++++ .../tasking/imagescaling/imagescaling.qbs | 18 ++ tests/manual/tasking/imagescaling/main.cpp | 18 ++ 10 files changed, 528 insertions(+) create mode 100644 tests/manual/tasking/imagescaling/CMakeLists.txt create mode 100644 tests/manual/tasking/imagescaling/downloaddialog.cpp create mode 100644 tests/manual/tasking/imagescaling/downloaddialog.h create mode 100644 tests/manual/tasking/imagescaling/downloaddialog.ui create mode 100644 tests/manual/tasking/imagescaling/imagescaling.cpp create mode 100644 tests/manual/tasking/imagescaling/imagescaling.h create mode 100644 tests/manual/tasking/imagescaling/imagescaling.qbs create mode 100644 tests/manual/tasking/imagescaling/main.cpp diff --git a/tests/manual/manual.qbs b/tests/manual/manual.qbs index 66c480c7a62..fecd79518c5 100644 --- a/tests/manual/manual.qbs +++ b/tests/manual/manual.qbs @@ -15,6 +15,7 @@ Project { "shootout/shootout.qbs", "subdirfileiterator/subdirfileiterator.qbs", "tasking/demo/demo.qbs", + "tasking/demo/imagescaling.qbs", "widgets/widgets.qbs", ] } diff --git a/tests/manual/tasking/CMakeLists.txt b/tests/manual/tasking/CMakeLists.txt index a16f5f12201..a27004eb3ec 100644 --- a/tests/manual/tasking/CMakeLists.txt +++ b/tests/manual/tasking/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(demo) +add_subdirectory(imagescaling) diff --git a/tests/manual/tasking/imagescaling/CMakeLists.txt b/tests/manual/tasking/imagescaling/CMakeLists.txt new file mode 100644 index 00000000000..cfa8121b922 --- /dev/null +++ b/tests/manual/tasking/imagescaling/CMakeLists.txt @@ -0,0 +1,11 @@ +add_qtc_test(tst_tasking_imagescaling + MANUALTEST + DEPENDS Tasking Qt::Concurrent Qt::Network Qt::Widgets + SOURCES + downloaddialog.cpp + downloaddialog.h + downloaddialog.ui + imagescaling.cpp + imagescaling.h + main.cpp +) diff --git a/tests/manual/tasking/imagescaling/downloaddialog.cpp b/tests/manual/tasking/imagescaling/downloaddialog.cpp new file mode 100644 index 00000000000..3547b880580 --- /dev/null +++ b/tests/manual/tasking/imagescaling/downloaddialog.cpp @@ -0,0 +1,41 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "downloaddialog.h" +#include "ui_downloaddialog.h" + +#include + +DownloadDialog::DownloadDialog(QWidget *parent) : QDialog(parent), ui(new Ui::DownloadDialog) +{ + ui->setupUi(this); + + ui->urlLineEdit->setPlaceholderText(tr("Enter the URL of an image to download")); + + connect(ui->addUrlButton, &QPushButton::clicked, this, [this] { + const auto text = ui->urlLineEdit->text(); + if (!text.isEmpty()) { + ui->urlListWidget->addItem(text); + ui->urlLineEdit->clear(); + } + }); + connect(ui->urlListWidget, &QListWidget::itemSelectionChanged, this, [this] { + ui->removeUrlButton->setEnabled(!ui->urlListWidget->selectedItems().empty()); + }); + connect(ui->clearUrlsButton, &QPushButton::clicked, ui->urlListWidget, &QListWidget::clear); + connect(ui->removeUrlButton, &QPushButton::clicked, this, + [this] { qDeleteAll(ui->urlListWidget->selectedItems()); }); +} + +DownloadDialog::~DownloadDialog() +{ + delete ui; +} + +QList DownloadDialog::getUrls() const +{ + QList urls; + for (auto row = 0; row < ui->urlListWidget->count(); ++row) + urls.push_back(QUrl(ui->urlListWidget->item(row)->text())); + return urls; +} diff --git a/tests/manual/tasking/imagescaling/downloaddialog.h b/tests/manual/tasking/imagescaling/downloaddialog.h new file mode 100644 index 00000000000..cc15d081893 --- /dev/null +++ b/tests/manual/tasking/imagescaling/downloaddialog.h @@ -0,0 +1,29 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef DOWNLOADDIALOG_H +#define DOWNLOADDIALOG_H + +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class DownloadDialog; +} +QT_END_NAMESPACE + +class DownloadDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DownloadDialog(QWidget *parent = nullptr); + ~DownloadDialog(); + + QList getUrls() const; + +private: + Ui::DownloadDialog *ui; +}; + +#endif // DOWNLOADDIALOG_H diff --git a/tests/manual/tasking/imagescaling/downloaddialog.ui b/tests/manual/tasking/imagescaling/downloaddialog.ui new file mode 100644 index 00000000000..c85a0635681 --- /dev/null +++ b/tests/manual/tasking/imagescaling/downloaddialog.ui @@ -0,0 +1,119 @@ + + + DownloadDialog + + + + 0 + 0 + 489 + 333 + + + + Dialog + + + + + + + + + + + + + + + + + + + + Add URL + + + + + + + false + + + Remove URL + + + + + + + Clear + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DownloadDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DownloadDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/tests/manual/tasking/imagescaling/imagescaling.cpp b/tests/manual/tasking/imagescaling/imagescaling.cpp new file mode 100644 index 00000000000..f380ae9d908 --- /dev/null +++ b/tests/manual/tasking/imagescaling/imagescaling.cpp @@ -0,0 +1,236 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "imagescaling.h" +#include "downloaddialog.h" + +#include + +Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDialog(this)) +{ + resize(800, 600); + + addUrlsButton = new QPushButton(tr("Add URLs")); +//! [1] + connect(addUrlsButton, &QPushButton::clicked, this, &Images::process); +//! [1] + + cancelButton = new QPushButton(tr("Cancel")); + cancelButton->setEnabled(false); +//! [2] + connect(cancelButton, &QPushButton::clicked, this, &Images::cancel); +//! [2] + + QHBoxLayout *buttonLayout = new QHBoxLayout(); + buttonLayout->addWidget(addUrlsButton); + buttonLayout->addWidget(cancelButton); + buttonLayout->addStretch(); + + statusBar = new QStatusBar(); + + imagesLayout = new QGridLayout(); + + mainLayout = new QVBoxLayout(); + mainLayout->addLayout(buttonLayout); + mainLayout->addLayout(imagesLayout); + mainLayout->addStretch(); + mainLayout->addWidget(statusBar); + setLayout(mainLayout); + +//! [6] + connect(&scalingWatcher, &QFutureWatcher>::finished, + this, &Images::scaleFinished); +//! [6] +} + +Images::~Images() +{ + cancel(); +} + +//! [3] +void Images::process() +{ + // Clean previous state + replies.clear(); + addUrlsButton->setEnabled(false); + + if (downloadDialog->exec() == QDialog::Accepted) { + + const auto urls = downloadDialog->getUrls(); + if (urls.empty()) + return; + + cancelButton->setEnabled(true); + + initLayout(urls.size()); + + downloadFuture = download(urls); + statusBar->showMessage(tr("Downloading...")); +//! [3] + + //! [4] + downloadFuture + .then([this](auto) { + cancelButton->setEnabled(false); + updateStatus(tr("Scaling...")); + //! [16] + scalingWatcher.setFuture(QtConcurrent::run(Images::scaled, + downloadFuture.results())); + //! [16] + }) + //! [4] + //! [5] + .onCanceled([this] { + updateStatus(tr("Download has been canceled.")); + }) + .onFailed([this](QNetworkReply::NetworkError error) { + updateStatus(tr("Download finished with error: %1").arg(error)); + // Abort all pending requests + abortDownload(); + }) + .onFailed([this](const std::exception &ex) { + updateStatus(tr(ex.what())); + }) + //! [5] + .then([this]() { + cancelButton->setEnabled(false); + addUrlsButton->setEnabled(true); + }); + } +} + +//! [7] +void Images::cancel() +{ + statusBar->showMessage(tr("Canceling...")); + + downloadFuture.cancel(); + abortDownload(); +} +//! [7] + +//! [15] +void Images::scaleFinished() +{ + const OptionalImages result = scalingWatcher.result(); + if (result.has_value()) { + const auto scaled = result.value(); + showImages(scaled); + updateStatus(tr("Finished")); + } else { + updateStatus(tr("Failed to extract image data.")); + } + addUrlsButton->setEnabled(true); +} +//! [15] + +//! [8] +QFuture Images::download(const QList &urls) +{ +//! [8] +//! [9] + QSharedPointer> promise(new QPromise()); + promise->start(); +//! [9] + + //! [10] + for (const auto &url : urls) { + QSharedPointer reply(qnam.get(QNetworkRequest(url))); + replies.push_back(reply); + //! [10] + + //! [11] + QtFuture::connect(reply.get(), &QNetworkReply::finished).then([=] { + if (promise->isCanceled()) { + if (!promise->future().isFinished()) + promise->finish(); + return; + } + + if (reply->error() != QNetworkReply::NoError) { + if (!promise->future().isFinished()) + throw reply->error(); + } + //! [12] + promise->addResult(reply->readAll()); + + // Report finished on the last download + if (promise->future().resultCount() == urls.size()) + promise->finish(); + //! [12] + }).onFailed([promise] (QNetworkReply::NetworkError error) { + promise->setException(std::make_exception_ptr(error)); + promise->finish(); + }).onFailed([promise] { + const auto ex = std::make_exception_ptr( + std::runtime_error("Unknown error occurred while downloading.")); + promise->setException(ex); + promise->finish(); + }); + } + //! [11] + +//! [13] + return promise->future(); +} +//! [13] + +//! [14] +Images::OptionalImages Images::scaled(const QList &data) +{ + QList scaled; + for (const auto &imgData : data) { + QImage image; + image.loadFromData(imgData); + if (image.isNull()) + return std::nullopt; + + scaled.push_back(image.scaled(100, 100, Qt::KeepAspectRatio)); + } + + return scaled; +} +//! [14] + +void Images::showImages(const QList &images) +{ + for (int i = 0; i < images.size(); ++i) { + labels[i]->setAlignment(Qt::AlignCenter); + labels[i]->setPixmap(QPixmap::fromImage(images[i])); + } +} + +void Images::initLayout(qsizetype count) +{ + // Clean old images + QLayoutItem *child; + while ((child = imagesLayout->takeAt(0)) != nullptr) { + child->widget()->setParent(nullptr); + delete child->widget(); + delete child; + } + labels.clear(); + + // Init the images layout for the new images + const auto dim = int(qSqrt(qreal(count))) + 1; + for (int i = 0; i < dim; ++i) { + for (int j = 0; j < dim; ++j) { + QLabel *imageLabel = new QLabel; + imageLabel->setFixedSize(100, 100); + imagesLayout->addWidget(imageLabel, i, j); + labels.append(imageLabel); + } + } +} + +void Images::updateStatus(const QString &msg) +{ + statusBar->showMessage(msg); +} + +void Images::abortDownload() +{ + for (auto reply : replies) + reply->abort(); +} diff --git a/tests/manual/tasking/imagescaling/imagescaling.h b/tests/manual/tasking/imagescaling/imagescaling.h new file mode 100644 index 00000000000..d66c9564d28 --- /dev/null +++ b/tests/manual/tasking/imagescaling/imagescaling.h @@ -0,0 +1,54 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef IMAGESCALING_H +#define IMAGESCALING_H + +#include +#include +#include +#include + +class DownloadDialog; +class Images : public QWidget +{ +Q_OBJECT +public: + Images(QWidget *parent = nullptr); + ~Images(); + + void initLayout(qsizetype count); + + QFuture download(const QList &urls); + void updateStatus(const QString &msg); + void showImages(const QList &images); + void abortDownload(); + +public slots: + void process(); + void cancel(); + +private slots: + void scaleFinished(); + +private: + //! [1] + using OptionalImages = std::optional>; + //! [1] + static OptionalImages scaled(const QList &data); + + QPushButton *addUrlsButton; + QPushButton *cancelButton; + QVBoxLayout *mainLayout; + QList labels; + QGridLayout *imagesLayout; + QStatusBar *statusBar; + DownloadDialog *downloadDialog; + + QNetworkAccessManager qnam; + QList> replies; + QFuture downloadFuture; + QFutureWatcher scalingWatcher; +}; + +#endif // IMAGESCALING_H diff --git a/tests/manual/tasking/imagescaling/imagescaling.qbs b/tests/manual/tasking/imagescaling/imagescaling.qbs new file mode 100644 index 00000000000..ea0698c7292 --- /dev/null +++ b/tests/manual/tasking/imagescaling/imagescaling.qbs @@ -0,0 +1,18 @@ +import qbs.FileInfo + +QtcManualtest { + name: "Tasking imagescaling" + type: ["application"] + + Depends { name: "Qt"; submodules: ["concurrent", "network", "widgets"] } + Depends { name: "Tasking" } + + files: [ + "downloaddialog.cpp", + "downloaddialog.h", + "downloaddialog.ui", + "imagescaling.cpp", + "imagescaling.h", + "main.cpp", + ] +} diff --git a/tests/manual/tasking/imagescaling/main.cpp b/tests/manual/tasking/imagescaling/main.cpp new file mode 100644 index 00000000000..a3ae6491f00 --- /dev/null +++ b/tests/manual/tasking/imagescaling/main.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "imagescaling.h" +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc,argv); + app.setOrganizationName("QtProject"); + app.setApplicationName(QObject::tr("Image Downloading and Scaling")); + + Images imageView; + imageView.setWindowTitle(QObject::tr("Image Downloading and Scaling")); + imageView.show(); + + return app.exec(); +} From 3f9704846b497119a06e16171e3baa2aa61c6a05 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 10:45:09 +0200 Subject: [PATCH 17/57] ImageScaling: Get rid of doc example markers In order to more easily compare both implementations. Change-Id: I4ff48cb4d4e61be4e2c8834f63091f4d825db0be Reviewed-by: Marcus Tillmanns Reviewed-by: Qt CI Bot Reviewed-by: --- .../tasking/imagescaling/imagescaling.cpp | 32 ------------------- .../tasking/imagescaling/imagescaling.h | 2 -- 2 files changed, 34 deletions(-) diff --git a/tests/manual/tasking/imagescaling/imagescaling.cpp b/tests/manual/tasking/imagescaling/imagescaling.cpp index f380ae9d908..362a30287e6 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.cpp +++ b/tests/manual/tasking/imagescaling/imagescaling.cpp @@ -11,15 +11,11 @@ Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDi resize(800, 600); addUrlsButton = new QPushButton(tr("Add URLs")); -//! [1] connect(addUrlsButton, &QPushButton::clicked, this, &Images::process); -//! [1] cancelButton = new QPushButton(tr("Cancel")); cancelButton->setEnabled(false); -//! [2] connect(cancelButton, &QPushButton::clicked, this, &Images::cancel); -//! [2] QHBoxLayout *buttonLayout = new QHBoxLayout(); buttonLayout->addWidget(addUrlsButton); @@ -37,10 +33,8 @@ Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDi mainLayout->addWidget(statusBar); setLayout(mainLayout); -//! [6] connect(&scalingWatcher, &QFutureWatcher>::finished, this, &Images::scaleFinished); -//! [6] } Images::~Images() @@ -48,7 +42,6 @@ Images::~Images() cancel(); } -//! [3] void Images::process() { // Clean previous state @@ -67,20 +60,14 @@ void Images::process() downloadFuture = download(urls); statusBar->showMessage(tr("Downloading...")); -//! [3] - //! [4] downloadFuture .then([this](auto) { cancelButton->setEnabled(false); updateStatus(tr("Scaling...")); - //! [16] scalingWatcher.setFuture(QtConcurrent::run(Images::scaled, downloadFuture.results())); - //! [16] }) - //! [4] - //! [5] .onCanceled([this] { updateStatus(tr("Download has been canceled.")); }) @@ -92,7 +79,6 @@ void Images::process() .onFailed([this](const std::exception &ex) { updateStatus(tr(ex.what())); }) - //! [5] .then([this]() { cancelButton->setEnabled(false); addUrlsButton->setEnabled(true); @@ -100,7 +86,6 @@ void Images::process() } } -//! [7] void Images::cancel() { statusBar->showMessage(tr("Canceling...")); @@ -108,9 +93,7 @@ void Images::cancel() downloadFuture.cancel(); abortDownload(); } -//! [7] -//! [15] void Images::scaleFinished() { const OptionalImages result = scalingWatcher.result(); @@ -123,24 +106,16 @@ void Images::scaleFinished() } addUrlsButton->setEnabled(true); } -//! [15] -//! [8] QFuture Images::download(const QList &urls) { -//! [8] -//! [9] QSharedPointer> promise(new QPromise()); promise->start(); -//! [9] - //! [10] for (const auto &url : urls) { QSharedPointer reply(qnam.get(QNetworkRequest(url))); replies.push_back(reply); - //! [10] - //! [11] QtFuture::connect(reply.get(), &QNetworkReply::finished).then([=] { if (promise->isCanceled()) { if (!promise->future().isFinished()) @@ -152,13 +127,11 @@ QFuture Images::download(const QList &urls) if (!promise->future().isFinished()) throw reply->error(); } - //! [12] promise->addResult(reply->readAll()); // Report finished on the last download if (promise->future().resultCount() == urls.size()) promise->finish(); - //! [12] }).onFailed([promise] (QNetworkReply::NetworkError error) { promise->setException(std::make_exception_ptr(error)); promise->finish(); @@ -169,14 +142,10 @@ QFuture Images::download(const QList &urls) promise->finish(); }); } - //! [11] -//! [13] return promise->future(); } -//! [13] -//! [14] Images::OptionalImages Images::scaled(const QList &data) { QList scaled; @@ -191,7 +160,6 @@ Images::OptionalImages Images::scaled(const QList &data) return scaled; } -//! [14] void Images::showImages(const QList &images) { diff --git a/tests/manual/tasking/imagescaling/imagescaling.h b/tests/manual/tasking/imagescaling/imagescaling.h index d66c9564d28..d3409e60a0f 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.h +++ b/tests/manual/tasking/imagescaling/imagescaling.h @@ -32,9 +32,7 @@ private slots: void scaleFinished(); private: - //! [1] using OptionalImages = std::optional>; - //! [1] static OptionalImages scaled(const QList &data); QPushButton *addUrlsButton; From 6b04989fce21ce524e66662fe534c8c932995d6b Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Thu, 1 Jun 2023 19:12:52 +0200 Subject: [PATCH 18/57] Utils: Introduce customMargin(const QMargins &) And re-use it for normalMargin() and noMargin() Change-Id: Id779e2fa052ab35df3dd917d890ca7808dfb1a8c Reviewed-by: hjk Reviewed-by: Reviewed-by: Qt CI Patch Build Bot --- src/libs/utils/layoutbuilder.cpp | 20 +++++++++----------- src/libs/utils/layoutbuilder.h | 2 ++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index bea7faafd3b..94aa80f02cc 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -676,24 +676,22 @@ LayoutItem st() LayoutItem noMargin() { - LayoutItem item; - item.onAdd = [](LayoutBuilder &builder) { - if (auto layout = builder.stack.last().layout) - layout->setContentsMargins(0, 0, 0, 0); - else if (auto widget = builder.stack.last().widget) - widget->setContentsMargins(0, 0, 0, 0); - }; - return item; + return customMargin({}); } LayoutItem normalMargin() +{ + return customMargin({9, 9, 9, 9}); +} + +LayoutItem customMargin(const QMargins &margin) { LayoutItem item; - item.onAdd = [](LayoutBuilder &builder) { + item.onAdd = [margin](LayoutBuilder &builder) { if (auto layout = builder.stack.last().layout) - layout->setContentsMargins(9, 9, 9, 9); + layout->setContentsMargins(margin); else if (auto widget = builder.stack.last().widget) - widget->setContentsMargins(9, 9, 9, 9); + widget->setContentsMargins(margin); }; return item; } diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 1f774ba1461..4a756139897 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -19,6 +19,7 @@ QT_BEGIN_NAMESPACE class QLayout; +class QMargins; class QObject; class QWidget; template T qobject_cast(QObject *object); @@ -202,6 +203,7 @@ QTCREATOR_UTILS_EXPORT LayoutItem empty(); QTCREATOR_UTILS_EXPORT LayoutItem hr(); QTCREATOR_UTILS_EXPORT LayoutItem noMargin(); QTCREATOR_UTILS_EXPORT LayoutItem normalMargin(); +QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin); QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment(); // "Setters" From 8b85ac30a49dec9a0b27af9a5c1d26b4a60470af Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 2 Jun 2023 15:15:48 +0200 Subject: [PATCH 19/57] Do not show Design Studio settings page in Qt Creator Change-Id: Iab2b8806f8041685440670fe741f25fb895afd99 Reviewed-by: Reviewed-by: Alessandro Portale Reviewed-by: Mahmoud Badri --- src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp | 7 ++++--- src/plugins/studiowelcome/examplecheckout.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp index c59fcbb2ea1..703c1cf7605 100644 --- a/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp +++ b/src/plugins/qmldesignerbase/qmldesignerbaseplugin.cpp @@ -21,7 +21,7 @@ class QmlDesignerBasePlugin::Data public: DesignerSettings settings; StudioStyle *style = nullptr; - StudioConfigSettingsPage studioConfigSettingsPage; + std::unique_ptr studioConfigSettingsPage; Data() : settings(Core::ICore::settings()) @@ -54,13 +54,14 @@ QStyle *QmlDesignerBasePlugin::style() StudioConfigSettingsPage *QmlDesignerBasePlugin::studioConfigSettingsPage() { - return &global->d->studioConfigSettingsPage; + return global->d->studioConfigSettingsPage.get(); } bool QmlDesignerBasePlugin::initialize(const QStringList &, QString *) { d = std::make_unique(); - + if (Core::ICore::settings()->value("QML/Designer/StandAloneMode", false).toBool()) + d->studioConfigSettingsPage = std::make_unique(); return true; } diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index 61a8e429d68..b919bf29394 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -105,9 +105,10 @@ DataModelDownloader::DataModelDownloader(QObject * /* parent */) return; auto studioWelcomePlugin = qobject_cast(plugin); - - if (studioWelcomePlugin) { - QObject::connect(QmlDesigner::QmlDesignerBasePlugin::studioConfigSettingsPage(), + QmlDesigner::StudioConfigSettingsPage *settingsPage + = QmlDesigner::QmlDesignerBasePlugin::studioConfigSettingsPage(); + if (studioWelcomePlugin && settingsPage) { + QObject::connect(settingsPage, &QmlDesigner::StudioConfigSettingsPage::examplesDownloadPathChanged, this, &DataModelDownloader::targetPathMustChange); From 969f560c61438971b57b20ee38b6bcdb537ac1b2 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 6 Jun 2023 09:19:40 +0200 Subject: [PATCH 20/57] SilverSearcher: Fix missing include Amends 64d209c24bb618fee5e0d4ba427d656c3b71e5ff. Change-Id: Ib73302e98e4779e8d8f232852d45903a4055172a Reviewed-by: Jarek Kobus --- src/plugins/silversearcher/silversearcherparser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/silversearcher/silversearcherparser.cpp b/src/plugins/silversearcher/silversearcherparser.cpp index 1a2f5e55e87..7880bd78eba 100644 --- a/src/plugins/silversearcher/silversearcherparser.cpp +++ b/src/plugins/silversearcher/silversearcherparser.cpp @@ -3,6 +3,8 @@ #include "silversearcherparser.h" +#include + using namespace Utils; namespace SilverSearcher { From d97d3f58ac976f05be0a43f2d807ce4f0240ab34 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 29 May 2023 20:16:19 +0200 Subject: [PATCH 21/57] TaskTree: Rename TaskItem into GroupItem Not all classes derived from TaskItem are tasks, but the common denominator is that all may be placed inside a group: thus GroupItem sounds more appropriate. Addresses the 10th point in the bugreport below. Task-number: QTCREATORBUG-28741 Change-Id: I94d728a8e39ec732810f2e5bbe6b9a76f3bc387c Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: hjk --- src/libs/solutions/tasking/tasktree.cpp | 81 ++++++++-------- src/libs/solutions/tasking/tasktree.h | 96 +++++++++---------- src/libs/utils/filestreamer.cpp | 22 ++--- src/plugins/autotest/testcodeparser.cpp | 2 +- src/plugins/autotest/testrunner.cpp | 2 +- .../clangtools/clangtoolruncontrol.cpp | 2 +- src/plugins/clangtools/clangtoolrunner.cpp | 6 +- src/plugins/clangtools/clangtoolrunner.h | 8 +- .../clangtools/documentclangtoolrunner.cpp | 2 +- .../coreplugin/locator/ilocatorfilter.cpp | 6 +- .../coreplugin/locator/ilocatorfilter.h | 8 +- src/plugins/coreplugin/locator/locator.cpp | 2 +- src/plugins/cppeditor/cppprojectupdater.cpp | 2 +- src/plugins/diffeditor/diffeditorplugin.cpp | 2 +- src/plugins/git/gitclient.cpp | 2 +- src/plugins/projectexplorer/extracompiler.cpp | 4 +- src/plugins/projectexplorer/extracompiler.h | 8 +- src/plugins/qmakeprojectmanager/qmakestep.cpp | 2 +- .../qnx/qnxdeployqtlibrariesdialog.cpp | 22 ++--- .../remotelinux/genericdirectuploadstep.cpp | 34 +++---- src/plugins/remotelinux/linuxdevicetester.cpp | 40 ++++---- src/plugins/remotelinux/linuxdevicetester.h | 4 +- src/plugins/remotelinux/rsyncdeploystep.cpp | 8 +- .../remotelinux/tarpackagedeploystep.cpp | 8 +- src/plugins/updateinfo/updateinfoplugin.cpp | 2 +- .../vcsbase/vcsbasediffeditorcontroller.cpp | 2 +- .../vcsbase/vcsbasediffeditorcontroller.h | 2 +- tests/auto/solutions/tasking/tst_tasking.cpp | 6 +- tests/auto/utils/async/tst_async.cpp | 2 +- .../tst_subdirfileiterator.cpp | 4 +- tests/manual/tasking/demo/main.cpp | 2 +- tests/manual/tasking/demo/taskwidget.cpp | 4 +- tests/manual/tasking/demo/taskwidget.h | 4 +- 33 files changed, 201 insertions(+), 200 deletions(-) diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index f9fe58375e8..2a4c7cefd98 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -43,11 +43,11 @@ private: }; /*! - \class Tasking::TaskItem + \class Tasking::GroupItem \inheaderfile solutions/tasking/tasktree.h \inmodule QtCreator \ingroup mainclasses - \brief The TaskItem class represents the basic element for composing nested tree structures. + \brief The GroupItem class represents the basic element for composing nested tree structures. */ /*! @@ -246,7 +246,7 @@ private: */ /*! - \typealias TaskItem::GroupSetupHandler + \typealias GroupItem::GroupSetupHandler Type alias for \c std::function. @@ -277,7 +277,7 @@ private: */ /*! - \typealias TaskItem::GroupEndHandler + \typealias GroupItem::GroupEndHandler Type alias for \c std::function\. @@ -290,13 +290,14 @@ private: */ /*! - \fn template TaskItem onGroupSetup(SetupHandler &&handler) + \fn template GroupItem onGroupSetup(SetupHandler &&handler) Constructs a group's element holding the group setup handler. The \a handler is invoked whenever the group starts. The passed \a handler is either of \c std::function or \c std::function - type. For more information on possible argument type, refer to \l {TaskItem::GroupSetupHandler}. + type. For more information on possible argument type, refer to + \l {GroupItem::GroupSetupHandler}. When the \a handler is invoked, none of the group's child tasks are running yet. @@ -304,7 +305,7 @@ private: after the storages are constructed, so that the \a handler may already perform some initial modifications to the active storages. - \sa TaskItem::GroupSetupHandler, onGroupDone, onGroupError + \sa GroupItem::GroupSetupHandler, onGroupDone, onGroupError */ /*! @@ -319,9 +320,9 @@ private: before the storages are destructed, so that the \a handler may still perform a last read of the active storages' data. - \sa TaskItem::GroupEndHandler, onGroupSetup, onGroupError + \sa GroupItem::GroupEndHandler, onGroupSetup, onGroupError */ -TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler) +GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler) { return Group::onGroupDone(handler); } @@ -338,9 +339,9 @@ TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler) before the storages are destructed, so that the \a handler may still perform a last read of the active storages' data. - \sa TaskItem::GroupEndHandler, onGroupSetup, onGroupDone + \sa GroupItem::GroupEndHandler, onGroupSetup, onGroupDone */ -TaskItem onGroupError(const TaskItem::GroupEndHandler &handler) +GroupItem onGroupError(const GroupItem::GroupEndHandler &handler) { return Group::onGroupError(handler); } @@ -386,7 +387,7 @@ TaskItem onGroupError(const TaskItem::GroupEndHandler &handler) \sa sequential, parallel */ -TaskItem parallelLimit(int limit) +GroupItem parallelLimit(int limit) { return Group::parallelLimit(qMax(limit, 0)); } @@ -399,21 +400,21 @@ TaskItem parallelLimit(int limit) \sa stopOnError, continueOnError, stopOnDone, continueOnDone, stopOnFinished, finishAllAndDone, finishAllAndError, WorkflowPolicy */ -TaskItem workflowPolicy(WorkflowPolicy policy) +GroupItem workflowPolicy(WorkflowPolicy policy) { return Group::workflowPolicy(policy); } -const TaskItem sequential = parallelLimit(1); -const TaskItem parallel = parallelLimit(0); +const GroupItem sequential = parallelLimit(1); +const GroupItem parallel = parallelLimit(0); -const TaskItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError); -const TaskItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError); -const TaskItem stopOnDone = workflowPolicy(WorkflowPolicy::StopOnDone); -const TaskItem continueOnDone = workflowPolicy(WorkflowPolicy::ContinueOnDone); -const TaskItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished); -const TaskItem finishAllAndDone = workflowPolicy(WorkflowPolicy::FinishAllAndDone); -const TaskItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError); +const GroupItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError); +const GroupItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError); +const GroupItem stopOnDone = workflowPolicy(WorkflowPolicy::StopOnDone); +const GroupItem continueOnDone = workflowPolicy(WorkflowPolicy::ContinueOnDone); +const GroupItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished); +const GroupItem finishAllAndDone = workflowPolicy(WorkflowPolicy::FinishAllAndDone); +const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError); static TaskAction toTaskAction(bool success) { @@ -483,11 +484,11 @@ void TreeStorageBase::activateStorage(int id) const m_storageData->m_activeStorage = id; } -void TaskItem::addChildren(const QList &children) +void GroupItem::addChildren(const QList &children) { QTC_ASSERT(m_type == Type::Group, qWarning("Only Group may have children, skipping..."); return); - for (const TaskItem &child : children) { + for (const GroupItem &child : children) { switch (child.m_type) { case Type::Group: m_children.append(child); @@ -534,7 +535,7 @@ void TaskItem::addChildren(const QList &children) } } -void TaskItem::setTaskSetupHandler(const TaskSetupHandler &handler) +void GroupItem::setTaskSetupHandler(const TaskSetupHandler &handler) { if (!handler) { qWarning("Setting empty Setup Handler is no-op, skipping..."); @@ -545,7 +546,7 @@ void TaskItem::setTaskSetupHandler(const TaskSetupHandler &handler) m_taskHandler.m_setupHandler = handler; } -void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler) +void GroupItem::setTaskDoneHandler(const TaskEndHandler &handler) { if (!handler) { qWarning("Setting empty Done Handler is no-op, skipping..."); @@ -556,7 +557,7 @@ void TaskItem::setTaskDoneHandler(const TaskEndHandler &handler) m_taskHandler.m_doneHandler = handler; } -void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler) +void GroupItem::setTaskErrorHandler(const TaskEndHandler &handler) { if (!handler) { qWarning("Setting empty Error Handler is no-op, skipping..."); @@ -567,8 +568,8 @@ void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler) m_taskHandler.m_errorHandler = handler; } -TaskItem TaskItem::withTimeout(const TaskItem &item, milliseconds timeout, - const GroupEndHandler &handler) +GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, + const GroupEndHandler &handler) { const TimeoutTask::EndHandler taskHandler = handler ? [handler](const milliseconds &) { handler(); } : TimeoutTask::EndHandler(); @@ -640,7 +641,7 @@ class TaskContainer Q_DISABLE_COPY_MOVE(TaskContainer) public: - TaskContainer(TaskTreePrivate *taskTreePrivate, const TaskItem &task, + TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode, TaskContainer *parentContainer) : m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {} TaskAction start(); @@ -653,7 +654,7 @@ public: bool isStarting() const { return isRunning() && m_runtimeData->m_startGuard.isLocked(); } struct ConstData { - ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task, TaskNode *parentNode, + ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode, TaskContainer *parentContainer, TaskContainer *thisContainer); ~ConstData() { qDeleteAll(m_children); } TaskTreePrivate * const m_taskTreePrivate = nullptr; @@ -662,7 +663,7 @@ public: const int m_parallelLimit = 1; const WorkflowPolicy m_workflowPolicy = WorkflowPolicy::StopOnError; - const TaskItem::GroupHandler m_groupHandler; + const GroupItem::GroupHandler m_groupHandler; const QList m_storageList; const QList m_children; const int m_taskCount = 0; @@ -693,7 +694,7 @@ class TaskNode Q_DISABLE_COPY_MOVE(TaskNode) public: - TaskNode(TaskTreePrivate *taskTreePrivate, const TaskItem &task, + TaskNode(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskContainer *parentContainer) : m_taskHandler(task.taskHandler()) , m_container(taskTreePrivate, task, this, parentContainer) @@ -711,7 +712,7 @@ public: TaskTree *taskTree() const { return m_container.m_constData.m_taskTreePrivate->q; } private: - const TaskItem::TaskHandler m_taskHandler; + const GroupItem::TaskHandler m_taskHandler; TaskContainer m_container; std::unique_ptr m_task; }; @@ -831,16 +832,16 @@ ReturnType invokeHandler(TaskContainer *container, Handler &&handler, Args &&... } static QList createChildren(TaskTreePrivate *taskTreePrivate, TaskContainer *container, - const TaskItem &task) + const GroupItem &task) { QList result; - const QList &children = task.children(); - for (const TaskItem &child : children) + const QList &children = task.children(); + for (const GroupItem &child : children) result.append(new TaskNode(taskTreePrivate, child, container)); return result; } -TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const TaskItem &task, +TaskContainer::ConstData::ConstData(TaskTreePrivate *taskTreePrivate, const GroupItem &task, TaskNode *parentNode, TaskContainer *parentContainer, TaskContainer *thisContainer) : m_taskTreePrivate(taskTreePrivate) @@ -1035,7 +1036,7 @@ void TaskContainer::stop() void TaskContainer::invokeEndHandler() { - const TaskItem::GroupHandler &groupHandler = m_constData.m_groupHandler; + const GroupItem::GroupHandler &groupHandler = m_constData.m_groupHandler; if (m_runtimeData->m_successBit && groupHandler.m_doneHandler) invokeHandler(this, groupHandler.m_doneHandler); else if (!m_runtimeData->m_successBit && groupHandler.m_errorHandler) @@ -1539,7 +1540,7 @@ void TaskNode::invokeEndHandler(bool success) static QByteArray load(const QString &fileName) { ... } static void save(const QString &fileName, const QByteArray &array) { ... } - static TaskItem copyRecipe(const QString &source, const QString &destination) + static GroupItem copyRecipe(const QString &source, const QString &destination) { struct CopyStorage { // [1] custom inter-task struct QByteArray content; // [2] custom inter-task data diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 647c680b5b0..dcd7b86ac18 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -123,7 +123,7 @@ enum class TaskAction }; Q_ENUM_NS(TaskAction); -class TASKING_EXPORT TaskItem +class TASKING_EXPORT GroupItem { public: // Internal, provided by QTC_DECLARE_CUSTOM_TASK @@ -156,7 +156,7 @@ public: std::optional m_workflowPolicy = {}; }; - QList children() const { return m_children; } + QList children() const { return m_children; } GroupData groupData() const { return m_groupData; } QList storageList() const { return m_storageList; } TaskHandler taskHandler() const { return m_taskHandler; } @@ -169,58 +169,58 @@ protected: TaskHandler }; - TaskItem() = default; - TaskItem(const GroupData &data) + GroupItem() = default; + GroupItem(const GroupData &data) : m_type(Type::GroupData) , m_groupData(data) {} - TaskItem(const TreeStorageBase &storage) + GroupItem(const TreeStorageBase &storage) : m_type(Type::Storage) , m_storageList{storage} {} - TaskItem(const TaskHandler &handler) + GroupItem(const TaskHandler &handler) : m_type(Type::TaskHandler) , m_taskHandler(handler) {} - void addChildren(const QList &children); + void addChildren(const QList &children); void setTaskSetupHandler(const TaskSetupHandler &handler); void setTaskDoneHandler(const TaskEndHandler &handler); void setTaskErrorHandler(const TaskEndHandler &handler); - static TaskItem groupHandler(const GroupHandler &handler) { return TaskItem({handler}); } - static TaskItem parallelLimit(int limit) { return TaskItem({{}, limit}); } - static TaskItem workflowPolicy(WorkflowPolicy policy) { return TaskItem({{}, {}, policy}); } - static TaskItem withTimeout(const TaskItem &item, std::chrono::milliseconds timeout, + static GroupItem groupHandler(const GroupHandler &handler) { return GroupItem({handler}); } + static GroupItem parallelLimit(int limit) { return GroupItem({{}, limit}); } + static GroupItem workflowPolicy(WorkflowPolicy policy) { return GroupItem({{}, {}, policy}); } + static GroupItem withTimeout(const GroupItem &item, std::chrono::milliseconds timeout, const GroupEndHandler &handler = {}); private: Type m_type = Type::Group; - QList m_children; + QList m_children; GroupData m_groupData; QList m_storageList; TaskHandler m_taskHandler; }; -class TASKING_EXPORT Group : public TaskItem +class TASKING_EXPORT Group : public GroupItem { public: - Group(const QList &children) { addChildren(children); } - Group(std::initializer_list children) { addChildren(children); } + Group(const QList &children) { addChildren(children); } + Group(std::initializer_list children) { addChildren(children); } // GroupData related: template - static TaskItem onGroupSetup(SetupHandler &&handler) { + static GroupItem onGroupSetup(SetupHandler &&handler) { return groupHandler({wrapGroupSetup(std::forward(handler))}); } - static TaskItem onGroupDone(const GroupEndHandler &handler) { + static GroupItem onGroupDone(const GroupEndHandler &handler) { return groupHandler({{}, handler}); } - static TaskItem onGroupError(const GroupEndHandler &handler) { + static GroupItem onGroupError(const GroupEndHandler &handler) { return groupHandler({{}, {}, handler}); } - using TaskItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel). - using TaskItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError. + using GroupItem::parallelLimit; // Default: 1 (sequential). 0 means unlimited (parallel). + using GroupItem::workflowPolicy; // Default: WorkflowPolicy::StopOnError. - TaskItem withTimeout(std::chrono::milliseconds timeout, - const GroupEndHandler &handler = {}) const { - return TaskItem::withTimeout(*this, timeout, handler); + GroupItem withTimeout(std::chrono::milliseconds timeout, + const GroupEndHandler &handler = {}) const { + return GroupItem::withTimeout(*this, timeout, handler); } private: @@ -244,31 +244,31 @@ private: }; template -static TaskItem onGroupSetup(SetupHandler &&handler) +static GroupItem onGroupSetup(SetupHandler &&handler) { return Group::onGroupSetup(std::forward(handler)); } -TASKING_EXPORT TaskItem onGroupDone(const TaskItem::GroupEndHandler &handler); -TASKING_EXPORT TaskItem onGroupError(const TaskItem::GroupEndHandler &handler); -TASKING_EXPORT TaskItem parallelLimit(int limit); -TASKING_EXPORT TaskItem workflowPolicy(WorkflowPolicy policy); +TASKING_EXPORT GroupItem onGroupDone(const GroupItem::GroupEndHandler &handler); +TASKING_EXPORT GroupItem onGroupError(const GroupItem::GroupEndHandler &handler); +TASKING_EXPORT GroupItem parallelLimit(int limit); +TASKING_EXPORT GroupItem workflowPolicy(WorkflowPolicy policy); -TASKING_EXPORT extern const TaskItem sequential; -TASKING_EXPORT extern const TaskItem parallel; +TASKING_EXPORT extern const GroupItem sequential; +TASKING_EXPORT extern const GroupItem parallel; -TASKING_EXPORT extern const TaskItem stopOnError; -TASKING_EXPORT extern const TaskItem continueOnError; -TASKING_EXPORT extern const TaskItem stopOnDone; -TASKING_EXPORT extern const TaskItem continueOnDone; -TASKING_EXPORT extern const TaskItem stopOnFinished; -TASKING_EXPORT extern const TaskItem finishAllAndDone; -TASKING_EXPORT extern const TaskItem finishAllAndError; +TASKING_EXPORT extern const GroupItem stopOnError; +TASKING_EXPORT extern const GroupItem continueOnError; +TASKING_EXPORT extern const GroupItem stopOnDone; +TASKING_EXPORT extern const GroupItem continueOnDone; +TASKING_EXPORT extern const GroupItem stopOnFinished; +TASKING_EXPORT extern const GroupItem finishAllAndDone; +TASKING_EXPORT extern const GroupItem finishAllAndError; -class TASKING_EXPORT Storage : public TaskItem +class TASKING_EXPORT Storage : public GroupItem { public: - Storage(const TreeStorageBase &storage) : TaskItem(storage) { } + Storage(const TreeStorageBase &storage) : GroupItem(storage) { } }; // Synchronous invocation. Similarly to Group - isn't counted as a task inside taskCount() @@ -281,7 +281,7 @@ public: private: template - static QList init(Function &&function) { + static QList init(Function &&function) { constexpr bool isInvocable = std::is_invocable_v>; static_assert(isInvocable, "Sync element: The synchronous function can't take any arguments."); @@ -310,17 +310,17 @@ private: }; template -class CustomTask : public TaskItem +class CustomTask : public GroupItem { public: using Task = typename Adapter::Type; using EndHandler = std::function; static Adapter *createAdapter() { return new Adapter; } - CustomTask() : TaskItem({&createAdapter}) {} + CustomTask() : GroupItem({&createAdapter}) {} template CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {}) - : TaskItem({&createAdapter, wrapSetup(std::forward(function)), - wrapEnd(done), wrapEnd(error)}) {} + : GroupItem({&createAdapter, wrapSetup(std::forward(function)), + wrapEnd(done), wrapEnd(error)}) {} template CustomTask &onSetup(SetupFunction &&function) { @@ -336,14 +336,14 @@ public: return *this; } - TaskItem withTimeout(std::chrono::milliseconds timeout, - const GroupEndHandler &handler = {}) const { - return TaskItem::withTimeout(*this, timeout, handler); + GroupItem withTimeout(std::chrono::milliseconds timeout, + const GroupEndHandler &handler = {}) const { + return GroupItem::withTimeout(*this, timeout, handler); } private: template - static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { + static GroupItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { static constexpr bool isDynamic = std::is_same_v, typename Adapter::Type &>>; constexpr bool isVoid = std::is_same_vdeleteLater(); @@ -49,8 +49,8 @@ protected: std::unique_ptr m_taskTree; private: - virtual TaskItem remoteTask() = 0; - virtual TaskItem localTask() = 0; + virtual GroupItem remoteTask() = 0; + virtual GroupItem localTask() = 0; }; static void localRead(QPromise &promise, const FilePath &filePath) @@ -84,7 +84,7 @@ signals: void readyRead(const QByteArray &newData); private: - TaskItem remoteTask() final { + GroupItem remoteTask() final { const auto setup = [this](Process &process) { const QStringList args = {"if=" + m_filePath.path()}; const FilePath dd = m_filePath.withNewPath("dd"); @@ -96,7 +96,7 @@ private: }; return ProcessTask(setup); } - TaskItem localTask() final { + GroupItem localTask() final { const auto setup = [this](Async &async) { async.setConcurrentCallData(localRead, m_filePath); Async *asyncPtr = &async; @@ -251,7 +251,7 @@ signals: void started(); private: - TaskItem remoteTask() final { + GroupItem remoteTask() final { const auto setup = [this](Process &process) { m_writeBuffer = new WriteBuffer(false, &process); connect(m_writeBuffer, &WriteBuffer::writeRequested, &process, &Process::writeRaw); @@ -272,7 +272,7 @@ private: }; return ProcessTask(setup, finalize, finalize); } - TaskItem localTask() final { + GroupItem localTask() final { const auto setup = [this](Async &async) { m_writeBuffer = new WriteBuffer(isBuffered(), &async); async.setConcurrentCallData(localWrite, m_filePath, m_writeData, m_writeBuffer); @@ -390,7 +390,7 @@ public: StreamResult m_streamResult = StreamResult::FinishedWithError; std::unique_ptr m_taskTree; - TaskItem task() { + GroupItem task() { if (m_streamerMode == StreamMode::Reader) return readerTask(); if (m_streamerMode == StreamMode::Writer) @@ -399,7 +399,7 @@ public: } private: - TaskItem readerTask() { + GroupItem readerTask() { const auto setup = [this](FileStreamReader &reader) { m_readBuffer.clear(); reader.setFilePath(m_source); @@ -409,14 +409,14 @@ private: }; return FileStreamReaderTask(setup); } - TaskItem writerTask() { + GroupItem writerTask() { const auto setup = [this](FileStreamWriter &writer) { writer.setFilePath(m_destination); writer.setWriteData(m_writeBuffer); }; return FileStreamWriterTask(setup); } - TaskItem transferTask() { + GroupItem transferTask() { const auto setup = [this](Async &async) { async.setConcurrentCallData(transfer, m_source, m_destination); }; diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index 66bbb256059..e78be579651 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -360,7 +360,7 @@ void TestCodeParser::scanForTests(const QSet &filePaths, using namespace Tasking; - QList tasks{parallelLimit(std::max(QThread::idealThreadCount() / 4, 1))}; + QList tasks{parallelLimit(std::max(QThread::idealThreadCount() / 4, 1))}; for (const FilePath &file : filteredFiles) { const auto setup = [this, codeParsers, file](Async &async) { async.setConcurrentCallData(parseFileForTests, codeParsers, file); diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 5c53d742e65..23912f15d98 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -347,7 +347,7 @@ void TestRunner::runTestsHelper() std::unique_ptr m_outputReader; }; - QList tasks{finishAllAndDone}; + QList tasks{finishAllAndDone}; for (ITestConfiguration *config : m_selectedTests) { QTC_ASSERT(config, continue); diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 11353b04ae4..39de004ac89 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -183,7 +183,7 @@ void ClangToolRunWorker::start() m_filesAnalyzed.clear(); m_filesNotAnalyzed.clear(); - QList tasks{parallelLimit(qMax(1, m_runSettings.parallelJobs()))}; + QList tasks{parallelLimit(qMax(1, m_runSettings.parallelJobs()))}; for (const AnalyzeUnit &unit : std::as_const(unitsToProcess)) { if (!m_diagnosticConfig.isEnabled(tool) && !m_runSettings.hasConfigFileForSourceFile(unit.file)) { diff --git a/src/plugins/clangtools/clangtoolrunner.cpp b/src/plugins/clangtools/clangtoolrunner.cpp index 3e2c7a6c111..6681a9133dd 100644 --- a/src/plugins/clangtools/clangtoolrunner.cpp +++ b/src/plugins/clangtools/clangtoolrunner.cpp @@ -101,9 +101,9 @@ static FilePath createOutputFilePath(const FilePath &dirPath, const FilePath &fi return {}; } -TaskItem clangToolTask(const AnalyzeInputData &input, - const AnalyzeSetupHandler &setupHandler, - const AnalyzeOutputHandler &outputHandler) +GroupItem clangToolTask(const AnalyzeInputData &input, + const AnalyzeSetupHandler &setupHandler, + const AnalyzeOutputHandler &outputHandler) { struct ClangToolStorage { QString name; diff --git a/src/plugins/clangtools/clangtoolrunner.h b/src/plugins/clangtools/clangtoolrunner.h index 3a0f59f3417..a8d1204c224 100644 --- a/src/plugins/clangtools/clangtoolrunner.h +++ b/src/plugins/clangtools/clangtoolrunner.h @@ -10,7 +10,7 @@ #include -namespace Tasking { class TaskItem; } +namespace Tasking { class GroupItem; } namespace ClangTools { namespace Internal { @@ -50,9 +50,9 @@ struct AnalyzeOutputData using AnalyzeSetupHandler = std::function; using AnalyzeOutputHandler = std::function; -Tasking::TaskItem clangToolTask(const AnalyzeInputData &input, - const AnalyzeSetupHandler &setupHandler, - const AnalyzeOutputHandler &outputHandler); +Tasking::GroupItem clangToolTask(const AnalyzeInputData &input, + const AnalyzeSetupHandler &setupHandler, + const AnalyzeOutputHandler &outputHandler); } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/documentclangtoolrunner.cpp b/src/plugins/clangtools/documentclangtoolrunner.cpp index 3de2c6d55cf..93cd27bac38 100644 --- a/src/plugins/clangtools/documentclangtoolrunner.cpp +++ b/src/plugins/clangtools/documentclangtoolrunner.cpp @@ -190,7 +190,7 @@ void DocumentClangToolRunner::run() vfso().update(); const ClangDiagnosticConfig config = diagnosticConfig(runSettings.diagnosticConfigId()); const Environment env = projectBuildEnvironment(project); - QList tasks{parallel}; + QList tasks{parallel}; const auto addClangTool = [this, &runSettings, &config, &env, &tasks](ClangToolType tool) { if (!toolEnabled(tool, config, runSettings)) return; diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp index 3a84a0b548b..0a4665361d2 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.cpp +++ b/src/plugins/coreplugin/locator/ilocatorfilter.cpp @@ -450,7 +450,7 @@ void LocatorMatcher::start() collectorStorage->m_collector = nullptr; }; - QList parallelTasks {parallelLimit(d->m_parallelLimit)}; + QList parallelTasks {parallelLimit(d->m_parallelLimit)}; const auto onSetup = [this, collectorStorage](const TreeStorage &storage, int index) { @@ -597,7 +597,7 @@ QString ILocatorFilter::shortcutString() const \internal Sets the refresh recipe for refreshing cached data. */ -void ILocatorFilter::setRefreshRecipe(const std::optional &recipe) +void ILocatorFilter::setRefreshRecipe(const std::optional &recipe) { m_refreshRecipe = recipe; } @@ -606,7 +606,7 @@ void ILocatorFilter::setRefreshRecipe(const std::optional &recipe) Returns the refresh recipe for refreshing cached data. By default, the locator filter has no recipe set, so that it won't be refreshed. */ -std::optional ILocatorFilter::refreshRecipe() const +std::optional ILocatorFilter::refreshRecipe() const { return m_refreshRecipe; } diff --git a/src/plugins/coreplugin/locator/ilocatorfilter.h b/src/plugins/coreplugin/locator/ilocatorfilter.h index b008c12bb5c..fbc68bc647c 100644 --- a/src/plugins/coreplugin/locator/ilocatorfilter.h +++ b/src/plugins/coreplugin/locator/ilocatorfilter.h @@ -139,7 +139,7 @@ class CORE_EXPORT LocatorMatcherTask final public: // The main task. Initial data (searchTerm) should be taken from storage.input(). // Results reporting is done via the storage.reportOutput(). - Tasking::TaskItem task = Tasking::Group{}; + Tasking::GroupItem task = Tasking::Group{}; // When constructing the task, don't place the storage inside the task above. Tasking::TreeStorage storage; @@ -270,8 +270,8 @@ protected: virtual void saveState(QJsonObject &object) const; virtual void restoreState(const QJsonObject &object); - void setRefreshRecipe(const std::optional &recipe); - std::optional refreshRecipe() const; + void setRefreshRecipe(const std::optional &recipe); + std::optional refreshRecipe() const; static bool isOldSetting(const QByteArray &state); @@ -289,7 +289,7 @@ private: QString m_description; QString m_defaultShortcut; std::optional m_defaultSearchText; - std::optional m_refreshRecipe; + std::optional m_refreshRecipe; QKeySequence m_defaultKeySequence; bool m_defaultIncludedByDefault = false; bool m_includedByDefault = m_defaultIncludedByDefault; diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp index 2deaef9fe09..cefce35a5b1 100644 --- a/src/plugins/coreplugin/locator/locator.cpp +++ b/src/plugins/coreplugin/locator/locator.cpp @@ -381,7 +381,7 @@ void Locator::refresh(const QList &filters) m_refreshingFilters = Utils::filteredUnique(m_refreshingFilters + filters); using namespace Tasking; - QList tasks{parallel}; + QList tasks{parallel}; for (ILocatorFilter *filter : std::as_const(m_refreshingFilters)) { const auto task = filter->refreshRecipe(); if (!task.has_value()) diff --git a/src/plugins/cppeditor/cppprojectupdater.cpp b/src/plugins/cppeditor/cppprojectupdater.cpp index c92d091396e..e4b57c90c5a 100644 --- a/src/plugins/cppeditor/cppprojectupdater.cpp +++ b/src/plugins/cppeditor/cppprojectupdater.cpp @@ -64,7 +64,7 @@ void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo, if (async.isResultAvailable()) storage->projectInfo = async.result(); }; - QList tasks{parallel}; + QList tasks{parallel}; tasks.append(AsyncTask(setupInfoGenerator, onInfoGeneratorDone)); for (QPointer compiler : compilers) { if (compiler && compiler->isDirty()) diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp index 99ceef2f0ab..867217d86d8 100644 --- a/src/plugins/diffeditor/diffeditorplugin.cpp +++ b/src/plugins/diffeditor/diffeditorplugin.cpp @@ -128,7 +128,7 @@ DiffFilesController::DiffFilesController(IDocument *document) outputList->resize(inputList.size()); using namespace std::placeholders; - QList tasks {parallel, finishAllAndDone}; + QList tasks {parallel, finishAllAndDone}; for (int i = 0; i < inputList.size(); ++i) { tasks.append(AsyncTask(std::bind(setupDiff, _1, inputList.at(i)), std::bind(onDiffDone, _1, i))); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index 935b56291ff..4f2bdc4912a 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -441,7 +441,7 @@ ShowController::ShowController(IDocument *document, const QString &id) }; using namespace std::placeholders; - QList tasks {parallel, continueOnDone, onGroupError(onFollowsError)}; + QList tasks {parallel, continueOnDone, onGroupError(onFollowsError)}; for (int i = 0, total = parents.size(); i < total; ++i) { tasks.append(ProcessTask(std::bind(setupFollow, _1, parents.at(i)), std::bind(onFollowDone, _1, i))); diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index 67bb4a63fc3..bbd34a798fa 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -137,7 +137,7 @@ QThreadPool *ExtraCompiler::extraCompilerThreadPool() return s_extraCompilerThreadPool(); } -TaskItem ExtraCompiler::compileFileItem() +GroupItem ExtraCompiler::compileFileItem() { return taskItemImpl(fromFileProvider()); } @@ -326,7 +326,7 @@ ProcessExtraCompiler::ProcessExtraCompiler(const Project *project, const FilePat ExtraCompiler(project, source, targets, parent) { } -TaskItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider) +GroupItem ProcessExtraCompiler::taskItemImpl(const ContentProvider &provider) { const auto setupTask = [=](Async &async) { async.setThreadPool(extraCompilerThreadPool()); diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h index 1dbda0e4242..82bd9070f25 100644 --- a/src/plugins/projectexplorer/extracompiler.h +++ b/src/plugins/projectexplorer/extracompiler.h @@ -24,7 +24,7 @@ class QPromise; class QThreadPool; QT_END_NAMESPACE -namespace Tasking { class TaskItem; } +namespace Tasking { class GroupItem; } namespace Utils { class Process; } namespace ProjectExplorer { @@ -49,7 +49,7 @@ public: Utils::FilePaths targets() const; void forEachTarget(std::function func) const; - Tasking::TaskItem compileFileItem(); + Tasking::GroupItem compileFileItem(); void compileFile(); bool isDirty() const; void block(); @@ -75,7 +75,7 @@ private: void compileContent(const QByteArray &content); void compileImpl(const ContentProvider &provider); void compileIfDirty(); - virtual Tasking::TaskItem taskItemImpl(const ContentProvider &provider) = 0; + virtual Tasking::GroupItem taskItemImpl(const ContentProvider &provider) = 0; const std::unique_ptr d; }; @@ -101,7 +101,7 @@ protected: virtual Tasks parseIssues(const QByteArray &stdErr); private: - Tasking::TaskItem taskItemImpl(const ContentProvider &provider) final; + Tasking::GroupItem taskItemImpl(const ContentProvider &provider) final; void runInThread(QPromise &promise, const Utils::FilePath &cmd, const Utils::FilePath &workDir, const QStringList &args, const ContentProvider &provider, diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index 0da8bebaad2..27e29e497e8 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -330,7 +330,7 @@ void QMakeStep::doRun() emit buildConfiguration()->buildDirectoryInitialized(); }; - QList processList = {ProcessTask(setupQMake, onProcessDone, onProcessError)}; + QList processList = {ProcessTask(setupQMake, onProcessDone, onProcessError)}; if (m_runMakeQmake) processList << ProcessTask(setupMakeQMake, onProcessDone, onProcessError); processList << onGroupDone(onDone); diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp index a5cc9cdad86..d9eb88273e6 100644 --- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp +++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp @@ -90,11 +90,11 @@ public: private: Group deployRecipe(); - TaskItem checkDirTask(); - TaskItem removeDirTask(); - TaskItem uploadTask(); - TaskItem chmodTask(const DeployableFile &file); - TaskItem chmodTree(); + GroupItem checkDirTask(); + GroupItem removeDirTask(); + GroupItem uploadTask(); + GroupItem chmodTask(const DeployableFile &file); + GroupItem chmodTree(); enum class CheckResult { RemoveDir, SkipRemoveDir, Abort }; CheckResult m_checkResult = CheckResult::Abort; @@ -117,7 +117,7 @@ QList collectFilesToUpload(const DeployableFile &deployable) return collected; } -TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask() +GroupItem QnxDeployQtLibrariesDialogPrivate::checkDirTask() { const auto setupHandler = [this](Process &process) { m_deployLogWindow->appendPlainText(Tr::tr("Checking existence of \"%1\"") @@ -145,7 +145,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::checkDirTask() return ProcessTask(setupHandler, doneHandler, errorHandler); } -TaskItem QnxDeployQtLibrariesDialogPrivate::removeDirTask() +GroupItem QnxDeployQtLibrariesDialogPrivate::removeDirTask() { const auto setupHandler = [this](Process &process) { if (m_checkResult != CheckResult::RemoveDir) @@ -162,7 +162,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::removeDirTask() return ProcessTask(setupHandler, {}, errorHandler); } -TaskItem QnxDeployQtLibrariesDialogPrivate::uploadTask() +GroupItem QnxDeployQtLibrariesDialogPrivate::uploadTask() { const auto setupHandler = [this](FileTransfer &transfer) { if (m_deployableFiles.isEmpty()) { @@ -196,7 +196,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::uploadTask() return FileTransferTask(setupHandler, {}, errorHandler); } -TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file) +GroupItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file) { const auto setupHandler = [=](Process &process) { process.setCommand({m_device->filePath("chmod"), @@ -215,7 +215,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTask(const DeployableFile &file return ProcessTask(setupHandler, {}, errorHandler); } -TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTree() +GroupItem QnxDeployQtLibrariesDialogPrivate::chmodTree() { const auto setupChmodHandler = [=](TaskTree &tree) { QList filesToChmod; @@ -223,7 +223,7 @@ TaskItem QnxDeployQtLibrariesDialogPrivate::chmodTree() if (file.isExecutable()) filesToChmod << file; } - QList chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; + QList chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; for (const DeployableFile &file : std::as_const(filesToChmod)) { QTC_ASSERT(file.isValid(), continue); chmodList.append(chmodTask(file)); diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp index 6074eaabaaf..714404ec5b7 100644 --- a/src/plugins/remotelinux/genericdirectuploadstep.cpp +++ b/src/plugins/remotelinux/genericdirectuploadstep.cpp @@ -75,13 +75,13 @@ public: using FilesToStat = std::function(UploadStorage *)>; using StatEndHandler = std::function; - TaskItem statTask(UploadStorage *storage, const DeployableFile &file, - StatEndHandler statEndHandler); - TaskItem statTree(const TreeStorage &storage, FilesToStat filesToStat, - StatEndHandler statEndHandler); - TaskItem uploadTask(const TreeStorage &storage); - TaskItem chmodTask(const DeployableFile &file); - TaskItem chmodTree(const TreeStorage &storage); + GroupItem statTask(UploadStorage *storage, const DeployableFile &file, + StatEndHandler statEndHandler); + GroupItem statTree(const TreeStorage &storage, FilesToStat filesToStat, + StatEndHandler statEndHandler); + GroupItem uploadTask(const TreeStorage &storage); + GroupItem chmodTask(const DeployableFile &file); + GroupItem chmodTree(const TreeStorage &storage); IncrementalDeployment m_incremental = IncrementalDeployment::NotSupported; bool m_ignoreMissingFiles = false; @@ -156,9 +156,9 @@ QDateTime GenericDirectUploadStep::timestampFromStat(const DeployableFile &file, return QDateTime::fromSecsSinceEpoch(secsSinceEpoch); } -TaskItem GenericDirectUploadStep::statTask(UploadStorage *storage, - const DeployableFile &file, - StatEndHandler statEndHandler) +GroupItem GenericDirectUploadStep::statTask(UploadStorage *storage, + const DeployableFile &file, + StatEndHandler statEndHandler) { const auto setupHandler = [=](Process &process) { // We'd like to use --format=%Y, but it's not supported by busybox. @@ -173,13 +173,13 @@ TaskItem GenericDirectUploadStep::statTask(UploadStorage *storage, return ProcessTask(setupHandler, endHandler, endHandler); } -TaskItem GenericDirectUploadStep::statTree(const TreeStorage &storage, - FilesToStat filesToStat, StatEndHandler statEndHandler) +GroupItem GenericDirectUploadStep::statTree(const TreeStorage &storage, + FilesToStat filesToStat, StatEndHandler statEndHandler) { const auto setupHandler = [=](TaskTree &tree) { UploadStorage *storagePtr = storage.activeStorage(); const QList files = filesToStat(storagePtr); - QList statList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; + QList statList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; for (const DeployableFile &file : std::as_const(files)) { QTC_ASSERT(file.isValid(), continue); statList.append(statTask(storagePtr, file, statEndHandler)); @@ -189,7 +189,7 @@ TaskItem GenericDirectUploadStep::statTree(const TreeStorage &sto return TaskTreeTask(setupHandler); } -TaskItem GenericDirectUploadStep::uploadTask(const TreeStorage &storage) +GroupItem GenericDirectUploadStep::uploadTask(const TreeStorage &storage) { const auto setupHandler = [this, storage](FileTransfer &transfer) { if (storage->filesToUpload.isEmpty()) { @@ -229,7 +229,7 @@ TaskItem GenericDirectUploadStep::uploadTask(const TreeStorage &s return FileTransferTask(setupHandler, {}, errorHandler); } -TaskItem GenericDirectUploadStep::chmodTask(const DeployableFile &file) +GroupItem GenericDirectUploadStep::chmodTask(const DeployableFile &file) { const auto setupHandler = [=](Process &process) { process.setCommand({deviceConfiguration()->filePath("chmod"), @@ -248,7 +248,7 @@ TaskItem GenericDirectUploadStep::chmodTask(const DeployableFile &file) return ProcessTask(setupHandler, {}, errorHandler); } -TaskItem GenericDirectUploadStep::chmodTree(const TreeStorage &storage) +GroupItem GenericDirectUploadStep::chmodTree(const TreeStorage &storage) { const auto setupChmodHandler = [=](TaskTree &tree) { QList filesToChmod; @@ -256,7 +256,7 @@ TaskItem GenericDirectUploadStep::chmodTree(const TreeStorage &st if (file.isExecutable()) filesToChmod << file; } - QList chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; + QList chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; for (const DeployableFile &file : std::as_const(filesToChmod)) { QTC_ASSERT(file.isValid(), continue); chmodList.append(chmodTask(file)); diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index 37aea361123..5392fb4245c 100644 --- a/src/plugins/remotelinux/linuxdevicetester.cpp +++ b/src/plugins/remotelinux/linuxdevicetester.cpp @@ -34,20 +34,20 @@ public: QStringList commandsToTest() const; - TaskItem echoTask(const QString &contents) const; - TaskItem unameTask() const; - TaskItem gathererTask() const; - TaskItem transferTask(FileTransferMethod method, - const TreeStorage &storage) const; - TaskItem transferTasks() const; - TaskItem commandTask(const QString &commandName) const; - TaskItem commandTasks() const; + GroupItem echoTask(const QString &contents) const; + GroupItem unameTask() const; + GroupItem gathererTask() const; + GroupItem transferTask(FileTransferMethod method, + const TreeStorage &storage) const; + GroupItem transferTasks() const; + GroupItem commandTask(const QString &commandName) const; + GroupItem commandTasks() const; GenericLinuxDeviceTester *q = nullptr; IDevice::Ptr m_device; std::unique_ptr m_taskTree; QStringList m_extraCommands; - QList m_extraTests; + QList m_extraTests; }; QStringList GenericLinuxDeviceTesterPrivate::commandsToTest() const @@ -90,7 +90,7 @@ QStringList GenericLinuxDeviceTesterPrivate::commandsToTest() const return commands; } -TaskItem GenericLinuxDeviceTesterPrivate::echoTask(const QString &contents) const +GroupItem GenericLinuxDeviceTesterPrivate::echoTask(const QString &contents) const { const auto setup = [this, contents](Process &process) { emit q->progressMessage(Tr::tr("Sending echo to device...")); @@ -114,7 +114,7 @@ TaskItem GenericLinuxDeviceTesterPrivate::echoTask(const QString &contents) cons return ProcessTask(setup, done, error); } -TaskItem GenericLinuxDeviceTesterPrivate::unameTask() const +GroupItem GenericLinuxDeviceTesterPrivate::unameTask() const { const auto setup = [this](Process &process) { emit q->progressMessage(Tr::tr("Checking kernel version...")); @@ -136,7 +136,7 @@ TaskItem GenericLinuxDeviceTesterPrivate::unameTask() const }; } -TaskItem GenericLinuxDeviceTesterPrivate::gathererTask() const +GroupItem GenericLinuxDeviceTesterPrivate::gathererTask() const { const auto setup = [this](DeviceUsedPortsGatherer &gatherer) { emit q->progressMessage(Tr::tr("Checking if specified ports are available...")); @@ -164,8 +164,8 @@ TaskItem GenericLinuxDeviceTesterPrivate::gathererTask() const }; } -TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method, - const TreeStorage &storage) const +GroupItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method, + const TreeStorage &storage) const { const auto setup = [this, method](FileTransfer &transfer) { emit q->progressMessage(Tr::tr("Checking whether \"%1\" works...") @@ -216,7 +216,7 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTask(FileTransferMethod method return FileTransferTestTask(setup, done, error); } -TaskItem GenericLinuxDeviceTesterPrivate::transferTasks() const +GroupItem GenericLinuxDeviceTesterPrivate::transferTasks() const { TreeStorage storage; return Group { @@ -231,7 +231,7 @@ TaskItem GenericLinuxDeviceTesterPrivate::transferTasks() const }; } -TaskItem GenericLinuxDeviceTesterPrivate::commandTask(const QString &commandName) const +GroupItem GenericLinuxDeviceTesterPrivate::commandTask(const QString &commandName) const { const auto setup = [this, commandName](Process &process) { emit q->progressMessage(Tr::tr("%1...").arg(commandName)); @@ -252,9 +252,9 @@ TaskItem GenericLinuxDeviceTesterPrivate::commandTask(const QString &commandName return ProcessTask(setup, done, error); } -TaskItem GenericLinuxDeviceTesterPrivate::commandTasks() const +GroupItem GenericLinuxDeviceTesterPrivate::commandTasks() const { - QList tasks {continueOnError}; + QList tasks {continueOnError}; tasks.append(onGroupSetup([this] { emit q->progressMessage(Tr::tr("Checking if required commands are available...")); })); @@ -279,7 +279,7 @@ void GenericLinuxDeviceTester::setExtraCommandsToTest(const QStringList &extraCo d->m_extraCommands = extraCommands; } -void GenericLinuxDeviceTester::setExtraTests(const QList &extraTests) +void GenericLinuxDeviceTester::setExtraTests(const QList &extraTests) { d->m_extraTests = extraTests; } @@ -295,7 +295,7 @@ void GenericLinuxDeviceTester::testDevice(const IDevice::Ptr &deviceConfiguratio d->m_taskTree.release()->deleteLater(); }; - QList taskItems = { + QList taskItems = { d->echoTask("Hello"), // No quoting necessary d->echoTask("Hello Remote World!"), // Checks quoting, too. d->unameTask(), diff --git a/src/plugins/remotelinux/linuxdevicetester.h b/src/plugins/remotelinux/linuxdevicetester.h index 3fda1ecb3a2..787d22fd109 100644 --- a/src/plugins/remotelinux/linuxdevicetester.h +++ b/src/plugins/remotelinux/linuxdevicetester.h @@ -7,7 +7,7 @@ #include -namespace Tasking { class TaskItem; } +namespace Tasking { class GroupItem; } namespace RemoteLinux { @@ -22,7 +22,7 @@ public: ~GenericLinuxDeviceTester() override; void setExtraCommandsToTest(const QStringList &extraCommands); - void setExtraTests(const QList &extraTests); + void setExtraTests(const QList &extraTests); void testDevice(const ProjectExplorer::IDevice::Ptr &deviceConfiguration) override; void stopTest() override; diff --git a/src/plugins/remotelinux/rsyncdeploystep.cpp b/src/plugins/remotelinux/rsyncdeploystep.cpp index 84eb47b8cc1..317b08bb77c 100644 --- a/src/plugins/remotelinux/rsyncdeploystep.cpp +++ b/src/plugins/remotelinux/rsyncdeploystep.cpp @@ -35,8 +35,8 @@ public: private: bool isDeploymentNecessary() const final; Group deployRecipe() final; - TaskItem mkdirTask(); - TaskItem transferTask(); + GroupItem mkdirTask(); + GroupItem transferTask(); mutable FilesToTransfer m_files; bool m_ignoreMissingFiles = false; @@ -85,7 +85,7 @@ bool RsyncDeployStep::isDeploymentNecessary() const return !m_files.empty(); } -TaskItem RsyncDeployStep::mkdirTask() +GroupItem RsyncDeployStep::mkdirTask() { const auto setupHandler = [this](Process &process) { QStringList remoteDirs; @@ -113,7 +113,7 @@ TaskItem RsyncDeployStep::mkdirTask() return ProcessTask(setupHandler, {}, errorHandler); } -TaskItem RsyncDeployStep::transferTask() +GroupItem RsyncDeployStep::transferTask() { const auto setupHandler = [this](FileTransfer &transfer) { transfer.setTransferMethod(FileTransferMethod::Rsync); diff --git a/src/plugins/remotelinux/tarpackagedeploystep.cpp b/src/plugins/remotelinux/tarpackagedeploystep.cpp index 6b50ca4dced..6deaea90662 100644 --- a/src/plugins/remotelinux/tarpackagedeploystep.cpp +++ b/src/plugins/remotelinux/tarpackagedeploystep.cpp @@ -55,8 +55,8 @@ private: QString remoteFilePath() const; bool isDeploymentNecessary() const final; Group deployRecipe() final; - TaskItem uploadTask(); - TaskItem installTask(); + GroupItem uploadTask(); + GroupItem installTask(); FilePath m_packageFilePath; }; @@ -71,7 +71,7 @@ bool TarPackageDeployStep::isDeploymentNecessary() const return hasLocalFileChanged(DeployableFile(m_packageFilePath, {})); } -TaskItem TarPackageDeployStep::uploadTask() +GroupItem TarPackageDeployStep::uploadTask() { const auto setupHandler = [this](FileTransfer &transfer) { const FilesToTransfer files {{m_packageFilePath, @@ -90,7 +90,7 @@ TaskItem TarPackageDeployStep::uploadTask() return FileTransferTask(setupHandler, doneHandler, errorHandler); } -TaskItem TarPackageDeployStep::installTask() +GroupItem TarPackageDeployStep::installTask() { const auto setupHandler = [this](Process &process) { const QString cmdLine = QLatin1String("cd / && tar xvf ") + remoteFilePath() diff --git a/src/plugins/updateinfo/updateinfoplugin.cpp b/src/plugins/updateinfo/updateinfoplugin.cpp index 749cbbbec64..4891d9e8fdf 100644 --- a/src/plugins/updateinfo/updateinfoplugin.cpp +++ b/src/plugins/updateinfo/updateinfoplugin.cpp @@ -135,7 +135,7 @@ void UpdateInfoPlugin::startCheckForUpdates() d->m_updateOutput = process.cleanedStdOut(); }; - QList tasks { ProcessTask(setupUpdate, updateDone) }; + QList tasks { ProcessTask(setupUpdate, updateDone) }; if (d->m_settings.checkForQtVersions) { const auto setupPackages = [doSetup](Process &process) { doSetup(process, {"se", "qt[.]qt[0-9][.][0-9]+$", "-g", "*=false,ifw.package.*=true"}); diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp index 696e96b2916..b60abaeb5f5 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp @@ -45,7 +45,7 @@ TreeStorage VcsBaseDiffEditorController::inputStorage() const return d->m_inputStorage; } -TaskItem VcsBaseDiffEditorController::postProcessTask() +GroupItem VcsBaseDiffEditorController::postProcessTask() { const auto setupDiffProcessor = [this](Async> &async) { const QString *storage = inputStorage().activeStorage(); diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h index 63976bc26cb..e4b88805065 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.h +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.h @@ -29,7 +29,7 @@ public: protected: Tasking::TreeStorage inputStorage() const; - Tasking::TaskItem postProcessTask(); + Tasking::GroupItem postProcessTask(); void setupCommand(Utils::Process &process, const QStringList &args) const; diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 294bea71b7e..69d87e8188e 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -197,8 +197,8 @@ public: TASKING_DECLARE_TASK(TickAndDoneTask, TickAndDoneTaskAdapter); template -TaskItem createBarrierAdvance(const TreeStorage &storage, - const SharedBarrierType &barrier, int taskId) +GroupItem createBarrierAdvance(const TreeStorage &storage, + const SharedBarrierType &barrier, int taskId) { return TickAndDoneTask([storage, barrier, taskId](TickAndDone &tickAndDone) { tickAndDone.setInterval(1ms); @@ -253,7 +253,7 @@ void tst_Tasking::testTree_data() }; const auto createTask = [storage, setupTask, setupDone, setupError]( - int taskId, bool successTask, milliseconds timeout = 0ms) -> TaskItem { + int taskId, bool successTask, milliseconds timeout = 0ms) -> GroupItem { if (successTask) return TestTask(setupTask(taskId, timeout), setupDone(taskId), setupError(taskId)); const Group root { diff --git a/tests/auto/utils/async/tst_async.cpp b/tests/auto/utils/async/tst_async.cpp index 5d4a81dc961..0e8bc1e2b03 100644 --- a/tests/auto/utils/async/tst_async.cpp +++ b/tests/auto/utils/async/tst_async.cpp @@ -500,7 +500,7 @@ void tst_Async::mapReduce_data() using SetupHandler = std::function &task, int input)>; using DoneHandler = std::function; - const auto createTask = [=](const TaskItem &executeMode, + const auto createTask = [=](const GroupItem &executeMode, const SetupHandler &setupHandler, const DoneHandler &doneHandler) { return Group { diff --git a/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp b/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp index 882a9993a26..50bbd222c9a 100644 --- a/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp +++ b/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp @@ -128,7 +128,7 @@ private slots: async.setConcurrentCallData(generate, parentPath, templateFile, s_treeDepth); }; }; - QList tasks {parallel}; + QList tasks {parallel}; for (int i = 0; i < tasksCount; ++i) { const QString dirName = QString("%1%2").arg(s_dirPrefix).arg(i); QVERIFY(parentDir.mkdir(dirName)); @@ -153,7 +153,7 @@ private slots: async.setConcurrentCallData(removeTree, parentPath); }; }; - QList tasks {parallel}; + QList tasks {parallel}; const int tasksCount = QThread::idealThreadCount(); for (int i = 0; i < tasksCount; ++i) { const QString dirName = QString("%1%2").arg(s_dirPrefix).arg(i); diff --git a/tests/manual/tasking/demo/main.cpp b/tests/manual/tasking/demo/main.cpp index c4b885efe2f..807def8e454 100644 --- a/tests/manual/tasking/demo/main.cpp +++ b/tests/manual/tasking/demo/main.cpp @@ -163,7 +163,7 @@ int main(int argc, char *argv[]) std::unique_ptr taskTree; - const auto createTask = [](TaskWidget *widget) -> TaskItem { + const auto createTask = [](TaskWidget *widget) -> GroupItem { const auto setupTask = [](TaskWidget *widget) { return [widget](milliseconds &taskObject) { taskObject = milliseconds{widget->busyTime() * 1000}; diff --git a/tests/manual/tasking/demo/taskwidget.cpp b/tests/manual/tasking/demo/taskwidget.cpp index 6bc68db1a1b..97d3d2b9ef7 100644 --- a/tests/manual/tasking/demo/taskwidget.cpp +++ b/tests/manual/tasking/demo/taskwidget.cpp @@ -158,7 +158,7 @@ void GroupWidget::updateExecuteMode() m_executeCombo->setCurrentIndex(m_executeCombo->findData((int)m_executeMode)); } -TaskItem GroupWidget::executeMode() const +GroupItem GroupWidget::executeMode() const { return m_executeMode == ExecuteMode::Sequential ? sequential : parallel; } @@ -174,7 +174,7 @@ void GroupWidget::updateWorkflowPolicy() m_workflowCombo->setCurrentIndex(m_workflowCombo->findData((int)m_workflowPolicy)); } -TaskItem GroupWidget::workflowPolicy() const +GroupItem GroupWidget::workflowPolicy() const { return Tasking::workflowPolicy(m_workflowPolicy); } diff --git a/tests/manual/tasking/demo/taskwidget.h b/tests/manual/tasking/demo/taskwidget.h index 3ce210eb0d4..a737e315a4e 100644 --- a/tests/manual/tasking/demo/taskwidget.h +++ b/tests/manual/tasking/demo/taskwidget.h @@ -62,10 +62,10 @@ public: GroupWidget(); void setExecuteMode(ExecuteMode mode); - Tasking::TaskItem executeMode() const; + Tasking::GroupItem executeMode() const; void setWorkflowPolicy(Tasking::WorkflowPolicy policy); - Tasking::TaskItem workflowPolicy() const; + Tasking::GroupItem workflowPolicy() const; private: void updateExecuteMode(); From 0a1073f7cdcef6303ca01afd472838af5a6530b7 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 2 Jun 2023 11:22:47 +0200 Subject: [PATCH 22/57] ClangCodeModel: Restart clangd on changes in VCS repository This should prevent the index from becoming stale after e.g. a git checkout. Change-Id: I94d364d3f2ec38889564c7859ca95ccb2de3019d Reviewed-by: Christian Stenger Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Qt CI Patch Build Bot --- .../clangmodelmanagersupport.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp index 42e77c11a34..a14fb81b901 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -686,6 +687,7 @@ void ClangModelManagerSupport::claimNonProjectSources(ClangdClient *client) // for the respective project to force re-parsing of open documents and re-indexing. // While this is not 100% bullet-proof, chances are good that in a typical session-based // workflow, e.g. a git branch switch will hit at least one open file. +// We also look for repository changes explicitly. void ClangModelManagerSupport::watchForExternalChanges() { connect(DocumentManager::instance(), &DocumentManager::filesChangedExternally, @@ -709,6 +711,23 @@ void ClangModelManagerSupport::watchForExternalChanges() return; } }); + + connect(VcsManager::instance(), &VcsManager::repositoryChanged, + this, [this](const FilePath &repoDir) { + if (sessionModeEnabled()) { + if (ClangdClient * const client = clientForProject(nullptr)) + scheduleClientRestart(client); + return; + } + for (const Project * const project : ProjectManager::projects()) { + const FilePath &projectDir = project->projectDirectory(); + if (repoDir == projectDir || repoDir.isChildOf(projectDir) + || projectDir.isChildOf(repoDir)) { + if (ClangdClient * const client = clientForProject(project)) + scheduleClientRestart(client); + } + } + }); } // If Qt Creator changes a file that is not open (e.g. as part of a quickfix), we have to From b3e51d62428efa807d7d41919fcf7595421fc3db Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Mon, 5 Jun 2023 11:19:13 +0200 Subject: [PATCH 23/57] ProjectExplorer: Fix build progress icon alignment The build progress warnings/error icons were not vertically centered. That was noticeable with the "Relaxed" toobar style. Fixes: QTCREATORBUG-29252 Change-Id: I921ccc936c3233b2b2f80979aaae2b09709dbcff Reviewed-by: Eike Ziller --- src/plugins/projectexplorer/buildprogress.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/buildprogress.cpp b/src/plugins/projectexplorer/buildprogress.cpp index a78b8a1b79d..7166ab474d9 100644 --- a/src/plugins/projectexplorer/buildprogress.cpp +++ b/src/plugins/projectexplorer/buildprogress.cpp @@ -60,8 +60,8 @@ BuildProgress::BuildProgress(TaskWindow *taskWindow, Qt::Orientation orientation m_errorLabel->setProperty("_q_custom_style_disabled", QVariant(true)); m_warningLabel->setProperty("_q_custom_style_disabled", QVariant(true)); - m_errorIcon->setAlignment(Qt::AlignRight); - m_warningIcon->setAlignment(Qt::AlignRight); + m_errorIcon->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + m_warningIcon->setAlignment(Qt::AlignRight | Qt::AlignVCenter); m_errorIcon->setPixmap(Utils::Icons::CRITICAL_TOOLBAR.pixmap()); m_warningIcon->setPixmap(Utils::Icons::WARNING_TOOLBAR.pixmap()); From beaf86f9deb219565da0c1b92e727d3138cf6526 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 6 Jun 2023 09:23:14 +0200 Subject: [PATCH 24/57] Python: filepathify python project path for saving Change-Id: Ibcea9de6c8844cb826b61aa40357890153923854 Reviewed-by: hjk --- src/plugins/python/pythonproject.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/plugins/python/pythonproject.cpp b/src/plugins/python/pythonproject.cpp index a29847ac7bc..86afde46e8b 100644 --- a/src/plugins/python/pythonproject.cpp +++ b/src/plugins/python/pythonproject.cpp @@ -52,12 +52,12 @@ public: QString name() const override { return QLatin1String("python"); } bool saveRawFileList(const QStringList &rawFileList); - bool saveRawList(const QStringList &rawList, const QString &fileName); + bool saveRawList(const QStringList &rawList, const FilePath &filePath); void parse(); QStringList processEntries(const QStringList &paths, QHash *map = nullptr) const; - bool writePyProjectFile(const QString &fileName, QString &content, + bool writePyProjectFile(const FilePath &filePath, QString &content, const QStringList &rawList, QString *errorMessage); void triggerParsing() final; @@ -268,25 +268,24 @@ void PythonBuildSystem::triggerParsing() bool PythonBuildSystem::saveRawFileList(const QStringList &rawFileList) { - const bool result = saveRawList(rawFileList, projectFilePath().toString()); + const bool result = saveRawList(rawFileList, projectFilePath()); // refresh(PythonProject::Files); return result; } -bool PythonBuildSystem::saveRawList(const QStringList &rawList, const QString &fileName) +bool PythonBuildSystem::saveRawList(const QStringList &rawList, const FilePath &filePath) { - const FilePath filePath = FilePath::fromString(fileName); - FileChangeBlocker changeGuarg(filePath); + const FileChangeBlocker changeGuarg(filePath); bool result = false; // New project file - if (fileName.endsWith(".pyproject")) { + if (filePath.endsWith(".pyproject")) { FileSaver saver(filePath, QIODevice::ReadOnly | QIODevice::Text); if (!saver.hasError()) { QString content = QTextStream(saver.file()).readAll(); if (saver.finalize(ICore::dialogParent())) { QString errorMessage; - result = writePyProjectFile(fileName, content, rawList, &errorMessage); + result = writePyProjectFile(filePath, content, rawList, &errorMessage); if (!errorMessage.isEmpty()) MessageManager::writeDisrupting(errorMessage); } @@ -305,13 +304,13 @@ bool PythonBuildSystem::saveRawList(const QStringList &rawList, const QString &f return result; } -bool PythonBuildSystem::writePyProjectFile(const QString &fileName, QString &content, - const QStringList &rawList, QString *errorMessage) +bool PythonBuildSystem::writePyProjectFile(const FilePath &filePath, QString &content, + const QStringList &rawList, QString *errorMessage) { - QFile file(fileName); + QFile file(filePath.toString()); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - *errorMessage = Tr::tr("Unable to open \"%1\" for reading: %2") - .arg(fileName, file.errorString()); + *errorMessage = Tr::tr("Unable to open \"%1\" for writing: %2") + .arg(filePath.toUserOutput(), file.errorString()); return false; } From d617b046c6d2405f2adddbfb0381fc2dc9ee4060 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 6 Jun 2023 09:34:24 +0200 Subject: [PATCH 25/57] Python: support remote file paths when loading projects Change-Id: I94f709275d3b226685439ac55d58b029d3323d12 Reviewed-by: hjk --- src/plugins/python/pythonproject.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plugins/python/pythonproject.cpp b/src/plugins/python/pythonproject.cpp index 86afde46e8b..e9d503037d0 100644 --- a/src/plugins/python/pythonproject.cpp +++ b/src/plugins/python/pythonproject.cpp @@ -90,14 +90,13 @@ private: static QJsonObject readObjJson(const FilePath &projectFile, QString *errorMessage) { - QFile file(projectFile.toString()); - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { - *errorMessage = Tr::tr("Unable to open \"%1\" for reading: %2") - .arg(projectFile.toUserOutput(), file.errorString()); - return QJsonObject(); + const expected_str fileContentsResult = projectFile.fileContents(); + if (!fileContentsResult) { + *errorMessage = fileContentsResult.error(); + return {}; } - const QByteArray content = file.readAll(); + const QByteArray content = *fileContentsResult; // This assumes the project file is formed with only one field called // 'files' that has a list associated of the files to include in the project. From a81c9b497a8a0f58f0920d1f4039d82cea4430b9 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 6 Jun 2023 13:43:13 +0200 Subject: [PATCH 26/57] Tests: Fix qbs build Change-Id: Ia4a54bab4208c5cdf1c7ce67f5caab84aa1583ef Reviewed-by: Christian Stenger --- tests/manual/manual.qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/manual/manual.qbs b/tests/manual/manual.qbs index fecd79518c5..35d08815d26 100644 --- a/tests/manual/manual.qbs +++ b/tests/manual/manual.qbs @@ -15,7 +15,7 @@ Project { "shootout/shootout.qbs", "subdirfileiterator/subdirfileiterator.qbs", "tasking/demo/demo.qbs", - "tasking/demo/imagescaling.qbs", + "tasking/imagescaling/imagescaling.qbs", "widgets/widgets.qbs", ] } From 9cc9ec2aec1a6de499737cdd1f021384e320a997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20L=C3=B6hning?= Date: Thu, 20 Apr 2023 23:54:17 +0200 Subject: [PATCH 27/57] SquishTests: Remove workaround for fixed bug Task-number: QTCREATORBUG-28985 Change-Id: Ib69ccd0f967484a9858ddebdf08a376035e42e90 Reviewed-by: Christian Stenger --- tests/system/suite_QMLS/tst_QMLS04/test.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/system/suite_QMLS/tst_QMLS04/test.py b/tests/system/suite_QMLS/tst_QMLS04/test.py index 2c96939151d..435d993cb2a 100644 --- a/tests/system/suite_QMLS/tst_QMLS04/test.py +++ b/tests/system/suite_QMLS/tst_QMLS04/test.py @@ -48,12 +48,10 @@ def main(): pass # open MyComponent.qml file for verification docOpened = openDocument(myCompTE) - # Work around QTCREATORBUG-28985 - test.xverify(docOpened, "Was MyComponent.qml properly generated in project explorer?") - saveAndExit() - return - # The workaround will be removed in master branch - # Following dead code left in intentionally to still allow merging forward changes in it. + if not test.verify(docOpened, "Was MyComponent.qml properly generated in project explorer?"): + test.fatal("Could not open MyComponent.qml.") + saveAndExit() + return editorArea = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") codeText = str(editorArea.plainText) # there should be Text item in new file From 1348f0cfe07866bc61df1aa23e7ad54d278299de Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 6 Jun 2023 11:07:40 +0200 Subject: [PATCH 28/57] Doc: Fix description of Add Class Member refactoring action The developer must enter the data type of the member only if automatic detection fails. Change-Id: I51c1f36aeb34fb6560701cbd30a5748be5f1cde0 Reviewed-by: Reviewed-by: Christian Kandeler --- doc/qtcreator/src/editors/creator-quick-fixes.qdoc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/qtcreator/src/editors/creator-quick-fixes.qdoc b/doc/qtcreator/src/editors/creator-quick-fixes.qdoc index 57bb973bb28..0f6e7fd58af 100644 --- a/doc/qtcreator/src/editors/creator-quick-fixes.qdoc +++ b/doc/qtcreator/src/editors/creator-quick-fixes.qdoc @@ -482,8 +482,9 @@ \row \li Add Class Member \li Adds a member declaration for the class member being - initialized if it is not yet declared. You must enter - the data type of the member. + initialized if it is not yet declared. If \QC cannot + automatically detect the data type of the member, you + must add it. \li Identifier \row \li Create Implementations for Member Functions From 9b6ff3fb36ac796f0c4e9f5af3d361fb1460fca7 Mon Sep 17 00:00:00 2001 From: hjk Date: Thu, 1 Jun 2023 14:30:06 +0200 Subject: [PATCH 29/57] Vcs: Use PagedSettings for common settings Also restrict the Reset VCS Cache button to the second column, as it was earlier. Change-Id: I291fdceb11df4ecdfdc0887fd521288d0b4544f3 Reviewed-by: Christian Stenger Reviewed-by: --- src/plugins/vcsbase/commonvcssettings.cpp | 103 ++++++++------------ src/plugins/vcsbase/commonvcssettings.h | 21 +--- src/plugins/vcsbase/vcsbaseplugin.cpp | 2 +- src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 20 ++-- src/plugins/vcsbase/vcsplugin.cpp | 14 +-- src/plugins/vcsbase/vcsplugin.h | 5 - 6 files changed, 54 insertions(+), 111 deletions(-) diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp index d286164b5b9..e0c4ba4af62 100644 --- a/src/plugins/vcsbase/commonvcssettings.cpp +++ b/src/plugins/vcsbase/commonvcssettings.cpp @@ -6,18 +6,13 @@ #include "vcsbaseconstants.h" #include "vcsbasetr.h" -#include -#include #include -#include #include #include #include -#include -#include - +using namespace Core; using namespace Utils; namespace VcsBase::Internal { @@ -33,10 +28,24 @@ static QString sshPasswordPromptDefault() return QLatin1String("ssh-askpass"); } +static CommonVcsSettings *s_instance; + +CommonVcsSettings &commonSettings() +{ + return *s_instance; +} + CommonVcsSettings::CommonVcsSettings() { + s_instance = this; + setSettingsGroup("VCS"); - setAutoApply(false); + setId(Constants::VCS_COMMON_SETTINGS_ID); + setDisplayName(Tr::tr("General")); + setCategory(Constants::VCS_SETTINGS_CATEGORY); + // The following act as blueprint for other pages in the same category: + setDisplayCategory(Tr::tr("Version Control")); + setCategoryIconPath(":/vcsbase/images/settingscategory_vcs.png"); nickNameMailMap.setSettingsKey("NickNameMailMap"); nickNameMailMap.setExpectedKind(PathChooser::File); @@ -76,69 +85,37 @@ CommonVcsSettings::CommonVcsSettings() lineWrapWidth.setSettingsKey("LineWrapWidth"); lineWrapWidth.setSuffix(Tr::tr(" characters")); lineWrapWidth.setDefaultValue(72); -} - -// CommonSettingsWidget - -class CommonSettingsWidget final : public Core::IOptionsPageWidget -{ -public: - CommonSettingsWidget(CommonOptionsPage *page) - { - CommonVcsSettings &s = page->settings(); - - auto cacheResetButton = new QPushButton(Tr::tr("Reset VCS Cache")); - cacheResetButton->setToolTip(Tr::tr("Reset information about which " - "version control system handles which directory.")); - - auto updatePath = [&s] { - Environment env; - env.appendToPath(Core::VcsManager::additionalToolsPath()); - s.sshPasswordPrompt.setEnvironment(env); - }; + setLayouter([this] { using namespace Layouting; - Column { - Row { s.lineWrap, s.lineWrapWidth, st }, + return Column { + Row { lineWrap, lineWrapWidth, st }, Form { - s.submitMessageCheckScript, br, - s.nickNameMailMap, br, - s.nickNameFieldListFile, br, - s.sshPasswordPrompt, br, - {}, cacheResetButton + submitMessageCheckScript, br, + nickNameMailMap, br, + nickNameFieldListFile, br, + sshPasswordPrompt, br, + empty, + PushButton { + text(Tr::tr("Reset VCS Cache")), + tooltip(Tr::tr("Reset information about which " + "version control system handles which directory.")), + onClicked(&VcsManager::clearVersionControlCache) + } } - }.attachTo(this); + }; + }); - updatePath(); + auto updatePath = [this] { + Environment env; + env.appendToPath(VcsManager::additionalToolsPath()); + sshPasswordPrompt.setEnvironment(env); + }; - connect(Core::VcsManager::instance(), &Core::VcsManager::configurationChanged, - this, updatePath); - connect(cacheResetButton, &QPushButton::clicked, - Core::VcsManager::instance(), &Core::VcsManager::clearVersionControlCache); + updatePath(); + connect(VcsManager::instance(), &VcsManager::configurationChanged, this, updatePath); - setOnApply([&s] { - if (s.isDirty()) { - s.apply(); - s.writeSettings(Core::ICore::settings()); - emit s.settingsChanged(); - } - }); - } -}; - -// CommonOptionsPage - -CommonOptionsPage::CommonOptionsPage() -{ - m_settings.readSettings(Core::ICore::settings()); - - setId(Constants::VCS_COMMON_SETTINGS_ID); - setDisplayName(Tr::tr("General")); - setCategory(Constants::VCS_SETTINGS_CATEGORY); - // The following act as blueprint for other pages in the same category: - setDisplayCategory(Tr::tr("Version Control")); - setCategoryIconPath(":/vcsbase/images/settingscategory_vcs.png"); - setWidgetCreator([this] { return new CommonSettingsWidget(this); }); + readSettings(); } } // VcsBase::Internal diff --git a/src/plugins/vcsbase/commonvcssettings.h b/src/plugins/vcsbase/commonvcssettings.h index a5005f252a5..0f591b27111 100644 --- a/src/plugins/vcsbase/commonvcssettings.h +++ b/src/plugins/vcsbase/commonvcssettings.h @@ -4,15 +4,12 @@ #pragma once #include - -#include +#include namespace VcsBase::Internal { -class CommonVcsSettings : public Utils::AspectContainer +class CommonVcsSettings : public Core::PagedSettings { - Q_OBJECT - public: CommonVcsSettings(); @@ -26,20 +23,8 @@ public: Utils::BoolAspect lineWrap{this}; Utils::IntegerAspect lineWrapWidth{this}; - -signals: - void settingsChanged(); }; -class CommonOptionsPage final : public Core::IOptionsPage -{ -public: - CommonOptionsPage(); - - CommonVcsSettings &settings() { return m_settings; } - -private: - CommonVcsSettings m_settings; -}; +CommonVcsSettings &commonSettings(); } // VcsBase::Internal diff --git a/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index b72657cdb3c..b73f97dd79c 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -750,7 +750,7 @@ FilePath source(IDocument *document) void setProcessEnvironment(Environment *e) { - const QString prompt = Internal::VcsPlugin::instance()->settings().sshPasswordPrompt.value(); + const QString prompt = Internal::commonSettings().sshPasswordPrompt().path(); if (!prompt.isEmpty()) e->set("SSH_ASKPASS", prompt); } diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 5ddab0da77f..03eaa49c857 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -103,11 +103,6 @@ namespace VcsBase { using namespace Internal; using namespace Utils; -static inline QString submitMessageCheckScript() -{ - return VcsPlugin::instance()->settings().submitMessageCheckScript.value(); -} - class VcsBaseSubmitEditorPrivate { public: @@ -176,15 +171,15 @@ void VcsBaseSubmitEditor::setParameters(const VcsBaseSubmitEditorParameters &par connect(descriptionEdit, &QTextEdit::textChanged, this, &VcsBaseSubmitEditor::fileContentsChanged); - const CommonVcsSettings &settings = VcsPlugin::instance()->settings(); + const CommonVcsSettings &settings = commonSettings(); // Add additional context menu settings - if (!settings.submitMessageCheckScript.value().isEmpty() + if (!settings.submitMessageCheckScript().isEmpty() || !settings.nickNameMailMap.value().isEmpty()) { auto sep = new QAction(this); sep->setSeparator(true); d->m_widget->addDescriptionEditContextMenuAction(sep); // Run check action - if (!settings.submitMessageCheckScript.value().isEmpty()) { + if (!settings.submitMessageCheckScript().isEmpty()) { auto checkAction = new QAction(Tr::tr("Check Message"), this); connect(checkAction, &QAction::triggered, this, &VcsBaseSubmitEditor::slotCheckSubmitMessage); @@ -203,7 +198,7 @@ void VcsBaseSubmitEditor::setParameters(const VcsBaseSubmitEditorParameters &par // wrapping. etc slotUpdateEditorSettings(); - connect(VcsPlugin::instance(), &VcsPlugin::settingsChanged, + connect(&settings, &CommonVcsSettings::changed, this, &VcsBaseSubmitEditor::slotUpdateEditorSettings); connect(Core::EditorManager::instance(), &Core::EditorManager::currentEditorChanged, this, [this] { @@ -229,9 +224,8 @@ VcsBaseSubmitEditor::~VcsBaseSubmitEditor() void VcsBaseSubmitEditor::slotUpdateEditorSettings() { - const CommonVcsSettings &s = VcsPlugin::instance()->settings(); - setLineWrapWidth(s.lineWrapWidth()); - setLineWrap(s.lineWrap()); + setLineWrapWidth(commonSettings().lineWrapWidth()); + setLineWrap(commonSettings().lineWrap()); } // Return a trimmed list of non-empty field texts @@ -527,7 +521,7 @@ void VcsBaseSubmitEditor::slotCheckSubmitMessage() bool VcsBaseSubmitEditor::checkSubmitMessage(QString *errorMessage) const { - const QString checkScript = submitMessageCheckScript(); + const QString checkScript = commonSettings().submitMessageCheckScript.value(); if (checkScript.isEmpty()) return true; QApplication::setOverrideCursor(Qt::WaitCursor); diff --git a/src/plugins/vcsbase/vcsplugin.cpp b/src/plugins/vcsbase/vcsplugin.cpp index 0596f406b8b..c61c8e0bec0 100644 --- a/src/plugins/vcsbase/vcsplugin.cpp +++ b/src/plugins/vcsbase/vcsplugin.cpp @@ -40,7 +40,7 @@ public: explicit VcsPluginPrivate(VcsPlugin *plugin) : q(plugin) { - QObject::connect(&m_settingsPage.settings(), &CommonVcsSettings::settingsChanged, + QObject::connect(&m_settings, &AspectContainer::changed, [this] { slotSettingsChanged(); }); slotSettingsChanged(); } @@ -57,7 +57,7 @@ public: void populateNickNameModel() { QString errorMessage; - if (!NickNameDialog::populateModelFromMailCapFile(m_settingsPage.settings().nickNameMailMap.filePath(), + if (!NickNameDialog::populateModelFromMailCapFile(m_settings.nickNameMailMap(), m_nickNameModel, &errorMessage)) { qWarning("%s", qPrintable(errorMessage)); @@ -71,7 +71,7 @@ public: } VcsPlugin *q; - CommonOptionsPage m_settingsPage; + CommonVcsSettings m_settings; QStandardItemModel *m_nickNameModel = nullptr; }; @@ -101,9 +101,6 @@ void VcsPlugin::initialize() return result; }); - connect(&d->m_settingsPage.settings(), &CommonVcsSettings::settingsChanged, - this, &VcsPlugin::settingsChanged); - JsonWizardFactory::registerPageFactory(new Internal::VcsConfigurationPageFactory); JsonWizardFactory::registerPageFactory(new Internal::VcsCommandPageFactory); @@ -146,11 +143,6 @@ VcsPlugin *VcsPlugin::instance() return m_instance; } -CommonVcsSettings &VcsPlugin::settings() const -{ - return d->m_settingsPage.settings(); -} - /* Delayed creation/update of the nick name model. */ QStandardItemModel *VcsPlugin::nickNameModel() { diff --git a/src/plugins/vcsbase/vcsplugin.h b/src/plugins/vcsbase/vcsplugin.h index 6d4b35c11fd..44be10b23e0 100644 --- a/src/plugins/vcsbase/vcsplugin.h +++ b/src/plugins/vcsbase/vcsplugin.h @@ -15,8 +15,6 @@ class VcsBaseSubmitEditor; namespace Internal { -class CommonVcsSettings; - class VcsPlugin : public ExtensionSystem::IPlugin { Q_OBJECT @@ -30,15 +28,12 @@ public: static VcsPlugin *instance(); - CommonVcsSettings &settings() const; - // Model of user nick names used for the submit // editor. Stored centrally here to achieve delayed // initialization and updating on settings change. QStandardItemModel *nickNameModel(); signals: - void settingsChanged(); void submitEditorAboutToClose(VcsBase::VcsBaseSubmitEditor *e, bool *result); private: From 1c2cf83c697d66069c6b51d614caea79d74e04eb Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 31 May 2023 17:11:05 +0200 Subject: [PATCH 30/57] QmlDesigner: Inline assetexportdialog.ui Change-Id: Ie22beaf507fcf146bcd3c5d228cafa33941425d8 Reviewed-by: Alessandro Portale --- src/plugins/qmldesigner/CMakeLists.txt | 2 +- .../assetexporterplugin/assetexportdialog.cpp | 120 +++++++++++------- .../assetexporterplugin/assetexportdialog.h | 22 ++-- .../assetexporterplugin/assetexportdialog.ui | 81 ------------ 4 files changed, 90 insertions(+), 135 deletions(-) delete mode 100644 src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 8cffad60f1e..30c93baca78 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -1096,7 +1096,7 @@ extend_qtc_plugin(assetexporterplugin extend_qtc_plugin(assetexporterplugin SOURCES_PREFIX assetexporterplugin SOURCES - assetexportdialog.h assetexportdialog.cpp assetexportdialog.ui + assetexportdialog.h assetexportdialog.cpp assetexporter.h assetexporter.cpp assetexporterplugin.h assetexporterplugin.cpp assetexporterview.h assetexporterview.cpp diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index e9b5ab7082b..64845068970 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -1,19 +1,24 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + #include "assetexportdialog.h" -#include "ui_assetexportdialog.h" +#include "../qmldesignertr.h" #include "assetexportpluginconstants.h" #include "filepathmodel.h" #include #include + #include #include #include #include -#include + +#include +#include #include +#include #include #include @@ -22,13 +27,21 @@ #include #include #include +#include +#include +#include +#include #include #include -namespace { -static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString &str, - Utils::OutputFormat format) { +using namespace ProjectExplorer; +using namespace Utils; + +namespace QmlDesigner { + +static void addFormattedMessage(OutputFormatter *formatter, const QString &str, OutputFormat format) +{ if (!formatter) return; @@ -42,79 +55,81 @@ static void addFormattedMessage(Utils::OutputFormatter *formatter, const QString if (isAtBottom) scroll->setValue(scroll->maximum()); } -} -using namespace ProjectExplorer; - -namespace QmlDesigner { -AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, +AssetExportDialog::AssetExportDialog(const FilePath &exportPath, AssetExporter &assetExporter, FilePathModel &model, QWidget *parent) : QDialog(parent), m_assetExporter(assetExporter), m_filePathModel(model), - m_ui(new Ui::AssetExportDialog), m_filesView(new QListView), m_exportLogs(new QPlainTextEdit), m_outputFormatter(new Utils::OutputFormatter()) { - m_ui->setupUi(this); + resize(768, 480); + setWindowTitle(Tr::tr("Export Components")); - m_ui->exportPath->setExpectedKind(Utils::PathChooser::Kind::SaveFile); - m_ui->exportPath->setFilePath( + m_stackedWidget = new QStackedWidget; + + m_exportProgress = new QProgressBar; + m_exportProgress->setRange(0,0); + + auto optionsWidget = new QWidget; + + auto advancedOptions = new DetailsWidget; + advancedOptions->setSummaryText(tr("Advanced Options")); + advancedOptions->setWidget(optionsWidget); + + m_buttonBox = new QDialogButtonBox; + m_buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Close); + + m_exportPath = new PathChooser; + m_exportPath->setExpectedKind(PathChooser::Kind::SaveFile); + m_exportPath->setFilePath( exportPath.pathAppended( ProjectExplorer::ProjectManager::startupProject()->displayName() + ".metadata" )); - m_ui->exportPath->setPromptDialogTitle(tr("Choose Export File")); - m_ui->exportPath->setPromptDialogFilter(tr("Metadata file (*.metadata)")); - m_ui->exportPath->lineEdit()->setReadOnly(true); - m_ui->exportPath->addButton(tr("Open"), this, [this]() { - Core::FileUtils::showInGraphicalShell(Core::ICore::dialogParent(), m_ui->exportPath->filePath()); + m_exportPath->setPromptDialogTitle(tr("Choose Export File")); + m_exportPath->setPromptDialogFilter(tr("Metadata file (*.metadata)")); + m_exportPath->lineEdit()->setReadOnly(true); + m_exportPath->addButton(tr("Open"), this, [this]() { + Core::FileUtils::showInGraphicalShell(Core::ICore::dialogParent(), m_exportPath->filePath()); }); - auto optionsWidget = new QWidget; - m_ui->advancedOptions->setSummaryText(tr("Advanced Options")); - m_ui->advancedOptions->setWidget(optionsWidget); - auto optionsLayout = new QHBoxLayout(optionsWidget); - optionsLayout->setContentsMargins(8, 8, 8, 8); - m_exportAssetsCheck = new QCheckBox(tr("Export assets"), this); m_exportAssetsCheck->setChecked(true); - optionsLayout->addWidget(m_exportAssetsCheck); m_perComponentExportCheck = new QCheckBox(tr("Export components separately"), this); m_perComponentExportCheck->setChecked(false); - optionsLayout->addWidget(m_perComponentExportCheck); - optionsLayout->addStretch(); - m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + m_buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); - m_ui->stackedWidget->addWidget(m_filesView); + m_stackedWidget->addWidget(m_filesView); m_filesView->setModel(&m_filePathModel); m_exportLogs->setReadOnly(true); m_outputFormatter->setPlainTextEdit(m_exportLogs); - m_ui->stackedWidget->addWidget(m_exportLogs); + m_stackedWidget->addWidget(m_exportLogs); switchView(false); - connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() { - m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + connect(m_buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() { + m_buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_assetExporter.cancel(); }); - m_exportBtn = m_ui->buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole); + m_exportBtn = m_buttonBox->addButton(tr("Export"), QDialogButtonBox::AcceptRole); m_exportBtn->setEnabled(false); connect(m_exportBtn, &QPushButton::clicked, this, &AssetExportDialog::onExport); connect(&m_filePathModel, &FilePathModel::modelReset, this, [this]() { - m_ui->exportProgress->setRange(0, 1000); - m_ui->exportProgress->setValue(0); + m_exportProgress->setRange(0, 1000); + m_exportProgress->setValue(0); m_exportBtn->setEnabled(true); }); - connect(m_ui->buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]() { + connect(m_buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, [this]() { close(); }); - m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(false); + m_buttonBox->button(QDialogButtonBox::Close)->setVisible(false); connect(&m_assetExporter, &AssetExporter::stateChanged, this, &AssetExportDialog::onExportStateChanged); @@ -123,7 +138,22 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, connect(TaskHub::instance(), &TaskHub::taskAdded, this, &AssetExportDialog::onTaskAdded); - m_ui->exportProgress->setRange(0,0); + using namespace Layouting; + + Column { + m_exportAssetsCheck, + m_perComponentExportCheck, + st, + noMargin(), + }.attachTo(optionsWidget); + + Column { + Form { Tr::tr("Export path:"), m_exportPath }, + advancedOptions, + m_stackedWidget, + m_exportProgress, + m_buttonBox, + }.attachTo(this); } AssetExportDialog::~AssetExportDialog() @@ -139,7 +169,7 @@ void AssetExportDialog::onExport() TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT); m_exportLogs->clear(); - Utils::FilePath selectedPath = m_ui->exportPath->filePath(); + Utils::FilePath selectedPath = m_exportPath->filePath(); Utils::FilePath exportPath = m_perComponentExportCheck->isChecked() ? (selectedPath.isDir() ? selectedPath : selectedPath.parentDir()) : selectedPath; @@ -154,28 +184,28 @@ void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newStat switch (newState) { case AssetExporter::ParsingState::ExportingDone: m_exportBtn->setVisible(false); - m_ui->buttonBox->button(QDialogButtonBox::Close)->setVisible(true); + m_buttonBox->button(QDialogButtonBox::Close)->setVisible(true); break; default: break; } m_exportBtn->setEnabled(newState == AssetExporter::ParsingState::ExportingDone); - m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy()); + m_buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(m_assetExporter.isBusy()); } void AssetExportDialog::updateExportProgress(double value) { value = std::max(0.0, std::min(1.0, value)); - m_ui->exportProgress->setValue(std::round(value * 1000)); + m_exportProgress->setValue(std::round(value * 1000)); } void AssetExportDialog::switchView(bool showExportView) { if (showExportView) - m_ui->stackedWidget->setCurrentWidget(m_exportLogs); + m_stackedWidget->setCurrentWidget(m_exportLogs); else - m_ui->stackedWidget->setCurrentWidget(m_filesView); + m_stackedWidget->setCurrentWidget(m_filesView); } void AssetExportDialog::onTaskAdded(const ProjectExplorer::Task &task) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h index dacab052d41..bf277f82bbe 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h @@ -1,25 +1,28 @@ // Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + #pragma once + #include "assetexporter.h" +#include + #include #include -#include "utils/fileutils.h" - -#include - QT_BEGIN_NAMESPACE class QPushButton; class QCheckBox; +class QDialogButtonBox; class QListView; class QPlainTextEdit; +class QProgressBar; +class QStackedWidget; QT_END_NAMESPACE - namespace Utils { class OutputFormatter; +class PathChooser; } namespace ProjectExplorer { @@ -27,7 +30,7 @@ class Task; } namespace QmlDesigner { -namespace Ui { class AssetExportDialog; } + class FilePathModel; class AssetExportDialog : public QDialog @@ -49,13 +52,16 @@ private: private: AssetExporter &m_assetExporter; FilePathModel &m_filePathModel; - std::unique_ptr m_ui; QPushButton *m_exportBtn = nullptr; QCheckBox *m_exportAssetsCheck = nullptr; QCheckBox *m_perComponentExportCheck = nullptr; QListView *m_filesView = nullptr; QPlainTextEdit *m_exportLogs = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; + Utils::PathChooser *m_exportPath = nullptr; + QDialogButtonBox *m_buttonBox = nullptr; + QStackedWidget *m_stackedWidget = nullptr; + QProgressBar *m_exportProgress = nullptr; }; -} +} // QmlDesigner diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui deleted file mode 100644 index fa807588676..00000000000 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui +++ /dev/null @@ -1,81 +0,0 @@ - - - QmlDesigner::AssetExportDialog - - - - 0 - 0 - 768 - 480 - - - - Export Components - - - - - - - 0 - 0 - - - - Export path: - - - - - - - - - - 1000 - - - 0 - - - - - - - - 0 - 8 - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Close - - - - - - - - - - - Utils::PathChooser - QWidget -
utils/pathchooser.h
- 1 -
- - Utils::DetailsWidget - QWidget -
utils/detailswidget.h
- 1 -
-
- - -
From 414e15617725df3e64364ff40c1970994c7a9fa0 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 6 Jun 2023 13:28:02 +0200 Subject: [PATCH 31/57] Markdown: Set focus to text editor at start Take two. Amends d9e3d32a80497fbef98073b06f1eaf9bf5811587 Change-Id: Ib3680df9ea7954e00326be2aee71ed2862a74a6e Reviewed-by: Reviewed-by: Christian Stenger --- src/plugins/texteditor/markdowneditor.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/texteditor/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp index 9c89636df8e..90ca41283ec 100644 --- a/src/plugins/texteditor/markdowneditor.cpp +++ b/src/plugins/texteditor/markdowneditor.cpp @@ -30,7 +30,7 @@ const char MARKDOWNVIEWER_MIME_TYPE[] = "text/markdown"; const char MARKDOWNVIEWER_TEXTEDITOR_RIGHT[] = "Markdown.TextEditorRight"; const char MARKDOWNVIEWER_SHOW_EDITOR[] = "Markdown.ShowEditor"; const char MARKDOWNVIEWER_SHOW_PREVIEW[] = "Markdown.ShowPreview"; -const bool kTextEditorRightDefault = true; +const bool kTextEditorRightDefault = false; const bool kShowEditorDefault = true; const bool kShowPreviewDefault = true; @@ -67,8 +67,8 @@ public: context->setContext(Core::Context(MARKDOWNVIEWER_TEXT_CONTEXT)); Core::ICore::addContextObject(context); + m_splitter->addWidget(m_textEditorWidget); // sets splitter->focusWidget() on non-Windows m_splitter->addWidget(m_previewWidget); - m_splitter->addWidget(m_textEditorWidget); setContext(Core::Context(MARKDOWNVIEWER_ID)); From e1f1d45225cc98d33b17735df0ee3465b5d8da52 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 6 Jun 2023 13:05:30 +0200 Subject: [PATCH 32/57] Beautifier: Fix restoring of settings Amends 7dd27301ab2ea3b5d049d167d51dca8b9b5461d5 Fixes: QTCREATORBUG-29250 Change-Id: Iaa0af369055708c738c37ffc466f3d0fe8901b89 Reviewed-by: Reviewed-by: Alessandro Portale --- src/plugins/beautifier/generalsettings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/beautifier/generalsettings.cpp b/src/plugins/beautifier/generalsettings.cpp index 2f7dc4c3a0c..b0da1fc2d4c 100644 --- a/src/plugins/beautifier/generalsettings.cpp +++ b/src/plugins/beautifier/generalsettings.cpp @@ -56,6 +56,7 @@ GeneralSettings::GeneralSettings() st }; }); + readSettings(); } QList GeneralSettings::allowedMimeTypes() const From 58c5c627a5fa7652c8b3e39b463985bca0e9468b Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 31 May 2023 14:29:49 +0200 Subject: [PATCH 33/57] General settings: Move toolbar style to theme setting And fix label style, we don't use book style capitalization for labels. Change-Id: Ibd98c911165f720adee73654d8214adfc02d9f9e Reviewed-by: Reviewed-by: Alessandro Portale Reviewed-by: Qt CI Bot Reviewed-by: Leena Miettinen --- src/plugins/coreplugin/generalsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp index 19b66e64497..7b14eb07baf 100644 --- a/src/plugins/coreplugin/generalsettings.cpp +++ b/src/plugins/coreplugin/generalsettings.cpp @@ -107,6 +107,7 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q) Form form; form.addRow({Tr::tr("Color:"), m_colorButton, resetColorButton, st}); form.addRow({Tr::tr("Theme:"), m_themeChooser}); + form.addRow({Tr::tr("Toolbar style:"), m_toolbarStyleBox, st}); form.addRow({Tr::tr("Language:"), m_languageBox, st}); if (!Utils::HostOsInfo::isMacHost()) { @@ -125,7 +126,6 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q) form.addRow({empty, m_showShortcutsInContextMenus}); form.addRow({Row{m_resetWarningsButton, st}}); form.addRow({Tr::tr("Text codec for tools:"), m_codecBox, st}); - form.addRow({Tr::tr("Toolbar Style:"), m_toolbarStyleBox, st}); Column{Group{title(Tr::tr("User Interface")), form}}.attachTo(this); fillLanguageBox(); From 52db6f38f70d9db2ad78b75d2f80ea6af9d3d4bd Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 14:27:20 +0200 Subject: [PATCH 34/57] Utils: Replace two deprecated uses of .count() Change-Id: I13d683b762c5093231e42c57d58ae322746d9922 Reviewed-by: Reviewed-by: Eike Ziller --- src/libs/utils/buildablehelperlibrary.cpp | 2 +- src/libs/utils/process.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp index cafd5b0452c..a134cb785d8 100644 --- a/src/libs/utils/buildablehelperlibrary.cpp +++ b/src/libs/utils/buildablehelperlibrary.cpp @@ -31,7 +31,7 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) int pos = output.indexOf(toolDir); if (pos == -1) return {}; - pos += toolDir.count(); + pos += toolDir.size(); int end = output.indexOf('\"', pos); if (end == -1) return {}; diff --git a/src/libs/utils/process.cpp b/src/libs/utils/process.cpp index 85d3fa54ed0..e1302671e34 100644 --- a/src/libs/utils/process.cpp +++ b/src/libs/utils/process.cpp @@ -134,7 +134,7 @@ private: } static QString formatField(int number, int fieldWidth, const QString &suffix = {}) { - return QString("%1%2").arg(number, fieldWidth - suffix.count()).arg(suffix); + return QString("%1%2").arg(number, fieldWidth - suffix.size()).arg(suffix); } static int toMs(quint64 nsesc) // nanoseconds to miliseconds From 9db19c653cfecd7b1120ec52a3ddb2f6777e9a54 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 14:32:35 +0200 Subject: [PATCH 35/57] All: Replace deprecated QLibraryInfo::location ... by QLibraryInfo::path() which exists since 6.0 Change-Id: I0e1e071e0d279ddaf1f1027a0e6ce350ab21739a Reviewed-by: Eike Ziller Reviewed-by: --- src/app/main.cpp | 2 +- src/libs/extensionsystem/pluginmanager.cpp | 2 +- src/libs/qmljs/qmljsmodelmanagerinterface.cpp | 4 ++-- src/plugins/coreplugin/generalsettings.cpp | 2 +- src/plugins/coreplugin/loggingmanager.cpp | 2 +- src/plugins/designer/formeditorplugin.cpp | 2 +- src/plugins/help/helpplugin.cpp | 2 +- src/plugins/qmldesigner/puppetenvironmentbuilder.cpp | 2 +- src/plugins/qmljstools/qmljsmodelmanager.cpp | 4 ++-- .../qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp | 2 +- .../qml2puppet/qml2puppet/instances/objectnodeinstance.cpp | 2 +- tests/auto/qml/codemodel/check/tst_check.cpp | 2 +- 12 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/app/main.cpp b/src/app/main.cpp index 3a2cb5bfaae..ec401522928 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -647,7 +647,7 @@ int main(int argc, char **argv) for (QString locale : std::as_const(uiLanguages)) { locale = QLocale(locale).name(); if (translator.load("qtcreator_" + locale, creatorTrPath)) { - const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + const QString &qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); const QString &qtTrFile = QLatin1String("qt_") + locale; // Binary installer puts Qt tr files into creatorTrPath if (qtTranslator.load(qtTrFile, qtTrPath) || qtTranslator.load(qtTrFile, creatorTrPath)) { diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index ae2dbf8903b..b567d753250 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -399,7 +399,7 @@ static QString filled(const QString &s, int min) QString PluginManager::systemInformation() { QString result; - CommandLine qtDiag(FilePath::fromString(QLibraryInfo::location(QLibraryInfo::BinariesPath)) + CommandLine qtDiag(FilePath::fromString(QLibraryInfo::path(QLibraryInfo::BinariesPath)) .pathAppended("qtdiag").withExecutableSuffix()); Process qtDiagProc; qtDiagProc.setCommand(qtDiag); diff --git a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp index 6bcfe7ee25c..66a670d1b8a 100644 --- a/src/libs/qmljs/qmljsmodelmanagerinterface.cpp +++ b/src/libs/qmljs/qmljsmodelmanagerinterface.cpp @@ -114,9 +114,9 @@ ModelManagerInterface::ModelManagerInterface(QObject *parent) qRegisterMetaType("QmlJS::PathsAndLanguages"); m_defaultProjectInfo.qtQmlPath = - FilePath::fromUserInput(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + FilePath::fromUserInput(QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)); m_defaultProjectInfo.qmllsPath = ModelManagerInterface::qmllsForBinPath( - FilePath::fromUserInput(QLibraryInfo::location(QLibraryInfo::BinariesPath)), + FilePath::fromUserInput(QLibraryInfo::path(QLibraryInfo::BinariesPath)), QLibraryInfo::version()); m_defaultProjectInfo.qtVersionString = QLibraryInfo::version().toString(); diff --git a/src/plugins/coreplugin/generalsettings.cpp b/src/plugins/coreplugin/generalsettings.cpp index 7b14eb07baf..eab29faf7d6 100644 --- a/src/plugins/coreplugin/generalsettings.cpp +++ b/src/plugins/coreplugin/generalsettings.cpp @@ -152,7 +152,7 @@ GeneralSettingsWidget::GeneralSettingsWidget(GeneralSettings *q) static bool hasQmFilesForLocale(const QString &locale, const QString &creatorTrPath) { - static const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + static const QString qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); const QString trFile = QLatin1String("/qt_") + locale + QLatin1String(".qm"); return QFile::exists(qtTrPath + trFile) || QFile::exists(creatorTrPath + trFile); diff --git a/src/plugins/coreplugin/loggingmanager.cpp b/src/plugins/coreplugin/loggingmanager.cpp index 665bf425568..608e7ce4706 100644 --- a/src/plugins/coreplugin/loggingmanager.cpp +++ b/src/plugins/coreplugin/loggingmanager.cpp @@ -105,7 +105,7 @@ static QList fetchOriginalRules() }; Utils::FilePath iniFile = Utils::FilePath::fromString( - QLibraryInfo::location(QLibraryInfo::DataPath)).pathAppended("qtlogging.ini"); + QLibraryInfo::path(QLibraryInfo::DataPath)).pathAppended("qtlogging.ini"); if (iniFile.exists()) appendRulesFromFile(iniFile.toString()); diff --git a/src/plugins/designer/formeditorplugin.cpp b/src/plugins/designer/formeditorplugin.cpp index 881e6a30eb5..fd9a78187c5 100644 --- a/src/plugins/designer/formeditorplugin.cpp +++ b/src/plugins/designer/formeditorplugin.cpp @@ -86,7 +86,7 @@ void FormEditorPlugin::initialize() if (!locale.isEmpty()) { auto qtr = new QTranslator(this); const QString creatorTrPath = ICore::resourcePath("translations").toString(); - const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + const QString qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); const QString trFile = "designer_" + locale; if (qtr->load(trFile, qtTrPath) || qtr->load(trFile, creatorTrPath)) QCoreApplication::installTranslator(qtr); diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index 7329b3dad67..9ad90528676 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -182,7 +182,7 @@ HelpPluginPrivate::HelpPluginPrivate() auto qtr = new QTranslator(this); auto qhelptr = new QTranslator(this); const QString creatorTrPath = ICore::resourcePath("translations").toString(); - const QString qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); + const QString qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); const QString trFile = QLatin1String("assistant_") + locale; const QString helpTrFile = QLatin1String("qt_help_") + locale; if (qtr->load(trFile, qtTrPath) || qtr->load(trFile, creatorTrPath)) diff --git a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp index 079cc0a326b..e52c0c70581 100644 --- a/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp +++ b/src/plugins/qmldesigner/puppetenvironmentbuilder.cpp @@ -205,7 +205,7 @@ void PuppetEnvironmentBuilder::addImportPaths() const } if (m_availablePuppetType == PuppetType::Fallback) - importPaths.prepend(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + importPaths.prepend(QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)); constexpr auto pathSep = Utils::HostOsInfo::pathListSeparator(); m_environment.appendOrSet("QML2_IMPORT_PATH", importPaths.join(pathSep), pathSep); diff --git a/src/plugins/qmljstools/qmljsmodelmanager.cpp b/src/plugins/qmljstools/qmljsmodelmanager.cpp index 914a4f77945..23834e82aa4 100644 --- a/src/plugins/qmljstools/qmljsmodelmanager.cpp +++ b/src/plugins/qmljstools/qmljsmodelmanager.cpp @@ -201,9 +201,9 @@ ModelManagerInterface::ProjectInfo ModelManager::defaultProjectInfoForProject( projectInfo.qmllsPath = ModelManagerInterface::qmllsForBinPath(qtVersion->hostBinPath(), v); projectInfo.qtVersionString = qtVersion->qtVersionString(); } else if (!activeKit || !activeKit->value(QtSupport::SuppliesQtQuickImportPath::id(), false).toBool()) { - projectInfo.qtQmlPath = FilePath::fromUserInput(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + projectInfo.qtQmlPath = FilePath::fromUserInput(QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)); projectInfo.qmllsPath = ModelManagerInterface::qmllsForBinPath( - FilePath::fromUserInput(QLibraryInfo::location(QLibraryInfo::BinariesPath)), QLibraryInfo::version()); + FilePath::fromUserInput(QLibraryInfo::path(QLibraryInfo::BinariesPath)), QLibraryInfo::version()); projectInfo.qtVersionString = QLatin1String(qVersion()); } diff --git a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp index d71836319cc..4cf135f0be7 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilerdetailsrewriter_test.cpp @@ -177,7 +177,7 @@ void QmlProfilerDetailsRewriterTest::seedRewriter() QmlJS::PathsAndLanguages lPaths; lPaths.maybeInsert( - Utils::FilePath::fromString(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)), + Utils::FilePath::fromString(QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)), QmlJS::Dialect::Qml); QmlJS::ModelManagerInterface::importScan(QmlJS::ModelManagerInterface::workingCopy(), lPaths, m_modelManager, false); diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 8a982f5cdd5..88689d4c304 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -851,7 +851,7 @@ static inline QString fixComponentPathForIncompatibleQt(const QString &component if (componentPath.contains(importString)) { int index = componentPath.indexOf(importString) + 8; const QString relativeImportPath = componentPath.right(componentPath.length() - index); - QString fixedComponentPath = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath) + relativeImportPath; + QString fixedComponentPath = QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath) + relativeImportPath; fixedComponentPath.replace(QLatin1Char('\\'), QLatin1Char('/')); if (QFileInfo::exists(fixedComponentPath)) return fixedComponentPath; diff --git a/tests/auto/qml/codemodel/check/tst_check.cpp b/tests/auto/qml/codemodel/check/tst_check.cpp index 56ccdfd2a37..befb363ae4c 100644 --- a/tests/auto/qml/codemodel/check/tst_check.cpp +++ b/tests/auto/qml/codemodel/check/tst_check.cpp @@ -73,7 +73,7 @@ void tst_Check::initTestCase() ModelManagerInterface *modelManager = ModelManagerInterface::instance(); PathsAndLanguages lPaths; - QStringList paths(QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath)); + QStringList paths(QLibraryInfo::path(QLibraryInfo::Qml2ImportsPath)); for (auto p: paths) lPaths.maybeInsert(Utils::FilePath::fromString(p), Dialect::Qml); ModelManagerInterface::importScan(ModelManagerInterface::workingCopy(), lPaths, From 390b4aa895da240efaddac04cf6174fb4166368a Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 10:26:19 +0200 Subject: [PATCH 36/57] ProjectExplorer: Replaace some QVariant::type() uses Deprecated since Qt 6.0 in favor of typeId() Change-Id: Ib6ab5196ad9ec4cb2d3dbc2d4f1ceca5cfdcdd07 Reviewed-by: Christian Kandeler --- .../projectexplorer/jsonwizard/jsonfieldpage.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp index 49a95711893..9e73aa479eb 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonfieldpage.cpp @@ -150,7 +150,7 @@ QVariant JsonFieldPage::Field::toSettings() const JsonFieldPage::Field *JsonFieldPage::Field::parse(const QVariant &input, QString *errorMessage) { - if (input.type() != QVariant::Map) { + if (input.typeId() != QVariant::Map) { *errorMessage = Tr::tr("Field is not an object."); return nullptr; } @@ -393,7 +393,7 @@ QDebug &operator<<(QDebug &debug, const JsonFieldPage::Field &field) bool LabelField::parseData(const QVariant &data, QString *errorMessage) { - if (data.type() != QVariant::Map) { + if (data.typeId() != QVariant::Map) { *errorMessage = Tr::tr("Label (\"%1\") data is not an object.").arg(name()); return false; } @@ -431,7 +431,7 @@ bool SpacerField::parseData(const QVariant &data, QString *errorMessage) if (data.isNull()) return true; - if (data.type() != QVariant::Map) { + if (data.typeId() != QVariant::Map) { *errorMessage = Tr::tr("Spacer (\"%1\") data is not an object.").arg(name()); return false; } @@ -476,7 +476,7 @@ bool LineEditField::parseData(const QVariant &data, QString *errorMessage) if (data.isNull()) return true; - if (data.type() != QVariant::Map) { + if (data.typeId() != QVariant::Map) { *errorMessage = Tr::tr("LineEdit (\"%1\") data is not an object.").arg(name()); return false; } @@ -673,7 +673,7 @@ bool TextEditField::parseData(const QVariant &data, QString *errorMessage) if (data.isNull()) return true; - if (data.type() != QVariant::Map) { + if (data.typeId() != QVariant::Map) { *errorMessage = Tr::tr("TextEdit (\"%1\") data is not an object.") .arg(name()); return false; @@ -756,7 +756,7 @@ bool PathChooserField::parseData(const QVariant &data, QString *errorMessage) if (data.isNull()) return true; - if (data.type() != QVariant::Map) { + if (data.typeId() != QVariant::Map) { *errorMessage = Tr::tr("PathChooser data is not an object."); return false; } @@ -861,7 +861,7 @@ bool CheckBoxField::parseData(const QVariant &data, QString *errorMessage) if (data.isNull()) return true; - if (data.type() != QVariant::Map) { + if (data.typeId() != QVariant::Map) { *errorMessage = Tr::tr("CheckBox (\"%1\") data is not an object.").arg(name()); return false; } From 723b71e8978e2271b33c06af50a151e63fc1c894 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 6 Jun 2023 22:36:51 +0200 Subject: [PATCH 37/57] QmlDesigner: fix typo Change-Id: I2390509fab6fc0a6b71f90bacc7173b4540c3dc3 Reviewed-by: Tim Jenssen --- src/plugins/qmldesigner/qmldesignerprojectmanager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp index 4d327b5491d..81b8a32a083 100644 --- a/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp +++ b/src/plugins/qmldesigner/qmldesignerprojectmanager.cpp @@ -335,12 +335,12 @@ void projectQmldirPaths(::ProjectExplorer::Target *target, QStringList &qmldirPa { ::QmlProjectManager::QmlBuildSystem *buildSystem = getQmlBuildSystem(target); - const Utils::FilePath pojectDirectoryPath = buildSystem->canonicalProjectDir(); + const Utils::FilePath projectDirectoryPath = buildSystem->canonicalProjectDir(); const QStringList importPaths = buildSystem->importPaths(); - const QDir pojectDirectory(pojectDirectoryPath.toString()); + const QDir projectDirectory(projectDirectoryPath.toString()); for (const QString &importPath : importPaths) - qmldirPaths.push_back(QDir::cleanPath(pojectDirectory.absoluteFilePath(importPath)) + qmldirPaths.push_back(QDir::cleanPath(projectDirectory.absoluteFilePath(importPath)) + "/qmldir"); } From 07fbf12574fab14a51a9c36d8f98933ff1813014 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Fri, 26 May 2023 10:27:39 +0200 Subject: [PATCH 38/57] McuSupport: Do not try to handle non-existing targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silences the warning message box if Mcu settings page had been opened and stays unconfigured, but Ok or Apply is used to close the settings dialog. Change-Id: I28179b7e7d306bfb08e8066982548ca6b954120d Reviewed-by: Sivert Krøvel Reviewed-by: Yasser Grimes Reviewed-by: Alessandro Portale --- src/plugins/mcusupport/mcusupportoptionspage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/mcusupport/mcusupportoptionspage.cpp b/src/plugins/mcusupport/mcusupportoptionspage.cpp index a05a3771b07..b303a7acae9 100644 --- a/src/plugins/mcusupport/mcusupportoptionspage.cpp +++ b/src/plugins/mcusupport/mcusupportoptionspage.cpp @@ -316,6 +316,9 @@ void McuSupportOptionsWidget::apply() m_settingsHandler->setAutomaticKitCreation(m_options.automaticKitCreationEnabled()); m_options.sdkRepository.expandVariablesAndWildcards(); + if (m_mcuTargetsComboBox->count() == 0) + return; + QMessageBox warningPopup(QMessageBox::Icon::Warning, Tr::tr("Warning"), Tr::tr("Unable to apply changes in Devices > MCU."), From bae8e28feedf87aae78d629bf24cbfd4cd3cf7cf Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 25 May 2023 11:00:13 +0200 Subject: [PATCH 39/57] TextEditor: Add tests for the generic highlighter Change-Id: I5a5092ed0a997800bf6653ab25d69f08f4aa0c37 Reviewed-by: Reviewed-by: Qt CI Bot Reviewed-by: Christian Stenger --- src/plugins/texteditor/CMakeLists.txt | 1 + src/plugins/texteditor/highlighter_test.cpp | 195 ++++++++++++++++++++ src/plugins/texteditor/highlighter_test.h | 26 +++ src/plugins/texteditor/texteditor.qbs | 2 + src/plugins/texteditor/texteditorplugin.cpp | 2 + 5 files changed, 226 insertions(+) create mode 100644 src/plugins/texteditor/highlighter_test.cpp create mode 100644 src/plugins/texteditor/highlighter_test.h diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt index 048cd40b1d5..2a99947b933 100644 --- a/src/plugins/texteditor/CMakeLists.txt +++ b/src/plugins/texteditor/CMakeLists.txt @@ -115,5 +115,6 @@ extend_qtc_plugin(TextEditor CONDITION WITH_TESTS SOURCES codeassist/codeassist_test.cpp codeassist/codeassist_test.h + highlighter_test.cpp highlighter_test.h texteditor_test.cpp ) diff --git a/src/plugins/texteditor/highlighter_test.cpp b/src/plugins/texteditor/highlighter_test.cpp new file mode 100644 index 00000000000..f0652a780b3 --- /dev/null +++ b/src/plugins/texteditor/highlighter_test.cpp @@ -0,0 +1,195 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "syntaxhighlighter.h" + +#include "highlighter_test.h" + +#include "fontsettings.h" +#include "textdocument.h" +#include "texteditor.h" +#include "texteditorsettings.h" + +#include +#include +#include + +#include + +namespace TextEditor::Internal { + +constexpr auto json = R"( +{ + "name": "Test", + "scope": "source.test", + "uuid": "test", + "patterns": [ + { + "match": "a", + "name": "keyword.test" + } + ] +} +)"; + +void GenerigHighlighterTests::initTestCase() +{ + QString title = "test.json"; + + Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, &title, json); + QVERIFY(editor); + m_editor = qobject_cast(editor); + m_editor->editorWidget()->configureGenericHighlighter(Utils::mimeTypeForName("application/json")); + QVERIFY(m_editor); + m_editor->textDocument()->syntaxHighlighter()->rehighlight(); +} + +using FormatRanges = QList; + +QTextCharFormat toFormat(const TextStyle &style) +{ + const static FontSettings fontSettings = TextEditorSettings::fontSettings(); + auto format = fontSettings.toTextCharFormat(style); + if (style == C_FUNCTION) + format.setFontWeight(QFont::Bold); // is explicitly set by the ksyntax format definition + if (style == C_TEXT) { + format = QTextCharFormat(); + format.setFontWeight(QFont::Bold); // is explicitly set by the ksyntax format definition + } + return format; +}; + +void GenerigHighlighterTests::testHighlight_data() +{ + QTest::addColumn("blockNumber"); + QTest::addColumn>("formatRanges"); + + // clang-format off + QTest::addRow("0:") + << 0 + << FormatRanges(); + QTest::addRow("1:{") + << 1 + << FormatRanges{{0, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("2: \"name\": \"Test\",") + << 2 + << FormatRanges{{0, 4, toFormat(C_VISUAL_WHITESPACE)}, + {4, 6, toFormat(C_TYPE)}, + {10, 1, toFormat(C_FUNCTION)}, + {11, 1, toFormat(C_VISUAL_WHITESPACE)}, + {12, 6, toFormat(C_STRING)}, + {18, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("3: \"scope\": \"source.test\",") + << 3 + << FormatRanges{{0, 4, toFormat(C_VISUAL_WHITESPACE)}, + {4, 7, toFormat(C_TYPE)}, + {11, 1, toFormat(C_FUNCTION)}, + {12, 1, toFormat(C_VISUAL_WHITESPACE)}, + {13, 13, toFormat(C_STRING)}, + {26, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("4: \"uuid\": \"test\",") + << 4 + << FormatRanges{{0, 4, toFormat(C_VISUAL_WHITESPACE)}, + {4, 6, toFormat(C_TYPE)}, + {10, 1, toFormat(C_FUNCTION)}, + {11, 1, toFormat(C_VISUAL_WHITESPACE)}, + {12, 6, toFormat(C_STRING)}, + {18, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("5: \"patterns\": [") + << 5 + << FormatRanges{{0, 4, toFormat(C_VISUAL_WHITESPACE)}, + {4, 10, toFormat(C_TYPE)}, + {14, 1, toFormat(C_FUNCTION)}, + {15, 1, toFormat(C_VISUAL_WHITESPACE)}, + {16, 1, toFormat(C_TEXT)}}; + QTest::addRow("6: {") + << 6 + << FormatRanges{{0, 8, toFormat(C_VISUAL_WHITESPACE)}, + {8, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("7: \"match\": \"a\",") + << 7 + << FormatRanges{{0, 12, toFormat(C_VISUAL_WHITESPACE)}, + {12, 7, toFormat(C_TYPE)}, + {19, 1, toFormat(C_FUNCTION)}, + {20, 1, toFormat(C_VISUAL_WHITESPACE)}, + {21, 3, toFormat(C_STRING)}, + {24, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("8: \"name\": \"keyword.test\"") + << 8 + << FormatRanges{{0, 12, toFormat(C_VISUAL_WHITESPACE)}, + {12, 6, toFormat(C_TYPE)}, + {18, 1, toFormat(C_FUNCTION)}, + {19, 1, toFormat(C_VISUAL_WHITESPACE)}, + {20, 14, toFormat(C_STRING)}}; + QTest::addRow("9: }") + << 9 + << FormatRanges{{0, 8, toFormat(C_VISUAL_WHITESPACE)}, + {8, 1, toFormat(C_FUNCTION)}}; + QTest::addRow("10: ]") + << 10 + << FormatRanges{{0, 4, toFormat(C_VISUAL_WHITESPACE)}, + {4, 1, toFormat(C_TEXT)}}; + QTest::addRow("11:}") + << 11 + << FormatRanges{{0, 1, toFormat(C_FUNCTION)}}; + // clang-format on +} + +void compareFormats(const QTextLayout::FormatRange &actual, const QTextLayout::FormatRange &expected) +{ + QCOMPARE(actual.start, expected.start); + QCOMPARE(actual.length, expected.length); + QCOMPARE(actual.format.foreground(), expected.format.foreground()); + QCOMPARE(actual.format.background(), expected.format.background()); + QCOMPARE(actual.format.fontWeight(), expected.format.fontWeight()); + QCOMPARE(actual.format.fontItalic(), expected.format.fontItalic()); + QCOMPARE(actual.format.underlineStyle(), expected.format.underlineStyle()); + QCOMPARE(actual.format.underlineColor(), expected.format.underlineColor()); +} + +void GenerigHighlighterTests::testHighlight() +{ + QFETCH(int, blockNumber); + QFETCH(FormatRanges, formatRanges); + + QTextBlock block = m_editor->textDocument()->document()->findBlockByNumber(blockNumber); + QVERIFY(block.isValid()); + + const QList actualFormats = block.layout()->formats(); + // full hash calculation for QTextCharFormat fails so just check the important entries of format + QCOMPARE(actualFormats.size(), formatRanges.size()); + for (int i = 0; i < formatRanges.size(); ++i) + compareFormats(actualFormats.at(i), formatRanges.at(i)); +} + +void GenerigHighlighterTests::testChange() +{ + QTextBlock block = m_editor->textDocument()->document()->findBlockByNumber(10); + QVERIFY(block.isValid()); + + QTextCursor c(block); + c.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); + c.removeSelectedText(); + m_editor->textDocument()->document()->undo(); + + block = m_editor->textDocument()->document()->findBlockByNumber(10); + QVERIFY(block.isValid()); + + const FormatRanges formatRanges = {{0, 4, toFormat(C_VISUAL_WHITESPACE)}, + {4, 1, toFormat(C_TEXT)}}; + const QList actualFormats = block.layout()->formats(); + // full hash calculation for QTextCharFormat fails so just check the important entries of format + QCOMPARE(actualFormats.size(), formatRanges.size()); + for (int i = 0; i < formatRanges.size(); ++i) + compareFormats(actualFormats.at(i), formatRanges.at(i)); +} + +void GenerigHighlighterTests::cleanupTestCase() +{ + if (m_editor) + Core::EditorManager::closeEditors({m_editor}); + QVERIFY(Core::EditorManager::currentEditor() == nullptr); +} + +} // namespace TextEditor::Internal diff --git a/src/plugins/texteditor/highlighter_test.h b/src/plugins/texteditor/highlighter_test.h new file mode 100644 index 00000000000..30c2848c5c1 --- /dev/null +++ b/src/plugins/texteditor/highlighter_test.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 + +#pragma once + +#include + +namespace TextEditor { class BaseTextEditor; } + +namespace TextEditor::Internal { + +class GenerigHighlighterTests : public QObject +{ + Q_OBJECT +private slots: + void initTestCase(); + void testHighlight_data(); + void testHighlight(); + void testChange(); + void cleanupTestCase(); + +private: + TextEditor::BaseTextEditor *m_editor = nullptr; +}; + +} // namespace TextEditor::Internal diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 071e0b5fc2a..fa8441f29ef 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -226,6 +226,8 @@ Project { files: [ "codeassist/codeassist_test.cpp", "codeassist/codeassist_test.h", + "highlighter_test.cpp", + "highlighter_test.h", "texteditor_test.cpp", ] } diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index 4d5c2268d1e..f82155c0a54 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -22,6 +22,7 @@ #ifdef WITH_TESTS #include "codeassist/codeassist_test.h" +#include "highlighter_test.h" #endif #include @@ -149,6 +150,7 @@ void TextEditorPlugin::initialize() #ifdef WITH_TESTS addTest(); + addTest(); #endif } From 16fc0fcf1cd906897863c5f0bb2e609af0adf56f Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 13:25:14 +0200 Subject: [PATCH 40/57] CppEditor: Reuse QScopeGuard instead of ExecuteOnDestruction Change-Id: Ia60c8eab687599dbaa519daeab74e8799c33af95 Reviewed-by: Qt CI Bot Reviewed-by: David Schulz --- .../clangtools/clangtoolsunittests.cpp | 1 - .../clangdiagnosticconfigswidget.cpp | 4 +- src/plugins/cppeditor/cppautocompleter.cpp | 18 ++--- .../cppbuiltinmodelmanagersupport.cpp | 4 +- src/plugins/cppeditor/cppeditordocument.cpp | 68 +++++++++---------- .../cppeditor/cppmodelmanager_test.cpp | 6 +- src/plugins/cppeditor/cpptoolstestcase.cpp | 10 ++- .../cppeditor/resourcepreviewhoverhandler.cpp | 4 +- .../languageclient/languageclientmanager.cpp | 1 - 9 files changed, 50 insertions(+), 66 deletions(-) diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp index 0672d430548..34ecf11e9a8 100644 --- a/src/plugins/clangtools/clangtoolsunittests.cpp +++ b/src/plugins/clangtools/clangtoolsunittests.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include diff --git a/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp b/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp index 8e7e1b8f84f..6863603c95a 100644 --- a/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp +++ b/src/plugins/cppeditor/clangdiagnosticconfigswidget.cpp @@ -8,7 +8,6 @@ #include "wrappablelineedit.h" #include -#include #include #include #include @@ -21,6 +20,7 @@ #include #include #include +#include #include #include @@ -323,7 +323,7 @@ void ClangDiagnosticConfigsWidget::sync() return; disconnectClangOnlyOptionsChanged(); - ExecuteOnDestruction e([this] { connectClangOnlyOptionsChanged(); }); + const QScopeGuard cleanup([this] { connectClangOnlyOptionsChanged(); }); // Update main button row const ClangDiagnosticConfig &config = currentConfig(); diff --git a/src/plugins/cppeditor/cppautocompleter.cpp b/src/plugins/cppeditor/cppautocompleter.cpp index 268dab96431..c587654e7e8 100644 --- a/src/plugins/cppeditor/cppautocompleter.cpp +++ b/src/plugins/cppeditor/cppautocompleter.cpp @@ -20,8 +20,8 @@ #include #include #include -#include +#include #include #endif // WITH_TESTS @@ -267,9 +267,7 @@ void AutoCompleterTest::testAutoComplete() QVERIFY(text.contains(QLatin1Char('|'))); - Utils::ExecuteOnDestruction guard([](){ - Core::EditorManager::closeAllEditors(false); - }); + const QScopeGuard cleanup([] { Core::EditorManager::closeAllEditors(false); }); QTextCursor tc = openEditor(text); QVERIFY(!tc.isNull()); @@ -328,9 +326,7 @@ void AutoCompleterTest::testSurroundWithSelection() QVERIFY(text.count(QLatin1Char('|')) == 2); - Utils::ExecuteOnDestruction guard([](){ - Core::EditorManager::closeAllEditors(false); - }); + const QScopeGuard cleanup([] { Core::EditorManager::closeAllEditors(false); }); QTextCursor tc = openEditor(text); QVERIFY(!tc.isNull()); @@ -363,9 +359,7 @@ void AutoCompleterTest::testAutoBackspace() QVERIFY(text.contains(QLatin1Char('|'))); - Utils::ExecuteOnDestruction guard([](){ - Core::EditorManager::closeAllEditors(false); - }); + const QScopeGuard cleanup([] { Core::EditorManager::closeAllEditors(false); }); QTextCursor tc = openEditor(text); QVERIFY(!tc.isNull()); @@ -405,9 +399,7 @@ void AutoCompleterTest::testInsertParagraph() QVERIFY(text.contains(QLatin1Char('|'))); - Utils::ExecuteOnDestruction guard([](){ - Core::EditorManager::closeAllEditors(false); - }); + const QScopeGuard cleanup([] { Core::EditorManager::closeAllEditors(false); }); QTextCursor tc = openEditor(text); QVERIFY(!tc.isNull()); diff --git a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp index c59fd475bda..efcbd51569d 100644 --- a/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp +++ b/src/plugins/cppeditor/cppbuiltinmodelmanagersupport.cpp @@ -16,12 +16,12 @@ #include #include #include -#include #include #include #include #include +#include #include using namespace Core; @@ -42,7 +42,7 @@ private: return; } - Utils::ExecuteOnDestruction reportPriority([this, report](){ report(priority()); }); + const QScopeGuard cleanup([this, report] { report(priority()); }); QTextCursor tc(editorWidget->document()); tc.setPosition(pos); diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 328b2c23460..a1d01575c58 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include #include +#include #include const char NO_PROJECT_CONFIGURATION[] = "NoProject"; @@ -437,47 +437,45 @@ TextEditor::TabSettings CppEditorDocument::tabSettings() const bool CppEditorDocument::save(QString *errorString, const FilePath &filePath, bool autoSave) { - ExecuteOnDestruction resetSettingsOnScopeExit; + if (!indenter()->formatOnSave() || autoSave) + return TextEditor::TextDocument::save(errorString, filePath, autoSave); - if (indenter()->formatOnSave() && !autoSave) { - auto *layout = qobject_cast(document()->documentLayout()); - const int documentRevision = layout->lastSaveRevision; + auto *layout = qobject_cast(document()->documentLayout()); + const int documentRevision = layout->lastSaveRevision; - TextEditor::RangesInLines editedRanges; - TextEditor::RangeInLines lastRange{-1, -1}; - for (int i = 0; i < document()->blockCount(); ++i) { - const QTextBlock block = document()->findBlockByNumber(i); - if (block.revision() == documentRevision) { - if (lastRange.startLine != -1) - editedRanges.push_back(lastRange); + TextEditor::RangesInLines editedRanges; + TextEditor::RangeInLines lastRange{-1, -1}; + for (int i = 0; i < document()->blockCount(); ++i) { + const QTextBlock block = document()->findBlockByNumber(i); + if (block.revision() == documentRevision) { + if (lastRange.startLine != -1) + editedRanges.push_back(lastRange); - lastRange.startLine = lastRange.endLine = -1; - continue; - } - - // block.revision() != documentRevision - if (lastRange.startLine == -1) - lastRange.startLine = block.blockNumber() + 1; - lastRange.endLine = block.blockNumber() + 1; + lastRange.startLine = lastRange.endLine = -1; + continue; } - if (lastRange.startLine != -1) - editedRanges.push_back(lastRange); - - if (!editedRanges.empty()) { - QTextCursor cursor(document()); - cursor.joinPreviousEditBlock(); - indenter()->format(editedRanges); - cursor.endEditBlock(); - } - - TextEditor::StorageSettings settings = storageSettings(); - resetSettingsOnScopeExit.reset( - [this, defaultSettings = settings]() { setStorageSettings(defaultSettings); }); - settings.m_cleanWhitespace = false; - setStorageSettings(settings); + // block.revision() != documentRevision + if (lastRange.startLine == -1) + lastRange.startLine = block.blockNumber() + 1; + lastRange.endLine = block.blockNumber() + 1; } + if (lastRange.startLine != -1) + editedRanges.push_back(lastRange); + + if (!editedRanges.empty()) { + QTextCursor cursor(document()); + cursor.joinPreviousEditBlock(); + indenter()->format(editedRanges); + cursor.endEditBlock(); + } + + TextEditor::StorageSettings settings = storageSettings(); + const QScopeGuard cleanup([this, settings] { setStorageSettings(settings); }); + settings.m_cleanWhitespace = false; + setStorageSettings(settings); + return TextEditor::TextDocument::save(errorString, filePath, autoSave); } diff --git a/src/plugins/cppeditor/cppmodelmanager_test.cpp b/src/plugins/cppeditor/cppmodelmanager_test.cpp index c3fb3416469..cb292293e44 100644 --- a/src/plugins/cppeditor/cppmodelmanager_test.cpp +++ b/src/plugins/cppeditor/cppmodelmanager_test.cpp @@ -20,11 +20,11 @@ #include #include -#include #include #include #include +#include #include #define VERIFY_DOCUMENT_REVISION(document, expectedRevision) \ @@ -1085,9 +1085,7 @@ void ModelManagerTest::testRenameIncludesInEditor() Core::IEditor *editor = Core::EditorManager::openEditor(mainFile); QVERIFY(editor); EditorCloser editorCloser(editor); - Utils::ExecuteOnDestruction saveAllFiles([](){ - Core::DocumentManager::saveAllModifiedDocumentsSilently(); - }); + const QScopeGuard cleanup([] { Core::DocumentManager::saveAllModifiedDocumentsSilently(); }); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); QVERIFY(modelManager->isCppEditor(editor)); QVERIFY(modelManager->workingCopy().get(mainFile)); diff --git a/src/plugins/cppeditor/cpptoolstestcase.cpp b/src/plugins/cppeditor/cpptoolstestcase.cpp index 706423c0515..810f66db49c 100644 --- a/src/plugins/cppeditor/cpptoolstestcase.cpp +++ b/src/plugins/cppeditor/cpptoolstestcase.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -376,11 +375,8 @@ ProjectOpenerAndCloser::~ProjectOpenerAndCloser() return; bool hasGcFinished = false; - QMetaObject::Connection connection; - Utils::ExecuteOnDestruction disconnect([&]() { QObject::disconnect(connection); }); - connection = QObject::connect(CppModelManager::instance(), &CppModelManager::gcFinished, [&]() { - hasGcFinished = true; - }); + auto connection = QObject::connect(CppModelManager::instance(), &CppModelManager::gcFinished, + [&hasGcFinished] { hasGcFinished = true; }); for (Project *project : std::as_const(m_openProjects)) ProjectExplorerPlugin::unloadProject(project); @@ -389,6 +385,8 @@ ProjectOpenerAndCloser::~ProjectOpenerAndCloser() t.start(); while (!hasGcFinished && t.elapsed() <= 30000) QCoreApplication::processEvents(); + + QObject::disconnect(connection); } ProjectInfo::ConstPtr ProjectOpenerAndCloser::open(const FilePath &projectFile, diff --git a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp index 6c282b879cc..0c5c43e83a8 100644 --- a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp +++ b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp @@ -8,13 +8,13 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include @@ -148,7 +148,7 @@ void ResourcePreviewHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos, ReportPriority report) { - Utils::ExecuteOnDestruction reportPriority([this, report](){ report(priority()); }); + const QScopeGuard cleanup([this, report] { report(priority()); }); if (editorWidget->extraSelectionTooltip(pos).isEmpty()) { const QTextBlock tb = editorWidget->document()->findBlock(pos); diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index 50a38071892..86485ff8aaf 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -28,7 +28,6 @@ #include #include -#include #include #include From 6d5b725d454b975df275f44d5e74507a4fa8face Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 16:34:44 +0200 Subject: [PATCH 41/57] Debugger: Replace some uses of deprecated QVariant::type Change-Id: I2225668b51d82e017cda305737f8c93fdaf97bd7 Reviewed-by: Eike Ziller --- src/plugins/debugger/qml/qmlengine.cpp | 4 ++-- src/plugins/debugger/qml/qmlinspectoragent.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 033069c1165..6a304f18a67 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -776,7 +776,7 @@ void QmlEngine::assignValueInDebugger(WatchItem *item, const QString &expression, const QVariant &editValue) { if (!expression.isEmpty()) { - QTC_CHECK(editValue.type() == QVariant::String); + QTC_CHECK(editValue.typeId() == QVariant::String); QVariant value; QString val = editValue.toString(); if (item->type == "boolean") @@ -855,7 +855,7 @@ static ConsoleItem *constructLogItemTree(const QVariant &result, QString text; ConsoleItem *item = nullptr; - if (result.type() == QVariant::Map) { + if (result.typeId() == QVariant::Map) { if (key.isEmpty()) text = "Object"; else diff --git a/src/plugins/debugger/qml/qmlinspectoragent.cpp b/src/plugins/debugger/qml/qmlinspectoragent.cpp index 4b632c0f0db..712b66e5b35 100644 --- a/src/plugins/debugger/qml/qmlinspectoragent.cpp +++ b/src/plugins/debugger/qml/qmlinspectoragent.cpp @@ -221,7 +221,7 @@ void QmlInspectorAgent::onResult(quint32 queryId, const QVariant &value, if (m_objectTreeQueryIds.contains(queryId)) { m_objectTreeQueryIds.removeOne(queryId); - if (value.type() == QVariant::List) { + if (value.typeId() == QVariant::List) { const QVariantList objList = value.toList(); for (const QVariant &var : objList) { // TODO: check which among the list is the actual @@ -289,7 +289,7 @@ static void sortChildrenIfNecessary(WatchItem *propertiesWatch) static bool insertChildren(WatchItem *parent, const QVariant &value) { - switch (value.type()) { + switch (value.typeId()) { case QVariant::Map: { const QVariantMap map = value.toMap(); for (auto it = map.begin(), end = map.end(); it != end; ++it) { From 2934e30ad0dbdc58b4f6fcb54dbf58ecc8465316 Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Mon, 5 Jun 2023 15:41:20 +0200 Subject: [PATCH 42/57] NetworkAccessManager: Remove unneeded includes Change-Id: I8263984565a9eaf2393612843c2eb79ce9e118f5 Reviewed-by: Eike Ziller Reviewed-by: Qt CI Bot Reviewed-by: --- src/libs/utils/networkaccessmanager.cpp | 7 +------ src/libs/utils/networkaccessmanager.h | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/libs/utils/networkaccessmanager.cpp b/src/libs/utils/networkaccessmanager.cpp index 53de288fabb..81ca77379e9 100644 --- a/src/libs/utils/networkaccessmanager.cpp +++ b/src/libs/utils/networkaccessmanager.cpp @@ -4,13 +4,9 @@ #include "networkaccessmanager.h" #include -#include #include #include - -#ifdef Q_OS_UNIX -#include -#endif +#include /*! \class Utils::NetworkAccessManager @@ -75,5 +71,4 @@ QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkR return QNetworkAccessManager::createRequest(op, req, outgoingData); } - } // namespace utils diff --git a/src/libs/utils/networkaccessmanager.h b/src/libs/utils/networkaccessmanager.h index bd6987474c9..725757c2fe6 100644 --- a/src/libs/utils/networkaccessmanager.h +++ b/src/libs/utils/networkaccessmanager.h @@ -7,10 +7,6 @@ #include -QT_BEGIN_NAMESPACE -class QUrl; -QT_END_NAMESPACE - namespace Utils { class QTCREATOR_UTILS_EXPORT NetworkAccessManager : public QNetworkAccessManager From 07374a7114474db3581edb6242abdb8f75d90c3a Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 6 Jun 2023 11:58:11 +0200 Subject: [PATCH 43/57] ClangFormat: Do not change incompatible settings Fixes: QTCREATORBUG-29190 Change-Id: I81730f59f05c8aba718a60deebfeb2b1b3aa0d7f Reviewed-by: Artem Sokolovskii Reviewed-by: Christian Kandeler --- src/plugins/clangformat/clangformatfile.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/plugins/clangformat/clangformatfile.cpp b/src/plugins/clangformat/clangformatfile.cpp index f0c48099131..fc1659b976c 100644 --- a/src/plugins/clangformat/clangformatfile.cpp +++ b/src/plugins/clangformat/clangformatfile.cpp @@ -158,8 +158,6 @@ CppEditor::CppCodeStyleSettings ClangFormatFile::toCppCodeStyleSettings( settings.indentSwitchLabels = style.IndentCaseLabels; #if LLVM_VERSION_MAJOR >= 11 settings.indentBlocksRelativeToSwitchLabels = style.IndentCaseBlocks; - settings.indentStatementsRelativeToSwitchLabels = style.IndentCaseBlocks; - settings.indentControlFlowRelativeToSwitchLabels = style.IndentCaseBlocks; #endif if (style.DerivePointerAlignment && ClangFormatSettings::instance().mode() == ClangFormatSettings::Mode::Formatting) { @@ -200,9 +198,7 @@ void ClangFormatFile::fromCppCodeStyleSettings(const CppEditor::CppCodeStyleSett m_style.IndentCaseLabels = settings.indentSwitchLabels; #if LLVM_VERSION_MAJOR >= 11 - m_style.IndentCaseBlocks = settings.indentBlocksRelativeToSwitchLabels - || settings.indentStatementsRelativeToSwitchLabels - || settings.indentControlFlowRelativeToSwitchLabels; + m_style.IndentCaseBlocks = settings.indentBlocksRelativeToSwitchLabels; #endif if (settings.extraPaddingForConditionsIfConfusingAlign) From d58214fc355ac106d87509058ade7803a05f6d93 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 6 Jun 2023 10:20:11 +0200 Subject: [PATCH 44/57] Doc: Update info on using the Locator - You can now locate files on remote file systems and switch the device root - You can now create directories from the locator Task-number: QTCREATORBUG-28996 Change-Id: I85ca475e76db7f8fa5d97b4e9d9c6b3aa80b4a32 Reviewed-by: Eike Ziller --- doc/qtcreator/src/editors/creator-locator.qdoc | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/qtcreator/src/editors/creator-locator.qdoc b/doc/qtcreator/src/editors/creator-locator.qdoc index e5a55f8d6a9..c7add7c980f 100644 --- a/doc/qtcreator/src/editors/creator-locator.qdoc +++ b/doc/qtcreator/src/editors/creator-locator.qdoc @@ -74,6 +74,11 @@ \section2 Locating Files + You can locate files in the local file system or on connected devices. By + default, the file system filter shows the files in the same folder as the + currently open file and lets you navigate the file system. Also, it shows + items that let you switch to another device root. + For example, to open a QML file called \e HelloWorld.qml in the currently open project using the locator: @@ -143,16 +148,19 @@ such as \c {Utils::*View}. \endif - \section2 Creating Files from Locator + \section2 Creating Files and Directories from Locator To create a new file and open it in the editor, type \c f followed by \key Space, followed by path and file name, and then press - \key Enter. + \key Enter or select \uicontrol {Create and Open File}. To create a + directory, select \uicontrol {Create Directory}. + + \section2 Opening Sessions from Locator You can use the filter that triggers menu commands to open \l{Managing Sessions}{sessions}. Enter \c {t yoursess} or \c {t sess yoursess} to trigger \uicontrol File > - \uicontrol Sessions > \e yoursessionname. + \uicontrol Sessions > \e . \section2 Default Filters From ce7677a7d7cfb0980e7e8c92d27e652ac35bc99b Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 30 May 2023 13:47:49 +0200 Subject: [PATCH 45/57] Kits: Sort kit parts by device Change-Id: I00e1db4897071ac6baf97c5bb6214c2658a7b9fb Reviewed-by: hjk --- .../cmakekitinformation.cpp | 98 +++++++------------ .../debugger/debuggerkitinformation.cpp | 20 +++- .../projectexplorer/kitinformation.cpp | 26 +++-- src/plugins/qtsupport/qtkitinformation.cpp | 64 ++++++------ 4 files changed, 110 insertions(+), 98 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index def549f5800..54add7395f4 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -75,22 +75,15 @@ public: m_comboBox->setEnabled(false); m_comboBox->setToolTip(ki->description()); - const QList tools = CMakeToolManager::cmakeTools(); - for (const CMakeTool *tool : tools) - cmakeToolAdded(tool->id()); - - updateComboBox(); refresh(); + connect(m_comboBox, &QComboBox::currentIndexChanged, this, &CMakeKitAspectWidget::currentCMakeToolChanged); CMakeToolManager *cmakeMgr = CMakeToolManager::instance(); - connect(cmakeMgr, &CMakeToolManager::cmakeAdded, - this, &CMakeKitAspectWidget::cmakeToolAdded); - connect(cmakeMgr, &CMakeToolManager::cmakeRemoved, - this, &CMakeKitAspectWidget::cmakeToolRemoved); - connect(cmakeMgr, &CMakeToolManager::cmakeUpdated, - this, &CMakeKitAspectWidget::cmakeToolUpdated); + connect(cmakeMgr, &CMakeToolManager::cmakeAdded, this, &CMakeKitAspectWidget::refresh); + connect(cmakeMgr, &CMakeToolManager::cmakeRemoved, this, &CMakeKitAspectWidget::refresh); + connect(cmakeMgr, &CMakeToolManager::cmakeUpdated, this, &CMakeKitAspectWidget::refresh); } ~CMakeKitAspectWidget() override @@ -112,6 +105,37 @@ private: void refresh() override { + const GuardLocker locker(m_ignoreChanges); + m_comboBox->clear(); + + IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); + const FilePath rootPath = device->rootPath(); + + const auto list = CMakeToolManager::cmakeTools(); + + m_comboBox->setEnabled(!list.isEmpty()); + + if (list.isEmpty()) { + m_comboBox->addItem(Tr::tr(""), Id().toSetting()); + return; + } + + const QList same = Utils::filtered(list, [rootPath](CMakeTool *item) { + return item->cmakeExecutable().isSameDevice(rootPath); + }); + const QList other = Utils::filtered(list, [rootPath](CMakeTool *item) { + return !item->cmakeExecutable().isSameDevice(rootPath); + }); + + for (CMakeTool *item : same) + m_comboBox->addItem(item->displayName(), item->id().toSetting()); + + if (!same.isEmpty() && !other.isEmpty()) + m_comboBox->insertSeparator(m_comboBox->count()); + + for (CMakeTool *item : other) + m_comboBox->addItem(item->displayName(), item->id().toSetting()); + CMakeTool *tool = CMakeKitAspect::cmakeTool(m_kit); m_comboBox->setCurrentIndex(tool ? indexOf(tool->id()) : -1); } @@ -125,58 +149,6 @@ private: return -1; } - void updateComboBox() - { - // remove unavailable cmake tool: - int pos = indexOf(Id()); - if (pos >= 0) - m_comboBox->removeItem(pos); - - if (m_comboBox->count() == 0) { - m_comboBox->addItem(Tr::tr(""), Id().toSetting()); - m_comboBox->setEnabled(false); - } else { - m_comboBox->setEnabled(true); - } - } - - void cmakeToolAdded(Id id) - { - const CMakeTool *tool = CMakeToolManager::findById(id); - QTC_ASSERT(tool, return); - - m_comboBox->addItem(tool->displayName(), tool->id().toSetting()); - updateComboBox(); - refresh(); - } - - void cmakeToolUpdated(Id id) - { - const int pos = indexOf(id); - QTC_ASSERT(pos >= 0, return); - - const CMakeTool *tool = CMakeToolManager::findById(id); - QTC_ASSERT(tool, return); - - m_comboBox->setItemText(pos, tool->displayName()); - } - - void cmakeToolRemoved(Id id) - { - const int pos = indexOf(id); - QTC_ASSERT(pos >= 0, return); - - { - // do not handle the current index changed signal - const GuardLocker locker(m_ignoreChanges); - m_comboBox->removeItem(pos); - } - - // update the checkbox and set the current index - updateComboBox(); - refresh(); - } - void currentCMakeToolChanged(int index) { if (m_ignoreChanges.isLocked()) diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp index b1ba6bf1d59..f2c149ad5a4 100644 --- a/src/plugins/debugger/debuggerkitinformation.cpp +++ b/src/plugins/debugger/debuggerkitinformation.cpp @@ -78,7 +78,25 @@ private: const GuardLocker locker(m_ignoreChanges); m_comboBox->clear(); m_comboBox->addItem(Tr::tr("None"), QString()); - for (const DebuggerItem &item : DebuggerItemManager::debuggers()) + + IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); + const Utils::FilePath path = device->rootPath(); + const QList list = DebuggerItemManager::debuggers(); + + const QList same = Utils::filtered(list, [path](const DebuggerItem &item) { + return item.command().isSameDevice(path); + }); + const QList other = Utils::filtered(list, [path](const DebuggerItem &item) { + return !item.command().isSameDevice(path); + }); + + for (const DebuggerItem &item : same) + m_comboBox->addItem(item.displayName(), item.id()); + + if (!same.isEmpty() && !other.isEmpty()) + m_comboBox->insertSeparator(m_comboBox->count()); + + for (const DebuggerItem &item : other) m_comboBox->addItem(item.displayName(), item.id()); const DebuggerItem *item = DebuggerKitAspect::debugger(m_kit); diff --git a/src/plugins/projectexplorer/kitinformation.cpp b/src/plugins/projectexplorer/kitinformation.cpp index fc8039b402d..e5cfd9f5d71 100644 --- a/src/plugins/projectexplorer/kitinformation.cpp +++ b/src/plugins/projectexplorer/kitinformation.cpp @@ -239,6 +239,8 @@ private: void refresh() override { + IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); + const GuardLocker locker(m_ignoreChanges); const QList keys = m_languageComboboxMap.keys(); for (const Id l : keys) { @@ -248,8 +250,21 @@ private: cb->clear(); cb->addItem(Tr::tr(""), QByteArray()); - for (ToolChain *tc : ltcList) - cb->addItem(tc->displayName(), tc->id()); + const QList same = Utils::filtered(ltcList, [device](ToolChain *tc) { + return tc->compilerCommand().isSameDevice(device->rootPath()); + }); + const QList other = Utils::filtered(ltcList, [device](ToolChain *tc) { + return !tc->compilerCommand().isSameDevice(device->rootPath()); + }); + + for (ToolChain *item : same) + cb->addItem(item->displayName(), item->id()); + + if (!same.isEmpty() && !other.isEmpty()) + cb->insertSeparator(cb->count()); + + for (ToolChain *item : other) + cb->addItem(item->displayName(), item->id()); cb->setEnabled(cb->count() > 1 && !m_isReadOnly); const int index = indexOf(cb, ToolChainKitAspect::toolChain(m_kit, l)); @@ -471,10 +486,9 @@ void ToolChainKitAspect::setup(Kit *k) // ID is not found: Might be an ABI string... lockToolchains = false; const QString abi = QString::fromUtf8(id); - const Toolchains possibleTcs = ToolChainManager::toolchains( - [abi, l](const ToolChain *t) { - return t->targetAbi().toString() == abi && t->language() == l; - }); + const Toolchains possibleTcs = ToolChainManager::toolchains([abi, l](const ToolChain *t) { + return t->targetAbi().toString() == abi && t->language() == l; + }); ToolChain *bestTc = nullptr; for (ToolChain *tc : possibleTcs) { if (!bestTc || tc->priority() > bestTc->priority()) diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 7fab3fbbcca..18d6d7b9c83 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -9,6 +9,7 @@ #include "qttestparser.h" #include "qtversionmanager.h" +#include #include #include #include @@ -16,6 +17,7 @@ #include #include +#include #include #include #include @@ -37,18 +39,20 @@ public: m_combo->setSizePolicy(QSizePolicy::Ignored, m_combo->sizePolicy().verticalPolicy()); m_combo->addItem(Tr::tr("None"), -1); - QList versionIds = Utils::transform(QtVersionManager::versions(), &QtVersion::uniqueId); - versionsChanged(versionIds, QList(), QList()); - m_manageButton = createManageButton(Constants::QTVERSION_SETTINGS_PAGE_ID); refresh(); m_combo->setToolTip(ki->description()); - connect(m_combo, &QComboBox::currentIndexChanged, - this, &QtKitAspectWidget::currentWasChanged); - connect(QtVersionManager::instance(), &QtVersionManager::qtVersionsChanged, - this, &QtKitAspectWidget::versionsChanged); + connect(m_combo, &QComboBox::currentIndexChanged, this, [this] { + if (!m_ignoreChanges.isLocked()) + currentWasChanged(m_combo->currentIndex()); + }); + + connect(QtVersionManager::instance(), + &QtVersionManager::qtVersionsChanged, + this, + &QtKitAspectWidget::refresh); } ~QtKitAspectWidget() final @@ -69,6 +73,30 @@ private: void refresh() final { + const GuardLocker locker(m_ignoreChanges); + m_combo->clear(); + + IDeviceConstPtr device = BuildDeviceKitAspect::device(kit()); + const FilePath deviceRoot = device->rootPath(); + + const QtVersions versions = QtVersionManager::versions(); + + const QList same = Utils::filtered(versions, [device](QtVersion *qt) { + return qt->qmakeFilePath().isSameDevice(device->rootPath()); + }); + const QList other = Utils::filtered(versions, [device](QtVersion *qt) { + return !qt->qmakeFilePath().isSameDevice(device->rootPath()); + }); + + for (QtVersion *item : same) + m_combo->addItem(item->displayName(), item->uniqueId()); + + if (!same.isEmpty() && !other.isEmpty()) + m_combo->insertSeparator(m_combo->count()); + + for (QtVersion *item : other) + m_combo->addItem(item->displayName(), item->uniqueId()); + m_combo->setCurrentIndex(findQtVersion(QtKitAspect::qtVersionId(m_kit))); } @@ -82,27 +110,6 @@ private: return name; } - void versionsChanged(const QList &added, const QList &removed, const QList &changed) - { - for (const int id : added) { - QtVersion *v = QtVersionManager::version(id); - QTC_CHECK(v); - QTC_CHECK(findQtVersion(id) < 0); - m_combo->addItem(itemNameFor(v), id); - } - for (const int id : removed) { - int pos = findQtVersion(id); - if (pos >= 0) // We do not include invalid Qt versions, so do not try to remove those. - m_combo->removeItem(pos); - } - for (const int id : changed) { - QtVersion *v = QtVersionManager::version(id); - int pos = findQtVersion(id); - QTC_CHECK(pos >= 0); - m_combo->setItemText(pos, itemNameFor(v)); - } - } - void currentWasChanged(int idx) { QtKitAspect::setQtVersionId(m_kit, m_combo->itemData(idx).toInt()); @@ -117,6 +124,7 @@ private: return -1; } + Guard m_ignoreChanges; QComboBox *m_combo; QWidget *m_manageButton; }; From e4dad9547f54b568c3263e9735af84bfb0d61d17 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Thu, 1 Jun 2023 16:47:50 +0200 Subject: [PATCH 46/57] Doc: Describe the Terminal output pane Fixes: QTCREATORBUG-29220 Change-Id: I407a7de1be7f485da99b445b967c768373c8c7f5 Reviewed-by: Marcus Tillmanns --- doc/qtcreator/images/icons/terminal-close.png | Bin 0 -> 257 bytes .../images/icons/terminal-create.png | Bin 0 -> 235 bytes .../images/qtcreator-output-panes-taskbar.png | Bin 1718 -> 0 bytes .../qtcreator-output-panes-taskbar.webp | Bin 0 -> 3428 bytes .../images/qtcreator-output-terminal.webp | Bin 0 -> 3064 bytes .../qtcreator-preferences-terminal.webp | Bin 0 -> 6012 bytes .../src/howto/creator-keyboard-shortcuts.qdoc | 2 +- .../howto/creator-only/creator-how-tos.qdoc | 2 +- .../src/howto/creator-only/qtcreator-faq.qdoc | 4 +- ...ator-projects-settings-run-desktop.qdocinc | 5 +- .../creator-file-system-view.qdoc | 4 +- .../user-interface/creator-projects-view.qdoc | 3 + .../src/user-interface/creator-ui.qdoc | 114 ++++++++++++++++-- 13 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 doc/qtcreator/images/icons/terminal-close.png create mode 100644 doc/qtcreator/images/icons/terminal-create.png delete mode 100644 doc/qtcreator/images/qtcreator-output-panes-taskbar.png create mode 100644 doc/qtcreator/images/qtcreator-output-panes-taskbar.webp create mode 100644 doc/qtcreator/images/qtcreator-output-terminal.webp create mode 100644 doc/qtcreator/images/qtcreator-preferences-terminal.webp diff --git a/doc/qtcreator/images/icons/terminal-close.png b/doc/qtcreator/images/icons/terminal-close.png new file mode 100644 index 0000000000000000000000000000000000000000..861c01a6d22dab34d89c595df90ed080c3618aa8 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QU=zopr04`Z& ArvLx| literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/icons/terminal-create.png b/doc/qtcreator/images/icons/terminal-create.png new file mode 100644 index 0000000000000000000000000000000000000000..02e8fb2185e6fbdaab8b016a6804c50d9c19babe GIT binary patch literal 235 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+Z1Qw*4AD5>I>DCfkb^+$ zep5l7h)m^{Bj*lXbF&F@uyL4`Y18;%YSIDih#!-q1RPmdyYC)Jx%*&sTAF>?JsZ}b z1kXch(F`7!-guXuF;JW;%QyAFH06y?)-s%EvYwc8tFgx}J@QsVd}W8_$(Z{J)yrbO z>c;bD|K&N*(UAC-y*T67WQGN6I*iyHTn@~3KI^nA$T{>QYhcvsRH-|6y~9n`OLU!Js`fq{X+)78&qol`;+05f%5#sB~S literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-output-panes-taskbar.png b/doc/qtcreator/images/qtcreator-output-panes-taskbar.png deleted file mode 100644 index e6430e4569d24efaf8b856fe48eb698c4e498512..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1718 zcmeAS@N?(olHy`uVBq!ia0y~yU@~W5V36csW?*3Wb^iZn1_nl*0G|+7K@cz$6buwJ zED#KwATBO04+6IG@=@}(Nc@=@jTQBw^C1q}rQ4GT8d%FEl9Z*p*Qad3)pa4L3) z>T)Pt7$_(hIAKEIgbNX!JEG*lnx3@mVRDF`emD40;NVM4)%4Fwx6l-tUe z+eVc~$(Ki!mzPg1-?XWG(}j*GmyR75CIkvjn6P2Oh7A)gOqg(C!-NYLrbfw6Eia!s zb<@;Mo2FivI`zV)sTVFRD0EqHVc~)cI~H8Hu%WqpE-b#VaN&gw z1t2>&m4ht-S$^Td!V4EbhF!P-(qVi}{VoFotE;DrV@Srmw=+4*Z@UOIyggEuyTNm< zj2fpO_YysKY4MngleRn%TJWsPx!IF@$=aL$|I5$tuJ*{boW{Lp`u5*(VfUPkSI_k~ z-t4tf%uAztdeDugfUY88QO(5n|GbU=@~ajI@8G<{lE-AekR{Iv!fKp-;Oy*2X>XpU zy}s(pd-T}j+1-VgL$gw%e3!<(`&GW6?uK`rtme{3cQoe}z4d#w?A;Od1s}cL+Lm`u z&szWI($c@rOUnbxtXJQxUDi5vk?i#cYNZ!1mc`bty3!rC?qxVzb;N!9#n*1V-n(>% zWdHHVyledP?Dfw1K32Yct9O3+#&kLJ5364u=4MM<99-XjMEb0~?VGdg`OEe^+rl0u zuUnJ;_r#+mg+kZU>^8WcdE_M=y^fdH>^?K=`!c=5XCFi#OSjBBmdq`4`hJeiLEe4E z^IxUsKHF2RXCAluNm29T_^3Nu+}{Qk=B>Ni+4`CFeNO$y$ED#ObsLY01k5VWPff!TJh$(1y__;neE#p{qX)XB+`Dt#6?Zil}T zLh3hIcP&_5_oMR&rl99 z(a&dmPEft`AocZ>s+v0`?tXjvXLe8g!LQJ1Yiw1~=T~}9cAlF~MqY;dY~lK(hdG)v z_+@3ij_Nq+sW`YZO>{rqp?T9{mPbp<>t<$yRoQMQe9o(HnPqz7wbG@BoQ;eZ9;|VH z@6NdNQS!Im!x6T5j>kS%@n2lQW7xVySAbN8I(L3eWOtb3JKwMZA3d(92@pC+vxqpm1_-)@89lZN2ByDv)lZ%@@6MX* zhkplptlaR)_vBaegv>L2S0!iN$(*9)wct_Y@wIRF&D-#6N{le8{+u?RUCrJ{R%+Zj z*sx{s<15eB+w6PX85~wLIp=N5cJ{2Biu~p&3^%`S6J4J2_|`uQ*5BDmN^(XIjxO6E zGQ0cPu@~P97YFxF;fzh6;M*0r+V9b%KgZ7dX4xK{;uUtsf2m$ZPFFam`7Xx&2dp#m zPW^r_zx@k~&Frr$A7yfye@ig)6EwT(T_F5+PSDo64;r_A{eE}s`lHMh*0W|7UCGM7 zT-dmI&a!X&g_g_YskEH*?vcJVM=CGt=9KNv$ zEDk#!Da=~2u2W$3@o=4bX1(5X!J02WC%6AoG$<9=D)Jv*yQdK?d0{s*3r+Ftm4gl%IxR)RB2lS+iUmANwfT_ z&v~xXl%E)3dR|#E?7U50M&O)>(U&qszg4|(?AG+1zsyS}Nk#Np;VBb^-G3h&chCOR zE^EB7>3-viN0}P0I*nhP&0obfd-bQi5gI3K_vvfA=GDvoIrIO&>xs2?r+>!9tYyt( zGS9ntuBPf-Z2kkaf@a6F|JdDr?0+^@^Wp!83u9#SoH$bhCma@Q30m-nxsrGJ#pFrT Sa~K#H7(8A5T-G@yGywoEVK!y>+I4GNu8QaH{M!2JP#2#DE&JsD^D;O0 zZrRz;H}~0=5KYr-q6gUemzw;)FSUK=w{vICoDoS=Gyc5R$NXLI%-iAzDw4SNtn%nj z*jl$u^6%rU~J|J8Lf|HpQ5~6?GSv)6QUQxm~;oGj`8)oS1@BI3I zd+wHQu?K8?rs@IJE5BApFhn^rXK+rx_2`mZ#K8mq{~w?KaFSR0?6*(n@K2wsZ|S}w z==t{>8%tK@F&ys9y`5F8{^60OBXV{XtoppY%o$KJL)e(p`p%Ggt_7Sm)d8KrKo`6wS# zGEH|bb5?1@-4vy{Z~X#SpNz>bjn(5+uDd#qU(w#`^faOWQFHHByh^bzJ9%Zl-I0VP zKc=c?+n2wtD_`*M<+8aMm1h68mt3{V^PJcizm>hj^zqrhuRkOn%b*G&7^C6m&~vMcJJLrMLs?*AJ9nzPLdWgivU z_HKCO=Jdnjv9gx7^Cb?gbq||sX6j2#);}~kHpgf4lPCq($4isz?zLUpAT)8IMEp}N z2G_^DDN@H~d~$NxCi!ftL)Yt_42LDU`44^6EGTT`IlTIhKu>%Pcg!67wu`Ugn>0VV zew@E(YT;B>nS8f{*+I+`-@n-_yZGg6j_0e>)>Wz7?DN%Y*x_>c_1QJQY?3~utzP|X z;&g7guIXniO8tap3qP;0^}duII5};e!V+=y*F8I(j@ry%lM`mXcc3)neez{fq2|=S zwW|}Y0)5R)gW|;bDtxBh>sV58IdxBpr`F+$tLp@B37}eb{AcOBDVM_Q)Yvb^1gVAC}N%=Uo zS;yYEeapVJKSzGmzaQBvzuoh6(=?I`n4o9#eoJ%zRLNOkTrT&g1pHb4!~f&_NA^eS z?>|2qUCZR;v@PLtgMbs01P6!WlHL3YEiBnPs+epnKF?-e%^B?VB8L091wcoP7+Q!pe|Gjm?|HNnQ%$)UM^Od6KFLvC%`+AP{^#gs2>V7!v z(M{jsc&_ch%t%9vdOj!hUrJupOcre#4(}fJBs1K-rmt7-6yGe}ZR6)8c7e6U!q&EN z_S(HSo{2kg+~(ZRaPFFD@uda*Y+!^4-iQ z^ItAJs&?Yewyq}o|Eh^oxK}ZEUeJ7dBsa-(wz#Kk{FUy2*Bd%dDHeFCTNlb|&Q^KH zn%`M8FXGZO*9VEk=l3Y`PoC8s^7x>dsmuvY-~ZDuOIqgXUoxA$pmq0``c&WBDjA;- zrL*j+>yMisxw%B`-PSzW**h#J1fQDo#%zi4rzN7Cdh^)K!`ATV7SH%Q?Eu#@=RE~G zXWTjadXb*mZT(em^>Q}NWwu;8C2Zl`i4Q%3ciogvX-SB7$!#<_W2kz2;tHK72WD(? zlsN2AbbZ6`==nSC4B?1ud4IPO-Q&#iZi!0gba_LXE*%$0sgX;`sEhN01QX!bP7KELJJ$a$&DrEK{oQ z{N$GE_jkEmmu;(?<-%tE>z>`jpT*ke`u5S0qv8S%)lP2@8BM5PWFX|dWsiZang06Y zB36|~mRa)Bc4yUZ?%)6HLg@}KFQII+j;Y@pJ{@0GJE!c<{!b|plcnM&6q?tFUA*+U zJS{El-5tZjJkE!M~pXRTfmk+CnbK1_xh-nPI67{?4 zh*x*|gx%(wXMN|rzSJzmKjT9`S6T=*xW)TzJgE!&+(tEDb|e`>Y8a`tPcT`&5c9E%Xs z6_}i%C|8vd)?;(}^fQH2iIXqW*qYdc4;lCz-y*nCJ7nUdHQQ%Rw^7>H&h;*fZMi6K zv{t^7qQ!2#!&@%Ah`a6iw!UqH^lh2r58RL3a}Uok{eJpv-c*Nd}{gX zxYNHE9v3`g9dJ;+ZhzR>ia+;Pv&9{3wVLJPX!_RZ9B-Y3)r-mtFEbwef56;rCi`39 zo9SlxE6-H?FH9*oyXxp%aqgg`jEcwGBqv@>?=*=nSpA~4_eiyTO7rW^mHTZFB+T!&b8+m7HPKcc*#mB6c=&*jx)z8J+FIt3d`C5PO zQj3gGykDayW5Va$GB4)Dwf^-1oD7WSS{}*^4aFv_n$Hq_dU3PD!ii40D(YlaewWW#<$Zi0SmiZykK0$R2@YqRpLlZK zfnm6rq_WrIVQiD8te9gG#GyQS!oR0gD)*gVE{L;}JXwDBb!KVk zdF2c9@8xF*H~f|PyJx}aJ^G*b1(k1$l65I&c>8;4vwMhxdag$Pz2^oBs;}Cc0_u-$ zDNX!csl0T3yNOWLCy#Z3Tql#yJ-zACR6XIr9Q%3VYkD)fGWl8vXo0a1H zMd2qo+~(YOY_^6k>2yOlv;)_<;75sE2DUd1vT9Dc$ii`d6(1|6(ceshPi|$?8D0YcS9s|f_+m2-&-ndEUv}Q{*fA)3Z*H%aN&6@Sw^gH+6t|Y+|$T`fJV8y}~Pg$-ZH};ZfsQq-&vb#Z;(ZhpYRJNr|bCTvg{d z3grZ?eVFd#07&F&L1~TT9Q!lao3sJ1rwPP52*0Gm^!ueyk)Ri^n_1m z*Xea}zU~M;D_50X^M23m#=7&NTu0C*J$(bVBzzan}I<;;E)Bw)rjx^>kFk`zl>{_&Mi<+y~`WDzIu0D z>P_2s@7}%p)v>|t#jZ25UxGfJx<8rcW%eeaAItwcNS{)OZ{+$OqZr+@Coq_`))883%U6JN>{uq%vFeJ}rHUapcr z^;q63T+_|}{F!8YOZ;#DNA{Om5%K&nbME#(FL;+*61zJfmCt2|*S_VKN|x#!72R=a))J+|q;`9Ebn9`D%!nY#`NX({N0z0AM%>PUnBfj8~E_xJv; zy1Q`wk+09xEP~H*9G#}N#@S}S<;-x7E8*_4YI6VfE%{?xQ}L<$(AIUD>Fs~-b{OrP znbfQ^U5c z|FZ6`L!RGLouE^(kEi`Vu(~%^d(wtpbEYl7i<lCOp!(J11o2s-)?wO4qLW#nt?5x$V^jQmW-@PanD5 z^KrcR?zhkm%QKeGbAC1)Jm33*Co-c;k^AHJ3d=l(<$qm1DNHOTT_?dEbi=y*2Z0&AVmy*;V}5vzK#MmTs07U6lCe zW|oJa?UvN*d3jmKR!rafaOI&_b0tLVKa1-;_Oq$~7;&Ymg?Ih2D_0cW?weoaR>igR zkVHOY%>Vhz`F_w%otE=}!oaHv+w{rTZZ(EKU!vVUJUztvdx)$8}m zhg)xR$Yk2v@@p;p`bVHFcloYI#hZNQX0B`1x_{WALXbz(?SU%)_nrAq*2oCnEIzJS z%YA_{Yn^o9`e_w=Ssot=Ty^EOW3QOR>ep%eVw&BTZF9U5RNZ7`YqYmE>Gm79UNisq zb0W-tFFkXCOI2cnW?byYl!wCAmAgD*gJVwzySmiJvL5wc`DW9aDYLztH4dasxa0F- z+It>l-Yd!)bpjXvhP-^AxTL(B``~wtyECQi^f;W7ORn!|-J*VL+K)B|d70a(;sWi; zPCpixWdj^>;0CJww>3udR^`MJY|JRBHICfP4Ne+YVXwJzO0$T)qa=r?`g3P z1?9hcHG=pj9qVM|PQI!qv9;{t^sUCT^|#HAkBXiDC}HV`s3)J-FS(VH|EyE#-Ts}| zja_}WF8Wcqc#HbX{eh43_=^2*`NpvBKXxG6Zf;xoe~INk99_RIUT-ip?Ztdm<`?r< zDi(Ilb9tt;`9zEW%^8|;{na|#)+uDq;ahrU5rLs4e?Xb#U%e`{zUH@GU z>EistWz=-j>QddTcP$qMIjUE3^~h_Zt>oowGu0=ei%|uOzd6*e6;B`S2guSG8rs z0j9MIE?)1Xw*L_hE0)n%yL7@azcSb2E8Tt@<=n&HEb8KwPN;b(nPBsJUV6E5P=8=p zx`UiwiONFZl~>Mwl{eN@;h!hJf#q*__16@&qql^v@;0cK2Vb7Fh+WffY2f+JHFr)6 z^6^_9`jsRhQua3fO?&K$zOI)*;`nnpBC%(Pk$-3h*k4|=`NqFfFxHV%YAXaL5)?%)J+vwHGHGCgk@bw;nXub z?HklOO;~C|w#m;0dDEpt=I*({v$9!H_r%7V9abj46$5q5okXMfG&Z>Q2DLghN^N7T zXMB5}?V-x8U=QG^)I`&{o-SAW7 zURS%+4OLO|PuN|)owlLMu6c*T>bCYB3ag)+RBe3xYmL(r^<9VEo`3b%nIHVmJ?j46 z8IwNko?E#1$+!E)levE8sWUwK6wDl^d**FiQOum+jvU!9V&}K4iwXGWdTf8Cnf6oj z-_0H=kI(kKUm_v-L{Gyr5C#A1X(Bv{j{6;-%v@8 z`O|_={}&W8yp8_&_amPgv(b{1=F;`&wErKA+>(FvJHM60++H1{oEnzfdA)t&<@sUF z99bWdj$gg`zRYbwT=VB&+wLjuvtJzZT=?$6Kjr6_i}a~KGu*oH`{YBfE^dvK+Ty^h zyCLMWKt+y^;wP1fKmF&N^C;jrXSUbZF7esIY3#yM;!k9Pj&<(P_V{_$Zrk(to{GTR zJP%nVF1+)9AsN_eH0!VN5q|k3-n?cj6CsZT)4ZuxDnhe;n*&?LOcht3)eUN|ND_WG zRm)^8XJGF-6X}IqQClWXnUy@tzgTaP)YO-5E1TP=n48Xjwun)kwJBoZTCP^3nJ-`Z zKKn0STGQOe+L1A3&iwx4?=L>M`)aTE`)JplALYHOFJu)ps!gkF-S;=?*t0JO%gt}U z+y6mMfA6l;S&C`5&7{}y%jfi8kWF&id+hJ@{+Ri~O7UE0owe(7fBmXUJvYg~=kn3( zzvcZ3l5@|$-(T{YSFUC+ux8oIBAxo(_>cF?M4rOO^}(S z?tJma`S%u8AD;c5Zg&H6`<4}lz%EaflE#HOi>^0ta&~@?IP`7B!(S6( zbj%B{%=L1QoxlIy|AMB5TXPC(e^mWH%sI)6b6cZnhU8}##;CU6>V4bp)-xuHZEo46 z(6G~x<(b%8>0Z^Ut-@0lR`+e_Y?`7nLBf6042D3?h(m|^pHF0IU$$+ArpLC84KB8U zot#f{QzsWH9bjetAm?!KevrxQN7)|CjK&hB3Uw2{Z{t&9SDetnByDy<;E3_+138Zq z=7l)iu2smtWH#%mvCAKUB1X$52eZuAKF^Ci-e&}u-DFX`H9_HUb@CN6zGs~q8N6)f zZ?J7rO*(V5h4bu#4>oZPlYP%_`dt(}MI>w01qM+y^+kcRB^lJ47IOS{R=SxYV;v|a zxlFnJ&!0Y3nKSC=XBTueh)ga#;>xkTH`QX3x^av2*+u!rX|r;_>oU3tKMUX}7j(X- z@NUBMkItLY{5FR~r~TAt*SB{}s1kd)JAKoMBh#KaO}1Sa|Lt0O$GpcInK~={XR+BY zUhtpgulfsn$N!wa{4dlu{S*E&ACFKvhfQtrsp%4@w(I<~Tk*c+*Wa}nQ}lP-*q^`U jWcUY}2rZqjU+40gyv;M+$#6wc{(-W=SIMLWo`DPiCS>WT literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-preferences-terminal.webp b/doc/qtcreator/images/qtcreator-preferences-terminal.webp new file mode 100644 index 0000000000000000000000000000000000000000..5772f8ee94ff9b049d626d0a7ef766a8d04699b8 GIT binary patch literal 6012 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 \uicontrol {Run Settings} > \uicontrol {Run in terminal}. To specify the terminal to use, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System. + \uicontrol Environment > \uicontrol System. To use an \l{Terminal} + {internal terminal}, select \uicontrol Edit > \uicontrol Preferences + > \uicontrol Terminal > \uicontrol {Use internal terminal}. \b {On Windows:} Output is displayed differently for \e{console applications} and \e{GUI applications}. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc index 27fe9ade256..c3f3688e892 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-run-desktop.qdocinc @@ -21,7 +21,10 @@ For console applications, check the \uicontrol{Run in terminal} check box. To specify the terminal to use on Linux and \macos, select \uicontrol Edit - > \uicontrol Preferences > \uicontrol Environment > \uicontrol System. + > \uicontrol Preferences > \uicontrol Environment > \uicontrol System. To use + an \l{Terminal}{internal terminal}, select \uicontrol Edit > + \uicontrol Preferences > \uicontrol Terminal > + \uicontrol {Use internal terminal}. To run with special environment variables set up, select them in the \uicontrol {Run Environment} section. For more information, see diff --git a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc index e47328e744d..c8ef1ee206f 100644 --- a/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-file-system-view.qdoc @@ -60,7 +60,9 @@ \li Open a terminal window in the selected directory or in the directory that has the file. To specify the terminal to use on Linux and \macos, select \uicontrol Edit > \uicontrol Preferences > - \uicontrol Environment > \uicontrol System. + \uicontrol Environment > \uicontrol System. To use an \l{Terminal} + {internal terminal}, select \uicontrol Edit > \uicontrol Preferences + > \uicontrol Terminal > \uicontrol {Use internal terminal}. \li Search from the selected directory. \li View file properties, such as name, path, MIME type, default editor, line endings, indentation, owner, size, last read and modified diff --git a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc index b0a98a81635..84453cf61b6 100644 --- a/doc/qtcreator/src/user-interface/creator-projects-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-projects-view.qdoc @@ -84,6 +84,9 @@ \li Open a terminal window in the project directory. To specify the terminal to use on Linux and \macos, select \uicontrol Edit > \uicontrol Preferences > \uicontrol Environment > \uicontrol System. + To use an \l{Terminal}{internal terminal}, select \uicontrol Edit > + \uicontrol Preferences > \uicontrol Terminal > + \uicontrol {Use internal terminal}. \li Open a terminal window in the project directory that you configured for building or running the project. \li Expand or collapse the tree view to show or hide all files and diff --git a/doc/qtcreator/src/user-interface/creator-ui.qdoc b/doc/qtcreator/src/user-interface/creator-ui.qdoc index 315720275a0..c4ba6cad417 100644 --- a/doc/qtcreator/src/user-interface/creator-ui.qdoc +++ b/doc/qtcreator/src/user-interface/creator-ui.qdoc @@ -376,13 +376,17 @@ \list - \li \uicontrol{Issues} + \li \l {Issues} - \li \uicontrol{Search Results} + \li \l {Search Results} - \li \uicontrol{Application Output} + \li \l {Application Output} - \li \uicontrol{Compile Output} + \li \l {Compile Output} + + \if defined(qtcreator) + \li \l {Terminal} + \endif \li \uicontrol {QML Debugger Console} @@ -396,7 +400,7 @@ \li \l{Using Squish}{Squish} test results and Squish Server and Runner logs - \li \uicontrol {To-Do Entries} + \li \l {To-Do Entries} \endif @@ -404,7 +408,7 @@ Output is available on the taskbar in all \l{Selecting Modes}{modes}. - \image qtcreator-output-panes-taskbar.png "Output on the taskbar" + \image qtcreator-output-panes-taskbar.webp "Output on the taskbar" You can view output in the following ways: @@ -415,7 +419,8 @@ \li Select \inlineimage icons/output-pane-menu.png , and then select the view to open. \li Select \uicontrol View > \uicontrol Output. - The menu items also display the keyboard shortcuts that you can use. + The menu items also display the \l{Keyboard Shortcuts} + {keyboard shortcuts} that you can use. \endlist To maximize an open output view, select the \inlineimage icons/arrowup.png @@ -644,6 +649,101 @@ \endlist \if defined(qtcreator) + + \section1 Terminal + + When you select the \uicontrol {Run in Terminal} check box and run an + application or the \uicontrol {Open Terminal} button to open a terminal, + the default terminal opens. On Linux and \macos, you can set the default + terminal by selecting \uicontrol Edit > \uicontrol Preferences > + \uicontrol Environment > \uicontrol System. + + To open the terminal on the taskbar instead of in a separate window, select + \uicontrol Edit > \uicontrol Preferences > \uicontrol Terminal > + \uicontrol {Use internal terminal}. + + \image qtcreator-output-terminal.webp {Terminal pane} + + To clear the terminal, select \inlineimage icons/clean_pane_small.png + (\uicontrol Clear). + + To close the current terminal, select \inlineimage icons/terminal-close.png + . + + To open new terminals as tabs, select \inlineimage icons/terminal-create.png + . + + To move between terminals, select the tabs or \inlineimage icons/arrowup.png + and \inlineimage icons/arrowdown.png + . + + To select a word in a terminal, double-click it. To select the whole line, + triple-click it. + + To open links in a browser, files in the editor, or folders in the + \l Projects view, hover the mouse over them, and press \key Ctrl. + + To \l{Finding and Replacing}{search} through the output, press \key {Ctrl+F}. + + To make the font larger or smaller, select the \inlineimage icons/plus.png + and \inlineimage icons/minus.png + buttons. You can also change the font size in terminal preferences. + + To open terminal preferences, select \inlineimage icons/settings.png + (\uicontrol Configure). + + Most of the \QC keyboard shortcuts are disabled in the terminal, except the + ones for opening terminal preferences or the locator and quitting \QC. To + send the escape key to the terminal instead of closing the terminal, select + \uicontrol {Shift+Esc}. You can also specify that the escape key is sent to + the terminal in terminal preferences. + + \section2 Setting Terminal Preferences + + To set preferences for the internal terminal, select \uicontrol Edit > + \uicontrol Preferences > \uicontrol Terminal, or select the + \uicontrol Configure button in the \uicontrol Terminal pane. + + \image qtcreator-preferences-terminal.webp {Terminal tab in Preferences} + + \table + \header + \li Option + \li Value + \row + \li \uicontrol {Use internal terminal} + \li Open the \uicontrol Terminal pane when you select + \uicontrol {Run in Terminal} or \uicontrol {Open Terminal}. + \row + \li \uicontrol {Send escape key to terminal} + \li Send the escape key to the terminal instead of closing the terminal. + \row + \li \uicontrol {Audible bell} + \li Play an audible bell when the a bell character is received. + \row + \li \uicontrol {Allow blinking cursor} + \li Allow the cursor to blink. + \row + \li \uicontrol {Font} + \li Select the \uicontrol {Font family} and \uicontrol Size for the text + in the terminal. You can also use the \inlineimage icons/plus.png + and \inlineimage icons/minus.png buttons in the \uicontrol Terminal + pane to change the font size. + \row + \li \uicontrol {Colors} + \li Set colors for the \uicontrol Terminal pane \uicontrol Foreground, + \uicontrol Background, \uicontrol Selection, and + \uicontrol {Find match}. + \row + \li \uicontrol {Default shell} + \li Set the full path to the default terminal executable in + \uicontrol {Shell path} and the arguments to pass to the shell + in \uicontrol {Shell arguments}. + \endtable + + To use an existing color scheme, select \uicontrol {Load Theme}. To revert + color changes, select \uicontrol {Reset Theme}. + \section1 To-Do Entries \uicontrol {To-Do Entries} lists the BUG, FIXME, NOTE, TODO, and From cdf7b6321809d39803bf4181cdacb36b7e90b992 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 30 May 2023 16:15:52 +0200 Subject: [PATCH 47/57] Doc: Add docs for using the Copilot plugin Fixes: QTCREATORBUG-29219 Change-Id: I58215bcf2de92e4619ebd0256ec5cbef9bad45bc Reviewed-by: Qt CI Patch Build Bot Reviewed-by: Marcus Tillmanns Reviewed-by: --- doc/qtcreator/images/icons/copilot.png | Bin 0 -> 257 bytes doc/qtcreator/images/qtcreator-copilot.gif | Bin 0 -> 135157 bytes .../images/qtcreator-preferences-copilot.webp | Bin 0 -> 7182 bytes doc/qtcreator/src/editors/creator-coding.qdoc | 8 +- .../editors/creator-only/creator-copilot.qdoc | 96 ++++++++++++++++++ .../creator-only/creator-language-server.qdoc | 4 +- .../creator-only/creator-mime-types.qdoc | 4 +- doc/qtcreator/src/qtcreator-toc.qdoc | 1 + 8 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 doc/qtcreator/images/icons/copilot.png create mode 100644 doc/qtcreator/images/qtcreator-copilot.gif create mode 100644 doc/qtcreator/images/qtcreator-preferences-copilot.webp create mode 100644 doc/qtcreator/src/editors/creator-only/creator-copilot.qdoc diff --git a/doc/qtcreator/images/icons/copilot.png b/doc/qtcreator/images/icons/copilot.png new file mode 100644 index 0000000000000000000000000000000000000000..94f0b72415bea34b916b82d025a311b092eb9022 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4i*LmhONKMUokK+obYsU4AD5h^!!GB=R%RA zAJ12>?3D1)zq^ZLw(B3kU7kK^#mz^m{xes4CMO)dv9`Cq@v6fg#?&kqpN{zp-&PBX z3a0P+J|{o#+|&K1mO0P4xkcl6d-~rSKbelK6=g1yhp{A=%@%Obzl`wwajbHmh@owtKjly?M3c*se>R+TIf{&bTtGSbt~Xi>oVZ z+2f8K6p83N{_3k~$06st56xYF-IR^5=zkO)Q?Yvby))d#clW;*esIU=Q$}s)Ck6%v N22WQ%mvv4FO#oR*bNm1R literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-copilot.gif b/doc/qtcreator/images/qtcreator-copilot.gif new file mode 100644 index 0000000000000000000000000000000000000000..452b102a3c146b23fcb11e6d996cfad2ddbe591c GIT binary patch literal 135157 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

LywJ1H#uE|=m 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 N$S%eufNzD2H2{)SRa5`~ literal 0 HcmV?d00001 diff --git a/doc/qtcreator/images/qtcreator-preferences-copilot.webp b/doc/qtcreator/images/qtcreator-preferences-copilot.webp new file mode 100644 index 0000000000000000000000000000000000000000..bd0422e5344b3ef0365378b2f35e557bb9cd6de1 GIT binary patch literal 7182 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 \uicontrol {About Plugins} > + \uicontrol Utilities > \uicontrol Copilot to enable the plugin. + \li Select \uicontrol {Restart Now} to restart \QC and load the plugin. + \endlist + + \section1 Setting Copilot Preferences + + To set preferences for using Copilot: + + \list 1 + \li Select \uicontrol Edit > \uicontrol Preferences > + \uicontrol Copilot. + \image qtcreator-preferences-copilot.webp {Copilot tab in Preferences} + \li Select \uicontrol {Sign In} to sign into your subscription, activate + your device, and authorize the GitHub Copilot plugin. + + The button turns into a \uicontrol {Sign Out} button. + \li Select the \uicontrol {Enable Copilot} check box to use Copilot. + \li In the \uicontrol {Node.js path} field, enter the full path to the + Node.js executable. + \li In the \uicontrol {Path to agent.js} field, enter the path to + agent.js in the Copilot Neovim plugin. + \li Select the \uicontrol {Request completions automatically} checkbox to + receive suggestions for the current text cursor position when you + make changes. + \endlist + + \section1 Pair-Programming with Copilot + + When you write code in the \l {Working in Edit Mode}{Edit} mode and + \uicontrol {Request completions automatically} is enabled, Copilot + automatically makes suggestions when you type. + + \image qtcreator-copilot.gif {Receiving suggestions from Copilot in the editor} + + To manually request a suggestion at the current editor's cursor position, + select \uicontrol {Request Copilot Suggestion} in the context menu. + + Hover the mouse over a suggestion to show a toolbar with + \inlineimage icons/prev.png + and \inlineimage icons/next.png + buttons for cycling between Copilot suggestions. + + To apply a suggestion as a whole, select \uicontrol Apply or press + the \key Tab key. + + To apply a suggestion word-by-word, select \uicontrol {Apply Word} + or press \key {Alt+Right}. + + \section1 Enabling and Disabling Copilot + + You can enable and disable the Copilot plugin either globally for all + projects or at project level for a particular project. + + To enable or disable Copilot suggestions globally, select the + \inlineimage icons/copilot.png + (\uicontrol {Toggle Copilot}) button. This also sets the value of the + \uicontrol {Enable Copilot} check box in \uicontrol Edit > + \uicontrol Preferences accordingly. + + To enable or disable Copilot suggestions for a particular project, + select \uicontrol Projects > \uicontrol {Project Settings} > + \uicontrol Copilot, and then select or deselect the + \uicontrol {Enable Copilot} check box. +*/ diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc index 348845a7bd2..43697c0f6d7 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc @@ -1,10 +1,10 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only /*! \previouspage creator-markdown-editor.html \page creator-language-servers.html - \nextpage creator-mime-types.html + \nextpage creator-copilot.html \title Using Language Servers diff --git a/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc b/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc index 7436a9c32a8..662b3c0fa20 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-mime-types.qdoc @@ -1,4 +1,4 @@ -// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only // ********************************************************************** @@ -8,7 +8,7 @@ // ********************************************************************** /*! - \previouspage creator-language-servers.html + \previouspage creator-copilot.html \page creator-mime-types.html \nextpage creator-modeling.html diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc index e18f60632b6..3215f402708 100644 --- a/doc/qtcreator/src/qtcreator-toc.qdoc +++ b/doc/qtcreator/src/qtcreator-toc.qdoc @@ -130,6 +130,7 @@ \endlist \li \l{Editing Markdown Files} \li \l{Using Language Servers} + \li \l{Using GitHub Copilot} \li \l{Editing MIME Types} \li \l{Modeling} \li \l{Editing State Charts} From 886ca55b5a96305e2f4b9b049dfb3c5109356812 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Tue, 6 Jun 2023 08:23:09 +0200 Subject: [PATCH 48/57] Terminal: Fix warnings about re-registered action Especially on Linux the pointer value of the Terminal might be reused, leading to warnings about actions being registered for the same context. Cleaning up the registration fixes this. Change-Id: Ie1d53bf79581e9f98576e7a4e70420ec63da0f86 Reviewed-by: Eike Ziller --- src/plugins/terminal/terminalwidget.cpp | 49 +++++++++++++++++-------- src/plugins/terminal/terminalwidget.h | 15 ++++---- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index 12a0521857c..b08038ee29d 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -252,23 +252,40 @@ void TerminalWidget::setupColors() update(); } +static RegisteredAction registerAction(Id commandId, const Context &context) +{ + QAction *action = new QAction; + ActionManager::registerAction(action, commandId, context); + + return RegisteredAction(action, [commandId](QAction *a) { + ActionManager::unregisterAction(a, commandId); + delete a; + }); +} + void TerminalWidget::setupActions() { - ActionManager::registerAction(&m_copy, Constants::COPY, m_context); - ActionManager::registerAction(&m_paste, Constants::PASTE, m_context); - ActionManager::registerAction(&m_close, Core::Constants::CLOSE, m_context); - ActionManager::registerAction(&m_clearTerminal, Constants::CLEAR_TERMINAL, m_context); - ActionManager::registerAction(&m_clearSelection, Constants::CLEARSELECTION, m_context); - ActionManager::registerAction(&m_moveCursorWordLeft, Constants::MOVECURSORWORDLEFT, m_context); - ActionManager::registerAction(&m_moveCursorWordRight, Constants::MOVECURSORWORDRIGHT, m_context); + m_copy = registerAction(Constants::COPY, m_context); + m_paste = registerAction(Constants::PASTE, m_context); + m_close = registerAction(Core::Constants::CLOSE, m_context); + m_clearTerminal = registerAction(Constants::CLEAR_TERMINAL, m_context); + m_clearSelection = registerAction(Constants::CLEARSELECTION, m_context); + m_moveCursorWordLeft = registerAction(Constants::MOVECURSORWORDLEFT, m_context); + m_moveCursorWordRight = registerAction(Constants::MOVECURSORWORDRIGHT, m_context); - connect(&m_copy, &QAction::triggered, this, &TerminalWidget::copyToClipboard); - connect(&m_paste, &QAction::triggered, this, &TerminalWidget::pasteFromClipboard); - connect(&m_close, &QAction::triggered, this, &TerminalWidget::closeTerminal); - connect(&m_clearTerminal, &QAction::triggered, this, &TerminalWidget::clearContents); - connect(&m_clearSelection, &QAction::triggered, this, &TerminalWidget::clearSelection); - connect(&m_moveCursorWordLeft, &QAction::triggered, this, &TerminalWidget::moveCursorWordLeft); - connect(&m_moveCursorWordRight, &QAction::triggered, this, &TerminalWidget::moveCursorWordRight); + connect(m_copy.get(), &QAction::triggered, this, &TerminalWidget::copyToClipboard); + connect(m_paste.get(), &QAction::triggered, this, &TerminalWidget::pasteFromClipboard); + connect(m_close.get(), &QAction::triggered, this, &TerminalWidget::closeTerminal); + connect(m_clearTerminal.get(), &QAction::triggered, this, &TerminalWidget::clearContents); + connect(m_clearSelection.get(), &QAction::triggered, this, &TerminalWidget::clearSelection); + connect(m_moveCursorWordLeft.get(), + &QAction::triggered, + this, + &TerminalWidget::moveCursorWordLeft); + connect(m_moveCursorWordRight.get(), + &QAction::triggered, + this, + &TerminalWidget::moveCursorWordRight); m_exit = unlockGlobalAction(Core::Constants::EXIT, m_context); m_options = unlockGlobalAction(Core::Constants::OPTIONS, m_context); @@ -401,7 +418,7 @@ void TerminalWidget::updateCopyState() if (!hasFocus()) return; - m_copy.setEnabled(m_selection.has_value()); + m_copy->setEnabled(m_selection.has_value()); } void TerminalWidget::setFont(const QFont &font) @@ -1095,7 +1112,7 @@ void TerminalWidget::keyPressEvent(QKeyEvent *event) } if (m_selection) - m_clearSelection.trigger(); + m_clearSelection->trigger(); else { QAction *returnAction = ActionManager::command(Core::Constants::S_RETURNTOEDITOR) ->actionForContext(Core::Constants::C_GLOBAL); diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index 4b82e4355a1..78e50db7ffc 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -25,6 +25,7 @@ namespace Terminal { using UnlockedGlobalAction = std::unique_ptr>; +using RegisteredAction = std::unique_ptr>; class TerminalWidget : public QAbstractScrollArea { @@ -239,13 +240,13 @@ private: Aggregation::Aggregate *m_aggregate{nullptr}; SearchHit m_lastSelectedHit{}; - QAction m_copy; - QAction m_paste; - QAction m_clearSelection; - QAction m_clearTerminal; - QAction m_moveCursorWordLeft; - QAction m_moveCursorWordRight; - QAction m_close; + RegisteredAction m_copy; + RegisteredAction m_paste; + RegisteredAction m_clearSelection; + RegisteredAction m_clearTerminal; + RegisteredAction m_moveCursorWordLeft; + RegisteredAction m_moveCursorWordRight; + RegisteredAction m_close; UnlockedGlobalAction m_findInDocument; UnlockedGlobalAction m_exit; From adcf52e0c44679de40db4eac448acc46c4239c70 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 16:42:31 +0200 Subject: [PATCH 49/57] Fix some more *::count() deprecation warnings Change-Id: Ib7d1552a6f7b167e15beb7ca1ef26c7d57e090e9 Reviewed-by: Eike Ziller --- src/libs/utils/fileutils.cpp | 2 +- src/libs/utils/macroexpander.cpp | 2 +- src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp | 8 ++++---- src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp | 2 +- src/plugins/debugger/uvsc/uvscutils.cpp | 6 +++--- src/plugins/qtsupport/baseqtversion.cpp | 3 +-- .../qml2puppet/instances/objectnodeinstance.cpp | 2 +- src/tools/sdktool/sdkpersistentsettings.cpp | 2 +- 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index 2cecc2810b9..0c6a321c714 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -133,7 +133,7 @@ bool FileSaverBase::write(const QByteArray &bytes) { if (m_hasError) return false; - return setResult(m_file->write(bytes) == bytes.count()); + return setResult(m_file->write(bytes) == bytes.size()); } bool FileSaverBase::setResult(bool ok) diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index 4dc18b23f6b..3ab7d92e9a6 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -77,7 +77,7 @@ public: MacroExpander::PrefixFunction pf = it.value(); if (found) *found = true; - return pf(QString::fromUtf8(variable.mid(it.key().count()))); + return pf(QString::fromUtf8(variable.mid(it.key().size()))); } } if (found) diff --git a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp index e8ff40b3105..baa884d9f23 100644 --- a/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeconfigitem.cpp @@ -272,10 +272,10 @@ CMakeConfigItem CMakeConfigItem::fromString(const QString &s) static QByteArray trimCMakeCacheLine(const QByteArray &in) { int start = 0; - while (start < in.count() && (in.at(start) == ' ' || in.at(start) == '\t')) + while (start < in.size() && (in.at(start) == ' ' || in.at(start) == '\t')) ++start; - return in.mid(start, in.count() - start - 1); + return in.mid(start, in.size() - start - 1); } static QByteArrayList splitCMakeCacheLine(const QByteArray &line) { @@ -377,9 +377,9 @@ CMakeConfig CMakeConfig::fromFile(const Utils::FilePath &cacheFile, QString *err const QByteArray value = pieces.at(2); if (key.endsWith("-ADVANCED") && value == "1") { - advancedSet.insert(key.left(key.count() - 9 /* "-ADVANCED" */)); + advancedSet.insert(key.left(key.size() - 9 /* "-ADVANCED" */)); } else if (key.endsWith("-STRINGS") && CMakeConfigItem::typeStringToType(type) == CMakeConfigItem::INTERNAL) { - valuesMap[key.left(key.count() - 8) /* "-STRINGS" */] = value; + valuesMap[key.left(key.size() - 8) /* "-STRINGS" */] = value; } else { CMakeConfigItem::Type t = CMakeConfigItem::typeStringToType(type); result << CMakeConfigItem(key, t, documentation, value); diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 3a7a7e6c534..4fef3bda467 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -431,7 +431,7 @@ static QVector extractToolChainsFromCache(const CMakeConfi for (const CMakeConfigItem &i : config) { if (!i.key.startsWith("CMAKE_") || !i.key.endsWith("_COMPILER")) continue; - const QByteArray language = i.key.mid(6, i.key.count() - 6 - 9); // skip "CMAKE_" and "_COMPILER" + const QByteArray language = i.key.mid(6, i.key.size() - 6 - 9); // skip "CMAKE_" and "_COMPILER" Id languageId; if (language == "CXX") { haveCCxxCompiler = true; diff --git a/src/plugins/debugger/uvsc/uvscutils.cpp b/src/plugins/debugger/uvsc/uvscutils.cpp index c08e28e4fda..b7fecfb2487 100644 --- a/src/plugins/debugger/uvsc/uvscutils.cpp +++ b/src/plugins/debugger/uvsc/uvscutils.cpp @@ -73,8 +73,8 @@ QByteArray encodeBreakPoint(BKTYPE type, const QString &exp, const QString &cmd) bkPtr->type = type; bkPtr->count = 1; bkPtr->accessSize = 0; - bkPtr->expressionLength = asciiExp.count() + 1; - bkPtr->commandLength = asciiCmd.count() + 1; + bkPtr->expressionLength = asciiExp.size() + 1; + bkPtr->commandLength = asciiCmd.size() + 1; return buffer; } @@ -185,7 +185,7 @@ QString adjustHexValue(QString hex, const QString &type) return QString::number(v); } else { const bool isUnsigned = type.startsWith("unsigned"); - switch (data.count()) { + switch (data.size()) { case 1: if (isUnsigned) { quint8 v = 0; diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 179649c53b8..4720718a860 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -2101,8 +2101,7 @@ static QByteArray scanQtBinaryForBuildString(const FilePath &library) static QStringList extractFieldsFromBuildString(const QByteArray &buildString) { - if (buildString.isEmpty() - || buildString.count() > 4096) + if (buildString.isEmpty() || buildString.size() > 4096) return QStringList(); const QRegularExpression buildStringMatcher("^Qt " diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index 88689d4c304..26999ef2a2f 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -581,7 +581,7 @@ void ObjectNodeInstance::refreshProperty(const PropertyName &name) if (oldValue.type() == QVariant::Url) { QByteArray key = oldValue.toUrl().toEncoded(QUrl::UrlFormattingOption(0x100)); - QString pixmapKey = QString::fromUtf8(key.constData(), key.count()); + QString pixmapKey = QString::fromUtf8(key.constData(), key.size()); QPixmapCache::remove(pixmapKey); } diff --git a/src/tools/sdktool/sdkpersistentsettings.cpp b/src/tools/sdktool/sdkpersistentsettings.cpp index 4ca9b5e3701..34b139e2324 100644 --- a/src/tools/sdktool/sdkpersistentsettings.cpp +++ b/src/tools/sdktool/sdkpersistentsettings.cpp @@ -316,7 +316,7 @@ bool SdkFileSaverBase::write(const QByteArray &bytes) { if (m_hasError) return false; - return setResult(m_file->write(bytes) == bytes.count()); + return setResult(m_file->write(bytes) == bytes.size()); } bool SdkFileSaverBase::setResult(bool ok) From a3fb6a3a1cfae36d67c1e88a08bc316be4846c06 Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 7 Jun 2023 09:28:54 +0200 Subject: [PATCH 50/57] Debugger: Fix compile warning Change-Id: I77399e4f8260a043dbc429649fe33513d19fc91a Reviewed-by: hjk --- src/plugins/debugger/gdb/gdbengine.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index f69c9f72d7b..7a9eab16b9c 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -1123,7 +1123,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data) const GdbMi frame = data["frame"]; // Jump over well-known frames. - static int stepCounter = 0; + //static int stepCounter = 0; if (debuggerSettings()->skipKnownFrames.value()) { if (reason == "end-stepping-range" || reason == "function-finished") { //showMessage(frame.toString()); @@ -1131,19 +1131,19 @@ void GdbEngine::handleStopResponse(const GdbMi &data) QString fileName = frame["file"].data(); if (isLeavableFunction(funcName, fileName)) { //showMessage(_("LEAVING ") + funcName); - ++stepCounter; + //++stepCounter; executeStepOut(); return; } if (isSkippableFunction(funcName, fileName)) { //showMessage(_("SKIPPING ") + funcName); - ++stepCounter; + //++stepCounter; executeStepIn(false); return; } //if (stepCounter) // qDebug() << "STEPCOUNTER:" << stepCounter; - stepCounter = 0; + //stepCounter = 0; } } From 0d4a5463972006a0bed5d8ce8834e357db359c74 Mon Sep 17 00:00:00 2001 From: hjk Date: Tue, 6 Jun 2023 15:44:59 +0200 Subject: [PATCH 51/57] Debugger: Use Utils::Text::Position instead of int line number The column is currently unused. Change-Id: Iabc57c8d21e807187783071efe9a82e9c1877181 Reviewed-by: hjk --- src/plugins/debugger/breakhandler.cpp | 68 ++++++++++---------- src/plugins/debugger/breakhandler.h | 7 +- src/plugins/debugger/breakpoint.cpp | 14 ++-- src/plugins/debugger/breakpoint.h | 3 +- src/plugins/debugger/cdb/cdbengine.cpp | 17 ++--- src/plugins/debugger/cdb/cdbparsehelpers.cpp | 3 +- src/plugins/debugger/dap/dapengine.cpp | 4 +- src/plugins/debugger/debuggerengine.cpp | 4 +- src/plugins/debugger/debuggerengine.h | 8 ++- src/plugins/debugger/debuggerplugin.cpp | 8 +-- src/plugins/debugger/debuggerprotocol.h | 3 +- src/plugins/debugger/gdb/gdbengine.cpp | 16 ++--- src/plugins/debugger/lldb/lldbengine.cpp | 8 +-- src/plugins/debugger/pdb/pdbengine.cpp | 4 +- src/plugins/debugger/qml/qmlengine.cpp | 14 ++-- src/plugins/debugger/sourceutils.cpp | 8 +-- src/plugins/debugger/uvsc/uvscengine.cpp | 4 +- 17 files changed, 101 insertions(+), 92 deletions(-) diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index 35915f9755f..ce539134551 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -84,9 +84,9 @@ public: { TextMark::updateLineNumber(lineNumber); QTC_ASSERT(m_bp, return); - m_bp->setLineNumber(lineNumber); + m_bp->setTextPosition({lineNumber, -1}); if (GlobalBreakpoint gbp = m_bp->globalBreakpoint()) - gbp->m_params.lineNumber = lineNumber; + gbp->m_params.textPosition.line = lineNumber; } void updateFilePath(const FilePath &fileName) final @@ -107,7 +107,7 @@ public: if (!gbp) return; BreakpointParameters params = gbp->m_params; - params.lineNumber = line; + params.textPosition.line = line; gbp->deleteBreakpoint(); BreakpointManager::createBreakpoint(params); } @@ -643,7 +643,7 @@ void BreakpointDialog::getParts(unsigned partsMask, BreakpointParameters *data) data->enabled = m_checkBoxEnabled->isChecked(); if (partsMask & FileAndLinePart) { - data->lineNumber = m_lineEditLineNumber->text().toInt(); + data->textPosition.line = m_lineEditLineNumber->text().toInt(); data->pathUsage = static_cast(m_comboBoxPathUsage->currentIndex()); data->fileName = m_pathChooserFileName->filePath(); } @@ -683,7 +683,7 @@ void BreakpointDialog::setParts(unsigned mask, const BreakpointParameters &data) if (mask & FileAndLinePart) { m_pathChooserFileName->setFilePath(data.fileName); - m_lineEditLineNumber->setText(QString::number(data.lineNumber)); + m_lineEditLineNumber->setText(QString::number(data.textPosition.line)); } if (mask & FunctionPart) @@ -889,7 +889,7 @@ BreakHandler::BreakHandler(DebuggerEngine *engine) bool BreakpointParameters::isLocatedAt(const FilePath &file, int line, const FilePath &markerFile) const { - return lineNumber == line && (fileName == file || fileName == markerFile); + return textPosition.line == line && (fileName == file || fileName == markerFile); } static bool isSimilarTo(const BreakpointParameters ¶ms, const BreakpointParameters &needle) @@ -911,7 +911,7 @@ static bool isSimilarTo(const BreakpointParameters ¶ms, const BreakpointPara // FIXME: breaks multiple breakpoints at the same location if (!params.fileName.isEmpty() && params.fileName == needle.fileName - && params.lineNumber == needle.lineNumber) + && params.textPosition == needle.textPosition) return true; return false; @@ -1064,7 +1064,7 @@ QVariant BreakpointItem::data(int column, int role) const return empty; } if (role == Qt::UserRole + 1) - return m_parameters.lineNumber; + return m_parameters.textPosition.line; break; case BreakpointAddressColumn: if (role == Qt::DisplayRole) { @@ -1121,7 +1121,7 @@ void BreakpointItem::addToCommand(DebuggerCommand *cmd) const cmd->arg("oneshot", requested.oneShot); cmd->arg("enabled", requested.enabled); cmd->arg("file", requested.fileName.path()); - cmd->arg("line", requested.lineNumber); + cmd->arg("line", requested.textPosition.line); cmd->arg("address", requested.address); cmd->arg("expression", requested.expression); } @@ -1186,12 +1186,13 @@ void BreakHandler::requestSubBreakpointEnabling(const SubBreakpoint &sbp, bool e } } -void BreakpointItem::setMarkerFileAndLine(const FilePath &fileName, int lineNumber) +void BreakpointItem::setMarkerFileAndPosition(const FilePath &fileName, + const Text::Position &textPosition) { - if (m_parameters.fileName == fileName && m_parameters.lineNumber == lineNumber) + if (m_parameters.fileName == fileName && m_parameters.textPosition == textPosition) return; m_parameters.fileName = fileName; - m_parameters.lineNumber = lineNumber; + m_parameters.textPosition = textPosition; destroyMarker(); updateMarker(); update(); @@ -1258,7 +1259,8 @@ void BreakHandler::removeDisassemblerMarker(const Breakpoint &bp) static bool matches(const Location &loc, const BreakpointParameters &bp) { - if (loc.fileName() == bp.fileName && loc.lineNumber() == bp.lineNumber && bp.lineNumber > 0) + if (loc.fileName() == bp.fileName && loc.textPosition() == bp.textPosition + && bp.textPosition.line > 0) return true; if (loc.address() == bp.address && bp.address > 0) return true; @@ -1836,9 +1838,9 @@ FilePath BreakpointItem::markerFileName() const int BreakpointItem::markerLineNumber() const { - if (m_parameters.lineNumber > 0) - return m_parameters.lineNumber; - return requestedParameters().lineNumber; + if (m_parameters.textPosition.line > 0) + return m_parameters.textPosition.line; + return requestedParameters().textPosition.line; } const BreakpointParameters &BreakpointItem::requestedParameters() const @@ -1869,7 +1871,7 @@ bool BreakpointItem::needsChange() const return true; if (oparams.command != m_parameters.command) return true; - if (oparams.type == BreakpointByFileAndLine && oparams.lineNumber != m_parameters.lineNumber) + if (oparams.type == BreakpointByFileAndLine && oparams.textPosition != m_parameters.textPosition) return true; // FIXME: Too strict, functions may have parameter lists, or not. // if (m_params.type == BreakpointByFunction && m_params.functionName != m_response.functionName) @@ -1950,8 +1952,8 @@ QString BreakpointItem::toolTip() const << "" << m_parameters.fileName.toUserOutput() << "" << "" << Tr::tr("Line Number:") - << "" << requested.lineNumber - << "" << m_parameters.lineNumber << ""; + << "" << requested.textPosition.line + << "" << m_parameters.textPosition.line << ""; } if (requested.type == BreakpointByFunction || m_parameters.type == BreakpointByFileAndLine) { str << "" << Tr::tr("Module:") @@ -2163,12 +2165,12 @@ QVariant GlobalBreakpointItem::data(int column, int role) const break; case BreakpointLineColumn: if (role == Qt::DisplayRole) { - if (m_params.lineNumber > 0) - return m_params.lineNumber; + if (m_params.textPosition.line > 0) + return m_params.textPosition.line; return empty; } if (role == Qt::UserRole + 1) - return m_params.lineNumber; + return m_params.textPosition.line; break; case BreakpointAddressColumn: if (role == Qt::DisplayRole) { @@ -2270,9 +2272,9 @@ void GlobalBreakpointItem::removeBreakpointFromModel() void GlobalBreakpointItem::updateLineNumber(int lineNumber) { - if (m_params.lineNumber == lineNumber) + if (m_params.textPosition.line == lineNumber) return; - m_params.lineNumber = lineNumber; + m_params.textPosition.line = lineNumber; update(); } @@ -2294,7 +2296,7 @@ FilePath GlobalBreakpointItem::markerFileName() const int GlobalBreakpointItem::markerLineNumber() const { - return m_params.lineNumber; + return m_params.textPosition.line; } void GlobalBreakpointItem::updateMarker() @@ -2306,7 +2308,7 @@ void GlobalBreakpointItem::updateMarker() return; } - const int line = m_params.lineNumber; + const int line = m_params.textPosition.line; if (m_marker) { if (m_params.fileName != m_marker->filePath()) m_marker->updateFilePath(m_params.fileName); @@ -2373,7 +2375,7 @@ QString GlobalBreakpointItem::toolTip() const << "" << m_params.fileName.toUserOutput() << "" << "" << Tr::tr("Line Number:") - << "" << m_params.lineNumber; + << "" << m_params.textPosition.line; } if (m_params.type == BreakpointByFunction || m_params.type == BreakpointByFileAndLine) { str << "" << Tr::tr("Module:") @@ -2487,7 +2489,7 @@ void BreakpointManager::setOrRemoveBreakpoint(const ContextData &location, const data.tracepoint = !tracePointMessage.isEmpty(); data.message = tracePointMessage; data.fileName = location.fileName; - data.lineNumber = location.lineNumber; + data.textPosition = location.textPosition; } else if (location.type == LocationByAddress) { data.type = BreakpointByAddress; data.tracepoint = !tracePointMessage.isEmpty(); @@ -2513,7 +2515,7 @@ GlobalBreakpoint BreakpointManager::findBreakpointFromContext(const ContextData GlobalBreakpoint bestMatch; theBreakpointManager->forItemsAtLevel<1>([&](const GlobalBreakpoint &gbp) { if (location.type == LocationByFile) { - if (gbp->m_params.isLocatedAt(location.fileName, location.lineNumber, FilePath())) { + if (gbp->m_params.isLocatedAt(location.fileName, location.textPosition.line, FilePath())) { matchLevel = 2; bestMatch = gbp; } else if (matchLevel < 2) { @@ -2522,7 +2524,7 @@ GlobalBreakpoint BreakpointManager::findBreakpointFromContext(const ContextData for (Breakpoint bp : handler->breakpoints()) { if (bp->globalBreakpoint() == gbp) { if (bp->fileName() == location.fileName - && bp->lineNumber() == location.lineNumber) { + && bp->textPosition() == location.textPosition) { matchLevel = 1; bestMatch = gbp; } @@ -2768,8 +2770,8 @@ void BreakpointManager::saveSessionData() map.insert("type", params.type); if (!params.fileName.isEmpty()) map.insert("filename", params.fileName.toSettings()); - if (params.lineNumber) - map.insert("linenumber", params.lineNumber); + if (params.textPosition.line) + map.insert("linenumber", params.textPosition.line); if (!params.functionName.isEmpty()) map.insert("funcname", params.functionName); if (params.address) @@ -2815,7 +2817,7 @@ void BreakpointManager::loadSessionData() params.fileName = FilePath::fromSettings(v); v = map.value("linenumber"); if (v.isValid()) - params.lineNumber = v.toString().toInt(); + params.textPosition.line = v.toString().toInt(); v = map.value("condition"); if (v.isValid()) params.condition = v.toString(); diff --git a/src/plugins/debugger/breakhandler.h b/src/plugins/debugger/breakhandler.h index 0b939a011fc..e850151c3be 100644 --- a/src/plugins/debugger/breakhandler.h +++ b/src/plugins/debugger/breakhandler.h @@ -98,7 +98,8 @@ public: QIcon icon(bool withLocationMarker = false) const; - void setMarkerFileAndLine(const Utils::FilePath &fileName, int lineNumber); + void setMarkerFileAndPosition(const Utils::FilePath &fileName, + const Utils::Text::Position &textPosition); bool needsChange() const; SubBreakpoint findOrCreateSubBreakpoint(const QString &responseId); @@ -128,14 +129,14 @@ public: QString message() const { return m_parameters.message; } QString command() const { return m_parameters.command; } quint64 address() const { return m_parameters.address; } - int lineNumber() const { return m_parameters.lineNumber; } + Utils::Text::Position textPosition() const { return m_parameters.textPosition; } bool isEnabled() const { return m_parameters.enabled; } bool isWatchpoint() const { return m_parameters.isWatchpoint(); } bool isTracepoint() const { return m_parameters.isTracepoint(); } bool isOneShot() const { return m_parameters.oneShot; } bool isPending() const { return m_parameters.pending; } - void setLineNumber(int lineNumber) { m_parameters.lineNumber = lineNumber; } + void setTextPosition(const Utils::Text::Position pos) { m_parameters.textPosition = pos; } void setFileName(const Utils::FilePath &fileName) { m_parameters.fileName = fileName; } void setFunctionName(const QString &functionName) { m_parameters.functionName = functionName; } void setPending(bool pending); diff --git a/src/plugins/debugger/breakpoint.cpp b/src/plugins/debugger/breakpoint.cpp index 79e2badf7b5..2fe9b2da368 100644 --- a/src/plugins/debugger/breakpoint.cpp +++ b/src/plugins/debugger/breakpoint.cpp @@ -27,7 +27,7 @@ namespace Internal { BreakpointParameters::BreakpointParameters(BreakpointType t) : type(t), enabled(true), pathUsage(BreakpointPathUsageEngineDefault), - ignoreCount(0), lineNumber(0), address(0), size(0), + ignoreCount(0), address(0), size(0), bitpos(0), bitsize(0), threadSpec(-1), tracepoint(false), oneShot(false) {} @@ -48,7 +48,7 @@ BreakpointParts BreakpointParameters::differencesTo parts |= ConditionPart; if (ignoreCount != rhs.ignoreCount) parts |= IgnoreCountPart; - if (lineNumber != rhs.lineNumber) + if (textPosition != rhs.textPosition) parts |= FileAndLinePart; if (address != rhs.address) parts |= AddressPart; @@ -73,7 +73,7 @@ bool BreakpointParameters::isValid() const { switch (type) { case BreakpointByFileAndLine: - return !fileName.isEmpty() && lineNumber > 0; + return !fileName.isEmpty() && textPosition.line > 0; case BreakpointByFunction: return !functionName.isEmpty(); case WatchpointAtAddress: @@ -116,7 +116,7 @@ void BreakpointParameters::updateLocation(const QString &location) { if (!location.isEmpty()) { int pos = location.indexOf(':'); - lineNumber = location.mid(pos + 1).toInt(); + textPosition.line = location.mid(pos + 1).toInt(); // FIXME: Handle column QString file = location.left(pos); if (file.startsWith('"') && file.endsWith('"')) file = file.mid(1, file.size() - 2); @@ -164,8 +164,8 @@ QString BreakpointParameters::toString() const ts << "Type: " << type; switch (type) { case BreakpointByFileAndLine: - ts << " FileName: " << fileName << ':' << lineNumber - << " PathUsage: " << pathUsage; + ts << " FileName: " << fileName << ':' << textPosition.line; + ts << " PathUsage: " << pathUsage; break; case BreakpointByFunction: case BreakpointOnQmlSignalEmit: @@ -295,7 +295,7 @@ void BreakpointParameters::updateFromGdbOutput(const GdbMi &bkpt, const Utils::F } else if (child.hasName("fullname")) { fullName = child.data(); } else if (child.hasName("line")) { - lineNumber = child.toInt(); + textPosition.line = child.toInt(); } else if (child.hasName("cond")) { // gdb 6.3 likes to "rewrite" conditions. Just accept that fact. condition = child.data(); diff --git a/src/plugins/debugger/breakpoint.h b/src/plugins/debugger/breakpoint.h index b5ea0628da7..af96f43d055 100644 --- a/src/plugins/debugger/breakpoint.h +++ b/src/plugins/debugger/breakpoint.h @@ -7,6 +7,7 @@ #include #include +#include namespace Debugger { namespace Internal { @@ -139,7 +140,7 @@ public: Utils::FilePath fileName;//!< Short name of source file. QString condition; //!< Condition associated with breakpoint. int ignoreCount; //!< Ignore count associated with breakpoint. - int lineNumber; //!< Line in source file. + Utils::Text::Position textPosition; //!< Line and column in source file. quint64 address; //!< Address for address based data breakpoints. QString expression; //!< Expression for expression based data breakpoints. uint size; //!< Size of watched area for data breakpoints. diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 7c70a3c31b0..c60052452a8 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -818,7 +818,7 @@ void CdbEngine::executeRunToLine(const ContextData &data) } else { bp.type =BreakpointByFileAndLine; bp.fileName = data.fileName; - bp.lineNumber = data.lineNumber; + bp.textPosition = data.textPosition; } runCommand({cdbAddBreakpointCommand(bp, m_sourcePathMappings), BuiltinCommand, @@ -854,7 +854,7 @@ void CdbEngine::executeJumpToLine(const ContextData &data) // Jump to source line: Resolve source line address and go to that location QString cmd; StringInputStream str(cmd); - str << "? `" << data.fileName.toUserOutput() << ':' << data.lineNumber << '`'; + str << "? `" << data.fileName.toUserOutput() << ':' << data.textPosition.line << '`'; runCommand({cmd, BuiltinCommand, [this, data](const DebuggerResponse &r) { handleJumpToLineAddressResolution(r, data); }}); } @@ -891,7 +891,7 @@ void CdbEngine::handleJumpToLineAddressResolution(const DebuggerResponse &respon const quint64 address = answer.toULongLong(&ok, 16); if (ok && address) { jumpToAddress(address); - gotoLocation(Location(context.fileName, context.lineNumber)); + gotoLocation(Location(context.fileName, context.textPosition)); } } @@ -2505,8 +2505,9 @@ void CdbEngine::insertBreakpoint(const Breakpoint &bp) if (!m_autoBreakPointCorrection && parameters.type == BreakpointByFileAndLine && debuggerSettings()->cdbBreakPointCorrection.value()) { - response.lineNumber = int(lineCorrection->fixLineNumber(parameters.fileName, - unsigned(parameters.lineNumber))); + response.textPosition.line = + int(lineCorrection->fixLineNumber(parameters.fileName, + unsigned(parameters.textPosition.line))); QString cmd = cdbAddBreakpointCommand(response, m_sourcePathMappings, responseId); runCommand({cmd, BuiltinCommand, handleBreakInsertCB}); } else { @@ -2979,7 +2980,7 @@ BreakpointParameters CdbEngine::parseBreakPoint(const GdbMi &gdbmi) result.fileName = Utils::FilePath::fromUserInput(mappedFile.fileName); const GdbMi lineNumber = gdbmi["srcline"]; if (lineNumber.isValid()) - result.lineNumber = lineNumber.data().toULongLong(nullptr, 0); + result.textPosition.line = lineNumber.data().toULongLong(nullptr, 0); } const GdbMi addressG = gdbmi["address"]; if (addressG.isValid()) @@ -3036,7 +3037,7 @@ void CdbEngine::handleBreakPoints(const DebuggerResponse &response) currentResponse.pending = reportedResponse.pending; currentResponse.enabled = reportedResponse.enabled; currentResponse.fileName = reportedResponse.fileName; - currentResponse.lineNumber = reportedResponse.lineNumber; + currentResponse.textPosition = reportedResponse.textPosition; formatCdbBreakPointResponse(bp->modelId(), responseId, currentResponse, str); if (debugBreakpoints) qDebug(" Setting for %s: %s\n", qPrintable(responseId), @@ -3053,7 +3054,7 @@ void CdbEngine::handleBreakPoints(const DebuggerResponse &response) currentResponse.pending = reportedResponse.pending; currentResponse.enabled = reportedResponse.enabled; currentResponse.fileName = reportedResponse.fileName; - currentResponse.lineNumber = reportedResponse.lineNumber; + currentResponse.textPosition = reportedResponse.textPosition; Breakpoint bp = sub->breakpoint(); QTC_ASSERT(bp, continue); formatCdbBreakPointResponse(bp->modelId(), responseId, currentResponse, str); diff --git a/src/plugins/debugger/cdb/cdbparsehelpers.cpp b/src/plugins/debugger/cdb/cdbparsehelpers.cpp index 2749c0445a5..dfea07a6c2a 100644 --- a/src/plugins/debugger/cdb/cdbparsehelpers.cpp +++ b/src/plugins/debugger/cdb/cdbparsehelpers.cpp @@ -161,7 +161,8 @@ QString cdbAddBreakpointCommand(const BreakpointParameters &bpIn, str << '`'; if (!params.module.isEmpty()) str << params.module << '!'; - str << cdbBreakPointFileName(params, sourcePathMapping) << ':' << params.lineNumber << '`'; + str << cdbBreakPointFileName(params, sourcePathMapping) + << ':' << params.textPosition.line << '`'; break; case WatchpointAtAddress: { // Read/write, no space here const unsigned size = params.size ? params.size : 1; diff --git a/src/plugins/debugger/dap/dapengine.cpp b/src/plugins/debugger/dap/dapengine.cpp index 9db7c0f7e5e..3f31129a5a8 100644 --- a/src/plugins/debugger/dap/dapengine.cpp +++ b/src/plugins/debugger/dap/dapengine.cpp @@ -281,7 +281,7 @@ static QJsonObject createBreakpoint(const Breakpoint &breakpoint) return QJsonObject(); QJsonObject bp; - bp["line"] = params.lineNumber; + bp["line"] = params.textPosition.line; bp["source"] = QJsonObject{{"name", params.fileName.fileName()}, {"path", params.fileName.path()}}; return bp; @@ -622,7 +622,7 @@ void DapEngine::handleOutput(const QJsonDocument &data) QString id = QString::number(body.value("hitBreakpointIds").toArray().first().toInteger()); const BreakpointParameters ¶ms = breakHandler()->findBreakpointByResponseId(id)->requestedParameters(); - gotoLocation(Location(params.fileName, params.lineNumber)); + gotoLocation(Location(params.fileName, params.textPosition)); } if (state() == InferiorStopRequested) diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 27c1373c99e..0c80b0cf9f0 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -142,7 +142,7 @@ static bool debuggerActionsEnabledHelper(DebuggerState state) Location::Location(const StackFrame &frame, bool marker) { m_fileName = frame.file; - m_lineNumber = frame.line; + m_textPosition = {frame.line, -1}; m_needsMarker = marker; m_functionName = frame.function; m_hasDebugInfo = frame.isUsable(); @@ -1075,7 +1075,7 @@ void DebuggerEngine::gotoLocation(const Location &loc) return; } const FilePath file = loc.fileName(); - const int line = loc.lineNumber(); + const int line = loc.textPosition().line; bool newEditor = false; IEditor *editor = EditorManager::openEditor(file, Id(), diff --git a/src/plugins/debugger/debuggerengine.h b/src/plugins/debugger/debuggerengine.h index ac91ecb6440..783fa9b9f17 100644 --- a/src/plugins/debugger/debuggerengine.h +++ b/src/plugins/debugger/debuggerengine.h @@ -219,12 +219,14 @@ public: Location(quint64 address) { m_address = address; } Location(const Utils::FilePath &file) { m_fileName = file; } Location(const Utils::FilePath &file, int line, bool marker = true) - { m_lineNumber = line; m_fileName = file; m_needsMarker = marker; } + { m_textPosition = {line, -1}; m_fileName = file; m_needsMarker = marker; } + Location(const Utils::FilePath &file, const Utils::Text::Position &pos, bool marker = true) + { m_textPosition = pos; m_fileName = file; m_needsMarker = marker; } Location(const StackFrame &frame, bool marker = true); Utils::FilePath fileName() const { return m_fileName; } QString functionName() const { return m_functionName; } QString from() const { return m_from; } - int lineNumber() const { return m_lineNumber; } + Utils::Text::Position textPosition() const { return m_textPosition; } void setNeedsRaise(bool on) { m_needsRaise = on; } void setNeedsMarker(bool on) { m_needsMarker = on; } void setFileName(const Utils::FilePath &fileName) { m_fileName = fileName; } @@ -240,7 +242,7 @@ private: bool m_needsMarker = false; bool m_needsRaise = true; bool m_hasDebugInfo = true; - int m_lineNumber = -1; + Utils::Text::Position m_textPosition; Utils::FilePath m_fileName; QString m_functionName; QString m_from; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 7ccc808b08f..d3a74d5d304 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -603,8 +603,8 @@ public: } else { //: Message tracepoint: %1 file, %2 line %3 function hit. message = Tr::tr("%1:%2 %3() hit").arg(data.fileName.fileName()). - arg(data.lineNumber). - arg(cppFunctionAt(data.fileName, data.lineNumber)); + arg(data.textPosition.line). + arg(cppFunctionAt(data.fileName, data.textPosition.line)); } QInputDialog dialog; // Create wide input dialog. dialog.setWindowFlags(dialog.windowFlags() & ~(Qt::MSWindowsFixedSizeDialogHint)); @@ -1881,7 +1881,7 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget, if (engine->hasCapability(RunToLineCapability)) { auto act = menu->addAction(args.address ? Tr::tr("Run to Address 0x%1").arg(args.address, 0, 16) - : Tr::tr("Run to Line %1").arg(args.lineNumber)); + : Tr::tr("Run to Line %1").arg(args.textPosition.line)); connect(act, &QAction::triggered, this, [args, engine] { QTC_ASSERT(engine, return); engine->executeRunToLine(args); @@ -1890,7 +1890,7 @@ void DebuggerPluginPrivate::requestContextMenu(TextEditorWidget *widget, if (engine->hasCapability(JumpToLineCapability)) { auto act = menu->addAction(args.address ? Tr::tr("Jump to Address 0x%1").arg(args.address, 0, 16) - : Tr::tr("Jump to Line %1").arg(args.lineNumber)); + : Tr::tr("Jump to Line %1").arg(args.textPosition.line)); connect(act, &QAction::triggered, this, [args, engine] { QTC_ASSERT(engine, return); engine->executeJumpToLine(args); diff --git a/src/plugins/debugger/debuggerprotocol.h b/src/plugins/debugger/debuggerprotocol.h index d06a95c7367..702da98c9ac 100644 --- a/src/plugins/debugger/debuggerprotocol.h +++ b/src/plugins/debugger/debuggerprotocol.h @@ -12,6 +12,7 @@ #include #include +#include namespace Utils { class ProcessHandle; } @@ -326,7 +327,7 @@ public: public: LocationType type = UnknownLocation; Utils::FilePath fileName; - int lineNumber = 0; + Utils::Text::Position textPosition; quint64 address = 0; }; diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 7a9eab16b9c..e5052d1b89d 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -1211,9 +1211,9 @@ void GdbEngine::handleStopResponse(const GdbMi &data) if (Breakpoint bp = breakHandler()->findBreakpointByResponseId(nr)) { const FilePath &bpFileName = bp->fileName(); if (!bpFileName.isEmpty()) - bp->setMarkerFileAndLine(bpFileName, lineNumber); + bp->setMarkerFileAndPosition(bpFileName, {lineNumber, -1}); else if (!fileName.isEmpty()) - bp->setMarkerFileAndLine(fileName, lineNumber); + bp->setMarkerFileAndPosition(fileName, {lineNumber, -1}); } } @@ -1946,13 +1946,13 @@ void GdbEngine::executeRunToLine(const ContextData &data) CHECK_STATE(InferiorStopOk); setTokenBarrier(); notifyInferiorRunRequested(); - showStatusMessage(Tr::tr("Run to line %1 requested...").arg(data.lineNumber), 5000); + showStatusMessage(Tr::tr("Run to line %1 requested...").arg(data.textPosition.line), 5000); #if 1 QString loc; if (data.address) loc = addressSpec(data.address); else - loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.lineNumber); + loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.textPosition.line); runCommand({"tbreak " + loc}); runCommand({"continue", NativeCommand|RunRequest, CB(handleExecuteRunToLine)}); @@ -1980,7 +1980,7 @@ void GdbEngine::executeJumpToLine(const ContextData &data) if (data.address) loc = addressSpec(data.address); else - loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.lineNumber); + loc = '"' + breakLocation(data.fileName) + '"' + ':' + QString::number(data.textPosition.line); runCommand({"tbreak " + loc}); notifyInferiorRunRequested(); @@ -2086,7 +2086,7 @@ QString GdbEngine::breakpointLocation(const BreakpointParameters &data) // The argument is simply a C-quoted version of the argument to the // non-MI "break" command, including the "original" quoting it wants. return "\"\\\"" + GdbMi::escapeCString(fileName) + "\\\":" - + QString::number(data.lineNumber) + '"'; + + QString::number(data.textPosition.line) + '"'; } QString GdbEngine::breakpointLocation2(const BreakpointParameters &data) @@ -2097,7 +2097,7 @@ QString GdbEngine::breakpointLocation2(const BreakpointParameters &data) const QString fileName = usage == BreakpointUseFullPath ? data.fileName.path() : breakLocation(data.fileName); - return GdbMi::escapeCString(fileName) + ':' + QString::number(data.lineNumber); + return GdbMi::escapeCString(fileName) + ':' + QString::number(data.textPosition.line); } void GdbEngine::handleInsertInterpreterBreakpoint(const DebuggerResponse &response, @@ -2236,7 +2236,7 @@ void GdbEngine::handleBreakInsert1(const DebuggerResponse &response, const Break // Older version of gdb don't know the -a option to set tracepoints // ^error,msg="mi_cmd_break_insert: Unknown option ``a''" const QString fileName = bp->fileName().toString(); - const int lineNumber = bp->lineNumber(); + const int lineNumber = bp->textPosition().line; DebuggerCommand cmd("trace \"" + GdbMi::escapeCString(fileName) + "\":" + QString::number(lineNumber), NeedsTemporaryStop); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 09b5d6eaec9..f7667e40e29 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -416,7 +416,7 @@ void LldbEngine::executeRunToLine(const ContextData &data) notifyInferiorRunRequested(); DebuggerCommand cmd("executeRunToLocation"); cmd.arg("file", data.fileName.path()); - cmd.arg("line", data.lineNumber); + cmd.arg("line", data.textPosition.line); cmd.arg("address", data.address); runCommand(cmd); } @@ -433,7 +433,7 @@ void LldbEngine::executeJumpToLine(const ContextData &data) { DebuggerCommand cmd("executeJumpToLocation"); cmd.arg("file", data.fileName.path()); - cmd.arg("line", data.lineNumber); + cmd.arg("line", data.textPosition.line); cmd.arg("address", data.address); runCommand(cmd); } @@ -561,7 +561,7 @@ void LldbEngine::updateBreakpointData(const Breakpoint &bp, const GdbMi &bkpt, b bp->setCondition(fromHex(bkpt["condition"].data())); bp->setHitCount(bkpt["hitcount"].toInt()); bp->setFileName(FilePath::fromUserInput(bkpt["file"].data())); - bp->setLineNumber(bkpt["line"].toInt()); + bp->setTextPosition({bkpt["line"].toInt(), -1}); GdbMi locations = bkpt["locations"]; const int numChild = locations.childCount(); @@ -574,7 +574,7 @@ void LldbEngine::updateBreakpointData(const Breakpoint &bp, const GdbMi &bkpt, b loc->params.address = location["addr"].toAddress(); loc->params.functionName = location["function"].data(); loc->params.fileName = FilePath::fromUserInput(location["file"].data()); - loc->params.lineNumber = location["line"].toInt(); + loc->params.textPosition.line = location["line"].toInt(); loc->displayName = QString("%1.%2").arg(bp->responseId()).arg(locid); } bp->setPending(false); diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index accf32d0e6e..8b548b9d3d0 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -220,7 +220,7 @@ void PdbEngine::insertBreakpoint(const Breakpoint &bp) if (params.type == BreakpointByFunction) loc = params.functionName; else - loc = params.fileName.toString() + ':' + QString::number(params.lineNumber); + loc = params.fileName.toString() + ':' + QString::number(params.textPosition.line); postDirectCommand("break " + loc); } @@ -476,7 +476,7 @@ void PdbEngine::handleOutput2(const QString &data) QTC_ASSERT(bp, continue); bp->setResponseId(bpnr); bp->setFileName(fileName); - bp->setLineNumber(lineNumber); + bp->setTextPosition({lineNumber, -1}); bp->adjustMarker(); bp->setPending(false); notifyBreakpointInsertOk(bp); diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 6a304f18a67..c0ecaf4849f 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -603,10 +603,10 @@ void QmlEngine::executeRunToLine(const ContextData &data) { QTC_ASSERT(state() == InferiorStopOk, qDebug() << state()); showStatusMessage(Tr::tr("Run to line %1 (%2) requested...") - .arg(data.lineNumber) + .arg(data.textPosition.line) .arg(data.fileName.toString()), 5000); - d->setBreakpoint(SCRIPTREGEXP, data.fileName.toString(), true, data.lineNumber); + d->setBreakpoint(SCRIPTREGEXP, data.fileName.toString(), true, data.textPosition.line); clearExceptionSelection(); d->continueDebugging(Continue); @@ -658,7 +658,7 @@ void QmlEngine::insertBreakpoint(const Breakpoint &bp) } else if (requested.type == BreakpointByFileAndLine) { d->setBreakpoint(SCRIPTREGEXP, requested.fileName.toString(), - requested.enabled, requested.lineNumber, 0, + requested.enabled, requested.textPosition.line, 0, requested.condition, requested.ignoreCount); } else if (requested.type == BreakpointOnQmlSignalEmit) { @@ -716,7 +716,7 @@ void QmlEngine::updateBreakpoint(const Breakpoint &bp) } else { d->clearBreakpoint(bp); d->setBreakpoint(SCRIPTREGEXP, requested.fileName.toString(), - requested.enabled, requested.lineNumber, 0, + requested.enabled, requested.textPosition.line, 0, requested.condition, requested.ignoreCount); d->breakpointsSync.insert(d->sequence, bp); } @@ -1727,7 +1727,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) //The breakpoint requested line should be same as //actual line if (bp && bp->state() != BreakpointInserted) { - bp->setLineNumber(line); + bp->setTextPosition({line, -1}); bp->setPending(false); engine->notifyBreakpointInsertOk(bp); } @@ -1865,7 +1865,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) setBreakpoint(SCRIPTREGEXP, params.fileName.toString(), params.enabled, - params.lineNumber, + params.textPosition.line, newColumn, params.condition, params.ignoreCount); @@ -1889,7 +1889,7 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) bp->setFunctionName(invocationText); } if (bp->state() != BreakpointInserted) { - bp->setLineNumber(breakData.value("sourceLine").toInt() + 1); + bp->setTextPosition({breakData.value("sourceLine").toInt() + 1, -1}); bp->setPending(false); engine->notifyBreakpointInsertOk(bp); } diff --git a/src/plugins/debugger/sourceutils.cpp b/src/plugins/debugger/sourceutils.cpp index 2149821662f..82f1929bb89 100644 --- a/src/plugins/debugger/sourceutils.cpp +++ b/src/plugins/debugger/sourceutils.cpp @@ -315,14 +315,14 @@ ContextData getLocationContext(TextDocument *document, int lineNumber) if (ln > 0) { data.type = LocationByFile; data.fileName = Utils::FilePath::fromString(fileName); - data.lineNumber = ln; + data.textPosition.line = ln; } } } } else { data.type = LocationByFile; data.fileName = document->filePath(); - data.lineNumber = lineNumber; + data.textPosition.line = lineNumber; } return data; } @@ -381,13 +381,13 @@ static void setValueAnnotationsHelper(BaseTextEditor *textEditor, if (!cppDocument) // For non-C++ documents. return; - const int firstLine = firstRelevantLine(cppDocument, loc.lineNumber(), 1); + const int firstLine = firstRelevantLine(cppDocument, loc.textPosition().line, 1); if (firstLine < 1) return; CPlusPlus::ExpressionUnderCursor expressionUnderCursor(cppDocument->languageFeatures()); QTextCursor tc = widget->textCursor(); - for (int lineNumber = loc.lineNumber(); lineNumber >= firstLine; --lineNumber) { + for (int lineNumber = loc.textPosition().line; lineNumber >= firstLine; --lineNumber) { const QTextBlock block = textDocument->document()->findBlockByNumber(lineNumber - 1); tc.setPosition(block.position()); for (; !tc.atBlockEnd(); tc.movePosition(QTextCursor::NextCharacter)) { diff --git a/src/plugins/debugger/uvsc/uvscengine.cpp b/src/plugins/debugger/uvsc/uvscengine.cpp index 06450651de2..8efa0fc2936 100644 --- a/src/plugins/debugger/uvsc/uvscengine.cpp +++ b/src/plugins/debugger/uvsc/uvscengine.cpp @@ -359,7 +359,7 @@ void UvscEngine::insertBreakpoint(const Breakpoint &bp) // Add file name. expression += "\\" + requested.fileName.toString(); // Add line number. - expression += "\\" + QString::number(requested.lineNumber); + expression += "\\" + QString::number(requested.textPosition.line); } handleInsertBreakpoint(expression, bp); @@ -777,7 +777,7 @@ void UvscEngine::handleInsertBreakpoint(const QString &exp, const Breakpoint &bp bp->setPending(false); bp->setResponseId(QString::number(tickMark)); bp->setAddress(address); - bp->setLineNumber(line); + bp->setTextPosition(Text::Position{int(line), -1}); bp->setFileName(FilePath::fromString(fileName)); bp->setFunctionName(function); notifyBreakpointInsertOk(bp); From be0a129543b1148c54d576d9d8773e765175e164 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 7 Jun 2023 08:01:45 +0200 Subject: [PATCH 52/57] CMake: fix find link at Change-Id: I1afcd3bd2b4db53ff8284f9acbe2b07102e66205 Reviewed-by: Eike Ziller Reviewed-by: --- src/plugins/cmakeprojectmanager/cmakeeditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 5193661e57b..3411fd85b58 100644 --- a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp @@ -157,7 +157,7 @@ void CMakeEditorWidget::findLinkAt(const QTextCursor &cursor, // find the beginning of a filename QString buffer; - int beginPos = column; + int beginPos = column - 1; while (beginPos >= 0) { if (isValidFileNameChar(block, beginPos)) { buffer.prepend(block.at(beginPos)); From 2d128e9c707791f3ea127322c18fdee828e685ab Mon Sep 17 00:00:00 2001 From: Marcus Tillmanns Date: Wed, 7 Jun 2023 09:28:40 +0200 Subject: [PATCH 53/57] Debugger: Fix BreakpointItem::needsChange Change-Id: I75680eddb7a6c2ba23148b66cf8dfcc08b07eca2 Reviewed-by: hjk --- src/plugins/debugger/breakhandler.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/debugger/breakhandler.cpp b/src/plugins/debugger/breakhandler.cpp index ce539134551..180b2c0a07d 100644 --- a/src/plugins/debugger/breakhandler.cpp +++ b/src/plugins/debugger/breakhandler.cpp @@ -1873,6 +1873,8 @@ bool BreakpointItem::needsChange() const return true; if (oparams.type == BreakpointByFileAndLine && oparams.textPosition != m_parameters.textPosition) return true; + if (oparams.pathUsage != m_parameters.pathUsage) + return true; // FIXME: Too strict, functions may have parameter lists, or not. // if (m_params.type == BreakpointByFunction && m_params.functionName != m_response.functionName) // return true; From 04b86eb3db9a42b2e3bf0e5c23f2bcb261002dbb Mon Sep 17 00:00:00 2001 From: Jarek Kobus Date: Sat, 3 Jun 2023 01:34:32 +0200 Subject: [PATCH 54/57] ImageScaling: Implement the example using TaskTree Change-Id: Iac54157955d5dffe12a7fdeed904fbcf62a2b667 Reviewed-by: Marcus Tillmanns Reviewed-by: Qt CI Bot Reviewed-by: --- .../tasking/imagescaling/imagescaling.cpp | 197 ++++++------------ .../tasking/imagescaling/imagescaling.h | 29 +-- 2 files changed, 69 insertions(+), 157 deletions(-) diff --git a/tests/manual/tasking/imagescaling/imagescaling.cpp b/tests/manual/tasking/imagescaling/imagescaling.cpp index 362a30287e6..e3acda749b2 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.cpp +++ b/tests/manual/tasking/imagescaling/imagescaling.cpp @@ -3,19 +3,24 @@ #include "imagescaling.h" #include "downloaddialog.h" +#include +#include -#include +using namespace Tasking; Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDialog(this)) { resize(800, 600); - addUrlsButton = new QPushButton(tr("Add URLs")); + QPushButton *addUrlsButton = new QPushButton(tr("Add URLs")); connect(addUrlsButton, &QPushButton::clicked, this, &Images::process); cancelButton = new QPushButton(tr("Cancel")); cancelButton->setEnabled(false); - connect(cancelButton, &QPushButton::clicked, this, &Images::cancel); + connect(cancelButton, &QPushButton::clicked, this, [this] { + statusBar->showMessage(tr("Canceled.")); + taskTree.reset(); + }); QHBoxLayout *buttonLayout = new QHBoxLayout(); buttonLayout->addWidget(addUrlsButton); @@ -32,141 +37,77 @@ Images::Images(QWidget *parent) : QWidget(parent), downloadDialog(new DownloadDi mainLayout->addStretch(); mainLayout->addWidget(statusBar); setLayout(mainLayout); - - connect(&scalingWatcher, &QFutureWatcher>::finished, - this, &Images::scaleFinished); } -Images::~Images() +static void scale(QPromise &promise, const QByteArray &data) { - cancel(); + const auto image = QImage::fromData(data); + if (image.isNull()) + promise.future().cancel(); + else + promise.addResult(image.scaled(100, 100, Qt::KeepAspectRatio)); } void Images::process() { - // Clean previous state - replies.clear(); - addUrlsButton->setEnabled(false); + if (downloadDialog->exec() != QDialog::Accepted) + return; - if (downloadDialog->exec() == QDialog::Accepted) { - - const auto urls = downloadDialog->getUrls(); - if (urls.empty()) - return; + const auto urls = downloadDialog->getUrls(); + initLayout(urls.size()); + const auto onRootSetup = [this] { + statusBar->showMessage(tr("Downloading and Scaling...")); cancelButton->setEnabled(true); + }; + const auto onRootDone = [this] { + statusBar->showMessage(tr("Finished.")); + cancelButton->setEnabled(false); + }; + QList tasks { + finishAllAndDone, + parallel, + onGroupSetup(onRootSetup), + onGroupDone(onRootDone) + }; - initLayout(urls.size()); + int i = 0; + for (const QUrl &url : urls) { + TreeStorage storage; - downloadFuture = download(urls); - statusBar->showMessage(tr("Downloading...")); + const auto onDownloadSetup = [this, url](NetworkQuery &query) { + query.setNetworkAccessManager(&qnam); + query.setRequest(QNetworkRequest(url)); + }; + const auto onDownloadDone = [storage](const NetworkQuery &query) { + *storage = query.reply()->readAll(); + }; + const auto onDownloadError = [this, i](const NetworkQuery &query) { + labels[i]->setText(tr("Download\nError.\nCode: %1.").arg(query.reply()->error())); + }; - downloadFuture - .then([this](auto) { - cancelButton->setEnabled(false); - updateStatus(tr("Scaling...")); - scalingWatcher.setFuture(QtConcurrent::run(Images::scaled, - downloadFuture.results())); - }) - .onCanceled([this] { - updateStatus(tr("Download has been canceled.")); - }) - .onFailed([this](QNetworkReply::NetworkError error) { - updateStatus(tr("Download finished with error: %1").arg(error)); - // Abort all pending requests - abortDownload(); - }) - .onFailed([this](const std::exception &ex) { - updateStatus(tr(ex.what())); - }) - .then([this]() { - cancelButton->setEnabled(false); - addUrlsButton->setEnabled(true); - }); - } -} + const auto onScalingSetup = [storage](ConcurrentCall &data) { + data.setConcurrentCallData(&scale, *storage); + }; + const auto onScalingDone = [this, i](const ConcurrentCall &data) { + labels[i]->setPixmap(QPixmap::fromImage(data.result())); + }; + const auto onScalingError = [this, i](const ConcurrentCall &) { + labels[i]->setText(tr("Image\nData\nError.")); + }; -void Images::cancel() -{ - statusBar->showMessage(tr("Canceling...")); - - downloadFuture.cancel(); - abortDownload(); -} - -void Images::scaleFinished() -{ - const OptionalImages result = scalingWatcher.result(); - if (result.has_value()) { - const auto scaled = result.value(); - showImages(scaled); - updateStatus(tr("Finished")); - } else { - updateStatus(tr("Failed to extract image data.")); - } - addUrlsButton->setEnabled(true); -} - -QFuture Images::download(const QList &urls) -{ - QSharedPointer> promise(new QPromise()); - promise->start(); - - for (const auto &url : urls) { - QSharedPointer reply(qnam.get(QNetworkRequest(url))); - replies.push_back(reply); - - QtFuture::connect(reply.get(), &QNetworkReply::finished).then([=] { - if (promise->isCanceled()) { - if (!promise->future().isFinished()) - promise->finish(); - return; - } - - if (reply->error() != QNetworkReply::NoError) { - if (!promise->future().isFinished()) - throw reply->error(); - } - promise->addResult(reply->readAll()); - - // Report finished on the last download - if (promise->future().resultCount() == urls.size()) - promise->finish(); - }).onFailed([promise] (QNetworkReply::NetworkError error) { - promise->setException(std::make_exception_ptr(error)); - promise->finish(); - }).onFailed([promise] { - const auto ex = std::make_exception_ptr( - std::runtime_error("Unknown error occurred while downloading.")); - promise->setException(ex); - promise->finish(); - }); + const Group group { + Storage(storage), + NetworkQueryTask(onDownloadSetup, onDownloadDone, onDownloadError), + ConcurrentCallTask(onScalingSetup, onScalingDone, onScalingError) + }; + tasks.append(group); + ++i; } - return promise->future(); -} - -Images::OptionalImages Images::scaled(const QList &data) -{ - QList scaled; - for (const auto &imgData : data) { - QImage image; - image.loadFromData(imgData); - if (image.isNull()) - return std::nullopt; - - scaled.push_back(image.scaled(100, 100, Qt::KeepAspectRatio)); - } - - return scaled; -} - -void Images::showImages(const QList &images) -{ - for (int i = 0; i < images.size(); ++i) { - labels[i]->setAlignment(Qt::AlignCenter); - labels[i]->setPixmap(QPixmap::fromImage(images[i])); - } + taskTree.reset(new TaskTree(tasks)); + connect(taskTree.get(), &TaskTree::done, this, [this] { taskTree.release()->deleteLater(); }); + taskTree->start(); } void Images::initLayout(qsizetype count) @@ -186,19 +127,9 @@ void Images::initLayout(qsizetype count) for (int j = 0; j < dim; ++j) { QLabel *imageLabel = new QLabel; imageLabel->setFixedSize(100, 100); + imageLabel->setAlignment(Qt::AlignCenter); imagesLayout->addWidget(imageLabel, i, j); labels.append(imageLabel); } } } - -void Images::updateStatus(const QString &msg) -{ - statusBar->showMessage(msg); -} - -void Images::abortDownload() -{ - for (auto reply : replies) - reply->abort(); -} diff --git a/tests/manual/tasking/imagescaling/imagescaling.h b/tests/manual/tasking/imagescaling/imagescaling.h index d3409e60a0f..91e89c13b28 100644 --- a/tests/manual/tasking/imagescaling/imagescaling.h +++ b/tests/manual/tasking/imagescaling/imagescaling.h @@ -4,10 +4,9 @@ #ifndef IMAGESCALING_H #define IMAGESCALING_H -#include -#include #include -#include +#include +#include class DownloadDialog; class Images : public QWidget @@ -15,27 +14,11 @@ class Images : public QWidget Q_OBJECT public: Images(QWidget *parent = nullptr); - ~Images(); - - void initLayout(qsizetype count); - - QFuture download(const QList &urls); - void updateStatus(const QString &msg); - void showImages(const QList &images); - void abortDownload(); - -public slots: - void process(); - void cancel(); - -private slots: - void scaleFinished(); private: - using OptionalImages = std::optional>; - static OptionalImages scaled(const QList &data); + void process(); + void initLayout(qsizetype count); - QPushButton *addUrlsButton; QPushButton *cancelButton; QVBoxLayout *mainLayout; QList labels; @@ -44,9 +27,7 @@ private: DownloadDialog *downloadDialog; QNetworkAccessManager qnam; - QList> replies; - QFuture downloadFuture; - QFutureWatcher scalingWatcher; + std::unique_ptr taskTree; }; #endif // IMAGESCALING_H From 2b4fe0aef84888983cc40bb0a68d5279c4b7759f Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Thu, 1 Jun 2023 17:56:58 +0200 Subject: [PATCH 55/57] Qml: fix Boot2Qt deployment from Windows host - fix QmlBuildSystem::target*() to resolve maybe device files - Cache lookup of qml path on device Task-number: QDS-9994 Change-Id: I5675368368f2d1cc513feb98fdcdd75fda1a764a Reviewed-by: hjk Reviewed-by: Qt CI Bot Reviewed-by: --- .../projectexplorer/devicesupport/idevice.cpp | 9 ++++ .../buildsystem/qmlbuildsystem.cpp | 51 +++++++------------ .../buildsystem/qmlbuildsystem.h | 3 -- .../qmlprojectrunconfiguration.cpp | 9 ++-- src/plugins/remotelinux/linuxdevice.cpp | 1 - 5 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/plugins/projectexplorer/devicesupport/idevice.cpp b/src/plugins/projectexplorer/devicesupport/idevice.cpp index 50f552444b3..12ff8e3cac3 100644 --- a/src/plugins/projectexplorer/devicesupport/idevice.cpp +++ b/src/plugins/projectexplorer/devicesupport/idevice.cpp @@ -142,6 +142,7 @@ public: FilePath debugServerPath; FilePath debugDumperPath = Core::ICore::resourcePath("debugger/"); FilePath qmlRunCommand; + bool qmlRunCommandChecked = false; bool emptyCommandAllowed = false; QList deviceIcons; @@ -603,12 +604,20 @@ void IDevice::setDebugServerPath(const FilePath &path) FilePath IDevice::qmlRunCommand() const { + if (!d->qmlRunCommandChecked) { + d->qmlRunCommandChecked = true; + QString runtime = d->qmlRunCommand.path(); + if (runtime.isEmpty()) + runtime = "qml"; + d->qmlRunCommand = searchExecutableInPath(runtime); + } return d->qmlRunCommand; } void IDevice::setQmlRunCommand(const FilePath &path) { d->qmlRunCommand = path; + d->qmlRunCommandChecked = false; } void IDevice::setExtraData(Id kind, const QVariant &data) diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp index 4ccb9ad48f0..559bdeb198c 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.cpp @@ -107,9 +107,8 @@ void QmlBuildSystem::updateDeploymentData() } ProjectExplorer::DeploymentData deploymentData; - for (const auto &file : m_projectItem->files()) { - deploymentData.addFile(file, targetFile(file).parentDir().toString()); - } + for (const auto &file : m_projectItem->files()) + deploymentData.addFile(file, m_projectItem->targetDirectory()); setDeploymentData(deploymentData); } @@ -160,9 +159,7 @@ void QmlBuildSystem::triggerParsing() Utils::FilePath QmlBuildSystem::canonicalProjectDir() const { - return BuildSystem::target() - ->project() - ->projectFilePath() + return projectFilePath() .canonicalPath() .normalizedPathName() .parentDir(); @@ -188,10 +185,11 @@ void QmlBuildSystem::refresh(RefreshOptions options) QmlJS::ModelManagerInterface::ProjectInfo projectInfo = modelManager->defaultProjectInfoForProject(project(), project()->files(Project::HiddenRccFolders)); - const QStringList searchPaths = makeAbsolute(canonicalProjectDir(), customImportPaths()); - for (const QString &searchPath : searchPaths) - projectInfo.importPaths.maybeInsert(Utils::FilePath::fromString(searchPath), + + for (const QString &searchPath : customImportPaths()) { + projectInfo.importPaths.maybeInsert(projectFilePath().pathAppended(searchPath), QmlJS::Dialect::Qml); + } modelManager->updateProjectInfo(projectInfo, project()); @@ -370,22 +368,23 @@ bool QmlBuildSystem::setMainUiFileInMainFile(const Utils::FilePath &newMainUiFil Utils::FilePath QmlBuildSystem::targetDirectory() const { - if (DeviceTypeKitAspect::deviceTypeId(kit()) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) - return canonicalProjectDir(); - - return m_projectItem ? Utils::FilePath::fromString(m_projectItem->targetDirectory()) - : Utils::FilePath(); + Utils::FilePath result; + if (DeviceTypeKitAspect::deviceTypeId(kit()) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE) { + result = canonicalProjectDir(); + } else if (IDevice::ConstPtr device = DeviceKitAspect::device(kit())) { + if (m_projectItem) + result = device->filePath(m_projectItem->targetDirectory()); + } + return result; } Utils::FilePath QmlBuildSystem::targetFile(const Utils::FilePath &sourceFile) const { - const QDir sourceDir(m_projectItem ? m_projectItem->sourceDirectory().path() - : canonicalProjectDir().toString()); - const QDir targetDir(targetDirectory().toString()); - const QString relative = sourceDir.relativeFilePath(sourceFile.toString()); - return Utils::FilePath::fromString(QDir::cleanPath(targetDir.absoluteFilePath(relative))); + const Utils::FilePath sourceDir = m_projectItem ? m_projectItem->sourceDirectory() + : canonicalProjectDir(); + const Utils::FilePath relative = sourceFile.relativePathFrom(sourceDir); + return targetDirectory().resolvePath(relative); } - void QmlBuildSystem::setSupportedLanguages(QStringList languages) { m_projectItem->setSupportedLanguages(languages); @@ -396,18 +395,6 @@ void QmlBuildSystem::setPrimaryLanguage(QString language) m_projectItem->setPrimaryLanguage(language); } -QStringList QmlBuildSystem::makeAbsolute(const Utils::FilePath &path, - const QStringList &relativePaths) -{ - if (path.isEmpty()) - return relativePaths; - - const QDir baseDir(path.toString()); - return Utils::transform(relativePaths, [&baseDir](const QString &path) { - return QDir::cleanPath(baseDir.absoluteFilePath(path)); - }); -} - void QmlBuildSystem::refreshFiles(const QSet & /*added*/, const QSet &removed) { if (m_blockFilesUpdate) { diff --git a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h index d275481f537..0a0e9cfcbaf 100644 --- a/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h +++ b/src/plugins/qmlprojectmanager/buildsystem/qmlbuildsystem.h @@ -89,9 +89,6 @@ public: bool addFiles(const QStringList &filePaths); void refreshProjectFile(); - static Utils::FilePath activeMainFilePath(); - static QStringList makeAbsolute(const Utils::FilePath &path, const QStringList &relativePaths); - void refreshFiles(const QSet &added, const QSet &removed); bool blockFilesUpdate() const; diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 756a0707d4a..77db8b073f5 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -91,11 +91,9 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) // arguments from .qmlproject file const QmlBuildSystem *bs = qobject_cast(target->buildSystem()); - const QStringList importPaths = QmlBuildSystem::makeAbsolute(bs->targetDirectory(), - bs->customImportPaths()); - for (const QString &importPath : importPaths) { + for (const QString &importPath : bs->customImportPaths()) { cmd.addArg("-I"); - cmd.addArg(importPath); + cmd.addArg(bs->targetDirectory().pathAppended(importPath).path()); } for (const QString &fileSelector : bs->customFileSelectors()) { @@ -114,8 +112,9 @@ QmlProjectRunConfiguration::QmlProjectRunConfiguration(Target *target, Id id) } const FilePath main = bs->targetFile(mainScript()); + if (!main.isEmpty()) - cmd.addArg(main.nativePath()); + cmd.addArg(main.path()); return cmd; }); diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index cc44dddb3c4..59af192eaa2 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -978,7 +978,6 @@ LinuxDevice::LinuxDevice() addDeviceAction({Tr::tr("Open Remote Shell"), [](const IDevice::Ptr &device, QWidget *) { device->openTerminal(Environment(), FilePath()); }}); - setQmlRunCommand(filePath("qml")); } void LinuxDevice::_setOsType(Utils::OsType osType) From adc901ba2cc2ac925b1fdc039e4c349734993355 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 6 Jun 2023 23:29:16 +0200 Subject: [PATCH 56/57] QmlProjectManager: fix warning when project has no mainScript happens at start when project is still parsing Change-Id: I02b1d52b41c4f092fb4f45cf3dcf192e29eda324 Reviewed-by: Marco Bubke Reviewed-by: --- src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp index 5d328fd934a..349c23a6cf4 100644 --- a/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp +++ b/src/plugins/qmlprojectmanager/qmlmainfileaspect.cpp @@ -210,7 +210,7 @@ void QmlMainFileAspect::changeCurrentFile(Core::IEditor *editor) bool QmlMainFileAspect::isQmlFilePresent() { bool qmlFileFound = false; - if (mainScriptSource() == FileInEditor) { + if (mainScriptSource() == FileInEditor && !mainScript().isEmpty()) { IDocument *document = EditorManager::currentDocument(); const MimeType mainScriptMimeType = mimeTypeForFile(mainScript()); if (document) { From ad5e8392fe867e6e0af15cd8f209753f60e8cac0 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Tue, 6 Jun 2023 23:30:03 +0200 Subject: [PATCH 57/57] ProjectExplorer: fix warning when there is no buildConfiguration For example in QML projects. Change-Id: I8ff53ce367f2b6f8d0baaf3c7ae1bd03acab84e0 Reviewed-by: Marco Bubke Reviewed-by: --- src/plugins/projectexplorer/buildstep.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index 32be37048e9..f4d5fba031c 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -150,8 +150,10 @@ QWidget *BuildStep::doCreateConfigWidget() for (BaseAspect *aspect : std::as_const(*this)) connect(aspect, &BaseAspect::changed, widget, recreateSummary); - connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged, - widget, recreateSummary); + if (buildConfiguration()) { + connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged, + widget, recreateSummary); + } recreateSummary();