diff --git a/dist/changelog/changes-11.0.0.md b/dist/changelog/changes-11.0.0.md new file mode 100644 index 00000000000..5600390c091 --- /dev/null +++ b/dist/changelog/changes-11.0.0.md @@ -0,0 +1,223 @@ +Qt Creator 11 +============= + +Qt Creator version 11 contains bug fixes and new features. + +The most important changes are listed in this document. For a complete list of +changes, see the Git log for the Qt Creator sources that you can check out from +the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/10.0..v11.0.0 + +General +------- + +* Added a `Terminal` view (QTCREATORBUG-8511) + * Opt-out via `Preferences` > `Terminal` preferences + * Added support for + * different shells, colors, fonts, and multiple tabs + * opening file paths in Qt Creator with `Ctrl+click` (`Cmd+click` on + macOS) +* Added a more spacious "relaxed" toolbar style `Environment > Interface` +* Added a pin button to progress details instead of automatically resetting + their position (QTCREATORBUG-28829) +* Improved the selection and navigation in the `Issues` view + (QTCREATORBUG-26128, QTCREATORBUG-27006, QTCREATORBUG-27506) +* Locator + * Improved performance + * Added the creation of directories to the `Files in File System` filter + * Added device roots and browsing remote file systems to the + `Files in File System` filter + +Editing +------- + +* Improved the performance of the multi-cursor support +* Fixed the saving of hardlinked files (QTCREATORBUG-19651) +* Fixed an issue of copy and paste with multiple cursors (QTCREATORBUG-29117) + +### C++ + +* Improved the style of forward declarations in the outline (QTCREATORBUG-312) +* Added highlighting for typed string literals and user-defined literals + (QTCREATORBUG-28869) +* Added the option to create class members from assignments (QTCREATORBUG-1918) +* Fixed that locator showed both the declaration and the definition of symbols + (QTCREATORBUG-13894) +* Fixed the handling of C++20 keywords and concepts +* Built-in + * Fixed support for `if`-statements with initializer (QTCREATORBUG-29182) + +### Language Server Protocol + +* Added experimental support for GitHub Copilot + ([GitHub documentation](https://github.com/features/copilot)) +* Added missing actions for opening the `Call Hierarchy` (QTCREATORBUG-28839, + QTCREATORBUG-28842) + +### QML + +* Fixed the reformatting in the presence of JavaScript directives and function + return type annotations (QTCREATORBUG-29001, QTCREATORBUG-29046) +* Fixed that reformatting changed `of` to `in` (QTCREATORBUG-29123) +* Fixed the completion for Qt Quick Controls (QTCREATORBUG-28648) + +### Python + +* Added the option to create a virtual environment (`venv`) to the Python + interpreter selector and the wizard (PYSIDE-2152) + +### Markdown + +* Added a Markdown editor with preview (QTCREATORBUG-27883) +* Added a wizard for Markdown files (QTCREATORBUG-29056) + +Projects +-------- + +* Made it possible to add devices without going through the wizard +* Added support for moving files to a different directory when renaming + (QTCREATORBUG-15981) + +### CMake + +* Implemented adding files to the project (QTCREATORBUG-25922, + QTCREATORBUG-26006, QTCREATORBUG-27213, QTCREATORBUG-27538, + QTCREATORBUG-28493, QTCREATORBUG-28904, QTCREATORBUG-28985, + QTCREATORBUG-29006) +* Fixed issues with detecting a configured Qt version when importing a build + (QTCREATORBUG-29075) + +### Python + +* Added an option for the interpreter to the wizards + +### vcpkg + +* Added experimental support for `vcpkg` + ([vcpgk documentation](https://vcpkg.io/en/)) +* Added an option for the `vcpkg` installation location +* Added a search dialog for packages +* Added a wizard and an editor for `vcpkg.json` files + +Debugging +--------- + +* Improved the UI for enabling and disabling debuggers (QTCREATORBUG-28627) + +### C++ + +* Added an option for the default number of array elements to show + (`Preferences > Debugger > Locals & Expressions > Default array size`) +* CDB + * Added automatic source file mapping for Qt packages + * Fixed the variables view on remote Windows devices (QTCREATORBUG-29000) +* LLDB + * Fixed that long lines in the application output were broken into multiple + lines (QTCREATORBUG-29098) + +### Qt Quick + +* Improved the auto-detection if QML debugging is required (QTCREATORBUG-28627) +* Added an option for disabling static analyzer messages to + `Qt Quick > QML/JS Editing` (QTCREATORBUG-29095) + +Analyzer +-------- + +### Clang + +* Fixed that a `.clang-tidy` file in the project directory was not used by + default (QTCREATORBUG-28852) + +### Axivion + +* Added experimental support + +Version Control Systems +----------------------- + +### Git + +* Instant Blame + * Improved the performance (QTCREATORBUG-29151) + * Fixed that it did not show at the end of the document + +Platforms +--------- + +### Android + +* Fixed an issue with building library targets (QTCREATORBUG-26980) + +### Remote Linux + +* Removed the automatic sourcing of target-side shell profiles + +### Docker + +* Added support for `qmake` based projects (QTCREATORBUG-29140) +* Fixed issues after deleting the Docker image for a registered Docker device + (QTCREATORBUG-28880) + +### QNX + +* Added `slog2info` as a requirement for devices +* Fixed the support for remote working directories (QTCREATORBUG-28900) + +Credits for these changes go to: +-------------------------------- +Aleksei German +Alessandro Portale +Alexander Drozdov +Alexander Pershin +Ali Kianian +Alibek Omarov +Amr Essam +Andre Hartmann +André Pönitz +Artem Mukhin +Artem Sokolovskii +Assam Boudjelthia +Björn Schäpers +Brook Cronin +Burak Hancerli +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Eike Ziller +Esa Törmänen +Fabian Kosmale +Filippo Gentile +Friedemann Kleint +Henning Gruendl +Jaroslaw Kobus +Jussi Witick +Kai Köhne +Knud Dollereder +Knut Petter Svendsen +Leena Miettinen +Mahmoud Badri +Marco Bubke +Marcus Tillmanns +Martin Delille +Mats Honkamaa +Miikka Heikkinen +Mitch Curtis +Niels Weber +Orgad Shaneh +Pranta Dastider +Robert Löhning +Samuel Ghinet +Semih Yavuz +Tasuku Suzuki +Thiago Macieira +Thomas Hartmann +Tim Jenssen +Tim Jenßen +Ulf Hermann +Vikas Pachdha +Yasser Grimes +Yixue Wang 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 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. */ diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 4b2e4bc008f..f4886dc760d 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -18199,16 +18199,16 @@ Möchten Sie sie jetzt auschecken? Bytes - KB - KB + KiB + KiB - GB - GB + GiB + GiB - TB - TB + TiB + TiB Enable crash reporting @@ -27194,12 +27194,8 @@ zu deaktivieren, deaktiviert auch die folgenden Plugins: Privat - Create a private check-in that is never synced. -Children of private check-ins are automatically private. -Private check-ins are not pushed to the remote repository by default. - Erstelle einen privaten Check-In, der niemals synchronisiert wird. -Kinder von privaten Check-Ins sind automatisch privat. -Private Check-Ins werden standardmäßig nicht zum entfernten Repository gepusht. + Create a private check-in that is never synced. Children of private check-ins are automatically private. Private check-ins are not pushed to the remote repository by default. + Erstelle einen privaten Check-In, der niemals synchronisiert wird. Kinder von privaten Check-Ins sind automatisch privat. Private Check-Ins werden standardmäßig nicht zum entfernten Repository gepusht. Tag names to apply; comma-separated. @@ -39112,7 +39108,7 @@ Sie werden erhalten. &Configure Project - Projekt &Konfigurieren + Projekt &konfigurieren Enable Kit for Project "%1" @@ -53204,12 +53200,9 @@ Wird ein Problem gefunden, dann wird die Anwendung angehalten und kann untersuch Hint: The second line of a commit message should be empty. Hinweis: Die zweite Zeile der Beschreibung sollte leer sein. - - <p>Writing good commit messages</p><ul><li>Avoid very short commit messages.</li><li>Consider the first line as subject (like in email) and keep it shorter than %n characters.</li><li>After an empty second line, a longer description can be added.</li><li>Describe why the change was done, not how it was done.</li></ul> - - <p>Gute Beschreibungen für Commits schreiben</p><ul><li>Vermeiden Sie sehr kurze Beschreibungen.</li><li>Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als ein Zeichen.</li><li>Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.</li><li>Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.</li></ul> - <p>Gute Beschreibungen für Commits schreiben</p><ul><li>Vermeiden Sie sehr kurze Beschreibungen.</li><li>Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als %n Zeichen.</li><li>Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.</li><li>Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.</li></ul> - + + <p>Writing good commit messages</p><ul><li>Avoid very short commit messages.</li><li>Consider the first line as a subject (like in emails) and keep it shorter than 72 characters.</li><li>After an empty second line, a longer description can be added.</li><li>Describe why the change was done, not how it was done.</li></ul> + <p>Gute Beschreibungen für Commits schreiben</p><ul><li>Vermeiden Sie sehr kurze Beschreibungen.</li><li>Betrachten Sie die erste Zeile als Betreff (wie in einer E-Mail) und halten Sie sie kürzer als 72 Zeichen.</li><li>Eine längere Beschreibung kann nach einer leeren zweiten Zeile folgen.</li><li>Beschreiben Sie, weshalb die Änderung vorgenommen wurde, nicht wie sie vorgenommen wurde.</li></ul> Update in progress 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 diff --git a/src/libs/solutions/tasking/CMakeLists.txt b/src/libs/solutions/tasking/CMakeLists.txt index 5beed2fe5b4..f70c910e042 100644 --- a/src/libs/solutions/tasking/CMakeLists.txt +++ b/src/libs/solutions/tasking/CMakeLists.txt @@ -1,9 +1,11 @@ add_qtc_library(Tasking OBJECT # Never add dependencies to non-Qt libraries for this library - DEPENDS 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/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/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 8697b9c009b..fa0a5ebacc9 100644 --- a/src/libs/solutions/tasking/tasking.qbs +++ b/src/libs/solutions/tasking/tasking.qbs @@ -1,11 +1,14 @@ QtcLibrary { name: "Tasking" - Depends { name: "Qt"; submodules: ["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/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 071190dd9eb..2a4c7cefd98 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -9,6 +9,8 @@ #include #include +using namespace std::chrono; + namespace Tasking { // That's cut down qtcassert.{c,h} to avoid the dependency. @@ -41,11 +43,119 @@ 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. +*/ + +/*! + \enum Tasking::WorkflowPolicy + + This enum describes the possible behavior of the Group element when any group's child task + finishes its execution. It's also used when the running Group is stopped. + + \value StopOnError + Default. Corresponds to the stopOnError global element. + If any child task finishes with an error, the group stops and finishes with an error. + If all child tasks finished with success, the group finishes with success. + If a group is empty, it finishes with success. + \value ContinueOnError + Corresponds to the continueOnError global element. + Similar to stopOnError, but in case any child finishes with an error, + the execution continues until all tasks finish, and the group reports an error + afterwards, even when some other tasks in the group finished with success. + If all child tasks finish successfully, the group finishes with success. + If a group is empty, it finishes with success. + \value StopOnDone + Corresponds to the stopOnDone global element. + If any child task finishes with success, the group stops and finishes with success. + If all child tasks finished with an error, the group finishes with an error. + If a group is empty, it finishes with an error. + \value ContinueOnDone + Corresponds to the continueOnDone global element. + Similar to stopOnDone, but in case any child finishes successfully, + the execution continues until all tasks finish, and the group reports success + afterwards, even when some other tasks in the group finished with an error. + If all child tasks finish with an error, the group finishes with an error. + If a group is empty, it finishes with an error. + \value StopOnFinished + Corresponds to the stopOnFinished global element. + The group starts as many tasks as it can. When any task finishes, + the group stops and reports the task's result. + Useful only in parallel mode. + In sequential mode, only the first task is started, and when finished, + the group finishes too, so the other tasks are always skipped. + If a group is empty, it finishes with an error. + \value FinishAllAndDone + Corresponds to the finishAllAndDone global element. + The group executes all tasks and ignores their return results. When all + tasks finished, the group finishes with success. + If a group is empty, it finishes with success. + \value FinishAllAndError + Corresponds to the finishAllAndError global element. + The group executes all tasks and ignores their return results. When all + tasks finished, the group finishes with an error. + If a group is empty, it finishes with an error. + + Whenever a child task's result causes the Group to stop, + i.e. in case of StopOnError, StopOnDone, or StopOnFinished policies, + the Group stops the other running child tasks (if any - for example in parallel mode), + and skips executing tasks it has not started yet (for example, in the sequential mode - + those, that are placed after the failed task). Both stopping and skipping child tasks + may happen when parallelLimit is used. + + The table below summarizes the differences between various workflow policies: + + \table + \header + \li \l WorkflowPolicy + \li Executes all child tasks + \li Result + \li Result when the group is empty + \row + \li StopOnError + \li Stops when any child task finished with an error and reports an error + \li An error when at least one child task failed, success otherwise + \li Success + \row + \li ContinueOnError + \li Yes + \li An error when at least one child task failed, success otherwise + \li Success + \row + \li StopOnDone + \li Stops when any child task finished with success and reports success + \li Success when at least one child task succeeded, an error otherwise + \li An error + \row + \li ContinueOnDone + \li Yes + \li Success when at least one child task succeeded, an error otherwise + \li An error + \row + \li StopOnFinished + \li Stops when any child task finished and reports child task's result + \li Success or an error, depending on the finished child task's result + \li An error + \row + \li FinishAllAndDone + \li Yes + \li Success + \li Success + \row + \li FinishAllAndError + \li Yes + \li An error + \li An error + \endtable + + If a child of a group is also a group, the child group runs its tasks according to its own + workflow policy. When a parent group stops the running child group because + of parent group's workflow policy, i.e. when the StopOnError, StopOnDone, or StopOnFinished + policy was used for the parent, the child group's result is reported according to the + \b Result column and to the \b {child group's workflow policy} row in the table above. */ /*! @@ -74,6 +184,43 @@ private: \sa sequential, parallelLimit */ +/*! + \variable stopOnError + A convenient global group's element describing the StopOnError workflow policy. + + This is the default workflow policy of the Group element. +*/ + +/*! + \variable continueOnError + A convenient global group's element describing the ContinueOnError workflow policy. +*/ + +/*! + \variable stopOnDone + A convenient global group's element describing the StopOnDone workflow policy. +*/ + +/*! + \variable continueOnDone + A convenient global group's element describing the ContinueOnDone workflow policy. +*/ + +/*! + \variable stopOnFinished + A convenient global group's element describing the StopOnFinished workflow policy. +*/ + +/*! + \variable finishAllAndDone + A convenient global group's element describing the FinishAllAndDone workflow policy. +*/ + +/*! + \variable finishAllAndError + A convenient global group's element describing the FinishAllAndError workflow policy. +*/ + /*! \enum Tasking::TaskAction @@ -99,7 +246,7 @@ private: */ /*! - \typealias TaskItem::GroupSetupHandler + \typealias GroupItem::GroupSetupHandler Type alias for \c std::function. @@ -130,7 +277,7 @@ private: */ /*! - \typealias TaskItem::GroupEndHandler + \typealias GroupItem::GroupEndHandler Type alias for \c std::function\. @@ -143,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. @@ -157,14 +305,14 @@ 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 */ /*! Constructs a group's element holding the group done handler. The \a handler is invoked whenever the group finishes with success. Depending on the group's workflow policy, this handler may also be called - when the running group is stopped (e.g. when optional element was used). + when the running group is stopped (e.g. when finishAllAndDone element was used). When the \a handler is invoked, all of the group's child tasks are already finished. @@ -172,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); } @@ -191,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); } @@ -239,24 +387,34 @@ TaskItem onGroupError(const TaskItem::GroupEndHandler &handler) \sa sequential, parallel */ -TaskItem parallelLimit(int limit) +GroupItem parallelLimit(int limit) { return Group::parallelLimit(qMax(limit, 0)); } -TaskItem workflowPolicy(WorkflowPolicy policy) +/*! + Constructs a group's workflow policy element for a given \a policy. + + For convenience, global elements may be used instead. + + \sa stopOnError, continueOnError, stopOnDone, continueOnDone, stopOnFinished, finishAllAndDone, + finishAllAndError, WorkflowPolicy +*/ +GroupItem workflowPolicy(WorkflowPolicy policy) { return Group::workflowPolicy(policy); } -const TaskItem sequential = parallelLimit(1); -const TaskItem 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 optional = workflowPolicy(WorkflowPolicy::Optional); +const GroupItem sequential = parallelLimit(1); +const GroupItem parallel = parallelLimit(0); + +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) { @@ -326,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); @@ -377,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..."); @@ -388,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..."); @@ -399,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..."); @@ -410,6 +568,23 @@ void TaskItem::setTaskErrorHandler(const TaskEndHandler &handler) m_taskHandler.m_errorHandler = handler; } +GroupItem GroupItem::withTimeout(const GroupItem &item, milliseconds timeout, + const GroupEndHandler &handler) +{ + const TimeoutTask::EndHandler taskHandler = handler + ? [handler](const milliseconds &) { handler(); } : TimeoutTask::EndHandler(); + return Group { + parallel, + stopOnFinished, + Group { + finishAllAndError, + TimeoutTask([timeout](milliseconds &timeoutData) { timeoutData = timeout; }, + taskHandler) + }, + item + }; +} + class TaskTreePrivate; class TaskNode; @@ -466,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(); @@ -479,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; @@ -488,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; @@ -505,8 +680,8 @@ public: const ConstData &m_constData; const QList m_storageIdList; - int m_doneCount = 0; bool m_successBit = true; + int m_doneCount = 0; Guard m_startGuard; }; @@ -519,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) @@ -537,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; }; @@ -657,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) @@ -701,13 +876,28 @@ void TaskContainer::RuntimeData::callStorageDoneHandlers() } } +static bool initialSuccessBit(WorkflowPolicy workflowPolicy) +{ + switch (workflowPolicy) { + case WorkflowPolicy::StopOnError: + case WorkflowPolicy::ContinueOnError: + case WorkflowPolicy::FinishAllAndDone: + return true; + case WorkflowPolicy::StopOnDone: + case WorkflowPolicy::ContinueOnDone: + case WorkflowPolicy::StopOnFinished: + case WorkflowPolicy::FinishAllAndError: + return false; + } + QTC_CHECK(false); + return false; +} + TaskContainer::RuntimeData::RuntimeData(const ConstData &constData) : m_constData(constData) , m_storageIdList(createStorages(constData)) -{ - m_successBit = m_constData.m_workflowPolicy != WorkflowPolicy::StopOnDone - && m_constData.m_workflowPolicy != WorkflowPolicy::ContinueOnDone; -} + , m_successBit(initialSuccessBit(m_constData.m_workflowPolicy)) +{} TaskContainer::RuntimeData::~RuntimeData() { @@ -720,10 +910,11 @@ TaskContainer::RuntimeData::~RuntimeData() bool TaskContainer::RuntimeData::updateSuccessBit(bool success) { - if (m_constData.m_workflowPolicy == WorkflowPolicy::Optional) - return m_successBit; - if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) { - m_successBit = success; + if (m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndDone + || m_constData.m_workflowPolicy == WorkflowPolicy::FinishAllAndError + || m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) { + if (m_constData.m_workflowPolicy == WorkflowPolicy::StopOnFinished) + m_successBit = success; return m_successBit; } @@ -753,7 +944,7 @@ TaskAction TaskContainer::start() } if (startAction == TaskAction::Continue) { if (m_constData.m_children.isEmpty()) - startAction = TaskAction::StopWithDone; + startAction = toTaskAction(m_runtimeData->m_successBit); } return continueStart(startAction, 0); } @@ -845,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) @@ -893,6 +1084,7 @@ void TaskNode::stop() if (!m_task) { m_container.stop(); + m_container.m_runtimeData->updateSuccessBit(false); m_container.invokeEndHandler(); return; } @@ -1331,77 +1523,11 @@ void TaskNode::invokeEndHandler(bool success) \section2 Workflow Policy The workflow policy element in a Group specifies how the group should behave - when any of its \e direct child's tasks finish: + when any of its \e direct child's tasks finish. For a detailed description of possible + policies, refer to WorkflowPolicy. - \table - \header - \li Workflow Policy - \li Description - \row - \li stopOnError - \li Default. If a task finishes with an error, the group: - \list 1 - \li Stops the running tasks (if any - for example, in parallel - mode). - \li Skips executing tasks it has not started yet (for example, in the - sequential mode - those, that are placed after the failed task). - \li Immediately finishes with an error. - \endlist - If all child tasks finish successfully, the group finishes with success. - \row - \li continueOnError - \li Similar to stopOnError, but in case any child finishes with - an error, the execution continues until all tasks finish, - and the group reports an error afterwards, even when some other - tasks in group finished with success. - If a task finishes with an error, the group: - \list 1 - \li Continues executing the tasks that are running or have not - started yet. - \li Finishes with an error when all tasks finish. - \endlist - If all tasks finish successfully, the group finishes with success. - \row - \li stopOnDone - \li If a task finishes with success, the group: - \list 1 - \li Stops the running tasks (if any - for example, in parallel - mode). - \li Skips executing tasks it has not started yet (for example, in the - sequential mode - those, that are placed after the successfully finished task). - \li Immediately finishes with success. - \endlist - If all tasks finish with an error, the group finishes with an error. - \row - \li continueOnDone - \li Similar to stopOnDone, but in case any child finishes - successfully, the execution continues until all tasks finish, - and the group reports success afterwards, even when some other - tasks in group finished with an error. - If a task finishes with success, the group: - \list 1 - \li Continues executing the tasks that are running or have not - started yet. - \li Finishes with success when all tasks finish. - \endlist - If all tasks finish with an error, the group finishes with an error. - \row - \li stopOnFinished - \li The group starts as many tasks as it can. When a task finishes, - the group stops and reports the task's result. - Useful only in parallel mode. - In sequential mode, only the first task is started, and when finished, - the group finishes too, so the other tasks are ignored. - \row - \li optional - \li The group executes all tasks and ignores their return state. When all - tasks finish, the group finishes with success. - \endtable - - When a Group is empty, it finishes immediately with success, - regardless of its workflow policy. - If a child of a group is also a group, the child group - runs its tasks according to its own workflow policy. + If a child of a group is also a group, the child group runs its tasks + according to its own workflow policy. \section2 Storage @@ -1411,10 +1537,10 @@ void TaskNode::invokeEndHandler(bool success) it from a source and writing it to a destination might look as follows: \code - static QByteArray load(const FilePath &fileName) { ... } - static void save(const FilePath &fileName, const QByteArray &array) { ... } + static QByteArray load(const QString &fileName) { ... } + static void save(const QString &fileName, const QByteArray &array) { ... } - static TaskItem diffRecipe(const FilePath &source, const FilePath &destination) + static GroupItem copyRecipe(const QString &source, const QString &destination) { struct CopyStorage { // [1] custom inter-task struct QByteArray content; // [2] custom inter-task data @@ -1448,6 +1574,13 @@ void TaskNode::invokeEndHandler(bool success) }; return root; } + + const QString source = ...; + const QString destination = ...; + TaskTree taskTree(copyRecipe(source, destination)); + connect(&taskTree, &TaskTree::done, + &taskTree, [] { qDebug() << "The copying finished successfully."; }); + tasktree.start(); \endcode In the example above, the inter-task data consists of a QByteArray content @@ -1659,9 +1792,16 @@ bool TaskTree::isRunning() const return d->m_root && d->m_root->isRunning(); } -bool TaskTree::runBlocking(const QFuture &future, int timeoutMs) +bool TaskTree::runBlocking() { - if (isRunning() || future.isCanceled()) + QPromise dummy; + dummy.start(); + return runBlocking(dummy.future()); +} + +bool TaskTree::runBlocking(const QFuture &future) +{ + if (future.isCanceled()) return false; bool ok = false; @@ -1680,17 +1820,7 @@ bool TaskTree::runBlocking(const QFuture &future, int timeoutMs) connect(this, &TaskTree::done, &loop, [finalize] { finalize(true); }); connect(this, &TaskTree::errorOccurred, &loop, [finalize] { finalize(false); }); - start(); - if (!isRunning()) - return ok; - - QTimer timer; - if (timeoutMs) { - timer.setSingleShot(true); - timer.setInterval(timeoutMs); - connect(&timer, &QTimer::timeout, this, &TaskTree::stop); - timer.start(); - } + QTimer::singleShot(0, this, &TaskTree::start); loop.exec(QEventLoop::ExcludeUserInputEvents); if (!ok) { @@ -1700,11 +1830,19 @@ bool TaskTree::runBlocking(const QFuture &future, int timeoutMs) return ok; } -bool TaskTree::runBlocking(int timeoutMs) +bool TaskTree::runBlocking(const Group &recipe, milliseconds timeout) { QPromise dummy; dummy.start(); - return runBlocking(dummy.future(), timeoutMs); + return TaskTree::runBlocking(recipe, dummy.future(), timeout); +} + +bool TaskTree::runBlocking(const Group &recipe, const QFuture &future, milliseconds timeout) +{ + const Group root = timeout == milliseconds::max() ? recipe + : Group { recipe.withTimeout(timeout) }; + TaskTree taskTree(root); + return taskTree.runBlocking(future); } int TaskTree::taskCount() const @@ -1749,4 +1887,95 @@ void TaskTreeTaskAdapter::start() task()->start(); } +using TimeoutCallback = std::function; + +struct TimerData +{ + system_clock::time_point m_deadline; + QPointer m_context; + TimeoutCallback m_callback; +}; + +QMutex s_mutex; +std::atomic_int s_timerId = 0; +QHash s_timerIdToTimerData = {}; +QMultiMap s_deadlineToTimerId = {}; + +static QList prepareForActivation(int timerId) +{ + QMutexLocker lock(&s_mutex); + const auto it = s_timerIdToTimerData.constFind(timerId); + if (it == s_timerIdToTimerData.cend()) + return {}; // the timer was already activated + + const system_clock::time_point deadline = it->m_deadline; + QList toActivate; + auto itMap = s_deadlineToTimerId.cbegin(); + while (itMap != s_deadlineToTimerId.cend()) { + if (itMap.key() > deadline) + break; + + const auto it = s_timerIdToTimerData.constFind(itMap.value()); + if (it != s_timerIdToTimerData.cend()) { + toActivate.append(it.value()); + s_timerIdToTimerData.erase(it); + } + itMap = s_deadlineToTimerId.erase(itMap); + } + return toActivate; +} + +static void removeTimerId(int timerId) +{ + QMutexLocker lock(&s_mutex); + const auto it = s_timerIdToTimerData.constFind(timerId); + QTC_ASSERT(it != s_timerIdToTimerData.cend(), + qWarning("Removing active timerId failed."); return); + + const system_clock::time_point deadline = it->m_deadline; + s_timerIdToTimerData.erase(it); + + const int removedCount = s_deadlineToTimerId.remove(deadline, timerId); + QTC_ASSERT(removedCount == 1, qWarning("Removing active timerId failed."); return); +} + +static void handleTimeout(int timerId) +{ + const QList toActivate = prepareForActivation(timerId); + for (const TimerData &timerData : toActivate) { + if (timerData.m_context) + QMetaObject::invokeMethod(timerData.m_context.get(), timerData.m_callback); + } +} + +static int scheduleTimeout(milliseconds timeout, QObject *context, const TimeoutCallback &callback) +{ + const int timerId = s_timerId.fetch_add(1) + 1; + const system_clock::time_point deadline = system_clock::now() + timeout; + QTimer::singleShot(timeout, context, [timerId] { handleTimeout(timerId); }); + QMutexLocker lock(&s_mutex); + s_timerIdToTimerData.emplace(timerId, TimerData{deadline, context, callback}); + s_deadlineToTimerId.insert(deadline, timerId); + return timerId; +} + +TimeoutTaskAdapter::TimeoutTaskAdapter() +{ + *task() = std::chrono::milliseconds::zero(); +} + +TimeoutTaskAdapter::~TimeoutTaskAdapter() +{ + if (m_timerId) + removeTimerId(*m_timerId); +} + +void TimeoutTaskAdapter::start() +{ + if (*task() == milliseconds::zero()) + QTimer::singleShot(0, this, [this] { emit done(true); }); + else + m_timerId = scheduleTimeout(*task(), this, [this] { m_timerId = {}; emit done(true); }); +} + } // namespace Tasking diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 1754e092631..dcd7b86ac18 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -16,6 +16,8 @@ QT_END_NAMESPACE namespace Tasking { +Q_NAMESPACE_EXPORT(TASKING_EXPORT) + class ExecutionContextActivator; class TaskContainer; class TaskTreePrivate; @@ -99,16 +101,19 @@ private: // b) On first done - continue executing all children and report done afterwards. // 3. Stops on first finished child. In sequential mode it will never run other children then the first one. // Useful only in parallel mode. -// 4. Always run all children, ignore their result and report done afterwards. +// 4. Always run all children, let them finish, ignore their results and report done afterwards. +// 5. Always run all children, let them finish, ignore their results and report error afterwards. enum class WorkflowPolicy { - StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done). - ContinueOnError, // 1b - The same, but children execution continues. Reports done when no children. - StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error). - ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children. - StopOnFinished, // 3 - Stops on first finished child and report its result. - Optional // 4 - Reports done after all children finished. + StopOnError, // 1a - Reports error on first child error, otherwise done (if all children were done). + ContinueOnError, // 1b - The same, but children execution continues. Reports done when no children. + StopOnDone, // 2a - Reports done on first child done, otherwise error (if all children were error). + ContinueOnDone, // 2b - The same, but children execution continues. Reports error when no children. + StopOnFinished, // 3 - Stops on first finished child and report its result. + FinishAllAndDone, // 4 - Reports done after all children finished. + FinishAllAndError // 5 - Reports error after all children finished. }; +Q_ENUM_NS(WorkflowPolicy); enum class TaskAction { @@ -116,8 +121,9 @@ enum class TaskAction StopWithDone, StopWithError }; +Q_ENUM_NS(TaskAction); -class TASKING_EXPORT TaskItem +class TASKING_EXPORT GroupItem { public: // Internal, provided by QTC_DECLARE_CUSTOM_TASK @@ -150,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; } @@ -163,52 +169,59 @@ 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 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. + + GroupItem withTimeout(std::chrono::milliseconds timeout, + const GroupEndHandler &handler = {}) const { + return GroupItem::withTimeout(*this, timeout, handler); + } private: template @@ -231,29 +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 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 optional; +TASKING_EXPORT extern const GroupItem sequential; +TASKING_EXPORT extern const GroupItem parallel; -class TASKING_EXPORT Storage : public TaskItem +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 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() @@ -266,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."); @@ -295,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) { @@ -321,9 +336,14 @@ public: return *this; } + 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_v &future, int timeoutMs = 0); - bool runBlocking(int timeoutMs = 0); + bool runBlocking(); + bool runBlocking(const QFuture &future); + static bool runBlocking(const Group &recipe, + std::chrono::milliseconds timeout = std::chrono::milliseconds::max()); + static bool runBlocking(const Group &recipe, const QFuture &future, + std::chrono::milliseconds timeout = std::chrono::milliseconds::max()); int taskCount() const; int progressMaximum() const { return taskCount(); } @@ -418,6 +442,17 @@ public: void start() final; }; +class TASKING_EXPORT TimeoutTaskAdapter : public TaskAdapter +{ +public: + TimeoutTaskAdapter(); + ~TimeoutTaskAdapter(); + void start() final; + +private: + std::optional m_timerId; +}; + } // namespace Tasking #define TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)\ @@ -430,3 +465,4 @@ using CustomTaskName = CustomTask>;\ } // namespace Tasking TASKING_DECLARE_TASK(TaskTreeTask, TaskTreeTaskAdapter); +TASKING_DECLARE_TASK(TimeoutTask, TimeoutTaskAdapter); diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index d3610b2c7da..999385e1ca9 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -670,6 +670,7 @@ public: // Used to block recursive editingFinished signals for example when return is pressed, and // the validation changes focus by opening a dialog bool m_blockAutoApply = false; + bool m_allowPathFromDevice = true; template void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w) { @@ -977,6 +978,13 @@ void StringAspect::setCommandVersionArguments(const QStringList &arguments) d->m_pathChooserDisplay->setCommandVersionArguments(arguments); } +void StringAspect::setAllowPathFromDevice(bool allowPathFromDevice) +{ + d->m_allowPathFromDevice = allowPathFromDevice; + if (d->m_pathChooserDisplay) + d->m_pathChooserDisplay->setAllowPathFromDevice(allowPathFromDevice); +} + /*! Sets \a elideMode as label elide mode. */ @@ -1122,6 +1130,7 @@ void StringAspect::addToLayout(LayoutItem &parent) d->m_pathChooserDisplay->setPromptDialogFilter(d->m_prompDialogFilter); d->m_pathChooserDisplay->setPromptDialogTitle(d->m_prompDialogTitle); d->m_pathChooserDisplay->setCommandVersionArguments(d->m_commandVersionArguments); + d->m_pathChooserDisplay->setAllowPathFromDevice(d->m_allowPathFromDevice); if (defaultValue() == value()) d->m_pathChooserDisplay->setDefaultValue(defaultValue()); else @@ -2153,8 +2162,11 @@ void DoubleAspect::setSingleStep(double step) Its visual representation is a QComboBox with three items. */ -TriStateAspect::TriStateAspect(const QString &onString, const QString &offString, +TriStateAspect::TriStateAspect(AspectContainer *container, + const QString &onString, + const QString &offString, const QString &defaultString) + : SelectionAspect(container) { setDisplayStyle(DisplayStyle::ComboBox); setDefaultValue(TriState::Default); @@ -2308,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 @@ -2319,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); })); } @@ -2334,6 +2346,10 @@ void IntegersAspect::setDefaultValue(const QList &value) A text display does not have a real value. */ +TextDisplay::TextDisplay(AspectContainer *container) + : BaseAspect(container) +{} + /*! Constructs a text display showing the \a message with an icon representing type \a type. diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index 35e8a62b8f7..b99c28c0bef 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -406,6 +406,7 @@ public: void setOpenTerminalHandler(const std::function &openTerminal); void setAutoApplyOnEditingFinished(bool applyOnEditingFinished); void setElideMode(Qt::TextElideMode elideMode); + void setAllowPathFromDevice(bool allowPathFromDevice); void validateInput(); @@ -548,7 +549,8 @@ class QTCREATOR_UTILS_EXPORT TriStateAspect : public SelectionAspect Q_OBJECT public: - TriStateAspect(const QString &onString = {}, + TriStateAspect(AspectContainer *container = nullptr, + const QString &onString = {}, const QString &offString = {}, const QString &defaultString = {}); @@ -608,6 +610,7 @@ class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect Q_OBJECT public: + explicit TextDisplay(AspectContainer *container); TextDisplay(const QString &message = {}, InfoLabel::InfoType type = InfoLabel::None); ~TextDisplay() override; diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp index f5bb1d20482..bd139d9b168 100644 --- a/src/libs/utils/devicefileaccess.cpp +++ b/src/libs/utils/devicefileaccess.cpp @@ -493,6 +493,20 @@ bool DesktopDeviceFileAccess::hasHardLinks(const FilePath &filePath) const if (s.st_nlink > 1) return true; } +#elif defined(Q_OS_WIN) + const HANDLE handle = CreateFile((wchar_t *) filePath.toUserOutput().utf16(), + 0, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + if (handle == INVALID_HANDLE_VALUE) + return false; + + FILE_STANDARD_INFO info; + if (GetFileInformationByHandleEx(handle, FileStandardInfo, &info, sizeof(info))) + return info.NumberOfLinks > 1; #else Q_UNUSED(filePath) #endif diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp index ae983b6f482..f96c5da8f2d 100644 --- a/src/libs/utils/deviceshell.cpp +++ b/src/libs/utils/deviceshell.cpp @@ -104,7 +104,7 @@ RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData) QWaitCondition waiter; const int id = ++m_currentId; - const auto it = m_commandOutput.insert(id, CommandRun{{-1, {}, {}}, &waiter}); + m_commandOutput.insert(id, CommandRun{{-1, {}, {}}, &waiter}); QMetaObject::invokeMethod(m_shellProcess.get(), [this, id, cmd, stdInData] { const QString command = QString("%1 \"%2\" %3\n").arg(id) @@ -115,6 +115,7 @@ RunResult DeviceShell::run(const CommandLine &cmd, const QByteArray &stdInData) waiter.wait(&m_commandMutex); + const auto it = m_commandOutput.constFind(id); const RunResult result = *it; m_commandOutput.erase(it); diff --git a/src/libs/utils/deviceshell.h b/src/libs/utils/deviceshell.h index 052aac1838a..e5bc4ad7afe 100644 --- a/src/libs/utils/deviceshell.h +++ b/src/libs/utils/deviceshell.h @@ -7,7 +7,7 @@ #include "fileutils.h" -#include +#include #include #include #include @@ -78,8 +78,7 @@ private: int m_currentId{0}; QMutex m_commandMutex; - // QMap is used here to preserve iterators - QMap m_commandOutput; + QHash m_commandOutput; QByteArray m_commandBuffer; State m_shellScriptState = State::Unknown; diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp index ae931ec1ca0..08b480c88cf 100644 --- a/src/libs/utils/fancylineedit.cpp +++ b/src/libs/utils/fancylineedit.cpp @@ -11,6 +11,7 @@ #include "utilsicons.h" #include "utilstr.h" +#include #include #include #include @@ -126,7 +127,7 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent) : m_completionShortcut(completionShortcut()->key(), parent), m_okTextColor(creatorTheme()->color(Theme::TextColorNormal)), m_errorTextColor(creatorTheme()->color(Theme::TextColorError)), - m_placeholderTextColor(creatorTheme()->color(Theme::PalettePlaceholderText)) + m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText)) { m_completionShortcut.setContext(Qt::WidgetShortcut); diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index ad871413a22..9059a37ecc3 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1914,7 +1914,7 @@ FilePath FilePath::canonicalPath() const return *this; } -#ifdef Q_OS_WINDOWS +#ifdef Q_OS_WIN DWORD flagsAndAttrs = FILE_ATTRIBUTE_NORMAL; if (isDir()) flagsAndAttrs |= FILE_FLAG_BACKUP_SEMANTICS; diff --git a/src/libs/utils/filestreamer.cpp b/src/libs/utils/filestreamer.cpp index d24b61dbde3..eb9e057c1a1 100644 --- a/src/libs/utils/filestreamer.cpp +++ b/src/libs/utils/filestreamer.cpp @@ -30,7 +30,7 @@ public: void start() { QTC_ASSERT(!m_taskTree, return); - const TaskItem task = m_filePath.needsDevice() ? remoteTask() : localTask(); + const GroupItem task = m_filePath.needsDevice() ? remoteTask() : localTask(); m_taskTree.reset(new TaskTree({task})); const auto finalize = [this](bool success) { m_taskTree.release()->deleteLater(); @@ -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); @@ -375,8 +375,7 @@ static void transfer(QPromise &promise, const FilePath &source, const File if (promise.isCanceled()) return; - TaskTree taskTree(transferTask(source, destination)); - if (!taskTree.runBlocking(promise.future())) + if (!TaskTree::runBlocking(transferTask(source, destination), promise.future())) promise.future().cancel(); } @@ -391,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) @@ -400,7 +399,7 @@ public: } private: - TaskItem readerTask() { + GroupItem readerTask() { const auto setup = [this](FileStreamReader &reader) { m_readBuffer.clear(); reader.setFilePath(m_source); @@ -410,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/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index a6cbebf3681..2cecc2810b9 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -629,12 +629,18 @@ FilePathInfo::FileFlags fileInfoFlagsfromStatMode(const QString &hexString, int FilePathInfo::FileFlags result; - if (mode & IRUSR) + if (mode & IRUSR) { result |= FilePathInfo::ReadOwnerPerm; - if (mode & IWUSR) + result |= FilePathInfo::ReadUserPerm; + } + if (mode & IWUSR) { result |= FilePathInfo::WriteOwnerPerm; - if (mode & IXUSR) + result |= FilePathInfo::WriteUserPerm; + } + if (mode & IXUSR) { result |= FilePathInfo::ExeOwnerPerm; + result |= FilePathInfo::ExeUserPerm; + } if (mode & IRGRP) result |= FilePathInfo::ReadGroupPerm; if (mode & IWGRP) 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 d716ad034c5..4a756139897 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -11,12 +11,15 @@ #if defined(UTILS_LIBRARY) # define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT +#elif defined(UTILS_STATIC_LIBRARY) +# define QTCREATOR_UTILS_EXPORT #else # define QTCREATOR_UTILS_EXPORT Q_DECL_IMPORT #endif QT_BEGIN_NAMESPACE class QLayout; +class QMargins; class QObject; class QWidget; template T qobject_cast(QObject *object); @@ -200,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" diff --git a/src/libs/utils/tooltip/tips.cpp b/src/libs/utils/tooltip/tips.cpp index ea20c735d80..180b8f960f2 100644 --- a/src/libs/utils/tooltip/tips.cpp +++ b/src/libs/utils/tooltip/tips.cpp @@ -133,9 +133,13 @@ TextTip::TextTip(QWidget *parent) : TipLabel(parent) setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, nullptr, this) / 255.0); } -static bool likelyContainsLink(const QString &s) +static bool likelyContainsLink(const QString &s, const Qt::TextFormat &format) { - return s.contains(QLatin1String("href"), Qt::CaseInsensitive); + if (s.contains(QLatin1String("href"), Qt::CaseInsensitive)) + return true; + if (format == Qt::MarkdownText) + return s.contains("]("); + return false; } void TextTip::setContent(const QVariant &content) @@ -148,13 +152,13 @@ void TextTip::setContent(const QVariant &content) m_format = item.second; } - bool containsLink = likelyContainsLink(m_text); + bool containsLink = likelyContainsLink(m_text, m_format); setOpenExternalLinks(containsLink); } bool TextTip::isInteractive() const { - return likelyContainsLink(m_text); + return likelyContainsLink(m_text, m_format); } void TextTip::configure(const QPoint &pos) diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index f33ae5aa6b3..a9e9596e772 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -122,7 +122,7 @@ private: QMap m_filesToPull; QStringList m_androidABIs; - BoolAspect *m_uninstallPreviousPackage = nullptr; + BoolAspect m_uninstallPreviousPackage{this}; bool m_uninstallPreviousPackageRun = false; bool m_useAndroiddeployqt = false; bool m_askForUninstall = false; @@ -143,17 +143,16 @@ AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Id id) setImmutable(true); setUserExpanded(true); - m_uninstallPreviousPackage = addAspect(); - m_uninstallPreviousPackage->setSettingsKey(UninstallPreviousPackageKey); - m_uninstallPreviousPackage->setLabel(Tr::tr("Uninstall the existing app before deployment"), + m_uninstallPreviousPackage.setSettingsKey(UninstallPreviousPackageKey); + m_uninstallPreviousPackage.setLabel(Tr::tr("Uninstall the existing app before deployment"), BoolAspect::LabelPlacement::AtCheckBox); - m_uninstallPreviousPackage->setValue(false); + m_uninstallPreviousPackage.setValue(false); const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit()); const bool forced = qt && qt->qtVersion() < QVersionNumber(5, 4, 0); if (forced) { - m_uninstallPreviousPackage->setValue(true); - m_uninstallPreviousPackage->setEnabled(false); + m_uninstallPreviousPackage.setValue(true); + m_uninstallPreviousPackage.setEnabled(false); } connect(this, &AndroidDeployQtStep::askForUninstall, @@ -274,7 +273,7 @@ bool AndroidDeployQtStep::init() emit addOutput(Tr::tr("Deploying to %1").arg(m_serialNumber), OutputFormat::NormalMessage); - m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value(); + m_uninstallPreviousPackageRun = m_uninstallPreviousPackage(); if (m_uninstallPreviousPackageRun) m_manifestName = AndroidManager::manifestPath(target()); diff --git a/src/plugins/autotest/ctest/ctestsettings.cpp b/src/plugins/autotest/ctest/ctestsettings.cpp index e707c3edb09..a2f3e904270 100644 --- a/src/plugins/autotest/ctest/ctestsettings.cpp +++ b/src/plugins/autotest/ctest/ctestsettings.cpp @@ -20,8 +20,8 @@ CTestSettings::CTestSettings(Id settingsId) setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY); setDisplayName(Tr::tr("CTest")); - setLayouter([this](QWidget *w) { - Row { Form { + setLayouter([this] { + return Row { Form { outputOnFail, br, scheduleRandom, br, stopOnFailure, br, @@ -39,7 +39,7 @@ CTestSettings::CTestSettings(Id settingsId) Row { testLoad, threshold} } } - }, st }.attachTo(w); + }, st }; }); outputOnFail.setSettingsKey("OutputOnFail"); diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index 8553702432f..e78be579651 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() @@ -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 206dca8da41..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{optional}; + QList tasks{finishAllAndDone}; for (ITestConfiguration *config : m_selectedTests) { QTC_ASSERT(config, continue); @@ -442,7 +442,7 @@ void TestRunner::runTestsHelper() } }; const Group group { - optional, + finishAllAndDone, Storage(storage), onGroupSetup(onSetup), ProcessTask(onProcessSetup, onProcessDone, onProcessDone) diff --git a/src/plugins/autotoolsprojectmanager/autogenstep.cpp b/src/plugins/autotoolsprojectmanager/autogenstep.cpp index c0a383205d0..090389716de 100644 --- a/src/plugins/autotoolsprojectmanager/autogenstep.cpp +++ b/src/plugins/autotoolsprojectmanager/autogenstep.cpp @@ -42,23 +42,23 @@ private: void doRun() final; bool m_runAutogen = false; + StringAspect m_arguments{this}; }; AutogenStep::AutogenStep(BuildStepList *bsl, Id id) : AbstractProcessStep(bsl, id) { - auto arguments = addAspect(); - arguments->setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments"); - arguments->setLabelText(Tr::tr("Arguments:")); - arguments->setDisplayStyle(StringAspect::LineEditDisplay); - arguments->setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs"); + m_arguments.setSettingsKey("AutotoolsProjectManager.AutogenStep.AdditionalArguments"); + m_arguments.setLabelText(Tr::tr("Arguments:")); + m_arguments.setDisplayStyle(StringAspect::LineEditDisplay); + m_arguments.setHistoryCompleter("AutotoolsPM.History.AutogenStepArgs"); - connect(arguments, &BaseAspect::changed, this, [this] { m_runAutogen = true; }); + connect(&m_arguments, &BaseAspect::changed, this, [this] { m_runAutogen = true; }); setWorkingDirectoryProvider([this] { return project()->projectDirectory(); }); - setCommandLineProvider([this, arguments] { + setCommandLineProvider([this] { return CommandLine(project()->projectDirectory() / "autogen.sh", - arguments->value(), + m_arguments(), CommandLine::Raw); }); diff --git a/src/plugins/bazaar/bazaarsettings.cpp b/src/plugins/bazaar/bazaarsettings.cpp index 344bdc6a095..dc6ef77dd48 100644 --- a/src/plugins/bazaar/bazaarsettings.cpp +++ b/src/plugins/bazaar/bazaarsettings.cpp @@ -66,10 +66,10 @@ BazaarSettings::BazaarSettings() timeout.setLabelText(Tr::tr("Timeout:")); timeout.setSuffix(Tr::tr("s")); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Group { title(Tr::tr("Configuration")), Row { binaryPath } @@ -88,7 +88,7 @@ BazaarSettings::BazaarSettings() Row { logCount, timeout, st } }, st - }.attachTo(widget); + }; }); } diff --git a/src/plugins/beautifier/abstractsettings.cpp b/src/plugins/beautifier/abstractsettings.cpp index 9b340cf9b37..1b4ff3aa10e 100644 --- a/src/plugins/beautifier/abstractsettings.cpp +++ b/src/plugins/beautifier/abstractsettings.cpp @@ -297,7 +297,7 @@ void AbstractSettings::read() void AbstractSettings::readDocumentation() { - const FilePath filename = documentationFilePath(); + const FilePath filename = documentationFilePath; if (filename.isEmpty()) { BeautifierPlugin::showError(Tr::tr("No documentation file specified.")); return; diff --git a/src/plugins/beautifier/abstractsettings.h b/src/plugins/beautifier/abstractsettings.h index 6b34baf58be..8e9ceea931d 100644 --- a/src/plugins/beautifier/abstractsettings.h +++ b/src/plugins/beautifier/abstractsettings.h @@ -51,7 +51,8 @@ public: Utils::FilePathAspect command{this}; Utils::StringAspect supportedMimeTypes{this}; - Utils::FilePathAspect documentationFilePath; // Intentionally not saved. + + Utils::FilePath documentationFilePath; QVersionNumber version() const; diff --git a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp index 36bd39c3c55..415d4ab20d6 100644 --- a/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp +++ b/src/plugins/beautifier/artisticstyle/artisticstylesettings.cpp @@ -59,11 +59,11 @@ ArtisticStyleSettings::ArtisticStyleSettings() customStyle.setSettingsKey("customStyle"); - documentationFilePath.setFilePath( + documentationFilePath = Core::ICore::userResourcePath(Beautifier::Constants::SETTINGS_DIRNAME) .pathAppended(Beautifier::Constants::DOCUMENTATION_DIRNAME) .pathAppended(SETTINGS_NAME) - .stringAppended(".xml")); + .stringAppended(".xml"); read(); } @@ -77,7 +77,7 @@ void ArtisticStyleSettings::createDocumentationFile() const if (process.result() != ProcessResult::FinishedWithSuccess) return; - QFile file(documentationFilePath().toFSPathString()); + QFile file(documentationFilePath.toFSPathString()); const QFileInfo fi(file); if (!fi.exists()) fi.dir().mkpath(fi.absolutePath()); diff --git a/src/plugins/beautifier/clangformat/clangformatsettings.cpp b/src/plugins/beautifier/clangformat/clangformatsettings.cpp index 40477b6ca82..6a31667edb7 100644 --- a/src/plugins/beautifier/clangformat/clangformatsettings.cpp +++ b/src/plugins/beautifier/clangformat/clangformatsettings.cpp @@ -63,16 +63,16 @@ ClangFormatSettings::ClangFormatSettings() customStyle.setSettingsKey("customStyle"); - documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) + documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) .pathAppended(Constants::DOCUMENTATION_DIRNAME) - .pathAppended(SETTINGS_NAME).stringAppended(".xml")); + .pathAppended(SETTINGS_NAME).stringAppended(".xml"); read(); } void ClangFormatSettings::createDocumentationFile() const { - QFile file(documentationFilePath().toFSPathString()); + QFile file(documentationFilePath.toFSPathString()); const QFileInfo fi(file); if (!fi.exists()) fi.dir().mkpath(fi.absolutePath()); diff --git a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp index 823471681fc..1aa9e8502c6 100644 --- a/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp +++ b/src/plugins/beautifier/uncrustify/uncrustifysettings.cpp @@ -66,9 +66,9 @@ UncrustifySettings::UncrustifySettings() specificConfigFile.setExpectedKind(Utils::PathChooser::File); specificConfigFile.setPromptDialogFilter(Tr::tr("Uncrustify file (*.cfg)")); - documentationFilePath.setFilePath(Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) + documentationFilePath = Core::ICore::userResourcePath(Constants::SETTINGS_DIRNAME) .pathAppended(Constants::DOCUMENTATION_DIRNAME) - .pathAppended(SETTINGS_NAME).stringAppended(".xml")); + .pathAppended(SETTINGS_NAME).stringAppended(".xml"); read(); } @@ -82,7 +82,7 @@ void UncrustifySettings::createDocumentationFile() const if (process.result() != ProcessResult::FinishedWithSuccess) return; - QFile file(documentationFilePath().toFSPathString()); + QFile file(documentationFilePath.toFSPathString()); const QFileInfo fi(file); if (!fi.exists()) fi.dir().mkpath(fi.absolutePath()); diff --git a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp index 04bcc08fa2d..e1b82ab3770 100644 --- a/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp +++ b/src/plugins/clangcodemodel/clangdsemantichighlighting.cpp @@ -421,8 +421,12 @@ void doSemanticHighlighting( if (ClangdClient * const client = ClangModelManagerSupport::clientForFile(filePath)) client->setVirtualRanges(filePath, virtualRanges, docRevision); }, Qt::QueuedConnection); +#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) + promise.addResults(results); +#else for (const HighlightingResult &r : results) promise.addResult(r); +#endif } } 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 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 40cbf75d47e..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; @@ -186,7 +186,7 @@ TaskItem clangToolTask(const AnalyzeInputData &input, Storage(storage), onGroupSetup(onSetup), Group { - optional, + finishAllAndDone, ProcessTask(onProcessSetup, onProcessDone, onProcessError) } }; 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 178022a91a6..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; @@ -209,7 +209,7 @@ void DocumentClangToolRunner::run() return !m_document->isModified() || isVFSOverlaySupported(executable); }; const auto outputHandler = [this](const AnalyzeOutputData &output) { onDone(output); }; - tasks.append(Group{optional, clangToolTask(input, setupHandler, outputHandler)}); + tasks.append(Group{finishAllAndDone, clangToolTask(input, setupHandler, outputHandler)}); }; addClangTool(ClangToolType::Tidy); addClangTool(ClangToolType::Clazy); diff --git a/src/plugins/classview/classviewmanager.cpp b/src/plugins/classview/classviewmanager.cpp index 7f4eeb955c6..90c834a6216 100644 --- a/src/plugins/classview/classviewmanager.cpp +++ b/src/plugins/classview/classviewmanager.cpp @@ -378,7 +378,7 @@ void Manager::gotoLocations(const QList &list) int line; int column; textEditor->convertPosition(textEditor->position(), &line, &column); - const SymbolLocation current(filePath, line, column); + const SymbolLocation current(filePath, line, column + 1); if (auto it = locations.constFind(current), end = locations.constEnd(); it != end) { // we already are at the symbol, cycle to next location ++it; diff --git a/src/plugins/clearcase/clearcaseplugin.cpp b/src/plugins/clearcase/clearcaseplugin.cpp index aa5c598c84c..943ffa194a4 100644 --- a/src/plugins/clearcase/clearcaseplugin.cpp +++ b/src/plugins/clearcase/clearcaseplugin.cpp @@ -227,6 +227,10 @@ private: Q_INVOKABLE void updateStatusActions(); QString commitDisplayName() const final; + QString commitAbortTitle() const final; + QString commitAbortMessage() const final; + QString commitErrorMessage(const QString &error) const final; + void checkOutCurrentFile(); void addCurrentFile(); void undoCheckOutCurrent(); @@ -948,9 +952,27 @@ void ClearCasePluginPrivate::updateActions(VcsBasePluginPrivate::ActionState as) QString ClearCasePluginPrivate::commitDisplayName() const { + //: Name of the "commit" action of the VCS return Tr::tr("Check In"); } +QString ClearCasePluginPrivate::commitAbortTitle() const +{ + return Tr::tr("Close Check In Editor"); +} + +QString ClearCasePluginPrivate::commitAbortMessage() const +{ + return Tr::tr("Closing this editor will abort the check in."); +} + +QString ClearCasePluginPrivate::commitErrorMessage(const QString &error) const +{ + if (error.isEmpty()) + return Tr::tr("Cannot check in."); + return Tr::tr("Cannot check in: %1.").arg(error); +} + void ClearCasePluginPrivate::checkOutCurrentFile() { const VcsBasePluginState state = currentState(); diff --git a/src/plugins/cmakeprojectmanager/cmakeeditor.cpp b/src/plugins/cmakeprojectmanager/cmakeeditor.cpp index 3411fd85b58..5193661e57b 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 - 1; + int beginPos = column; while (beginPos >= 0) { if (isValidFileNameChar(block, beginPos)) { buffer.prepend(block.at(beginPos)); diff --git a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp index 295b664b9a0..dca6d47e203 100644 --- a/src/plugins/cmakeprojectmanager/cmakeformatter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeformatter.cpp @@ -65,9 +65,9 @@ public: autoFormatMime.setDefaultValue("text/x-cmake"); autoFormatMime.setLabelText(Tr::tr("Restrict to MIME types:")); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Row { Tr::tr("CMakeFormat command:"), command }, Space(10), Group { @@ -79,7 +79,7 @@ public: } }, st - }.attachTo(widget); + }; }); ActionContainer *menu = ActionManager::createMenu(Constants::CMAKEFORMATTER_MENU_ID); diff --git a/src/plugins/conan/conaninstallstep.cpp b/src/plugins/conan/conaninstallstep.cpp index cae573845e8..2a634190365 100644 --- a/src/plugins/conan/conaninstallstep.cpp +++ b/src/plugins/conan/conaninstallstep.cpp @@ -112,7 +112,7 @@ ConanInstallStep::ConanInstallStep(BuildStepList *bsl, Id id) return param.summary(displayName()); }); - connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [this](Project * project) { + connect(ProjectManager::instance(), &ProjectManager::projectAdded, this, [](Project * project) { connect(project, &Project::addedTarget, project, [project] (Target *target) { connectTarget(project, target); }); diff --git a/src/plugins/copilot/authwidget.cpp b/src/plugins/copilot/authwidget.cpp index 31b4660b680..f29e3d3a150 100644 --- a/src/plugins/copilot/authwidget.cpp +++ b/src/plugins/copilot/authwidget.cpp @@ -93,7 +93,7 @@ void AuthWidget::updateClient(const Utils::FilePath &nodeJs, const Utils::FilePa m_client = nullptr; setState(Tr::tr("Sign in"), false); m_button->setEnabled(false); - if (!nodeJs.exists() || !agent.exists()) { + if (!nodeJs.isExecutableFile() || !agent.exists()) { return; } diff --git a/src/plugins/copilot/copilot.qbs b/src/plugins/copilot/copilot.qbs index cf59d954caa..714c45543d4 100644 --- a/src/plugins/copilot/copilot.qbs +++ b/src/plugins/copilot/copilot.qbs @@ -5,6 +5,7 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "LanguageClient" } + Depends { name: "ProjectExplorer" } Depends { name: "TextEditor" } Depends { name: "Qt"; submodules: ["widgets", "xml", "network"] } diff --git a/src/plugins/copilot/copilothoverhandler.cpp b/src/plugins/copilot/copilothoverhandler.cpp index b252cedc77d..135cbd8390f 100644 --- a/src/plugins/copilot/copilothoverhandler.cpp +++ b/src/plugins/copilot/copilothoverhandler.cpp @@ -139,6 +139,7 @@ void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget, void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) { + Q_UNUSED(point) auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); if (!suggestion) @@ -147,8 +148,12 @@ void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const Q auto tooltipWidget = new CopilotCompletionToolTip(suggestion->completions(), suggestion->currentCompletion(), editorWidget); - const qreal deltay = 2 * editorWidget->textDocument()->fontSettings().lineSpacing(); - ToolTip::show(point - QPoint{0, int(deltay)}, tooltipWidget, editorWidget); + + const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor()); + QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft()) + - Utils::ToolTip::offsetFromPosition(); + pos.ry() -= tooltipWidget->sizeHint().height(); + ToolTip::show(pos, tooltipWidget, editorWidget); } } // namespace Copilot::Internal diff --git a/src/plugins/copilot/copilotplugin.cpp b/src/plugins/copilot/copilotplugin.cpp index 43effda321c..526a6061cbe 100644 --- a/src/plugins/copilot/copilotplugin.cpp +++ b/src/plugins/copilot/copilotplugin.cpp @@ -120,6 +120,9 @@ void CopilotPlugin::extensionsInitialized() void CopilotPlugin::restartClient() { LanguageClient::LanguageClientManager::shutdownClient(m_client); + + if (!CopilotSettings::instance().nodeJsPath().isExecutableFile()) + return; m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(), CopilotSettings::instance().distPath()); } diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp index bac2c176afa..b8567f2feb4 100644 --- a/src/plugins/copilot/copilotsettings.cpp +++ b/src/plugins/copilot/copilotsettings.cpp @@ -53,7 +53,7 @@ CopilotSettings::CopilotSettings() nodeJsPath.setHistoryCompleter("Copilot.NodePath.History"); nodeJsPath.setDisplayName(Tr::tr("Node.js Path")); nodeJsPath.setToolTip( - Tr::tr("Select path to node.js executable. See https://nodejs.org/de/download/" + Tr::tr("Select path to node.js executable. See https://nodejs.org/en/download/" "for installation instructions.")); distPath.setExpectedKind(PathChooser::File); @@ -63,7 +63,7 @@ CopilotSettings::CopilotSettings() distPath.setHistoryCompleter("Copilot.DistPath.History"); distPath.setDisplayName(Tr::tr("Agent.js path")); distPath.setToolTip(Tr::tr( - "Select path to agent.js in copilot neovim plugin. See " + "Select path to agent.js in Copilot Neovim plugin. See " "https://github.com/github/copilot.vim#getting-started for installation instructions.")); autoComplete.setDisplayName(Tr::tr("Auto Complete")); @@ -71,7 +71,7 @@ CopilotSettings::CopilotSettings() autoComplete.setLabelText(Tr::tr("Request completions automatically")); autoComplete.setDefaultValue(true); autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor " - "position after changes to the document")); + "position after changes to the document.")); initEnableAspect(enableCopilot); } diff --git a/src/plugins/coreplugin/actionmanager/command.cpp b/src/plugins/coreplugin/actionmanager/command.cpp index ab78bf50892..23b398791f2 100644 --- a/src/plugins/coreplugin/actionmanager/command.cpp +++ b/src/plugins/coreplugin/actionmanager/command.cpp @@ -347,8 +347,10 @@ void Internal::CommandPrivate::setCurrentContext(const Context &context) m_context = context; QAction *currentAction = nullptr; - for (int i = 0; i < m_context.size(); ++i) { - if (QAction *a = m_contextActionMap.value(m_context.at(i), nullptr)) { + for (const Id &id : std::as_const(m_context)) { + if (id == Constants::C_GLOBAL_CUTOFF) + break; + if (QAction *a = m_contextActionMap.value(id, nullptr)) { currentAction = a; break; } 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); diff --git a/src/plugins/coreplugin/coreconstants.h b/src/plugins/coreplugin/coreconstants.h index 0b4831e51b6..513d02eb6ee 100644 --- a/src/plugins/coreplugin/coreconstants.h +++ b/src/plugins/coreplugin/coreconstants.h @@ -46,6 +46,11 @@ const char C_EDITORMANAGER[] = "Core.EditorManager"; const char C_NAVIGATION_PANE[] = "Core.NavigationPane"; const char C_PROBLEM_PANE[] = "Core.ProblemPane"; const char C_GENERAL_OUTPUT_PANE[] = "Core.GeneralOutputPane"; +// Special context that leads to all "more specific" contexts to be ignored. +// If you use Context(mycontextId, C_GLOBAL_CUTOFF) for a widget that has focus, +// mycontextId will be enabled but the contexts for all parent widgets, the manually added +// "additional" contexts, and the global context will be turned off. +const char C_GLOBAL_CUTOFF[] = "Global Cutoff"; // Default editor kind const char K_DEFAULT_TEXT_EDITOR_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("QtC::Core", "Plain Text Editor"); diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.cpp b/src/plugins/coreplugin/dialogs/ioptionspage.cpp index 1912045af53..4def6095a11 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.cpp +++ b/src/plugins/coreplugin/dialogs/ioptionspage.cpp @@ -199,15 +199,6 @@ void IOptionsPage::setSettings(AspectContainer *settings) m_settings = settings; } -void IOptionsPage::setLayouter(const std::function &layouter) -{ - m_widgetCreator = [layouter] { - auto widget = new IOptionsPageWidget; - layouter(widget); - return widget; - }; -} - void IOptionsPage::setLayouter(const std::function &layouter) { m_widgetCreator = [layouter] { diff --git a/src/plugins/coreplugin/dialogs/ioptionspage.h b/src/plugins/coreplugin/dialogs/ioptionspage.h index a38bf863c36..f4801b7c734 100644 --- a/src/plugins/coreplugin/dialogs/ioptionspage.h +++ b/src/plugins/coreplugin/dialogs/ioptionspage.h @@ -74,7 +74,6 @@ protected: void setCategoryIcon(const Utils::Icon &categoryIcon) { m_categoryIcon = categoryIcon; } void setCategoryIconPath(const Utils::FilePath &categoryIconPath); void setSettings(Utils::AspectContainer *settings); - void setLayouter(const std::function &layouter); void setLayouter(const std::function &layouter); // Used in FontSettingsPage. FIXME? 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/locator/ilocatorfilter.cpp b/src/plugins/coreplugin/locator/ilocatorfilter.cpp index f6b39996d8f..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) { @@ -470,7 +470,7 @@ void LocatorMatcher::start() for (const LocatorMatcherTask &task : std::as_const(d->m_tasks)) { const auto storage = task.storage; const Group group { - optional, + finishAllAndDone, Storage(storage), onGroupSetup(onSetup(storage, index)), onGroupDone(onDone(storage)), @@ -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 55b82920329..cefce35a5b1 100644 --- a/src/plugins/coreplugin/locator/locator.cpp +++ b/src/plugins/coreplugin/locator/locator.cpp @@ -381,14 +381,14 @@ 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()) continue; const Group group { - optional, + finishAllAndDone, *task, onGroupDone([this, filter] { m_refreshingFilters.removeOne(filter); }) }; 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(), diff --git a/src/plugins/coreplugin/plugindialog.cpp b/src/plugins/coreplugin/plugindialog.cpp index f6ea25e719c..24d8c8dff12 100644 --- a/src/plugins/coreplugin/plugindialog.cpp +++ b/src/plugins/coreplugin/plugindialog.cpp @@ -17,14 +17,11 @@ #include #include +#include -#include #include #include -#include -#include #include -#include using namespace Utils; @@ -35,30 +32,27 @@ PluginDialog::PluginDialog(QWidget *parent) : QDialog(parent), m_view(new ExtensionSystem::PluginView(this)) { - auto vl = new QVBoxLayout(this); - - auto filterLayout = new QHBoxLayout; - vl->addLayout(filterLayout); auto filterEdit = new Utils::FancyLineEdit(this); filterEdit->setFocus(); filterEdit->setFiltering(true); connect(filterEdit, &Utils::FancyLineEdit::filterChanged, m_view, &ExtensionSystem::PluginView::setFilter); - filterLayout->addWidget(filterEdit); - - vl->addWidget(m_view); - - m_detailsButton = new QPushButton(Tr::tr("Details"), this); - m_errorDetailsButton = new QPushButton(Tr::tr("Error Details"), this); - m_installButton = new QPushButton(Tr::tr("Install Plugin..."), this); - m_detailsButton->setEnabled(false); - m_errorDetailsButton->setEnabled(false); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - buttonBox->addButton(m_detailsButton, QDialogButtonBox::ActionRole); - buttonBox->addButton(m_errorDetailsButton, QDialogButtonBox::ActionRole); - buttonBox->addButton(m_installButton, QDialogButtonBox::ActionRole); - vl->addWidget(buttonBox); + m_detailsButton = buttonBox->addButton(Tr::tr("Details"), QDialogButtonBox::ActionRole); + m_detailsButton->setEnabled(false); + m_errorDetailsButton = buttonBox->addButton(Tr::tr("Error Details"), + QDialogButtonBox::ActionRole); + m_errorDetailsButton->setEnabled(false); + m_installButton = buttonBox->addButton(Tr::tr("Install Plugin..."), + QDialogButtonBox::ActionRole); + + using namespace Layouting; + Column { + filterEdit, + m_view, + buttonBox, + }.attachTo(this); resize(650, 400); setWindowTitle(Tr::tr("Installed Plugins")); @@ -116,13 +110,16 @@ void PluginDialog::openDetails(ExtensionSystem::PluginSpec *spec) return; QDialog dialog(this); dialog.setWindowTitle(Tr::tr("Plugin Details of %1").arg(spec->name())); - auto layout = new QVBoxLayout; - dialog.setLayout(layout); auto details = new ExtensionSystem::PluginDetailsView(&dialog); - layout->addWidget(details); details->update(spec); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); - layout->addWidget(buttons); + + using namespace Layouting; + Column { + details, + buttons, + }.attachTo(&dialog); + connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); dialog.resize(400, 500); @@ -136,13 +133,16 @@ void PluginDialog::openErrorDetails() return; QDialog dialog(this); dialog.setWindowTitle(Tr::tr("Plugin Errors of %1").arg(spec->name())); - auto layout = new QVBoxLayout; - dialog.setLayout(layout); auto errors = new ExtensionSystem::PluginErrorView(&dialog); - layout->addWidget(errors); errors->update(spec); QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); - layout->addWidget(buttons); + + using namespace Layouting; + Column { + errors, + buttons, + }.attachTo(&dialog); + connect(buttons, &QDialogButtonBox::accepted, &dialog, &QDialog::accept); connect(buttons, &QDialogButtonBox::rejected, &dialog, &QDialog::reject); dialog.resize(500, 300); diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp index c5694016d49..ac2dfd6c240 100644 --- a/src/plugins/coreplugin/plugininstallwizard.cpp +++ b/src/plugins/coreplugin/plugininstallwizard.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include #include @@ -80,19 +80,15 @@ public: , m_data(data) { setTitle(Tr::tr("Source")); - auto vlayout = new QVBoxLayout; - setLayout(vlayout); auto label = new QLabel( "

" + Tr::tr("Choose source location. This can be a plugin library file or a zip file.") + "

"); label->setWordWrap(true); - vlayout->addWidget(label); auto chooser = new PathChooser; chooser->setExpectedKind(PathChooser::Any); - vlayout->addWidget(chooser); connect(chooser, &PathChooser::textChanged, this, [this, chooser] { m_data->sourcePath = chooser->filePath(); updateWarnings(); @@ -101,7 +97,8 @@ public: m_info = new InfoLabel; m_info->setType(InfoLabel::Error); m_info->setVisible(false); - vlayout->addWidget(m_info); + + Layouting::Column { label, chooser, m_info }.attachTo(this); } void updateWarnings() @@ -153,8 +150,6 @@ public: , m_data(data) { setTitle(Tr::tr("Check Archive")); - auto vlayout = new QVBoxLayout; - setLayout(vlayout); m_label = new InfoLabel; m_label->setElideMode(Qt::ElideNone); @@ -163,13 +158,11 @@ public: m_output = new QTextEdit; m_output->setReadOnly(true); - auto hlayout = new QHBoxLayout; - hlayout->addWidget(m_label, 1); - hlayout->addStretch(); - hlayout->addWidget(m_cancelButton); - - vlayout->addLayout(hlayout); - vlayout->addWidget(m_output); + using namespace Layouting; + Column { + Row { m_label, st, m_cancelButton }, + m_output, + }.attachTo(this); } void initializePage() final @@ -322,13 +315,9 @@ public: , m_data(data) { setTitle(Tr::tr("Install Location")); - auto vlayout = new QVBoxLayout; - setLayout(vlayout); auto label = new QLabel("

" + Tr::tr("Choose install location.") + "

"); label->setWordWrap(true); - vlayout->addWidget(label); - vlayout->addSpacing(10); auto localInstall = new QRadioButton(Tr::tr("User plugins")); localInstall->setChecked(!m_data->installIntoApplication); @@ -338,10 +327,6 @@ public: localLabel->setWordWrap(true); localLabel->setAttribute(Qt::WA_MacSmallSize, true); - vlayout->addWidget(localInstall); - vlayout->addWidget(localLabel); - vlayout->addSpacing(10); - auto appInstall = new QRadioButton( Tr::tr("%1 installation").arg(Constants::IDE_DISPLAY_NAME)); appInstall->setChecked(m_data->installIntoApplication); @@ -351,8 +336,11 @@ public: .arg(Constants::IDE_DISPLAY_NAME)); appLabel->setWordWrap(true); appLabel->setAttribute(Qt::WA_MacSmallSize, true); - vlayout->addWidget(appInstall); - vlayout->addWidget(appLabel); + + using namespace Layouting; + Column { + label, Space(10), localInstall, localLabel, Space(10), appInstall, appLabel, + }.attachTo(this); auto group = new QButtonGroup(this); group->addButton(localInstall); @@ -375,12 +363,9 @@ public: { setTitle(Tr::tr("Summary")); - auto vlayout = new QVBoxLayout; - setLayout(vlayout); - m_summaryLabel = new QLabel(this); m_summaryLabel->setWordWrap(true); - vlayout->addWidget(m_summaryLabel); + Layouting::Column { m_summaryLabel }.attachTo(this); } void initializePage() final diff --git a/src/plugins/coreplugin/systemsettings.cpp b/src/plugins/coreplugin/systemsettings.cpp index 5d9a5fd3f9f..f6641bad08f 100644 --- a/src/plugins/coreplugin/systemsettings.cpp +++ b/src/plugins/coreplugin/systemsettings.cpp @@ -51,8 +51,7 @@ const char showCrashButtonKey[] = "ShowCrashButton"; // TODO: move to somewhere in Utils static QString formatSize(qint64 size) { - QStringList units {Tr::tr("Bytes"), Tr::tr("KB"), Tr::tr("MB"), - Tr::tr("GB"), Tr::tr("TB")}; + QStringList units{Tr::tr("Bytes"), Tr::tr("KiB"), Tr::tr("MiB"), Tr::tr("GiB"), Tr::tr("TiB")}; double outputSize = size; int i; for (i = 0; i < units.size() - 1; ++i) { diff --git a/src/plugins/cppcheck/cppcheckoptions.cpp b/src/plugins/cppcheck/cppcheckoptions.cpp index 8392f64600c..1289ba0e0e2 100644 --- a/src/plugins/cppcheck/cppcheckoptions.cpp +++ b/src/plugins/cppcheck/cppcheckoptions.cpp @@ -108,11 +108,11 @@ CppcheckOptions::CppcheckOptions() readSettings(); } -std::function CppcheckOptions::layouter() +std::function CppcheckOptions::layouter() { - return [this](QWidget *widget) { + return [this] { using namespace Layouting; - Form { + return Form { binary, br, Tr::tr("Checks:"), Flow { warning, @@ -132,7 +132,7 @@ std::function CppcheckOptions::layouter() addIncludePaths, guessArguments } - }.attachTo(widget); + }; }; } diff --git a/src/plugins/cppcheck/cppcheckoptions.h b/src/plugins/cppcheck/cppcheckoptions.h index a6ccdd98ba0..eb3e1c7ddbf 100644 --- a/src/plugins/cppcheck/cppcheckoptions.h +++ b/src/plugins/cppcheck/cppcheckoptions.h @@ -12,7 +12,7 @@ class CppcheckOptions final : public Core::PagedSettings public: CppcheckOptions(); - std::function layouter(); + std::function layouter(); Utils::FilePathAspect binary{this}; Utils::BoolAspect warning{this}; diff --git a/src/plugins/cppcheck/cppcheckplugin.cpp b/src/plugins/cppcheck/cppcheckplugin.cpp index 890e2e3c0ad..b025762c8c6 100644 --- a/src/plugins/cppcheck/cppcheckplugin.cpp +++ b/src/plugins/cppcheck/cppcheckplugin.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -111,8 +112,7 @@ void CppcheckPluginPrivate::startManualRun() manualRunTool.updateOptions(); - auto optionsWidget = new QWidget; - options.layouter()(optionsWidget); + auto optionsWidget = options.layouter()().emerge(); ManualRunDialog dialog(optionsWidget, project); if (dialog.exec() == ManualRunDialog::Rejected) 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/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index 90ddfbdf905..31d69f7363d 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -414,6 +414,7 @@ F2TestCase::F2TestCase(CppEditorAction action, } else { currentTextEditor->convertPosition(targetTestFile->m_targetCursorPosition, &expectedLine, &expectedColumn); + ++expectedColumn; if (useClangd && (tag == "classDestructor" || tag == "fromDestructorDefinitionSymbol" || tag == "fromDestructorBody")) { --expectedColumn; // clangd goes before the ~, built-in code model after diff --git a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp index 0937df32ab6..6c282b879cc 100644 --- a/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp +++ b/src/plugins/cppeditor/resourcepreviewhoverhandler.cpp @@ -166,7 +166,7 @@ void ResourcePreviewHoverHandler::operateTooltip(TextEditorWidget *editorWidget, { const QString tt = makeTooltip(); if (!tt.isEmpty()) - Utils::ToolTip::show(point, tt, editorWidget); + Utils::ToolTip::show(point, tt, Qt::MarkdownText, editorWidget); else Utils::ToolTip::hide(); } @@ -180,10 +180,8 @@ QString ResourcePreviewHoverHandler::makeTooltip() const const Utils::MimeType mimeType = Utils::mimeTypeForFile(m_resPath); if (mimeType.name().startsWith("image", Qt::CaseInsensitive)) - ret += QString("
").arg(m_resPath); - - ret += QString("%2") - .arg(m_resPath, QDir::toNativeSeparators(m_resPath)); + ret += QString("![image](%1) \n").arg(m_resPath); + ret += QString("[%1](%2)").arg(QDir::toNativeSeparators(m_resPath), m_resPath); return ret; } diff --git a/src/plugins/cppeditor/semantichighlighter.cpp b/src/plugins/cppeditor/semantichighlighter.cpp index c04b9270aa9..156a1d40524 100644 --- a/src/plugins/cppeditor/semantichighlighter.cpp +++ b/src/plugins/cppeditor/semantichighlighter.cpp @@ -64,6 +64,7 @@ void SemanticHighlighter::run() m_revision = documentRevision(); m_seenBlocks.clear(); + m_nextResultToHandle = m_resultCount = 0; qCDebug(log) << "starting runner for document revision" << m_revision; m_watcher->setFuture(m_highlightingRunner()); } @@ -92,6 +93,21 @@ void SemanticHighlighter::onHighlighterResultAvailable(int from, int to) return; } + QTC_CHECK(from == m_resultCount); + m_resultCount = to; + if (to - m_nextResultToHandle >= 100) { + handleHighlighterResults(); + m_nextResultToHandle = to; + } +} + +void SemanticHighlighter::handleHighlighterResults() +{ + int from = m_nextResultToHandle; + const int to = m_resultCount; + if (from >= to) + return; + QElapsedTimer t; t.start(); @@ -177,6 +193,8 @@ void SemanticHighlighter::onHighlighterFinished() { QTC_ASSERT(m_watcher, return); + handleHighlighterResults(); + QElapsedTimer t; t.start(); diff --git a/src/plugins/cppeditor/semantichighlighter.h b/src/plugins/cppeditor/semantichighlighter.h index fb1a704e6c3..ea49f289a01 100644 --- a/src/plugins/cppeditor/semantichighlighter.h +++ b/src/plugins/cppeditor/semantichighlighter.h @@ -67,6 +67,7 @@ public: private: void onHighlighterResultAvailable(int from, int to); + void handleHighlighterResults(); void onHighlighterFinished(); void connectWatcher(); @@ -82,6 +83,8 @@ private: QScopedPointer> m_watcher; QHash m_formatMap; std::set m_seenBlocks; + int m_nextResultToHandle = 0; + int m_resultCount = 0; HighlightingRunner m_highlightingRunner; }; diff --git a/src/plugins/cvs/cvssettings.cpp b/src/plugins/cvs/cvssettings.cpp index e2247f7dbe0..9e1523486fa 100644 --- a/src/plugins/cvs/cvssettings.cpp +++ b/src/plugins/cvs/cvssettings.cpp @@ -59,9 +59,9 @@ CvsSettings::CvsSettings() diffIgnoreBlankLines.setSettingsKey("DiffIgnoreBlankLines"); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Group { title(Tr::tr("Configuration")), Form { @@ -80,7 +80,7 @@ CvsSettings::CvsSettings() } }, st - }.attachTo(widget); + }; }); } diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp index 4b67e82fd86..8c118f553b6 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp @@ -101,11 +101,11 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target) addDataExtractor(this, &DebuggerRunConfigurationAspect::useMultiProcess, &Data::useMultiProcess); addDataExtractor(this, &DebuggerRunConfigurationAspect::overrideStartup, &Data::overrideStartup); - m_cppAspect = new TriStateAspect(Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic")); + m_cppAspect = new TriStateAspect(nullptr, Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic")); m_cppAspect->setLabelText(Tr::tr("C++ debugger:")); m_cppAspect->setSettingsKey("RunConfiguration.UseCppDebugger"); - m_qmlAspect = new TriStateAspect(Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic")); + m_qmlAspect = new TriStateAspect(nullptr, Tr::tr("Enabled"), Tr::tr("Disabled"), Tr::tr("Automatic")); m_qmlAspect->setLabelText(Tr::tr("QML debugger:")); m_qmlAspect->setSettingsKey("RunConfiguration.UseQmlDebugger"); diff --git a/src/plugins/debugger/gdb/gdboptionspage.cpp b/src/plugins/debugger/gdb/gdboptionspage.cpp index 3d24dd6c9cf..37008cabcd1 100644 --- a/src/plugins/debugger/gdb/gdboptionspage.cpp +++ b/src/plugins/debugger/gdb/gdboptionspage.cpp @@ -32,7 +32,7 @@ public: setCategory(Constants::DEBUGGER_SETTINGS_CATEGORY); setSettings(&debuggerSettings()->page2); - setLayouter([](QWidget *w) { + setLayouter([] { using namespace Layouting; DebuggerSettings &s = *debuggerSettings(); @@ -84,7 +84,7 @@ public: Column { s.gdbPostAttachCommands }, }; - Grid { general, extended, br, startup, attach }.attachTo(w); + return Grid { general, extended, br, startup, attach }; }); } }; diff --git a/src/plugins/diffeditor/diffeditorplugin.cpp b/src/plugins/diffeditor/diffeditorplugin.cpp index fe4c4f9a6a5..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, optional}; + 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/fakevim/fakevimactions.cpp b/src/plugins/fakevim/fakevimactions.cpp index dc051d81040..916fea16097 100644 --- a/src/plugins/fakevim/fakevimactions.cpp +++ b/src/plugins/fakevim/fakevimactions.cpp @@ -10,6 +10,13 @@ // Qt Creator. The idea is to keep this file here in a "clean" state that // allows easy reuse with any QTextEdit or QPlainTextEdit derived class. +#ifndef FAKEVIM_STANDALONE +#include +#include +#include +#include +#endif + #include #include #include @@ -18,8 +25,7 @@ using namespace Utils; -namespace FakeVim { -namespace Internal { +namespace FakeVim::Internal { #ifdef FAKEVIM_STANDALONE FvBaseAspect::FvBaseAspect() @@ -62,13 +68,31 @@ QString FvBaseAspect::settingsKey() const void setAutoApply(bool ) {} #endif + +static FakeVimSettings *s_settings; + +FakeVimSettings &settings() +{ + return *s_settings; +} + FakeVimSettings::FakeVimSettings() { - setAutoApply(false); + s_settings = this; #ifndef FAKEVIM_STANDALONE + const char SETTINGS_CATEGORY[] = "D.FakeVim"; + const char SETTINGS_ID[] = "A.FakeVim.General"; + + setId(SETTINGS_ID); + setDisplayName(Tr::tr("General")); + setCategory(SETTINGS_CATEGORY); + setDisplayCategory(Tr::tr("FakeVim")); + setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png"); + setup(&useFakeVim, false, "UseFakeVim", {}, Tr::tr("Use FakeVim")); #endif + // Specific FakeVim settings setup(&readVimRc, false, "ReadVimRc", {}, Tr::tr("Read .vimrc from location:")); setup(&vimRcPath, QString(), "VimRcPath", {}, {}); // Tr::tr("Path to .vimrc") @@ -135,6 +159,121 @@ FakeVimSettings::FakeVimSettings() "%USERPROFILE%\\_vimrc on Windows, ~/.vimrc otherwise.")); vimRcPath.setPlaceHolderText(Tr::tr("Default: %1").arg(vimrcDefault)); vimRcPath.setDisplayStyle(FvStringAspect::PathChooserDisplay); + + setLayouter([this] { + using namespace Layouting; + using namespace TextEditor; + + Row bools { + Column { + autoIndent, + smartIndent, + expandTab, + smartTab, + hlSearch, + showCmd, + startOfLine, + passKeys, + blinkingCursor + }, + Column { + incSearch, + useCoreSearch, + ignoreCase, + smartCase, + wrapScan, + showMarks, + passControlKey, + relativeNumber, + tildeOp + } + }; + + Row ints { shiftWidth, tabStop, scrollOff, st }; + + vimRcPath.setEnabler(&readVimRc); + + Column strings { + backspace, + isKeyword, + Row {readVimRc, vimRcPath} + }; + + return Column { + useFakeVim, + + Group { + title(Tr::tr("Vim Behavior")), + Column { + bools, + ints, + strings + } + }, + + Group { + title(Tr::tr("Plugin Emulation")), + Column { + emulateVimCommentary, + emulateReplaceWithRegister, + emulateArgTextObj, + emulateExchange, + emulateSurround + } + }, + + Row { + PushButton { + text(Tr::tr("Copy Text Editor Settings")), + onClicked([this] { + TabSettings ts = TextEditorSettings::codeStyle()->tabSettings(); + TypingSettings tps = TextEditorSettings::typingSettings(); + expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy); + tabStop.setValue(ts.m_tabSize); + shiftWidth.setValue(ts.m_indentSize); + smartTab.setValue(tps.m_smartBackspaceBehavior + == TypingSettings::BackspaceFollowsPreviousIndents); + autoIndent.setValue(true); + smartIndent.setValue(tps.m_autoIndent); + incSearch.setValue(true); + }), + }, + PushButton { + text(Tr::tr("Set Qt Style")), + onClicked([this] { + expandTab.setVolatileValue(true); + tabStop.setVolatileValue(4); + shiftWidth.setVolatileValue(4); + smartTab.setVolatileValue(true); + autoIndent.setVolatileValue(true); + smartIndent.setVolatileValue(true); + incSearch.setVolatileValue(true); + backspace.setVolatileValue(QString("indent,eol,start")); + passKeys.setVolatileValue(true); + }), + }, + PushButton { + text(Tr::tr("Set Plain Style")), + onClicked([this] { + expandTab.setVolatileValue(false); + tabStop.setVolatileValue(8); + shiftWidth.setVolatileValue(8); + smartTab.setVolatileValue(false); + autoIndent.setVolatileValue(false); + smartIndent.setVolatileValue(false); + incSearch.setVolatileValue(false); + backspace.setVolatileValue(QString()); + passKeys.setVolatileValue(false); + }), + }, + st + }, + st + }; + }); + + readSettings(); + #endif } @@ -187,11 +326,4 @@ void FakeVimSettings::setup(FvBaseAspect *aspect, m_nameToAspect[shortName] = aspect; } -FakeVimSettings *fakeVimSettings() -{ - static FakeVimSettings s; - return &s; -} - -} // namespace Internal -} // namespace FakeVim +} // FakeVim::Internal diff --git a/src/plugins/fakevim/fakevimactions.h b/src/plugins/fakevim/fakevimactions.h index 582630e83d1..e3b14b0d47a 100644 --- a/src/plugins/fakevim/fakevimactions.h +++ b/src/plugins/fakevim/fakevimactions.h @@ -4,7 +4,7 @@ #pragma once #ifndef FAKEVIM_STANDALONE -# include +# include #endif #include @@ -13,8 +13,7 @@ #include #include -namespace FakeVim { -namespace Internal { +namespace FakeVim::Internal { #ifdef FAKEVIM_STANDALONE class FvBaseAspect @@ -68,7 +67,7 @@ public: #else -using FvAspectContainer = Utils::AspectContainer; +using FvAspectContainer = Core::PagedSettings; using FvBaseAspect = Utils::BaseAspect; using FvBoolAspect = Utils::BoolAspect; using FvIntegerAspect = Utils::IntegerAspect; @@ -145,7 +144,6 @@ private: QHash m_aspectToName; }; -FakeVimSettings *fakeVimSettings(); +FakeVimSettings &settings(); -} // namespace Internal -} // namespace FakeVim +} // FakeVim::Internal diff --git a/src/plugins/fakevim/fakevimhandler.cpp b/src/plugins/fakevim/fakevimhandler.cpp index 2eb052432cf..edfa315af98 100644 --- a/src/plugins/fakevim/fakevimhandler.cpp +++ b/src/plugins/fakevim/fakevimhandler.cpp @@ -408,9 +408,8 @@ static QRegularExpression vimPatternToQtPattern(const QString &needle) */ // FIXME: Option smartcase should be used only if search was typed by user. - const bool ignoreCaseOption = fakeVimSettings()->ignoreCase.value(); - const bool smartCaseOption = fakeVimSettings()->smartCase.value(); - const bool initialIgnoreCase = ignoreCaseOption + const bool smartCaseOption = settings().smartCase(); + const bool initialIgnoreCase = settings().ignoreCase() && !(smartCaseOption && needle.contains(QRegularExpression("[A-Z]"))); bool ignorecase = initialIgnoreCase; @@ -2373,7 +2372,7 @@ public: QString surroundFunction; // Used for storing the function name provided to ys{motion}f } g; - FakeVimSettings &s = *fakeVimSettings(); + FakeVimSettings &s = settings(); }; static void initSingleShotTimer(QTimer *timer, @@ -2527,7 +2526,7 @@ void FakeVimHandler::Private::leaveFakeVim(bool needUpdate) // The command might have destroyed the editor. if (m_textedit || m_plaintextedit) { - if (s.showMarks.value()) + if (s.showMarks()) updateSelection(); updateMiniBuffer(); @@ -2576,7 +2575,7 @@ bool FakeVimHandler::Private::wantsOverride(QKeyEvent *ev) // We are interested in overriding most Ctrl key combinations. if (isOnlyControlModifier(mods) - && !s.passControlKey.value() + && !s.passControlKey() && ((key >= Key_A && key <= Key_Z && key != Key_K) || key == Key_BracketLeft || key == Key_BracketRight)) { // Ctrl-K is special as it is the Core's default notion of Locator @@ -3059,7 +3058,7 @@ void FakeVimHandler::Private::stopIncrementalFind() void FakeVimHandler::Private::updateFind(bool isComplete) { - if (!isComplete && !s.incSearch.value()) + if (!isComplete && !s.incSearch()) return; g.currentMessage.clear(); @@ -3161,7 +3160,7 @@ void FakeVimHandler::Private::pushUndoState(bool overwrite) pos = firstPositionInLine(lineForPosition(pos)); else if (isVisualBlockMode()) pos = blockAt(pos).position() + qMin(columnAt(anchor()), columnAt(position())); - } else if (g.movetype == MoveLineWise && s.startOfLine.value()) { + } else if (g.movetype == MoveLineWise && s.startOfLine()) { QTextCursor tc = m_cursor; if (g.submode == ShiftLeftSubMode || g.submode == ShiftRightSubMode || g.submode == IndentSubMode) { @@ -3621,7 +3620,7 @@ void FakeVimHandler::Private::finishMovement(const QString &dotCommandMovement) return; } else if (g.submode == ExchangeSubMode) { exchangeRange(currentRange()); - } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister.value()) { + } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) { pushUndoState(false); beginEditBlock(); replaceWithRegister(currentRange()); @@ -3734,7 +3733,7 @@ void FakeVimHandler::Private::clearCurrentMode() void FakeVimHandler::Private::updateSelection() { QList selections = m_extraSelections; - if (s.showMarks.value()) { + if (s.showMarks()) { for (auto it = m_buffer->marks.cbegin(), end = m_buffer->marks.cend(); it != end; ++it) { QTextEdit::ExtraSelection sel; sel.cursor = m_cursor; @@ -3753,7 +3752,7 @@ void FakeVimHandler::Private::updateSelection() void FakeVimHandler::Private::updateHighlights() { - if (s.useCoreSearch.value() || !s.hlSearch.value() || g.highlightsCleared) { + if (s.useCoreSearch() || !s.hlSearch() || g.highlightsCleared) { if (m_highlighted.isEmpty()) return; m_highlighted.clear(); @@ -3800,7 +3799,7 @@ void FakeVimHandler::Private::updateMiniBuffer() } else if (!g.mapStates.isEmpty() && !g.mapStates.last().silent) { // Do not reset previous message when after running a mapped command. return; - } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd.value()) { + } else if (g.mode == CommandMode && !g.currentCommand.isEmpty() && s.showCmd()) { msg = g.currentCommand; messageLevel = MessageShowCmd; } else if (g.mode == CommandMode && isVisualMode()) { @@ -3907,7 +3906,7 @@ bool FakeVimHandler::Private::handleCommandSubSubMode(const Input &input) handled = selectBlockTextObject(g.subsubdata.is('i'), '{', '}'); else if (input.is('"') || input.is('\'') || input.is('`')) handled = selectQuotedStringTextObject(g.subsubdata.is('i'), input.asChar()); - else if (input.is('a') && s.emulateArgTextObj.value()) + else if (input.is('a') && s.emulateArgTextObj()) handled = selectArgumentTextObject(g.subsubdata.is('i')); else handled = false; @@ -4050,7 +4049,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input) g.subsubmode = NoSubSubMode; } else if (input.is('/') || input.is('?')) { g.lastSearchForward = input.is('/'); - if (s.useCoreSearch.value()) { + if (s.useCoreSearch()) { // re-use the core dialog. g.findPending = true; m_findStartPosition = position(); @@ -4225,7 +4224,7 @@ bool FakeVimHandler::Private::handleMovement(const Input &input) m_cursor = EDITOR(cursorForPosition(QPoint(0, EDITOR(height()) / 2))); handleStartOfLine(); } else if (input.is('n') || input.is('N')) { - if (s.useCoreSearch.value()) { + if (s.useCoreSearch()) { bool forward = (input.is('n')) ? g.lastSearchForward : !g.lastSearchForward; int pos = position(); q->findNextRequested(!forward); @@ -4314,7 +4313,7 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input) handled = handleNoSubMode(input); } else if (g.submode == ExchangeSubMode) { handled = handleExchangeSubMode(input); - } else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange.value()) { + } else if (g.submode == ChangeSubMode && input.is('x') && s.emulateExchange()) { // Exchange submode is "cx", so we need to switch over from ChangeSubMode here g.submode = ExchangeSubMode; handled = true; @@ -4324,15 +4323,15 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input) } else if (g.submode == AddSurroundingSubMode) { handled = handleAddSurroundingSubMode(input); } else if (g.submode == ChangeSubMode && (input.is('s') || input.is('S')) - && s.emulateSurround.value()) { + && s.emulateSurround()) { g.submode = ChangeSurroundingSubMode; g.surroundUpperCaseS = input.is('S'); handled = true; - } else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround.value()) { + } else if (g.submode == DeleteSubMode && input.is('s') && s.emulateSurround()) { g.submode = DeleteSurroundingSubMode; handled = true; } else if (g.submode == YankSubMode && (input.is('s') || input.is('S')) - && s.emulateSurround.value()) { + && s.emulateSurround()) { g.submode = AddSurroundingSubMode; g.movetype = MoveInclusive; g.surroundUpperCaseS = input.is('S'); @@ -4341,10 +4340,9 @@ EventResult FakeVimHandler::Private::handleCommandMode(const Input &input) || g.submode == DeleteSubMode || g.submode == YankSubMode) { handled = handleChangeDeleteYankSubModes(input); - } else if (g.submode == CommentSubMode && s.emulateVimCommentary.value()) { + } else if (g.submode == CommentSubMode && s.emulateVimCommentary()) { handled = handleCommentSubMode(input); - } else if (g.submode == ReplaceWithRegisterSubMode - && s.emulateReplaceWithRegister.value()) { + } else if (g.submode == ReplaceWithRegisterSubMode && s.emulateReplaceWithRegister()) { handled = handleReplaceWithRegisterSubMode(input); } else if (g.submode == ReplaceSubMode) { handled = handleReplaceSubMode(input); @@ -4487,7 +4485,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) setTargetColumn(); } else if (input.isControl('a')) { changeNumberTextObject(count()); - } else if (g.gflag && input.is('c') && s.emulateVimCommentary.value()) { + } else if (g.gflag && input.is('c') && s.emulateVimCommentary()) { if (isVisualMode()) { pushUndoState(); @@ -4512,7 +4510,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) pushUndoState(); setAnchor(); } - } else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister.value()) { + } else if (g.gflag && input.is('r') && s.emulateReplaceWithRegister()) { g.submode = ReplaceWithRegisterSubMode; if (isVisualMode()) { dotCommand = visualDotCommand() + QString::number(count()) + "gr"; @@ -4671,7 +4669,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) int repeat = count(); while (--repeat >= 0) redo(); - } else if (input.is('S') && isVisualMode() && s.emulateSurround.value()) { + } else if (input.is('S') && isVisualMode() && s.emulateSurround()) { g.submode = AddSurroundingSubMode; g.subsubmode = SurroundSubSubMode; } else if (input.is('s')) { @@ -4756,7 +4754,7 @@ bool FakeVimHandler::Private::handleNoSubMode(const Input &input) if (isVisualMode()) { leaveVisualMode(); finishMovement(); - } else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp.value())) { + } else if (g.gflag || (g.submode == InvertCaseSubMode && s.tildeOp())) { if (atEndOfLine()) moveLeft(); setAnchor(); @@ -5404,8 +5402,8 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input) if (!handleInsertInEditor(Input(Qt::Key_Backspace, Qt::NoModifier))) { joinPreviousEditBlock(); if (!m_buffer->lastInsertion.isEmpty() - || s.backspace.value().contains("start") - || s.backspace.value().contains("2")) { + || s.backspace().contains("start") + || s.backspace().contains("2")) { const int line = cursorLine() + 1; const Column col = cursorColumn(); QString data = lineContents(line); @@ -5438,7 +5436,7 @@ void FakeVimHandler::Private::handleInsertMode(const Input &input) } else if (input.isKey(Key_Tab)) { if (q->tabPressedInInsertMode()) { m_buffer->insertState.insertingSpaces = true; - if (s.expandTab.value()) { + if (s.expandTab()) { const int ts = s.tabStop(); const int col = logicalCursorColumn(); QString str = QString(ts - col % ts, ' '); @@ -5494,7 +5492,7 @@ void FakeVimHandler::Private::insertInInsertMode(const QString &text) { joinPreviousEditBlock(); insertText(text); - if (s.smartIndent.value() && isElectricCharacter(text.at(0))) { + if (s.smartIndent() && isElectricCharacter(text.at(0))) { const QString leftText = block().text() .left(position() - 1 - block().position()); if (leftText.simplified().isEmpty()) { @@ -6265,7 +6263,7 @@ bool FakeVimHandler::Private::handleExMoveCommand(const ExCommand &cmd) if (!insertAtEnd) moveUp(1); - if (s.startOfLine.value()) + if (s.startOfLine()) moveToFirstNonBlankOnLine(); if (lastAnchor.line >= startLine && lastAnchor.line <= endLine) @@ -6794,7 +6792,7 @@ QTextCursor FakeVimHandler::Private::search(const SearchData &sd, int startPos, } if (tc.isNull()) { - if (s.wrapScan.value()) { + if (s.wrapScan()) { tc = QTextCursor(document()); tc.movePosition(sd.forward ? StartOfDocument : EndOfDocument); if (sd.forward) @@ -6954,7 +6952,7 @@ void FakeVimHandler::Private::shiftRegionRight(int repeat) std::swap(beginLine, endLine); targetPos = position(); } - if (s.startOfLine.value()) + if (s.startOfLine()) targetPos = firstPositionInLine(beginLine); const int sw = s.shiftWidth(); @@ -7106,7 +7104,7 @@ void FakeVimHandler::Private::setupCharClass() const QChar c = QLatin1Char(i); m_charClass[i] = c.isSpace() ? 0 : 1; } - const QString conf = s.isKeyword.value(); + const QString conf = s.isKeyword(); for (const QString &part : conf.split(',')) { if (part.contains('-')) { const int from = someInt(part.section('-', 0, 0)); @@ -7594,7 +7592,7 @@ void FakeVimHandler::Private::transformText(const Range &range, const Transforma void FakeVimHandler::Private::insertText(QTextCursor &tc, const QString &text) { - if (s.passKeys.value()) { + if (s.passKeys()) { if (tc.hasSelection() && text.isEmpty()) { QKeyEvent event(QEvent::KeyPress, Qt::Key_Delete, Qt::NoModifier, QString()); passEventToEditor(event, tc); @@ -7937,7 +7935,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace) moveRight(); // If the line we started from is a comment, remove the comment string from the next line - if (startingLineIsComment && s.formatOptions.value().contains('f')) { + if (startingLineIsComment && s.formatOptions().contains('f')) { if (characterAtCursor() == '/' && characterAt(position() + 1) == '/') moveRight(2); else if (characterAtCursor() == '*' || characterAtCursor() == '#') @@ -7955,7 +7953,7 @@ void FakeVimHandler::Private::joinLines(int count, bool preserveSpace) void FakeVimHandler::Private::insertNewLine() { - if (m_buffer->editBlockLevel <= 1 && s.passKeys.value()) { + if (m_buffer->editBlockLevel <= 1 && s.passKeys()) { QKeyEvent event(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier, "\n"); if (passEventToEditor(event, m_cursor)) return; @@ -7967,7 +7965,7 @@ void FakeVimHandler::Private::insertNewLine() bool FakeVimHandler::Private::handleInsertInEditor(const Input &input) { - if (m_buffer->editBlockLevel > 0 || !s.passKeys.value()) + if (m_buffer->editBlockLevel > 0 || !s.passKeys()) return false; joinPreviousEditBlock(); @@ -8712,10 +8710,10 @@ QString FakeVimHandler::Private::tabExpand(int n) const void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool forceAutoIndent) { - if (!forceAutoIndent && !s.autoIndent.value() && !s.smartIndent.value()) + if (!forceAutoIndent && !s.autoIndent() && !s.smartIndent()) return; - if (s.smartIndent.value()) { + if (s.smartIndent()) { QTextBlock bl = block(); Range range(bl.position(), bl.position()); indentText(range, '\n'); @@ -8734,7 +8732,7 @@ void FakeVimHandler::Private::insertAutomaticIndentation(bool goingDown, bool fo void FakeVimHandler::Private::handleStartOfLine() { - if (s.startOfLine.value()) + if (s.startOfLine()) moveToFirstNonBlankOnLine(); } @@ -9331,7 +9329,7 @@ void FakeVimHandler::Private::getRegisterType(int *reg, bool *isClipboard, bool *reg = c.toLower().unicode(); if (c == '"') { - QStringList list = s.clipboard.value().split(','); + QStringList list = s.clipboard().split(','); clipboard = list.contains("unnamedplus"); selection = list.contains("unnamed"); } else if (c == '+') { @@ -9385,7 +9383,7 @@ void FakeVimHandler::updateGlobalMarksFilenames(const QString &oldFileName, cons bool FakeVimHandler::eventFilter(QObject *ob, QEvent *ev) { #ifndef FAKEVIM_STANDALONE - if (!fakeVimSettings()->useFakeVim.value()) + if (!settings().useFakeVim()) return QObject::eventFilter(ob, ev); #endif diff --git a/src/plugins/fakevim/fakevimplugin.cpp b/src/plugins/fakevim/fakevimplugin.cpp index 49354921325..54ae4ca93f8 100644 --- a/src/plugins/fakevim/fakevimplugin.cpp +++ b/src/plugins/fakevim/fakevimplugin.cpp @@ -340,134 +340,6 @@ private: using ExCommandMap = QMap; using UserCommandMap = QMap; -static void layoutPage(QWidget *widget) -{ - using namespace Layouting; - FakeVimSettings &s = *fakeVimSettings(); - - Row bools { - Column { - s.autoIndent, - s.smartIndent, - s.expandTab, - s.smartTab, - s.hlSearch, - s.showCmd, - s.startOfLine, - s.passKeys, - s.blinkingCursor - }, - Column { - s.incSearch, - s.useCoreSearch, - s.ignoreCase, - s.smartCase, - s.wrapScan, - s.showMarks, - s.passControlKey, - s.relativeNumber, - s.tildeOp - } - }; - - Row ints { s.shiftWidth, s.tabStop, s.scrollOff, st }; - - Column strings { - s.backspace, - s.isKeyword, - Row {s.readVimRc, s.vimRcPath} - }; - - Column { - s.useFakeVim, - - Group { - title(Tr::tr("Vim Behavior")), - Column { - bools, - ints, - strings - } - }, - - Group { - title(Tr::tr("Plugin Emulation")), - Column { - s.emulateVimCommentary, - s.emulateReplaceWithRegister, - s.emulateArgTextObj, - s.emulateExchange, - s.emulateSurround - } - }, - - Row { - PushButton { - text(Tr::tr("Copy Text Editor Settings")), - onClicked([&s] { - TabSettings ts = TextEditorSettings::codeStyle()->tabSettings(); - TypingSettings tps = TextEditorSettings::typingSettings(); - s.expandTab.setValue(ts.m_tabPolicy != TabSettings::TabsOnlyTabPolicy); - s.tabStop.setValue(ts.m_tabSize); - s.shiftWidth.setValue(ts.m_indentSize); - s.smartTab.setValue(tps.m_smartBackspaceBehavior - == TypingSettings::BackspaceFollowsPreviousIndents); - s.autoIndent.setValue(true); - s.smartIndent.setValue(tps.m_autoIndent); - s.incSearch.setValue(true); - }), - }, - PushButton { - text(Tr::tr("Set Qt Style")), - onClicked([&s] { - s.expandTab.setVolatileValue(true); - s.tabStop.setVolatileValue(4); - s.shiftWidth.setVolatileValue(4); - s.smartTab.setVolatileValue(true); - s.autoIndent.setVolatileValue(true); - s.smartIndent.setVolatileValue(true); - s.incSearch.setVolatileValue(true); - s.backspace.setVolatileValue(QString("indent,eol,start")); - s.passKeys.setVolatileValue(true); - }), - }, - PushButton { - text(Tr::tr("Set Plain Style")), - onClicked([&s] { - s.expandTab.setVolatileValue(false); - s.tabStop.setVolatileValue(8); - s.shiftWidth.setVolatileValue(8); - s.smartTab.setVolatileValue(false); - s.autoIndent.setVolatileValue(false); - s.smartIndent.setVolatileValue(false); - s.incSearch.setVolatileValue(false); - s.backspace.setVolatileValue(QString()); - s.passKeys.setVolatileValue(false); - }), - }, - st - }, - st - - }.attachTo(widget); - - s.vimRcPath.setEnabler(&s.readVimRc); -} - -class FakeVimOptionPage : public IOptionsPage -{ -public: - FakeVimOptionPage() - { - setId(SETTINGS_ID); - setDisplayName(Tr::tr("General")); - setCategory(SETTINGS_CATEGORY); - setDisplayCategory(Tr::tr("FakeVim")); - setCategoryIconPath(":/fakevim/images/settingscategory_fakevim.png"); - setLayouter(&layoutPage); - setSettings(fakeVimSettings()); - } -}; /////////////////////////////////////////////////////////////////////// // @@ -507,7 +379,6 @@ public: int cursorPos, int anchorPos, int messageLevel); void handleExCommand(FakeVimHandler *handler, bool *handled, const ExCommand &cmd); - void writeSettings(); void readSettings(); void handleDelayedQuitAll(bool forced); @@ -1107,7 +978,7 @@ IAssistProcessor *FakeVimCompletionAssistProvider::createProcessor(const AssistI class FakeVimPluginRunData { public: - FakeVimOptionPage optionsPage; + FakeVimSettings settings; FakeVimExCommandsPage exCommandsPage; FakeVimUserCommandsPage userCommandsPage; @@ -1179,7 +1050,7 @@ void FakeVimPluginPrivate::initialize() Command *cmd = nullptr; - cmd = ActionManager::registerAction(fakeVimSettings()->useFakeVim.action(), + cmd = ActionManager::registerAction(settings().useFakeVim.action(), INSTALL_HANDLER, Context(Core::Constants::C_GLOBAL), true); cmd->setDefaultKeySequence(QKeySequence(useMacShortcuts ? Tr::tr("Meta+Shift+Y,Meta+Shift+Y") : Tr::tr("Alt+Y,Alt+Y"))); @@ -1217,7 +1088,7 @@ void FakeVimPluginPrivate::initialize() connect(DocumentManager::instance(), &DocumentManager::documentRenamed, this, &FakeVimPluginPrivate::documentRenamed); - FakeVimSettings &s = *fakeVimSettings(); + FakeVimSettings &s = settings(); connect(&s.useFakeVim, &FvBoolAspect::valueChanged, this, &FakeVimPluginPrivate::setUseFakeVim); connect(&s.readVimRc, &FvBaseAspect::changed, @@ -1235,7 +1106,7 @@ void FakeVimPluginPrivate::initialize() connect(this, &FakeVimPluginPrivate::delayedQuitAllRequested, this, &FakeVimPluginPrivate::handleDelayedQuitAll, Qt::QueuedConnection); - setCursorBlinking(s.blinkingCursor.value()); + setCursorBlinking(s.blinkingCursor()); } void FakeVimPluginPrivate::userActionTriggered(int key) @@ -1244,7 +1115,7 @@ void FakeVimPluginPrivate::userActionTriggered(int key) FakeVimHandler *handler = m_editorToHandler[editor].handler; if (handler) { // If disabled, enable FakeVim mode just for single user command. - bool enableFakeVim = !fakeVimSettings()->useFakeVim.value(); + bool enableFakeVim = !settings().useFakeVim(); if (enableFakeVim) setUseFakeVimInternal(true); @@ -1270,26 +1141,18 @@ void FakeVimPluginPrivate::createRelativeNumberWidget(IEditor *editor) { if (auto textEditor = TextEditorWidget::fromEditor(editor)) { auto relativeNumbers = new RelativeNumbersColumn(textEditor); - connect(&fakeVimSettings()->relativeNumber, &FvBaseAspect::changed, + connect(&settings().relativeNumber, &FvBaseAspect::changed, relativeNumbers, &QObject::deleteLater); - connect(&fakeVimSettings()->useFakeVim, &FvBaseAspect::changed, + connect(&settings().useFakeVim, &FvBaseAspect::changed, relativeNumbers, &QObject::deleteLater); relativeNumbers->show(); } } -void FakeVimPluginPrivate::writeSettings() -{ - QSettings *settings = ICore::settings(); - fakeVimSettings()->writeSettings(settings); -} - void FakeVimPluginPrivate::readSettings() { QSettings *settings = ICore::settings(); - fakeVimSettings()->readSettings(settings); - m_exCommandMap = m_defaultExCommandMap; int size = settings->beginReadArray(exCommandMapGroup); for (int i = 0; i < size; ++i) { @@ -1318,9 +1181,9 @@ void FakeVimPluginPrivate::maybeReadVimRc() //qDebug() << theFakeVimSetting(ConfigReadVimRc) // << theFakeVimSetting(ConfigReadVimRc)->value(); //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value(); - if (!fakeVimSettings()->readVimRc.value()) + if (!settings().readVimRc()) return; - QString fileName = fakeVimSettings()->vimRcPath.value(); + QString fileName = settings().vimRcPath(); if (fileName.isEmpty()) { fileName = QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QLatin1String(HostOsInfo::isWindowsHost() ? "/_vimrc" : "/.vimrc"); @@ -1330,7 +1193,6 @@ void FakeVimPluginPrivate::maybeReadVimRc() QPlainTextEdit editor; FakeVimHandler handler(&editor); handler.handleCommand("source " + fileName); - //writeSettings(); //qDebug() << theFakeVimSetting(ConfigShiftWidth)->value(); } @@ -1659,9 +1521,9 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) return; TabSettings tabSettings; - tabSettings.m_indentSize = fakeVimSettings()->shiftWidth(); - tabSettings.m_tabSize = fakeVimSettings()->tabStop(); - tabSettings.m_tabPolicy = fakeVimSettings()->expandTab() + tabSettings.m_indentSize = settings().shiftWidth(); + tabSettings.m_tabSize = settings().tabStop(); + tabSettings.m_tabPolicy = settings().expandTab() ? TabSettings::SpacesOnlyTabPolicy : TabSettings::TabsOnlyTabPolicy; tabSettings.m_continuationAlignBehavior = tew->textDocument()->tabSettings().m_continuationAlignBehavior; @@ -1885,19 +1747,15 @@ void FakeVimPluginPrivate::editorOpened(IEditor *editor) *output = proc.cleanedStdOut(); }); - connect(ICore::instance(), &ICore::saveSettingsRequested, - this, &FakeVimPluginPrivate::writeSettings); - - handler->setCurrentFileName(editor->document()->filePath().toString()); handler->installEventFilter(); // pop up the bar - if (fakeVimSettings()->useFakeVim.value()) { + if (settings().useFakeVim()) { resetCommandBuffer(); handler->setupWidget(); - if (fakeVimSettings()->relativeNumber.value()) + if (settings().relativeNumber()) createRelativeNumberWidget(editor); } } @@ -1939,8 +1797,8 @@ void FakeVimPluginPrivate::setUseFakeVim(bool on) //qDebug() << "SET USE FAKEVIM" << on; Find::setUseFakeVim(on); setUseFakeVimInternal(on); - setShowRelativeLineNumbers(fakeVimSettings()->relativeNumber.value()); - setCursorBlinking(fakeVimSettings()->blinkingCursor.value()); + setShowRelativeLineNumbers(settings().relativeNumber()); + setCursorBlinking(settings().blinkingCursor()); } void FakeVimPluginPrivate::setUseFakeVimInternal(bool on) @@ -1968,7 +1826,7 @@ void FakeVimPluginPrivate::setUseFakeVimInternal(bool on) void FakeVimPluginPrivate::setShowRelativeLineNumbers(bool on) { - if (on && fakeVimSettings()->useFakeVim.value()) { + if (on && settings().useFakeVim()) { for (auto it = m_editorToHandler.constBegin(); it != m_editorToHandler.constEnd(); ++it) createRelativeNumberWidget(it.key()); } @@ -1979,7 +1837,7 @@ void FakeVimPluginPrivate::setCursorBlinking(bool on) if (m_savedCursorFlashTime == 0) m_savedCursorFlashTime = QGuiApplication::styleHints()->cursorFlashTime(); - const bool blink = on || !fakeVimSettings()->useFakeVim.value(); + const bool blink = on || !settings().useFakeVim(); QGuiApplication::styleHints()->setCursorFlashTime(blink ? m_savedCursorFlashTime : 0); } @@ -2123,7 +1981,7 @@ void FakeVimPluginPrivate::handleDelayedQuitAll(bool forced) void FakeVimPluginPrivate::quitFakeVim() { - fakeVimSettings()->useFakeVim.setValue(false); + settings().useFakeVim.setValue(false); } void FakeVimPluginPrivate::resetCommandBuffer() diff --git a/src/plugins/fossil/fossilcommitwidget.cpp b/src/plugins/fossil/fossilcommitwidget.cpp index 355a792bfda..72fba5ef86f 100644 --- a/src/plugins/fossil/fossilcommitwidget.cpp +++ b/src/plugins/fossil/fossilcommitwidget.cpp @@ -93,8 +93,8 @@ FossilCommitWidget::FossilCommitWidget() : m_commitPanel(new QWidget) m_invalidBranchLabel->setType(InfoLabel::Error); m_isPrivateCheckBox = new QCheckBox(Tr::tr("Private")); - m_isPrivateCheckBox->setToolTip(Tr::tr("Create a private check-in that is never synced.\n" - "Children of private check-ins are automatically private.\n" + m_isPrivateCheckBox->setToolTip("" + Tr::tr("Create a private check-in that is never synced. " + "Children of private check-ins are automatically private. " "Private check-ins are not pushed to the remote repository by default.")); m_tagsLineEdit = new QLineEdit; diff --git a/src/plugins/fossil/fossilsettings.cpp b/src/plugins/fossil/fossilsettings.cpp index d62f48f1566..0662445b131 100644 --- a/src/plugins/fossil/fossilsettings.cpp +++ b/src/plugins/fossil/fossilsettings.cpp @@ -88,9 +88,9 @@ FossilSettings::FossilSettings() logCount.setToolTip(Tr::tr("The number of recent commit log entries to show. " "Choose 0 to see all entries.")); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Group { title(Tr::tr("Configuration")), Row { binaryPath } @@ -117,7 +117,7 @@ FossilSettings::FossilSettings() }, }, st - }.attachTo(widget); + }; }); } diff --git a/src/plugins/fossil/pullorpushdialog.cpp b/src/plugins/fossil/pullorpushdialog.cpp index 5ebf9869d3f..b6473a68b41 100644 --- a/src/plugins/fossil/pullorpushdialog.cpp +++ b/src/plugins/fossil/pullorpushdialog.cpp @@ -34,7 +34,7 @@ PullOrPushDialog::PullOrPushDialog(Mode mode, QWidget *parent) m_localPathChooser->setPromptDialogFilter(Tr::tr(Constants::FOSSIL_FILE_FILTER)); m_urlButton = new QRadioButton(Tr::tr("Specify URL:")); - m_urlButton->setToolTip(Tr::tr("For example: https://[user[:pass]@]host[:port]/[path]")); + m_urlButton->setToolTip(Tr::tr("For example: \"https://[user[:pass]@]host[:port]/[path]\".")); m_urlLineEdit = new QLineEdit; m_urlLineEdit->setEnabled(false); diff --git a/src/plugins/fossil/wizard/projects/vcs/wizard.json b/src/plugins/fossil/wizard/projects/vcs/wizard.json index 56e08b63774..7f5fb174520 100644 --- a/src/plugins/fossil/wizard/projects/vcs/wizard.json +++ b/src/plugins/fossil/wizard/projects/vcs/wizard.json @@ -86,7 +86,7 @@ { "name": "Repo", "trDisplayName": "Remote repository:", - "trToolTip": "For example: https://[user[:pass]@]host[:port]/[path]", + "trToolTip": "For example: \"https://[user[:pass]@]host[:port]/[path]\".", "type": "LineEdit", "enabled": "%{isCloneRepo}", "mandatory": false diff --git a/src/plugins/git/gerrit/gerritoptionspage.cpp b/src/plugins/git/gerrit/gerritoptionspage.cpp index 3f225d82eb1..3d77c26aa46 100644 --- a/src/plugins/git/gerrit/gerritoptionspage.cpp +++ b/src/plugins/git/gerrit/gerritoptionspage.cpp @@ -43,8 +43,8 @@ public: curlChooser->setCommandVersionArguments({"-V"}); auto portSpinBox = new QSpinBox(this); - portSpinBox->setValue(p->server.port); portSpinBox->setRange(1, 65535); + portSpinBox->setValue(p->server.port); auto httpsCheckBox = new QCheckBox(Git::Tr::tr("HTTPS")); httpsCheckBox->setChecked(p->https); diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index f0427e08268..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))); @@ -465,11 +465,11 @@ ShowController::ShowController(IDocument *document, const QString &id) parallel, onGroupSetup([this] { setStartupFile(VcsBase::source(this->document()).toString()); }), Group { - optional, + finishAllAndDone, ProcessTask(setupDescription, onDescriptionDone), Group { parallel, - optional, + finishAllAndDone, onGroupSetup(desciptionDetailsSetup), ProcessTask(setupBranches, onBranchesDone, onBranchesError), ProcessTask(setupPrecedes, onPrecedesDone, onPrecedesError), @@ -2873,7 +2873,7 @@ bool GitClient::addAndCommit(const FilePath &repositoryDirectory, GitPlugin::updateCurrentBranch(); return true; } - VcsOutputWindow::appendError(Tr::tr("Cannot commit %n files", nullptr, commitCount) + "\n"); + VcsOutputWindow::appendError(Tr::tr("Cannot commit %n file(s)", nullptr, commitCount) + "\n"); return false; } diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index 2161cbbf68e..f6324f152de 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -21,13 +21,12 @@ #include #include -#include #include #include #include -#include using namespace Core; +using namespace TextEditor; using namespace Utils; using namespace VcsBase; @@ -43,91 +42,106 @@ public: QString id() const { return recurseSubmodules ? ref + ".Rec" : ref; } }; -class GitGrepRunner +static QStringView nextLine(QStringView *remainingInput) { - using PromiseType = QPromise; - -public: - GitGrepRunner(const TextEditor::FileFindParameters ¶meters) - : m_parameters(parameters) - { - m_directory = FilePath::fromString(parameters.additionalParameters.toString()); - m_vcsBinary = GitClient::instance()->vcsBinary(); - m_environment = GitClient::instance()->processEnvironment(); + const int newLinePos = remainingInput->indexOf('\n'); + if (newLinePos < 0) { + QStringView ret = *remainingInput; + *remainingInput = QStringView(); + return ret; } + QStringView ret = remainingInput->left(newLinePos); + *remainingInput = remainingInput->mid(newLinePos + 1); + return ret; +} - struct Match - { - Match() = default; - Match(int start, int length) : - matchStart(start), matchLength(length) {} +struct Match +{ + Match() = default; + Match(int start, int length) : + matchStart(start), matchLength(length) {} - int matchStart = 0; - int matchLength = 0; - QStringList regexpCapturedTexts; - }; + int matchStart = 0; + int matchLength = 0; + QStringList regexpCapturedTexts; +}; - void processLine(const QString &line, SearchResultItems *resultList) const - { +static void processLine(QStringView line, SearchResultItems *resultList, + const std::optional ®Exp, const QString &ref, + const FilePath &directory) +{ + if (line.isEmpty()) + return; + static const QLatin1String boldRed("\x1b[1;31m"); + static const QLatin1String resetColor("\x1b[m"); + SearchResultItem result; + const int lineSeparator = line.indexOf(QChar::Null); + QStringView filePath = line.left(lineSeparator); + if (!ref.isEmpty() && filePath.startsWith(ref)) + filePath = filePath.mid(ref.length()); + result.setFilePath(directory.pathAppended(filePath.toString())); + const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1); + const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt(); + QString text = line.mid(textSeparator + 1).toString(); + QList matches; + while (true) { + const int matchStart = text.indexOf(boldRed); + if (matchStart == -1) + break; + const int matchTextStart = matchStart + boldRed.size(); + const int matchEnd = text.indexOf(resetColor, matchTextStart); + QTC_ASSERT(matchEnd != -1, break); + const int matchLength = matchEnd - matchTextStart; + Match match(matchStart, matchLength); + const QString matchText = text.mid(matchTextStart, matchLength); + if (regExp) + match.regexpCapturedTexts = regExp->match(matchText).capturedTexts(); + matches.append(match); + text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size()); + } + result.setDisplayText(text); + + for (const auto &match : std::as_const(matches)) { + result.setMainRange(lineNumber, match.matchStart, match.matchLength); + result.setUserData(match.regexpCapturedTexts); + result.setUseTextEditorFont(true); + resultList->append(result); + } +} + +static SearchResultItems parse(const QFuture &future, const QString &input, + const std::optional ®Exp, const QString &ref, + const FilePath &directory) +{ + SearchResultItems items; + QStringView remainingInput(input); + while (true) { + if (future.isCanceled()) + return {}; + + if (remainingInput.isEmpty()) + break; + + const QStringView line = nextLine(&remainingInput); if (line.isEmpty()) - return; - static const QLatin1String boldRed("\x1b[1;31m"); - static const QLatin1String resetColor("\x1b[m"); - SearchResultItem result; - const int lineSeparator = line.indexOf(QChar::Null); - QString filePath = line.left(lineSeparator); - if (!m_ref.isEmpty() && filePath.startsWith(m_ref)) - filePath.remove(0, m_ref.length()); - result.setFilePath(m_directory.pathAppended(filePath)); - const int textSeparator = line.indexOf(QChar::Null, lineSeparator + 1); - const int lineNumber = line.mid(lineSeparator + 1, textSeparator - lineSeparator - 1).toInt(); - QString text = line.mid(textSeparator + 1); - QRegularExpression regexp; - QList matches; - if (m_parameters.flags & FindRegularExpression) { - const QRegularExpression::PatternOptions patternOptions = - (m_parameters.flags & FindCaseSensitively) - ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption; - regexp.setPattern(m_parameters.text); - regexp.setPatternOptions(patternOptions); - } - for (;;) { - const int matchStart = text.indexOf(boldRed); - if (matchStart == -1) - break; - const int matchTextStart = matchStart + boldRed.size(); - const int matchEnd = text.indexOf(resetColor, matchTextStart); - QTC_ASSERT(matchEnd != -1, break); - const int matchLength = matchEnd - matchTextStart; - Match match(matchStart, matchLength); - const QString matchText = text.mid(matchTextStart, matchLength); - if (m_parameters.flags & FindRegularExpression) - match.regexpCapturedTexts = regexp.match(matchText).capturedTexts(); - matches.append(match); - text = text.left(matchStart) + matchText + text.mid(matchEnd + resetColor.size()); - } - result.setDisplayText(text); + continue; - for (const auto &match : std::as_const(matches)) { - result.setMainRange(lineNumber, match.matchStart, match.matchLength); - result.setUserData(match.regexpCapturedTexts); - resultList->append(result); - } + processLine(line, &items, regExp, ref, directory); } + return items; +} - void read(PromiseType &fi, const QString &text) - { - SearchResultItems resultList; - QString t = text; - QTextStream stream(&t); - while (!stream.atEnd() && !fi.isCanceled()) - processLine(stream.readLine(), &resultList); - if (!resultList.isEmpty() && !fi.isCanceled()) - fi.addResult(resultList); - } +static void runGitGrep(QPromise &promise, const FileFindParameters ¶meters) +{ + const FilePath directory = FilePath::fromString(parameters.additionalParameters.toString()); + const GitGrepParameters gitParameters + = parameters.searchEngineParameters.value(); + const QString ref = gitParameters.ref.isEmpty() ? QString() : gitParameters.ref + ':'; + + const auto setupProcess = [&](Process &process) { + const FilePath vcsBinary = GitClient::instance()->vcsBinary(); + const Environment environment = GitClient::instance()->processEnvironment(); - void operator()(PromiseType &promise) - { QStringList arguments = { "-c", "color.grep.match=bold red", "-c", "color.grep=always", @@ -135,60 +149,41 @@ public: "-c", "color.grep.lineNumber=", "grep", "-zn", "--no-full-name" }; - if (!(m_parameters.flags & FindCaseSensitively)) + if (!(parameters.flags & FindCaseSensitively)) arguments << "-i"; - if (m_parameters.flags & FindWholeWords) + if (parameters.flags & FindWholeWords) arguments << "-w"; - if (m_parameters.flags & FindRegularExpression) + if (parameters.flags & FindRegularExpression) arguments << "-P"; else arguments << "-F"; - arguments << "-e" << m_parameters.text; - GitGrepParameters params = m_parameters.searchEngineParameters.value(); - if (params.recurseSubmodules) + arguments << "-e" << parameters.text; + if (gitParameters.recurseSubmodules) arguments << "--recurse-submodules"; - if (!params.ref.isEmpty()) { - arguments << params.ref; - m_ref = params.ref + ':'; + if (!gitParameters.ref.isEmpty()) { + arguments << gitParameters.ref; } const QStringList filterArgs = - m_parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters - : m_parameters.nameFilters; + parameters.nameFilters.isEmpty() ? QStringList("*") // needed for exclusion filters + : parameters.nameFilters; const QStringList exclusionArgs = - Utils::transform(m_parameters.exclusionFilters, [](const QString &filter) { - return QString(":!" + filter); - }); + Utils::transform(parameters.exclusionFilters, [](const QString &filter) { + return QString(":!" + filter); + }); arguments << "--" << filterArgs << exclusionArgs; - Process process; - process.setEnvironment(m_environment); - process.setCommand({m_vcsBinary, arguments}); - process.setWorkingDirectory(m_directory); - process.setStdOutCallback([this, &promise](const QString &text) { read(promise, text); }); - process.start(); - process.waitForFinished(); + process.setEnvironment(environment); + process.setCommand({vcsBinary, arguments}); + process.setWorkingDirectory(directory); + }; - switch (process.result()) { - case ProcessResult::TerminatedAbnormally: - case ProcessResult::StartFailed: - case ProcessResult::Hang: - promise.future().cancel(); - break; - case ProcessResult::FinishedWithSuccess: - case ProcessResult::FinishedWithError: - // When no results are found, git-grep exits with non-zero status. - // Do not consider this as an error. - break; - } - } + const auto outputParser = [&ref, &directory](const QFuture &future, const QString &input, + const std::optional ®Exp) { + return parse(future, input, regExp, ref, directory); + }; -private: - FilePath m_vcsBinary; - FilePath m_directory; - QString m_ref; - TextEditor::FileFindParameters m_parameters; - Environment m_environment; -}; + TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser); +} static bool isGitDirectory(const FilePath &path) { @@ -211,18 +206,16 @@ GitGrep::GitGrep(GitClient *client) m_treeLineEdit->setValidator(new QRegularExpressionValidator(refExpression, this)); layout->addWidget(m_treeLineEdit); // asynchronously check git version, add "recurse submodules" option if available - Utils::onResultReady(client->gitVersion(), - this, + Utils::onResultReady(client->gitVersion(), this, [this, pLayout = QPointer(layout)](unsigned version) { - if (version >= 0x021300 && pLayout) { - m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules")); - pLayout->addWidget(m_recurseSubmodules); - } - }); - TextEditor::FindInFiles *findInFiles = TextEditor::FindInFiles::instance(); + if (version >= 0x021300 && pLayout) { + m_recurseSubmodules = new QCheckBox(Tr::tr("Recurse submodules")); + pLayout->addWidget(m_recurseSubmodules); + } + }); + FindInFiles *findInFiles = FindInFiles::instance(); QTC_ASSERT(findInFiles, return); - connect(findInFiles, &TextEditor::FindInFiles::pathChanged, - m_widget, [this](const FilePath &path) { + connect(findInFiles, &FindInFiles::pathChanged, m_widget, [this](const FilePath &path) { setEnabled(isGitDirectory(path)); }); connect(this, &SearchEngine::enabledChanged, m_widget, &QWidget::setEnabled); @@ -271,14 +264,14 @@ void GitGrep::writeSettings(QSettings *settings) const settings->setValue(GitGrepRef, m_treeLineEdit->text()); } -QFuture GitGrep::executeSearch(const TextEditor::FileFindParameters ¶meters, - TextEditor::BaseFileFind * /*baseFileFind*/) +QFuture GitGrep::executeSearch(const FileFindParameters ¶meters, + BaseFileFind *) { - return Utils::asyncRun(GitGrepRunner(parameters)); + return Utils::asyncRun(runGitGrep, parameters); } IEditor *GitGrep::openEditor(const SearchResultItem &item, - const TextEditor::FileFindParameters ¶meters) + const FileFindParameters ¶meters) { const GitGrepParameters params = parameters.searchEngineParameters.value(); const QStringList &itemPath = item.path(); diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h index 2fd114e36ec..fda6fe56ebd 100644 --- a/src/plugins/git/gitgrep.h +++ b/src/plugins/git/gitgrep.h @@ -9,10 +9,7 @@ QT_BEGIN_NAMESPACE class QCheckBox; QT_END_NAMESPACE -namespace Utils { -class FancyLineEdit; -class SearchResultItem; -} +namespace Utils { class FancyLineEdit; } namespace Git::Internal { diff --git a/src/plugins/git/gitsettings.cpp b/src/plugins/git/gitsettings.cpp index 6cbec254445..4ebf7debeed 100644 --- a/src/plugins/git/gitsettings.cpp +++ b/src/plugins/git/gitsettings.cpp @@ -90,8 +90,8 @@ GitSettings::GitSettings() instantBlame.setSettingsKey("Git Instant"); instantBlame.setDefaultValue(true); instantBlame.setLabelText(Tr::tr("Add instant blame annotations to editor")); - instantBlame.setToolTip(Tr::tr("Directly annotate each line in the editor " - "when scrolling through the document.")); + instantBlame.setToolTip( + Tr::tr("Annotate the current line in the editor with Git \"blame\" output.")); graphLog.setSettingsKey("GraphLog"); diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 9d2356a9d4d..130961ebd1b 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -356,6 +356,7 @@ void Client::setName(const QString &name) QString Client::name() const { if (d->m_project && !d->m_project->displayName().isEmpty()) + //: for return Tr::tr("%1 for %2").arg(d->m_displayName, d->m_project->displayName()); return d->m_displayName; } @@ -555,11 +556,17 @@ Client::State Client::state() const QString Client::stateString() const { switch (d->m_state){ + //: language client state case Uninitialized: return Tr::tr("uninitialized"); + //: language client state case InitializeRequested: return Tr::tr("initialize requested"); + //: language client state case Initialized: return Tr::tr("initialized"); + //: language client state case ShutdownRequested: return Tr::tr("shutdown requested"); + //: language client state case Shutdown: return Tr::tr("shut down"); + //: language client state case Error: return Tr::tr("error"); } return {}; @@ -1970,7 +1977,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe if (std::optional> error = initResponse.error()) { if (std::optional data = error->data()) { if (data->retry()) { - const QString title(Tr::tr("Language Server \"%1\" Initialize Error").arg(m_displayName)); + const QString title(Tr::tr("Language Server \"%1\" Initialization Error").arg(m_displayName)); auto result = QMessageBox::warning(Core::ICore::dialogParent(), title, error->message(), @@ -1983,7 +1990,7 @@ void ClientPrivate::initializeCallback(const InitializeRequest::Response &initRe } } } - q->setError(Tr::tr("Initialize error: ") + error->message()); + q->setError(Tr::tr("Initialization error: %1.").arg(error->message())); emit q->finished(); return; } diff --git a/src/plugins/languageclient/progressmanager.cpp b/src/plugins/languageclient/progressmanager.cpp index 285080e4980..6b82d289dfc 100644 --- a/src/plugins/languageclient/progressmanager.cpp +++ b/src/plugins/languageclient/progressmanager.cpp @@ -8,6 +8,7 @@ #include #include +#include using namespace LanguageServerProtocol; @@ -81,9 +82,28 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr auto interface = new QFutureInterface(); interface->reportStarted(); interface->setProgressRange(0, 100); // LSP always reports percentage of the task - const QString title = m_titles.value(token, begin.title()); - Core::FutureProgress *progress = Core::ProgressManager::addTask( - interface->future(), title, languageClientProgressId(token)); + ProgressItem progressItem; + progressItem.futureInterface = interface; + progressItem.title = m_titles.value(token, begin.title()); + if (LOGPROGRESS().isDebugEnabled()) + progressItem.timer.start(); + progressItem.showBarTimer = new QTimer(); + progressItem.showBarTimer->setSingleShot(true); + progressItem.showBarTimer->setInterval(750); + progressItem.showBarTimer->callOnTimeout([this, token]() { spawnProgressBar(token); }); + progressItem.showBarTimer->start(); + m_progress[token] = progressItem; + reportProgress(token, begin); +} + +void ProgressManager::spawnProgressBar(const LanguageServerProtocol::ProgressToken &token) +{ + ProgressItem &progressItem = m_progress[token]; + QTC_ASSERT(progressItem.futureInterface, return); + Core::FutureProgress *progress + = Core::ProgressManager::addTask(progressItem.futureInterface->future(), + progressItem.title, + languageClientProgressId(token)); const std::function clickHandler = m_clickHandlers.value(token); if (clickHandler) QObject::connect(progress, &Core::FutureProgress::clicked, clickHandler); @@ -92,23 +112,26 @@ void ProgressManager::beginProgress(const ProgressToken &token, const WorkDonePr QObject::connect(progress, &Core::FutureProgress::canceled, cancelHandler); else progress->setCancelEnabled(false); - m_progress[token] = {progress, interface}; - if (LOGPROGRESS().isDebugEnabled()) - m_timer[token].start(); - reportProgress(token, begin); + if (!progressItem.message.isEmpty()) { + progress->setSubtitle(progressItem.message); + progress->setSubtitleVisibleInStatusBar(true); + } + progressItem.progressInterface = progress; } void ProgressManager::reportProgress(const ProgressToken &token, const WorkDoneProgressReport &report) { - const LanguageClientProgress &progress = m_progress.value(token); + ProgressItem &progress = m_progress[token]; + const std::optional &message = report.message(); if (progress.progressInterface) { - const std::optional &message = report.message(); if (message.has_value()) { progress.progressInterface->setSubtitle(*message); const bool showSubtitle = !message->isEmpty(); progress.progressInterface->setSubtitleVisibleInStatusBar(showSubtitle); } + } else if (message.has_value()) { + progress.message = *message; } if (progress.futureInterface) { if (const std::optional &percentage = report.percentage(); percentage.has_value()) @@ -118,7 +141,7 @@ void ProgressManager::reportProgress(const ProgressToken &token, void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProgressEnd &end) { - const LanguageClientProgress &progress = m_progress.value(token); + const ProgressItem &progress = m_progress.value(token); const QString &message = end.message().value_or(QString()); if (progress.progressInterface) { if (!message.isEmpty()) { @@ -127,11 +150,11 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg } progress.progressInterface->setSubtitle(message); progress.progressInterface->setSubtitleVisibleInStatusBar(!message.isEmpty()); - auto timer = m_timer.take(token); - if (timer.isValid()) { + if (progress.timer.isValid()) { qCDebug(LOGPROGRESS) << QString("%1 took %2") .arg(progress.progressInterface->title()) - .arg(QTime::fromMSecsSinceStartOfDay(timer.elapsed()) + .arg(QTime::fromMSecsSinceStartOfDay( + progress.timer.elapsed()) .toString(Qt::ISODateWithMs)); } } @@ -140,7 +163,8 @@ void ProgressManager::endProgress(const ProgressToken &token, const WorkDoneProg void ProgressManager::endProgressReport(const ProgressToken &token) { - const LanguageClientProgress &progress = m_progress.take(token); + ProgressItem progress = m_progress.take(token); + delete progress.showBarTimer; if (progress.futureInterface) progress.futureInterface->reportFinished(); delete progress.futureInterface; diff --git a/src/plugins/languageclient/progressmanager.h b/src/plugins/languageclient/progressmanager.h index 1e0ad781806..05b9640745d 100644 --- a/src/plugins/languageclient/progressmanager.h +++ b/src/plugins/languageclient/progressmanager.h @@ -11,6 +11,10 @@ #include #include +QT_BEGIN_NAMESPACE +class QTimer; +QT_END_NAMESPACE + namespace LanguageServerProtocol { class ProgressParams; class ProgressToken; @@ -46,15 +50,20 @@ private: const LanguageServerProtocol::WorkDoneProgressReport &report); void endProgress(const LanguageServerProtocol::ProgressToken &token, const LanguageServerProtocol::WorkDoneProgressEnd &end); + void spawnProgressBar(const LanguageServerProtocol::ProgressToken &token); - struct LanguageClientProgress { + struct ProgressItem + { QPointer progressInterface = nullptr; QFutureInterface *futureInterface = nullptr; + QElapsedTimer timer; + QTimer *showBarTimer = nullptr; + QString message; + QString title; }; - QMap m_progress; + QMap m_progress; QMap m_titles; - QMap m_timer; QMap> m_clickHandlers; QMap> m_cancelHandlers; }; diff --git a/src/plugins/macros/CMakeLists.txt b/src/plugins/macros/CMakeLists.txt index aef9a332ddc..588a9a330f3 100644 --- a/src/plugins/macros/CMakeLists.txt +++ b/src/plugins/macros/CMakeLists.txt @@ -9,7 +9,6 @@ add_qtc_plugin(Macros macrolocatorfilter.cpp macrolocatorfilter.h macromanager.cpp macromanager.h macrooptionspage.cpp macrooptionspage.h - macrooptionswidget.cpp macrooptionswidget.h macros.qrc macrosconstants.h macrosplugin.cpp macrosplugin.h diff --git a/src/plugins/macros/macrooptionspage.cpp b/src/plugins/macros/macrooptionspage.cpp index 6295ea94e78..616547283e0 100644 --- a/src/plugins/macros/macrooptionspage.cpp +++ b/src/plugins/macros/macrooptionspage.cpp @@ -3,15 +3,195 @@ #include "macrooptionspage.h" +#include "macro.h" #include "macromanager.h" -#include "macrooptionswidget.h" #include "macrosconstants.h" #include "macrostr.h" +#include +#include +#include +#include + #include -namespace Macros { -namespace Internal { +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Macros::Internal { + +const int NAME_ROLE = Qt::UserRole; +const int WRITE_ROLE = Qt::UserRole + 1; + +class MacroOptionsWidget final : public Core::IOptionsPageWidget +{ +public: + MacroOptionsWidget(); + + void initialize(); + + void apply() final; + +private: + void remove(); + void changeCurrentItem(QTreeWidgetItem *current); + + void createTable(); + + void changeDescription(const QString &description); + + QStringList m_macroToRemove; + bool m_changingCurrent = false; + + QMap m_macroToChange; + + QTreeWidget *m_treeWidget; + QPushButton *m_removeButton; + QGroupBox *m_macroGroup; + QLineEdit *m_description; +}; + +MacroOptionsWidget::MacroOptionsWidget() +{ + m_treeWidget = new QTreeWidget; + m_treeWidget->setTextElideMode(Qt::ElideLeft); + m_treeWidget->setUniformRowHeights(true); + m_treeWidget->setSortingEnabled(true); + m_treeWidget->setColumnCount(3); + m_treeWidget->header()->setSortIndicatorShown(true); + m_treeWidget->header()->setStretchLastSection(true); + m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder); + m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")}); + + m_description = new QLineEdit; + + m_removeButton = new QPushButton(Tr::tr("Remove")); + + m_macroGroup = new QGroupBox(Tr::tr("Macro"), this); + + using namespace Layouting; + + Row { + Tr::tr("Description:"), m_description + }.attachTo(m_macroGroup); + + Column { + Group { + title(Tr::tr("Preferences")), + Row { + m_treeWidget, + Column { m_removeButton, st }, + } + }, + m_macroGroup + }.attachTo(this); + + connect(m_treeWidget, &QTreeWidget::currentItemChanged, + this, &MacroOptionsWidget::changeCurrentItem); + connect(m_removeButton, &QPushButton::clicked, + this, &MacroOptionsWidget::remove); + connect(m_description, &QLineEdit::textChanged, + this, &MacroOptionsWidget::changeDescription); + + initialize(); +} + +void MacroOptionsWidget::initialize() +{ + m_macroToRemove.clear(); + m_macroToChange.clear(); + m_treeWidget->clear(); + changeCurrentItem(nullptr); + + // Create the treeview + createTable(); +} + +void MacroOptionsWidget::createTable() +{ + QDir dir(MacroManager::macrosDirectory()); + const Utils::Id base = Utils::Id(Constants::PREFIX_MACRO); + for (Macro *macro : MacroManager::macros()) { + QFileInfo fileInfo(macro->fileName()); + if (fileInfo.absoluteDir() == dir.absolutePath()) { + auto macroItem = new QTreeWidgetItem(m_treeWidget); + macroItem->setText(0, macro->displayName()); + macroItem->setText(1, macro->description()); + macroItem->setData(0, NAME_ROLE, macro->displayName()); + macroItem->setData(0, WRITE_ROLE, macro->isWritable()); + + Core::Command *command = + Core::ActionManager::command(base.withSuffix(macro->displayName())); + if (command && command->action()) { + macroItem->setText(2, + command->action()->shortcut().toString(QKeySequence::NativeText)); + } + } + } +} + +void MacroOptionsWidget::changeCurrentItem(QTreeWidgetItem *current) +{ + m_changingCurrent = true; + m_removeButton->setEnabled(current); + m_macroGroup->setEnabled(current); + if (!current) { + m_description->clear(); + } else { + m_description->setText(current->text(1)); + m_description->setEnabled(current->data(0, WRITE_ROLE).toBool()); + } + m_changingCurrent = false; +} + +void MacroOptionsWidget::remove() +{ + QTreeWidgetItem *current = m_treeWidget->currentItem(); + m_macroToRemove.append(current->data(0, NAME_ROLE).toString()); + delete current; +} + +void MacroOptionsWidget::apply() +{ + // Remove macro + for (const QString &name : std::as_const(m_macroToRemove)) { + MacroManager::instance()->deleteMacro(name); + m_macroToChange.remove(name); + } + + // Change macro + for (auto it = m_macroToChange.cbegin(), end = m_macroToChange.cend(); it != end; ++it) + MacroManager::instance()->changeMacro(it.key(), it.value()); + + // Reinitialize the page + initialize(); +} + +void MacroOptionsWidget::changeDescription(const QString &description) +{ + QTreeWidgetItem *current = m_treeWidget->currentItem(); + if (m_changingCurrent || !current) + return; + + QString macroName = current->data(0, NAME_ROLE).toString(); + m_macroToChange[macroName] = description; + current->setText(1, description); + QFont font = current->font(1); + font.setItalic(true); + current->setFont(1, font); +} MacroOptionsPage::MacroOptionsPage() { @@ -21,5 +201,4 @@ MacroOptionsPage::MacroOptionsPage() setWidgetCreator([] { return new MacroOptionsWidget; }); } -} // Internal -} // Macros +} // Macros::Internal diff --git a/src/plugins/macros/macrooptionspage.h b/src/plugins/macros/macrooptionspage.h index e9948cb1673..511b8b2b870 100644 --- a/src/plugins/macros/macrooptionspage.h +++ b/src/plugins/macros/macrooptionspage.h @@ -5,8 +5,7 @@ #include -namespace Macros { -namespace Internal { +namespace Macros::Internal { class MacroOptionsPage final : public Core::IOptionsPage { @@ -14,5 +13,4 @@ public: MacroOptionsPage(); }; -} // namespace Internal -} // namespace Macros +} // Macros::Internal diff --git a/src/plugins/macros/macrooptionswidget.cpp b/src/plugins/macros/macrooptionswidget.cpp deleted file mode 100644 index cd11ab64ef3..00000000000 --- a/src/plugins/macros/macrooptionswidget.cpp +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (C) 2016 Nicolas Arnaud-Cormos -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "macrooptionswidget.h" - -#include "macro.h" -#include "macromanager.h" -#include "macrosconstants.h" -#include "macrostr.h" - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Macros::Internal { - -const int NAME_ROLE = Qt::UserRole; -const int WRITE_ROLE = Qt::UserRole + 1; - -MacroOptionsWidget::MacroOptionsWidget() -{ - m_treeWidget = new QTreeWidget; - m_treeWidget->setTextElideMode(Qt::ElideLeft); - m_treeWidget->setUniformRowHeights(true); - m_treeWidget->setSortingEnabled(true); - m_treeWidget->setColumnCount(3); - m_treeWidget->header()->setSortIndicatorShown(true); - m_treeWidget->header()->setStretchLastSection(true); - m_treeWidget->header()->setSortIndicator(0, Qt::AscendingOrder); - m_treeWidget->setHeaderLabels({Tr::tr("Name"), Tr::tr("Description"), Tr::tr("Shortcut")}); - - m_description = new QLineEdit; - - m_removeButton = new QPushButton(Tr::tr("Remove")); - - m_macroGroup = new QGroupBox(Tr::tr("Macro"), this); - - using namespace Layouting; - - Row { - Tr::tr("Description:"), m_description - }.attachTo(m_macroGroup); - - Column { - Group { - title(Tr::tr("Preferences")), - Row { - m_treeWidget, - Column { m_removeButton, st }, - } - }, - m_macroGroup - }.attachTo(this); - - connect(m_treeWidget, &QTreeWidget::currentItemChanged, - this, &MacroOptionsWidget::changeCurrentItem); - connect(m_removeButton, &QPushButton::clicked, - this, &MacroOptionsWidget::remove); - connect(m_description, &QLineEdit::textChanged, - this, &MacroOptionsWidget::changeDescription); - - initialize(); -} - -MacroOptionsWidget::~MacroOptionsWidget() = default; - -void MacroOptionsWidget::initialize() -{ - m_macroToRemove.clear(); - m_macroToChange.clear(); - m_treeWidget->clear(); - changeCurrentItem(nullptr); - - // Create the treeview - createTable(); -} - -void MacroOptionsWidget::createTable() -{ - QDir dir(MacroManager::macrosDirectory()); - const Utils::Id base = Utils::Id(Constants::PREFIX_MACRO); - for (Macro *macro : MacroManager::macros()) { - QFileInfo fileInfo(macro->fileName()); - if (fileInfo.absoluteDir() == dir.absolutePath()) { - auto macroItem = new QTreeWidgetItem(m_treeWidget); - macroItem->setText(0, macro->displayName()); - macroItem->setText(1, macro->description()); - macroItem->setData(0, NAME_ROLE, macro->displayName()); - macroItem->setData(0, WRITE_ROLE, macro->isWritable()); - - Core::Command *command = - Core::ActionManager::command(base.withSuffix(macro->displayName())); - if (command && command->action()) { - macroItem->setText(2, - command->action()->shortcut().toString(QKeySequence::NativeText)); - } - } - } -} - -void MacroOptionsWidget::changeCurrentItem(QTreeWidgetItem *current) -{ - m_changingCurrent = true; - m_removeButton->setEnabled(current); - m_macroGroup->setEnabled(current); - if (!current) { - m_description->clear(); - } else { - m_description->setText(current->text(1)); - m_description->setEnabled(current->data(0, WRITE_ROLE).toBool()); - } - m_changingCurrent = false; -} - -void MacroOptionsWidget::remove() -{ - QTreeWidgetItem *current = m_treeWidget->currentItem(); - m_macroToRemove.append(current->data(0, NAME_ROLE).toString()); - delete current; -} - -void MacroOptionsWidget::apply() -{ - // Remove macro - for (const QString &name : std::as_const(m_macroToRemove)) { - MacroManager::instance()->deleteMacro(name); - m_macroToChange.remove(name); - } - - // Change macro - for (auto it = m_macroToChange.cbegin(), end = m_macroToChange.cend(); it != end; ++it) - MacroManager::instance()->changeMacro(it.key(), it.value()); - - // Reinitialize the page - initialize(); -} - -void MacroOptionsWidget::changeDescription(const QString &description) -{ - QTreeWidgetItem *current = m_treeWidget->currentItem(); - if (m_changingCurrent || !current) - return; - - QString macroName = current->data(0, NAME_ROLE).toString(); - m_macroToChange[macroName] = description; - current->setText(1, description); - QFont font = current->font(1); - font.setItalic(true); - current->setFont(1, font); -} - -} // Macros::Internal diff --git a/src/plugins/macros/macrooptionswidget.h b/src/plugins/macros/macrooptionswidget.h deleted file mode 100644 index 9810da6dd18..00000000000 --- a/src/plugins/macros/macrooptionswidget.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) 2016 Nicolas Arnaud-Cormos -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -#include -#include - -QT_BEGIN_NAMESPACE -class QGroupBox; -class QLineEdit; -class QPushButton; -class QTreeWidget; -class QTreeWidgetItem; -QT_END_NAMESPACE - -namespace Macros { -namespace Internal { - -class MacroOptionsWidget final : public Core::IOptionsPageWidget -{ - Q_OBJECT - -public: - MacroOptionsWidget(); - ~MacroOptionsWidget() final; - - void initialize(); - - void apply() final; - -private: - void remove(); - void changeCurrentItem(QTreeWidgetItem *current); - - void createTable(); - - void changeDescription(const QString &description); - -private: - QStringList m_macroToRemove; - bool m_changingCurrent = false; - - QMap m_macroToChange; - - QTreeWidget *m_treeWidget; - QPushButton *m_removeButton; - QGroupBox *m_macroGroup; - QLineEdit *m_description; -}; - -} // namespace Internal -} // namespace Macros diff --git a/src/plugins/macros/macros.qbs b/src/plugins/macros/macros.qbs index f6579c7394d..c975f756d7d 100644 --- a/src/plugins/macros/macros.qbs +++ b/src/plugins/macros/macros.qbs @@ -29,8 +29,6 @@ QtcPlugin { "macromanager.h", "macrooptionspage.cpp", "macrooptionspage.h", - "macrooptionswidget.cpp", - "macrooptionswidget.h", "macros.qrc", "macrosconstants.h", "macrosplugin.cpp", diff --git a/src/plugins/mcusupport/CMakeLists.txt b/src/plugins/mcusupport/CMakeLists.txt index b0ff6d8fc45..e2d81ae5a73 100644 --- a/src/plugins/mcusupport/CMakeLists.txt +++ b/src/plugins/mcusupport/CMakeLists.txt @@ -24,7 +24,7 @@ add_qtc_plugin(McuSupport settingshandler.cpp settingshandler.h mcuqmlprojectnode.cpp mcuqmlprojectnode.h mcubuildstep.cpp mcubuildstep.h - dialogs/mcukitcreationdialog.h dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.ui + dialogs/mcukitcreationdialog.cpp dialogs/mcukitcreationdialog.h ) add_subdirectory(test) diff --git a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp index 7327f582115..9702fb77bfb 100644 --- a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp +++ b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "mcukitcreationdialog.h" -#include "ui_mcukitcreationdialog.h" #include "../mcuabstractpackage.h" #include "../mcusupportconstants.h" @@ -10,11 +9,15 @@ #include #include +#include #include -#include +#include #include +#include +#include +#include #include namespace McuSupport::Internal { @@ -24,18 +27,56 @@ McuKitCreationDialog::McuKitCreationDialog(const MessagesList &messages, McuPackagePtr qtMCUPackage, QWidget *parent) : QDialog(parent) - , ui(new Ui::McuKitCreationDialog) , m_messages(messages) { - ui->setupUi(this); - ui->iconLabel->setPixmap(Utils::Icon(":/mcusupport/images/mcusupportdevice.png").pixmap()); - m_previousButton = ui->buttonBox->addButton("<", QDialogButtonBox::ActionRole); - m_nextButton = ui->buttonBox->addButton(">", QDialogButtonBox::ActionRole); - m_fixButton = ui->buttonBox->addButton(Tr::tr("Fix"), QDialogButtonBox::ActionRole); - m_helpButton = ui->buttonBox->addButton(Tr::tr("Help"), QDialogButtonBox::HelpRole); - // prevent clicking the buttons from closing the message box - m_nextButton->disconnect(); - m_previousButton->disconnect(); + resize(500, 300); + setWindowTitle(Tr::tr("Qt for MCUs Kit Creation")); + + m_iconLabel = new QLabel; + m_iconLabel->setAlignment(Qt::AlignTop); + + m_textLabel = new QLabel; + + m_informationLabel = new QLabel; + m_informationLabel->setWordWrap(true); + m_informationLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_informationLabel->setAlignment(Qt::AlignTop); + + m_qtMCUsPathLabel = new QLabel; + + auto line = new QFrame; + line->setFrameShape(QFrame::VLine); + line->setFrameShadow(QFrame::Sunken); + + auto buttonBox = new QDialogButtonBox(Qt::Vertical); + buttonBox->setStandardButtons(QDialogButtonBox::Ignore); + + m_messageCountLabel = new QLabel; + m_messageCountLabel->setAlignment(Qt::AlignCenter); + + using namespace Layouting; + Row { + Column { + Row { + m_iconLabel, + Column { + m_textLabel, + m_informationLabel, + }, + }, + m_qtMCUsPathLabel, + }, + line, + Column { + buttonBox, + m_messageCountLabel, + }, + }.attachTo(this); + + m_previousButton = buttonBox->addButton("<", QDialogButtonBox::ActionRole); + m_nextButton = buttonBox->addButton(">", QDialogButtonBox::ActionRole); + QPushButton *fixButton = buttonBox->addButton(Tr::tr("Fix"), QDialogButtonBox::ActionRole); + QPushButton *helpButton = buttonBox->addButton(Tr::tr("Help"), QDialogButtonBox::HelpRole); if (messages.size() == 1) { m_nextButton->setVisible(false); @@ -46,20 +87,22 @@ McuKitCreationDialog::McuKitCreationDialog(const MessagesList &messages, updateMessage(1); if (qtMCUPackage->isValidStatus()) - ui->qtMCUsPathLabel->setText( + m_qtMCUsPathLabel->setText( Tr::tr("Qt for MCUs path %1").arg(qtMCUPackage->path().toUserOutput())); connect(m_nextButton, &QPushButton::clicked, [=] { updateMessage(1); }); connect(m_previousButton, &QPushButton::clicked, [=] { updateMessage(-1); }); - connect(m_fixButton, &QPushButton::clicked, [=] { + connect(fixButton, &QPushButton::clicked, [=] { // Open the MCU Options widget on the current platform settingsHandler->setInitialPlatformName(m_messages[m_currentIndex].platform); Core::ICore::showOptionsDialog(Constants::SETTINGS_ID); // reset the initial platform name settingsHandler->setInitialPlatformName(""); }); - connect(m_helpButton, &QPushButton::clicked, [] { + connect(helpButton, &QPushButton::clicked, [] { QDesktopServices::openUrl(QUrl("https://doc.qt.io/QtForMCUs/qtul-prerequisites.html")); }); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); } void McuKitCreationDialog::updateMessage(const int inc) @@ -67,30 +110,25 @@ void McuKitCreationDialog::updateMessage(const int inc) m_currentIndex += inc; m_nextButton->setEnabled(m_currentIndex < (m_messages.size() - 1)); m_previousButton->setEnabled(m_currentIndex > 0); - ui->textLabel->setText(QString("%1 %2 : %3") + m_textLabel->setText(QString("%1 %2 : %3") .arg(Tr::tr("Target"), (m_messages[m_currentIndex].status == McuSupportMessage::Warning ? Tr::tr("Warning") : Tr::tr("Error")), m_messages[m_currentIndex].platform)); - ui->iconLabel->setPixmap( + m_iconLabel->setPixmap( QApplication::style() ->standardIcon(m_messages[m_currentIndex].status == McuSupportMessage::Warning ? QStyle::SP_MessageBoxWarning : QStyle::SP_MessageBoxCritical) .pixmap(64, 64)); - ui->informationLabel->setText(QString("%1: %2

%3: %4") + m_informationLabel->setText(QString("%1: %2

%3: %4") .arg(Tr::tr("Package"), m_messages[m_currentIndex].packageName, Tr::tr("Status"), m_messages.at(m_currentIndex).message)); - ui->messageCountLabel->setText(QString("%1 / %2").arg(QString::number(m_currentIndex + 1), + m_messageCountLabel->setText(QString("%1 / %2").arg(QString::number(m_currentIndex + 1), QString::number(m_messages.size()))); } -McuKitCreationDialog::~McuKitCreationDialog() -{ - delete ui; -} - } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h index 7164c6d0efb..6caae59678f 100644 --- a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h +++ b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.h @@ -7,11 +7,11 @@ #include "../settingshandler.h" #include -#include -namespace Ui { -class McuKitCreationDialog; -} +QT_BEGIN_NAMESPACE +class QLabel; +class QPushButton; +QT_END_NAMESPACE namespace McuSupport::Internal { @@ -24,18 +24,19 @@ public: const SettingsHandler::Ptr &settingsHandler, McuPackagePtr qtMCUPackage, QWidget *parent = nullptr); - ~McuKitCreationDialog(); private slots: void updateMessage(const int inc); private: - Ui::McuKitCreationDialog *ui; int m_currentIndex = -1; + QLabel *m_iconLabel; + QLabel *m_textLabel; + QLabel *m_informationLabel; + QLabel *m_qtMCUsPathLabel; + QLabel *m_messageCountLabel; QPushButton *m_previousButton; QPushButton *m_nextButton; - QPushButton *m_helpButton; - QPushButton *m_fixButton; const MessagesList &m_messages; }; } // namespace McuSupport::Internal diff --git a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.ui b/src/plugins/mcusupport/dialogs/mcukitcreationdialog.ui deleted file mode 100644 index f6194bbe6e3..00000000000 --- a/src/plugins/mcusupport/dialogs/mcukitcreationdialog.ui +++ /dev/null @@ -1,154 +0,0 @@ - - - McuKitCreationDialog - - - - 0 - 0 - 349 - 200 - - - - QtMCUs Kit Creation - - - true - - - - - - QtMCUs path: - - - - - - - Qt::Vertical - - - - - - - 1/3 - - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - QDialogButtonBox::Ignore - - - - - - - Dialog text - - - Qt::RichText - - - true - - - - - - - - 64 - 64 - - - - - 70 - 1000 - - - - - - - false - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - TextLabel - - - Qt::RichText - - - - - - - - - - - buttonBox - accepted() - McuKitCreationDialog - accept() - - - 339 - 23 - - - 157 - 199 - - - - - buttonBox - rejected() - McuKitCreationDialog - reject() - - - 339 - 29 - - - 286 - 199 - - - - - diff --git a/src/plugins/mcusupport/mcusupport.qbs b/src/plugins/mcusupport/mcusupport.qbs index 7b686075cac..173fe55e6e8 100644 --- a/src/plugins/mcusupport/mcusupport.qbs +++ b/src/plugins/mcusupport/mcusupport.qbs @@ -58,7 +58,6 @@ QtcPlugin { "mcuhelpers.h", "settingshandler.h", "settingshandler.cpp", - "dialogs/mcukitcreationdialog.ui", "dialogs/mcukitcreationdialog.h", "dialogs/mcukitcreationdialog.cpp", ] diff --git a/src/plugins/mcusupport/mcusupport.qrc b/src/plugins/mcusupport/mcusupport.qrc index abbfb969e3a..7aeb7b93873 100644 --- a/src/plugins/mcusupport/mcusupport.qrc +++ b/src/plugins/mcusupport/mcusupport.qrc @@ -20,5 +20,6 @@ wizards/qmlproject/module.qmlproject.tpl wizards/qmlproject/component.qml.tpl wizards/qmlproject/wizard.json + wizards/qmlproject/Qul.cmake diff --git a/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt b/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt index 49a4a8f949f..47eed40c785 100644 --- a/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt +++ b/src/plugins/mcusupport/wizards/qmlproject/CMakeLists.txt @@ -4,5 +4,4 @@ project(%{CorrectedProjectName} VERSION 0.0.1 LANGUAGES C CXX ASM) find_package(Qul) -qul_add_target(%{CorrectedProjectName} QML_PROJECT %{QmlProjectFile} GENERATE_ENTRYPOINT) -app_target_setup_os(%{CorrectedProjectName}) +add_subdirectory(qmlproject) diff --git a/src/plugins/mcusupport/wizards/qmlproject/Qul.cmake b/src/plugins/mcusupport/wizards/qmlproject/Qul.cmake new file mode 100644 index 00000000000..1ece8f48d6d --- /dev/null +++ b/src/plugins/mcusupport/wizards/qmlproject/Qul.cmake @@ -0,0 +1,3 @@ + +qul_add_target(%{CorrectedProjectName} QML_PROJECT %{QmlProjectFile} GENERATE_ENTRYPOINT) +app_target_setup_os(%{CorrectedProjectName}) diff --git a/src/plugins/mcusupport/wizards/qmlproject/wizard.json b/src/plugins/mcusupport/wizards/qmlproject/wizard.json index 4fcf4f6fd31..9c1e036e910 100644 --- a/src/plugins/mcusupport/wizards/qmlproject/wizard.json +++ b/src/plugins/mcusupport/wizards/qmlproject/wizard.json @@ -12,6 +12,7 @@ "options": [ + { "key": "QmlProjectDirectory", "value": "%{ProjectDirectory}/qmlproject"}, { "key": "CorrectedProjectName", "value": "%{JS: '%{ProjectName}'.replace(/-/g, '_')}"}, { "key": "MainQmlFile", "value": "%{CorrectedProjectName}.qml" }, { "key": "QmlProjectFile", "value": "%{CorrectedProjectName}.qmlproject" }, @@ -55,49 +56,54 @@ "source": "CMakeLists.txt", "openAsProject": true }, + { + "source": "Qul.cmake", + "target": "%{QmlProjectDirectory}/CMakeLists.txt", + "openInEditor": false + }, { "source": "BackendObject.h", - "target": "%{ProjectDirectory}/src/%{InterfaceFile}", + "target": "%{QmlProjectDirectory}/src/%{InterfaceFile}", "openInEditor": true }, { "source": "component.qml.tpl", - "target": "%{ProjectDirectory}/imports/CustomModule/%{QmlComponent}", + "target": "%{QmlProjectDirectory}/imports/CustomModule/%{QmlComponent}", "openInEditor": true }, { "source": "module.qmlproject.tpl", - "target": "%{ProjectDirectory}/imports/CustomModule/%{ModuleFile}", + "target": "%{QmlProjectDirectory}/imports/CustomModule/%{ModuleFile}", "openInEditor": true }, { "source": "project.qmlproject.tpl", - "target": "%{ProjectDirectory}/%{QmlProjectFile}", + "target": "%{QmlProjectDirectory}/%{QmlProjectFile}", "openInEditor": true }, { "source": "main.qml.tpl", - "target": "%{ProjectDirectory}/%{MainQmlFile}", + "target": "%{QmlProjectDirectory}/%{MainQmlFile}", "openInEditor": true }, { "source": "../icon.png", - "target": "%{ProjectDirectory}/images/icon.png", + "target": "%{QmlProjectDirectory}/images/icon.png", "isBinary": true }, { "source": "DejaVuSansMono.ttf", - "target": "%{ProjectDirectory}/fonts/DejaVuSansMono.ttf", + "target": "%{QmlProjectDirectory}/fonts/DejaVuSansMono.ttf", "isBinary": true }, { "source": "LICENSE", - "target": "%{ProjectDirectory}/fonts/LICENSE", + "target": "%{QmlProjectDirectory}/fonts/LICENSE", "isBinary": true }, { "source": "translation.nb_NO.ts", - "target": "%{ProjectDirectory}/translations/%{TsFile}", + "target": "%{QmlProjectDirectory}/translations/%{TsFile}", "openInEditor": false }, { diff --git a/src/plugins/mercurial/mercurialsettings.cpp b/src/plugins/mercurial/mercurialsettings.cpp index 0ced239122e..0b493e277bf 100644 --- a/src/plugins/mercurial/mercurialsettings.cpp +++ b/src/plugins/mercurial/mercurialsettings.cpp @@ -48,10 +48,10 @@ MercurialSettings::MercurialSettings() diffIgnoreBlankLines.setSettingsKey("diffIgnoreBlankLines"); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Group { title(Tr::tr("Configuration")), Row { binaryPath } @@ -71,7 +71,7 @@ MercurialSettings::MercurialSettings() }, st - }.attachTo(widget); + }; }); } diff --git a/src/plugins/mesonprojectmanager/CMakeLists.txt b/src/plugins/mesonprojectmanager/CMakeLists.txt index d4d7420d7b0..3c541a1219e 100644 --- a/src/plugins/mesonprojectmanager/CMakeLists.txt +++ b/src/plugins/mesonprojectmanager/CMakeLists.txt @@ -75,8 +75,6 @@ add_qtc_plugin(MesonProjectManager toolssettingsaccessor.h toolssettingspage.cpp toolssettingspage.h - toolssettingswidget.cpp - toolssettingswidget.h tooltreeitem.cpp tooltreeitem.h toolwrapper.cpp diff --git a/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp b/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp index 13617fa0140..6d3e5f20c4f 100644 --- a/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp +++ b/src/plugins/mesonprojectmanager/mesonbuildsystem.cpp @@ -192,7 +192,7 @@ void MesonBuildSystem::init() bool MesonBuildSystem::parseProject() { QTC_ASSERT(buildConfiguration(), return false); - if (!isSetup(buildConfiguration()->buildDirectory()) && Settings::instance()->autorunMeson.value()) + if (!isSetup(buildConfiguration()->buildDirectory()) && settings().autorunMeson()) return configure(); LEAVE_IF_BUSY(); LOCK(); diff --git a/src/plugins/mesonprojectmanager/mesonpluginconstants.h b/src/plugins/mesonprojectmanager/mesonpluginconstants.h index 995e41cbb59..108c125f502 100644 --- a/src/plugins/mesonprojectmanager/mesonpluginconstants.h +++ b/src/plugins/mesonprojectmanager/mesonpluginconstants.h @@ -18,7 +18,6 @@ const char PARAMETERS_KEY[] = "MesonProjectManager.BuildConfig.Parameters"; // Settings page namespace SettingsPage { -const char GENERAL_ID[] = "A.MesonProjectManager.SettingsPage.General"; const char TOOLS_ID[] = "Z.MesonProjectManager.SettingsPage.Tools"; const char CATEGORY[] = "Z.Meson"; } // namespace SettingsPage diff --git a/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs b/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs index 9de8b4ccde9..c721ab2d207 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs +++ b/src/plugins/mesonprojectmanager/mesonprojectmanager.qbs @@ -95,8 +95,6 @@ Project { "toolssettingsaccessor.h", "toolssettingspage.cpp", "toolssettingspage.h", - "toolssettingswidget.cpp", - "toolssettingswidget.h", "tooltreeitem.cpp", "tooltreeitem.h", "versionhelper.h", diff --git a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp index f15f65c4afd..87beb589bbd 100644 --- a/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp +++ b/src/plugins/mesonprojectmanager/mesonprojectplugin.cpp @@ -45,7 +45,7 @@ public: ~MesonProjectPluginPrivate() {} private: - GeneralSettingsPage m_generalSettingsPage; + Settings m_settings; ToolsSettingsPage m_toolslSettingsPage; ToolsSettingsAccessor m_toolsSettings; MesonToolKitAspect m_mesonKitAspect; @@ -60,7 +60,6 @@ private: void saveAll() { m_toolsSettings.saveMesonTools(MesonTools::tools(), ICore::dialogParent()); - Settings::instance()->writeSettings(ICore::settings()); } }; @@ -76,7 +75,6 @@ void MesonProjectPlugin::initialize() ProjectManager::registerProjectType(Constants::Project::MIMETYPE); FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson.build"); FileIconProvider::registerIconOverlayForFilename(Constants::Icons::MESON, "meson_options.txt"); - Settings::instance()->readSettings(ICore::settings()); } } // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/ninjabuildstep.cpp b/src/plugins/mesonprojectmanager/ninjabuildstep.cpp index 325056d4918..ac41aeecfaa 100644 --- a/src/plugins/mesonprojectmanager/ninjabuildstep.cpp +++ b/src/plugins/mesonprojectmanager/ninjabuildstep.cpp @@ -44,7 +44,7 @@ NinjaBuildStep::NinjaBuildStep(BuildStepList *bsl, Id id) setUseEnglishOutput(); connect(target(), &ProjectExplorer::Target::parsingFinished, this, &NinjaBuildStep::update); - connect(&Settings::instance()->verboseNinja, &BaseAspect::changed, + connect(&settings().verboseNinja, &BaseAspect::changed, this, &NinjaBuildStep::commandChanged); } @@ -119,17 +119,15 @@ QWidget *NinjaBuildStep::createConfigWidget() // --verbose is only supported since // https://github.com/ninja-build/ninja/commit/bf7517505ad1def03e13bec2b4131399331bc5c4 // TODO check when to switch back to --verbose -Utils::CommandLine NinjaBuildStep::command() +CommandLine NinjaBuildStep::command() { - Utils::CommandLine cmd = [this] { - auto tool = NinjaToolKitAspect::ninjaTool(kit()); - if (tool) - return Utils::CommandLine{tool->exe()}; - return Utils::CommandLine{}; - }(); + CommandLine cmd; + if (auto tool = NinjaToolKitAspect::ninjaTool(kit())) + cmd.setExecutable(tool->exe()); + if (!m_commandArgs.isEmpty()) - cmd.addArgs(m_commandArgs, Utils::CommandLine::RawType::Raw); - if (Settings::instance()->verboseNinja.value()) + cmd.addArgs(m_commandArgs, CommandLine::RawType::Raw); + if (settings().verboseNinja()) cmd.addArg("-v"); cmd.addArg(m_targetName); return cmd; diff --git a/src/plugins/mesonprojectmanager/settings.cpp b/src/plugins/mesonprojectmanager/settings.cpp index 6a211a375a7..e1fe290bc5c 100644 --- a/src/plugins/mesonprojectmanager/settings.cpp +++ b/src/plugins/mesonprojectmanager/settings.cpp @@ -8,13 +8,26 @@ #include -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { + +static Settings *s_instance; + +Settings &settings() +{ + return *s_instance; +} Settings::Settings() { + s_instance = this; + setSettingsGroup("MesonProjectManager"); - setAutoApply(false); + + setId("A.MesonProjectManager.SettingsPage.General"); + setDisplayName(Tr::tr("General")); + setDisplayCategory("Meson"); + setCategory(Constants::SettingsPage::CATEGORY); + setCategoryIconPath(Constants::Icons::MESON_BW); autorunMeson.setSettingsKey("meson.autorun"); autorunMeson.setLabelText(Tr::tr("Autorun Meson")); @@ -24,36 +37,16 @@ Settings::Settings() verboseNinja.setLabelText(Tr::tr("Ninja verbose mode")); verboseNinja.setToolTip(Tr::tr("Enables verbose mode by default when invoking Ninja.")); - registerAspect(&autorunMeson); - registerAspect(&verboseNinja); -} - -Settings *Settings::instance() -{ - static Settings m_settings; - return &m_settings; -} - -GeneralSettingsPage::GeneralSettingsPage() -{ - setId(Constants::SettingsPage::GENERAL_ID); - setDisplayName(Tr::tr("General")); - setDisplayCategory("Meson"); - setCategory(Constants::SettingsPage::CATEGORY); - setCategoryIconPath(Constants::Icons::MESON_BW); - setSettings(Settings::instance()); - - setLayouter([](QWidget *widget) { - Settings &s = *Settings::instance(); + setLayouter([this] { using namespace Layouting; - - Column { - s.autorunMeson, - s.verboseNinja, + return Column { + autorunMeson, + verboseNinja, st, - }.attachTo(widget); + }; }); + + readSettings(); } -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/settings.h b/src/plugins/mesonprojectmanager/settings.h index 2e23d0f4482..34c2e9e97d9 100644 --- a/src/plugins/mesonprojectmanager/settings.h +++ b/src/plugins/mesonprojectmanager/settings.h @@ -7,25 +7,17 @@ #include -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { -class Settings : public Utils::AspectContainer +class Settings : public Core::PagedSettings { public: Settings(); - static Settings *instance(); - - Utils::BoolAspect autorunMeson; - Utils::BoolAspect verboseNinja; + Utils::BoolAspect autorunMeson{this}; + Utils::BoolAspect verboseNinja{this}; }; -class GeneralSettingsPage final : public Core::IOptionsPage -{ -public: - GeneralSettingsPage(); -}; +Settings &settings(); -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/toolssettingspage.cpp b/src/plugins/mesonprojectmanager/toolssettingspage.cpp index 4ac576b365e..67ecd99aacc 100644 --- a/src/plugins/mesonprojectmanager/toolssettingspage.cpp +++ b/src/plugins/mesonprojectmanager/toolssettingspage.cpp @@ -5,10 +5,120 @@ #include "mesonpluginconstants.h" #include "mesonprojectmanagertr.h" -#include "toolssettingswidget.h" +#include "toolitemsettings.h" +#include "toolsmodel.h" +#include "tooltreeitem.h" + +#include +#include + +#include +#include +#include + +using namespace Utils; + +namespace MesonProjectManager::Internal { + +class ToolsSettingsWidget final : public Core::IOptionsPageWidget +{ +public: + ToolsSettingsWidget(); + +private: + void apply() final { m_model.apply(); } + + void cloneMesonTool(); + void removeMesonTool(); + void currentMesonToolChanged(const QModelIndex &newCurrent); + + ToolsModel m_model; + ToolItemSettings *m_itemSettings; + ToolTreeItem *m_currentItem = nullptr; + + QTreeView *m_mesonList; + DetailsWidget *m_mesonDetails; + QPushButton *m_cloneButton; + QPushButton *m_removeButton; +}; + +ToolsSettingsWidget::ToolsSettingsWidget() +{ + m_mesonList = new QTreeView; + m_mesonList->setModel(&m_model); + m_mesonList->expandAll(); + m_mesonList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); + m_mesonList->header()->setSectionResizeMode(1, QHeaderView::Stretch); + + m_itemSettings = new ToolItemSettings; + + m_mesonDetails = new DetailsWidget; + m_mesonDetails->setState(DetailsWidget::NoSummary); + m_mesonDetails->setVisible(false); + m_mesonDetails->setWidget(m_itemSettings); + + auto addButton = new QPushButton(Tr::tr("Add")); + + m_cloneButton = new QPushButton(Tr::tr("Clone")); + m_cloneButton->setEnabled(false); + + m_removeButton = new QPushButton(Tr::tr("Remove")); + m_removeButton->setEnabled(false); + + auto makeDefaultButton = new QPushButton(Tr::tr("Make Default")); + makeDefaultButton->setEnabled(false); + makeDefaultButton->setVisible(false); + makeDefaultButton->setToolTip(Tr::tr("Set as the default Meson executable to use " + "when creating a new kit or when no value is set.")); + + using namespace Layouting; + + Row { + Column { + m_mesonList, + m_mesonDetails + }, + Column { + addButton, + m_cloneButton, + m_removeButton, + makeDefaultButton, + st + } + }.attachTo(this); + + connect(m_mesonList->selectionModel(), &QItemSelectionModel::currentChanged, + this, &ToolsSettingsWidget::currentMesonToolChanged); + connect(m_itemSettings, &ToolItemSettings::applyChanges, &m_model, &ToolsModel::updateItem); + + connect(addButton, &QPushButton::clicked, &m_model, &ToolsModel::addMesonTool); + connect(m_cloneButton, &QPushButton::clicked, this, &ToolsSettingsWidget::cloneMesonTool); + connect(m_removeButton, &QPushButton::clicked, this, &ToolsSettingsWidget::removeMesonTool); +} + +void ToolsSettingsWidget::cloneMesonTool() +{ + if (m_currentItem) { + auto newItem = m_model.cloneMesonTool(m_currentItem); + m_mesonList->setCurrentIndex(newItem->index()); + } +} + +void ToolsSettingsWidget::removeMesonTool() +{ + if (m_currentItem) + m_model.removeMesonTool(m_currentItem); +} + +void ToolsSettingsWidget::currentMesonToolChanged(const QModelIndex &newCurrent) +{ + m_currentItem = m_model.mesoneToolTreeItem(newCurrent); + m_itemSettings->load(m_currentItem); + m_mesonDetails->setVisible(m_currentItem); + m_cloneButton->setEnabled(m_currentItem); + m_removeButton->setEnabled(m_currentItem && !m_currentItem->isAutoDetected()); +} -namespace MesonProjectManager { -namespace Internal { ToolsSettingsPage::ToolsSettingsPage() { @@ -18,5 +128,4 @@ ToolsSettingsPage::ToolsSettingsPage() setWidgetCreator([]() { return new ToolsSettingsWidget; }); } -} // namespace Internal } // namespace MesonProjectManager diff --git a/src/plugins/mesonprojectmanager/toolssettingspage.h b/src/plugins/mesonprojectmanager/toolssettingspage.h index de62b94d918..815120c30f0 100644 --- a/src/plugins/mesonprojectmanager/toolssettingspage.h +++ b/src/plugins/mesonprojectmanager/toolssettingspage.h @@ -5,8 +5,7 @@ #include -namespace MesonProjectManager { -namespace Internal { +namespace MesonProjectManager::Internal { class ToolsSettingsPage final : public Core::IOptionsPage { @@ -14,5 +13,4 @@ public: ToolsSettingsPage(); }; -} // namespace Internal -} // namespace MesonProjectManager +} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/toolssettingswidget.cpp b/src/plugins/mesonprojectmanager/toolssettingswidget.cpp deleted file mode 100644 index ef30d79ae43..00000000000 --- a/src/plugins/mesonprojectmanager/toolssettingswidget.cpp +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "toolssettingswidget.h" - -#include "mesonprojectmanagertr.h" -#include "toolsmodel.h" -#include "tooltreeitem.h" - -#include -#include - -#include -#include -#include - -using namespace Utils; - -namespace MesonProjectManager::Internal { - -ToolsSettingsWidget::ToolsSettingsWidget() - : Core::IOptionsPageWidget() -{ - m_mesonList = new QTreeView; - m_mesonList->setModel(&m_model); - m_mesonList->expandAll(); - m_mesonList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); - m_mesonList->header()->setSectionResizeMode(1, QHeaderView::Stretch); - - m_itemSettings = new ToolItemSettings; - - m_mesonDetails = new DetailsWidget; - m_mesonDetails->setState(DetailsWidget::NoSummary); - m_mesonDetails->setVisible(false); - m_mesonDetails->setWidget(m_itemSettings); - - auto addButton = new QPushButton(Tr::tr("Add")); - - m_cloneButton = new QPushButton(Tr::tr("Clone")); - m_cloneButton->setEnabled(false); - - m_removeButton = new QPushButton(Tr::tr("Remove")); - m_removeButton->setEnabled(false); - - auto makeDefaultButton = new QPushButton(Tr::tr("Make Default")); - makeDefaultButton->setEnabled(false); - makeDefaultButton->setVisible(false); - makeDefaultButton->setToolTip(Tr::tr("Set as the default Meson executable to use " - "when creating a new kit or when no value is set.")); - - using namespace Layouting; - - Row { - Column { - m_mesonList, - m_mesonDetails - }, - Column { - addButton, - m_cloneButton, - m_removeButton, - makeDefaultButton, - st - } - }.attachTo(this); - - connect(m_mesonList->selectionModel(), &QItemSelectionModel::currentChanged, - this, &ToolsSettingsWidget::currentMesonToolChanged); - connect(m_itemSettings, &ToolItemSettings::applyChanges, &m_model, &ToolsModel::updateItem); - - connect(addButton, &QPushButton::clicked, &m_model, &ToolsModel::addMesonTool); - connect(m_cloneButton, &QPushButton::clicked, this, &ToolsSettingsWidget::cloneMesonTool); - connect(m_removeButton, &QPushButton::clicked, this, &ToolsSettingsWidget::removeMesonTool); -} - -ToolsSettingsWidget::~ToolsSettingsWidget() = default; - -void ToolsSettingsWidget::cloneMesonTool() -{ - if (m_currentItem) { - auto newItem = m_model.cloneMesonTool(m_currentItem); - m_mesonList->setCurrentIndex(newItem->index()); - } -} - -void ToolsSettingsWidget::removeMesonTool() -{ - if (m_currentItem) { - m_model.removeMesonTool(m_currentItem); - } -} - -void ToolsSettingsWidget::currentMesonToolChanged(const QModelIndex &newCurrent) -{ - m_currentItem = m_model.mesoneToolTreeItem(newCurrent); - m_itemSettings->load(m_currentItem); - m_mesonDetails->setVisible(m_currentItem); - m_cloneButton->setEnabled(m_currentItem); - m_removeButton->setEnabled(m_currentItem && !m_currentItem->isAutoDetected()); -} - -void ToolsSettingsWidget::apply() -{ - m_model.apply(); -} - -} // MesonProjectManager::Internal diff --git a/src/plugins/mesonprojectmanager/toolssettingswidget.h b/src/plugins/mesonprojectmanager/toolssettingswidget.h deleted file mode 100644 index 1c39b7460ab..00000000000 --- a/src/plugins/mesonprojectmanager/toolssettingswidget.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (C) 2020 Alexis Jeandet. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include "toolitemsettings.h" -#include "toolsmodel.h" - -#include - -namespace Utils { class DetailsWidget; } - -namespace MesonProjectManager::Internal { - -class ToolTreeItem; - -class ToolsSettingsWidget final : public Core::IOptionsPageWidget -{ -public: - explicit ToolsSettingsWidget(); - ~ToolsSettingsWidget(); - -private: - void apply() final; - - void cloneMesonTool(); - void removeMesonTool(); - void currentMesonToolChanged(const QModelIndex &newCurrent); - - ToolsModel m_model; - ToolItemSettings *m_itemSettings; - ToolTreeItem *m_currentItem = nullptr; - - QTreeView *m_mesonList; - Utils::DetailsWidget *m_mesonDetails; - QPushButton *m_cloneButton; - QPushButton *m_removeButton; -}; - -} // MesonProjectManager::Internal diff --git a/src/plugins/nim/project/nimbletaskstep.cpp b/src/plugins/nim/project/nimbletaskstep.cpp index e072b9422ee..1885a9daa15 100644 --- a/src/plugins/nim/project/nimbletaskstep.cpp +++ b/src/plugins/nim/project/nimbletaskstep.cpp @@ -47,8 +47,8 @@ private: bool validate(); - StringAspect *m_taskName = nullptr; - StringAspect *m_taskArgs = nullptr; + StringAspect m_taskName{this}; + StringAspect m_taskArgs{this}; QStandardItemModel m_tasks; bool m_selecting = false; @@ -62,19 +62,17 @@ NimbleTaskStep::NimbleTaskStep(BuildStepList *parentList, Id id) setDisplayName(display); setCommandLineProvider([this] { - QString args = m_taskName->value() + " " + m_taskArgs->value(); + QString args = m_taskName() + " " + m_taskArgs(); return CommandLine(Nim::nimblePathFromKit(target()->kit()), args, CommandLine::Raw); }); setWorkingDirectoryProvider([this] { return project()->projectDirectory(); }); - m_taskName = addAspect(); - m_taskName->setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKNAME); + m_taskName.setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKNAME); - m_taskArgs = addAspect(); - m_taskArgs->setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKARGS); - m_taskArgs->setDisplayStyle(StringAspect::LineEditDisplay); - m_taskArgs->setLabelText(Tr::tr("Task arguments:")); + m_taskArgs.setSettingsKey(Constants::C_NIMBLETASKSTEP_TASKARGS); + m_taskArgs.setDisplayStyle(StringAspect::LineEditDisplay); + m_taskArgs.setLabelText(Tr::tr("Task arguments:")); } QWidget *NimbleTaskStep::createConfigWidget() @@ -96,15 +94,14 @@ QWidget *NimbleTaskStep::createConfigWidget() QTC_ASSERT(buildSystem, return widget); updateTaskList(); - selectTask(m_taskName->value()); + selectTask(m_taskName()); connect(&m_tasks, &QAbstractItemModel::dataChanged, this, &NimbleTaskStep::onDataChanged); connect(buildSystem, &NimbleBuildSystem::tasksChanged, this, &NimbleTaskStep::updateTaskList); setSummaryUpdater([this] { - return QString("%1: nimble %2 %3") - .arg(displayName(), m_taskName->value(), m_taskArgs->value()); + return QString("%1: nimble %2 %3").arg(displayName(), m_taskName(), m_taskArgs()); }); return widget; @@ -199,24 +196,24 @@ void NimbleTaskStep::uncheckedAllDifferentFrom(QStandardItem *toSkip) void NimbleTaskStep::setTaskName(const QString &name) { - if (m_taskName->value() == name) + if (m_taskName() == name) return; - m_taskName->setValue(name); + m_taskName.setValue(name); selectTask(name); } bool NimbleTaskStep::validate() { - if (m_taskName->value().isEmpty()) + if (m_taskName().isEmpty()) return true; auto nimbleBuildSystem = dynamic_cast(buildSystem()); QTC_ASSERT(nimbleBuildSystem, return false); - auto matchName = [this](const NimbleTask &task) { return task.name == m_taskName->value(); }; + auto matchName = [this](const NimbleTask &task) { return task.name == m_taskName(); }; if (!Utils::contains(nimbleBuildSystem->tasks(), matchName)) { emit addTask(BuildSystemTask(Task::Error, Tr::tr("Nimble task %1 not found.") - .arg(m_taskName->value()))); + .arg(m_taskName()))); emitFaultyConfigurationMessage(); return false; } diff --git a/src/plugins/nim/settings/nimsettings.cpp b/src/plugins/nim/settings/nimsettings.cpp index fdf44ccd366..fac77109fde 100644 --- a/src/plugins/nim/settings/nimsettings.cpp +++ b/src/plugins/nim/settings/nimsettings.cpp @@ -33,15 +33,15 @@ NimSettings::NimSettings() setDisplayCategory(Tr::tr("Nim")); setCategoryIconPath(":/nim/images/settingscategory_nim.png"); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Group { title("Nimsuggest"), Column { nimSuggestPath } }, st - }.attachTo(widget); + }; }); // code style factory diff --git a/src/plugins/perforce/perforceplugin.cpp b/src/plugins/perforce/perforceplugin.cpp index 5a7e74450dd..ae61d12d342 100644 --- a/src/plugins/perforce/perforceplugin.cpp +++ b/src/plugins/perforce/perforceplugin.cpp @@ -226,6 +226,10 @@ public: void discardCommit() override { cleanCommitMessageFile(); } QString commitDisplayName() const final; + QString commitAbortTitle() const final; + QString commitAbortMessage() const final; + QString commitErrorMessage(const QString &error) const final; + void p4Diff(const PerforceDiffParameters &p); void openCurrentFile(); @@ -1443,9 +1447,27 @@ void PerforceDiffConfig::triggerReRun() QString PerforcePluginPrivate::commitDisplayName() const { + //: Name of the "commit" action of the VCS return Tr::tr("Submit"); } +QString PerforcePluginPrivate::commitAbortTitle() const +{ + return Tr::tr("Close Submit Editor"); +} + +QString PerforcePluginPrivate::commitAbortMessage() const +{ + return Tr::tr("Closing this editor will abort the submit."); +} + +QString PerforcePluginPrivate::commitErrorMessage(const QString &error) const +{ + if (error.isEmpty()) + return Tr::tr("Cannot submit."); + return Tr::tr("Cannot submit: %1.").arg(error); +} + void PerforcePluginPrivate::p4Diff(const FilePath &workingDir, const QStringList &files) { PerforceDiffParameters p; diff --git a/src/plugins/perforce/perforcesettings.cpp b/src/plugins/perforce/perforcesettings.cpp index ee02465530e..47ef5a03b35 100644 --- a/src/plugins/perforce/perforcesettings.cpp +++ b/src/plugins/perforce/perforcesettings.cpp @@ -216,7 +216,7 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings) setCategory(VcsBase::Constants::VCS_SETTINGS_CATEGORY); setSettings(settings); - setLayouter([settings](QWidget *widget) { + setLayouter([settings] { PerforceSettings &s = *settings; using namespace Layouting; @@ -224,7 +224,8 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings) errorLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); errorLabel->setFilled(true); auto testButton = new QPushButton(Tr::tr("Test")); - QObject::connect(testButton, &QPushButton::clicked, widget, [settings, errorLabel, testButton] { + QObject::connect(testButton, &QPushButton::clicked, errorLabel, + [settings, errorLabel, testButton] { testButton->setEnabled(false); auto checker = new PerforceChecker(errorLabel); checker->setUseOverideCursor(true); @@ -271,13 +272,13 @@ PerforceSettingsPage::PerforceSettingsPage(PerforceSettings *settings) } }; - Column { + return Column { config, environment, misc, Row { errorLabel, st, testButton }, st - }.attachTo(widget); + }; }); } diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 00ea43530b9..d16e868a5cc 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -134,8 +134,7 @@ add_qtc_plugin(ProjectExplorer projectexplorerconstants.cpp projectexplorerconstants.h projectexplorericons.cpp projectexplorericons.h - projectexplorersettings.h - projectexplorersettingspage.cpp projectexplorersettingspage.h + projectexplorersettings.cpp projectexplorersettings.h projectexplorertr.h projectfilewizardextension.cpp projectfilewizardextension.h projectimporter.cpp projectimporter.h diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp index 6284a960417..28169513cb7 100644 --- a/src/plugins/projectexplorer/buildmanager.cpp +++ b/src/plugins/projectexplorer/buildmanager.cpp @@ -442,16 +442,6 @@ int BuildManager::getErrorTaskCount() return errors; } -void BuildManager::setCompileOutputSettings(const CompileOutputSettings &settings) -{ - d->m_outputWindow->setSettings(settings); -} - -const CompileOutputSettings &BuildManager::compileOutputSettings() -{ - return d->m_outputWindow->settings(); -} - QString BuildManager::displayNameForStepId(Id stepId) { if (stepId == Constants::BUILDSTEPS_CLEAN) { @@ -853,7 +843,7 @@ bool BuildManager::buildLists(const QList bsls, const QStringLi return false; } - if (d->m_outputWindow->settings().popUp) + if (CompileOutputSettings::instance().popUp()) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); startBuildQueue(); return true; @@ -866,7 +856,7 @@ void BuildManager::appendStep(BuildStep *step, const QString &name) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); return; } - if (d->m_outputWindow->settings().popUp) + if (CompileOutputSettings::instance().popUp()) d->m_outputWindow->popup(IOutputPane::NoModeSwitch); startBuildQueue(); } diff --git a/src/plugins/projectexplorer/buildmanager.h b/src/plugins/projectexplorer/buildmanager.h index d7d2c91562a..d9478db4093 100644 --- a/src/plugins/projectexplorer/buildmanager.h +++ b/src/plugins/projectexplorer/buildmanager.h @@ -10,12 +10,10 @@ #include namespace ProjectExplorer { -class RunConfiguration; -namespace Internal { class CompileOutputSettings; } - -class Task; class Project; +class RunConfiguration; +class Task; enum class BuildForRunConfigStatus { Building, NotBuilding, BuildFailed }; enum class ConfigSelection { All, Active }; @@ -66,9 +64,6 @@ public: static int getErrorTaskCount(); - static void setCompileOutputSettings(const Internal::CompileOutputSettings &settings); - static const Internal::CompileOutputSettings &compileOutputSettings(); - static QString displayNameForStepId(Utils::Id stepId); public slots: 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()); diff --git a/src/plugins/projectexplorer/buildpropertiessettings.cpp b/src/plugins/projectexplorer/buildpropertiessettings.cpp index b409a2a2e2c..6df8fdd328e 100644 --- a/src/plugins/projectexplorer/buildpropertiessettings.cpp +++ b/src/plugins/projectexplorer/buildpropertiessettings.cpp @@ -16,8 +16,8 @@ namespace ProjectExplorer { const char DEFAULT_BUILD_DIRECTORY_TEMPLATE[] = "../%{JS: Util.asciify(\"build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}\")}"; -BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect() - : TriStateAspect{Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default")} +BuildPropertiesSettings::BuildTriStateAspect::BuildTriStateAspect(AspectContainer *container) + : TriStateAspect(container, Tr::tr("Enable"), Tr::tr("Disable"), Tr::tr("Use Project Default")) {} BuildPropertiesSettings::BuildPropertiesSettings() @@ -29,10 +29,10 @@ BuildPropertiesSettings::BuildPropertiesSettings() setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); setSettings(this); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Form { buildDirectoryTemplate, br, separateDebugInfo, br, @@ -40,10 +40,9 @@ BuildPropertiesSettings::BuildPropertiesSettings() qtQuickCompiler }, st - }.attachTo(widget); + }; }); - registerAspect(&buildDirectoryTemplate); buildDirectoryTemplate.setDisplayStyle(StringAspect::LineEditDisplay); buildDirectoryTemplate.setSettingsKey("Directories/BuildDirectory.TemplateV2"); buildDirectoryTemplate.setDefaultValue(DEFAULT_BUILD_DIRECTORY_TEMPLATE); @@ -51,15 +50,12 @@ BuildPropertiesSettings::BuildPropertiesSettings() buildDirectoryTemplate.setUseGlobalMacroExpander(); buildDirectoryTemplate.setUseResetButton(); - registerAspect(&separateDebugInfo); separateDebugInfo.setSettingsKey("ProjectExplorer/Settings/SeparateDebugInfo"); separateDebugInfo.setLabelText(Tr::tr("Separate debug info:")); - registerAspect(&qmlDebugging); qmlDebugging.setSettingsKey("ProjectExplorer/Settings/QmlDebugging"); qmlDebugging.setLabelText(Tr::tr("QML debugging:")); - registerAspect(&qtQuickCompiler); qtQuickCompiler.setSettingsKey("ProjectExplorer/Settings/QtQuickCompiler"); qtQuickCompiler.setLabelText(Tr::tr("Use qmlcachegen:")); diff --git a/src/plugins/projectexplorer/buildpropertiessettings.h b/src/plugins/projectexplorer/buildpropertiessettings.h index 4d028788d51..3b1b2b7c698 100644 --- a/src/plugins/projectexplorer/buildpropertiessettings.h +++ b/src/plugins/projectexplorer/buildpropertiessettings.h @@ -17,13 +17,13 @@ public: class BuildTriStateAspect : public Utils::TriStateAspect { public: - BuildTriStateAspect(); + explicit BuildTriStateAspect(AspectContainer *container); }; - Utils::StringAspect buildDirectoryTemplate; - BuildTriStateAspect separateDebugInfo; - BuildTriStateAspect qmlDebugging; - BuildTriStateAspect qtQuickCompiler; + Utils::StringAspect buildDirectoryTemplate{this}; + BuildTriStateAspect separateDebugInfo{this}; + BuildTriStateAspect qmlDebugging{this}; + BuildTriStateAspect qtQuickCompiler{this}; Utils::BoolAspect showQtSettings; QString defaultBuildDirectoryTemplate(); diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index f345792ca83..bafc31fc14a 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -17,7 +17,9 @@ #include #include #include + #include +#include #include #include #include @@ -43,9 +45,6 @@ namespace Internal { const char SETTINGS_KEY[] = "ProjectExplorer/CompileOutput/Zoom"; const char C_COMPILE_OUTPUT[] = "ProjectExplorer.CompileOutput"; -const char POP_UP_KEY[] = "ProjectExplorer/Settings/ShowCompilerOutput"; -const char WRAP_OUTPUT_KEY[] = "ProjectExplorer/Settings/WrapBuildOutput"; -const char MAX_LINES_KEY[] = "ProjectExplorer/Settings/MaxBuildOutputLines"; const char OPTIONS_PAGE_ID[] = "C.ProjectExplorer.CompileOutputOptions"; CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : @@ -101,8 +100,17 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : Tr::tr("O")); ExtensionSystem::PluginManager::addObject(m_handler); setupContext(C_COMPILE_OUTPUT, m_outputWindow); - loadSettings(); updateFromSettings(); + + m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput()); + m_outputWindow->setMaxCharCount(m_settings.maxCharCount()); + + connect(&m_settings.wrapOutput, &Utils::BaseAspect::changed, m_outputWindow, [this] { + m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput()); + }); + connect(&m_settings.maxCharCount, &Utils::BaseAspect::changed, m_outputWindow, [this] { + m_outputWindow->setMaxCharCount(m_settings.maxCharCount()); + }); } CompileOutputWindow::~CompileOutputWindow() @@ -115,10 +123,7 @@ CompileOutputWindow::~CompileOutputWindow() void CompileOutputWindow::updateFromSettings() { - m_outputWindow->setWordWrapEnabled(m_settings.wrapOutput); - m_outputWindow->setMaxCharCount(m_settings.maxCharCount); } - bool CompileOutputWindow::hasFocus() const { return m_outputWindow->window()->focusWidget() == m_outputWindow; @@ -213,13 +218,6 @@ void CompileOutputWindow::reset() m_outputWindow->reset(); } -void CompileOutputWindow::setSettings(const CompileOutputSettings &settings) -{ - m_settings = settings; - storeSettings(); - updateFromSettings(); -} - Utils::OutputFormatter *CompileOutputWindow::outputFormatter() const { return m_outputWindow->outputFormatter(); @@ -231,75 +229,49 @@ void CompileOutputWindow::updateFilter() filterUsesRegexp(), filterIsInverted()); } -const bool kPopUpDefault = false; -const bool kWrapOutputDefault = true; +// CompileOutputSettings -void CompileOutputWindow::loadSettings() +static CompileOutputSettings *s_compileOutputSettings; + +CompileOutputSettings &CompileOutputSettings::instance() { - QSettings * const s = Core::ICore::settings(); - m_settings.popUp = s->value(POP_UP_KEY, kPopUpDefault).toBool(); - m_settings.wrapOutput = s->value(WRAP_OUTPUT_KEY, kWrapOutputDefault).toBool(); - m_settings.maxCharCount = s->value(MAX_LINES_KEY, - Core::Constants::DEFAULT_MAX_CHAR_COUNT).toInt() * 100; + return *s_compileOutputSettings; } -void CompileOutputWindow::storeSettings() const +CompileOutputSettings::CompileOutputSettings() { - Utils::QtcSettings *const s = Core::ICore::settings(); - s->setValueWithDefault(POP_UP_KEY, m_settings.popUp, kPopUpDefault); - s->setValueWithDefault(WRAP_OUTPUT_KEY, m_settings.wrapOutput, kWrapOutputDefault); - s->setValueWithDefault(MAX_LINES_KEY, - m_settings.maxCharCount / 100, - Core::Constants::DEFAULT_MAX_CHAR_COUNT); -} + s_compileOutputSettings = this; -class CompileOutputSettingsWidget : public Core::IOptionsPageWidget -{ -public: - CompileOutputSettingsWidget() - { - const CompileOutputSettings &settings = BuildManager::compileOutputSettings(); - m_wrapOutputCheckBox.setText(Tr::tr("Word-wrap output")); - m_wrapOutputCheckBox.setChecked(settings.wrapOutput); - m_popUpCheckBox.setText(Tr::tr("Open Compile Output when building")); - m_popUpCheckBox.setChecked(settings.popUp); - m_maxCharsBox.setMaximum(100000000); - m_maxCharsBox.setValue(settings.maxCharCount); - const auto layout = new QVBoxLayout(this); - layout->addWidget(&m_wrapOutputCheckBox); - layout->addWidget(&m_popUpCheckBox); - const auto maxCharsLayout = new QHBoxLayout; - const QString msg = Tr::tr("Limit output to %1 characters"); - const QStringList parts = msg.split("%1") << QString() << QString(); - maxCharsLayout->addWidget(new QLabel(parts.at(0).trimmed())); - maxCharsLayout->addWidget(&m_maxCharsBox); - maxCharsLayout->addWidget(new QLabel(parts.at(1).trimmed())); - maxCharsLayout->addStretch(1); - layout->addLayout(maxCharsLayout); - layout->addStretch(1); - } - - void apply() final - { - CompileOutputSettings s; - s.wrapOutput = m_wrapOutputCheckBox.isChecked(); - s.popUp = m_popUpCheckBox.isChecked(); - s.maxCharCount = m_maxCharsBox.value(); - BuildManager::setCompileOutputSettings(s); - } - -private: - QCheckBox m_wrapOutputCheckBox; - QCheckBox m_popUpCheckBox; - QSpinBox m_maxCharsBox; -}; - -CompileOutputSettingsPage::CompileOutputSettingsPage() -{ setId(OPTIONS_PAGE_ID); setDisplayName(Tr::tr("Compile Output")); setCategory(Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); - setWidgetCreator([] { return new CompileOutputSettingsWidget; }); + + wrapOutput.setSettingsKey("ProjectExplorer/Settings/WrapBuildOutput"); + wrapOutput.setDefaultValue(true); + wrapOutput.setLabelText(Tr::tr("Word-wrap output")); + + popUp.setSettingsKey("ProjectExplorer/Settings/ShowCompilerOutput"); + popUp.setLabelText(Tr::tr("Open Compile Output when building")); + + maxCharCount.setSettingsKey("ProjectExplorer/Settings/MaxBuildOutputLines"); + maxCharCount.setRange(1, Core::Constants::DEFAULT_MAX_CHAR_COUNT); + maxCharCount.setDefaultValue(Core::Constants::DEFAULT_MAX_CHAR_COUNT); + maxCharCount.setToSettingsTransformation([](const QVariant &v) { return v.toInt() / 100; }); + maxCharCount.setFromSettingsTransformation([](const QVariant &v) { return v.toInt() * 100; }); + + setLayouter([this] { + using namespace Layouting; + const QString msg = Tr::tr("Limit output to %1 characters"); + const QStringList parts = msg.split("%1") << QString() << QString(); + return Column { + wrapOutput, + popUp, + Row { parts.at(0), maxCharCount, parts.at(1), st }, + st + }; + }); + + readSettings(); } } // Internal diff --git a/src/plugins/projectexplorer/compileoutputwindow.h b/src/plugins/projectexplorer/compileoutputwindow.h index e1f000de9ae..89c7b749f2e 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.h +++ b/src/plugins/projectexplorer/compileoutputwindow.h @@ -26,6 +26,18 @@ namespace Internal { class ShowOutputTaskHandler; class CompileOutputTextEdit; +class CompileOutputSettings final : public Core::PagedSettings +{ +public: + CompileOutputSettings(); + + static CompileOutputSettings &instance(); + + Utils::BoolAspect popUp{this}; + Utils::BoolAspect wrapOutput{this}; + Utils::IntegerAspect maxCharCount{this}; +}; + class CompileOutputWindow final : public Core::IOutputPane { Q_OBJECT @@ -57,19 +69,13 @@ public: void flush(); void reset(); - const CompileOutputSettings &settings() const { return m_settings; } - void setSettings(const CompileOutputSettings &settings); - Utils::OutputFormatter *outputFormatter() const; private: void updateFilter() override; const QList outputWindows() const override { return {m_outputWindow}; } - void loadSettings(); - void storeSettings() const; void updateFromSettings(); - Core::OutputWindow *m_outputWindow; ShowOutputTaskHandler *m_handler; QToolButton *m_cancelBuildButton; @@ -77,11 +83,5 @@ private: CompileOutputSettings m_settings; }; -class CompileOutputSettingsPage final : public Core::IOptionsPage -{ -public: - CompileOutputSettingsPage(); -}; - } // namespace Internal } // namespace ProjectExplorer 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/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index eecf5332b00..d313cb433de 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -57,7 +57,6 @@ #include "project.h" #include "projectexplorericons.h" #include "projectexplorersettings.h" -#include "projectexplorersettingspage.h" #include "projectexplorertr.h" #include "projectfilewizardextension.h" #include "projectmanager.h" @@ -667,7 +666,6 @@ public: // Settings pages ProjectExplorerSettingsPage m_projectExplorerSettingsPage; AppOutputSettingsPage m_appOutputSettingsPage; - CompileOutputSettingsPage m_compileOutputSettingsPage; DeviceSettingsPage m_deviceSettingsPage; SshSettingsPage m_sshSettingsPage; CustomParsersSettingsPage m_customParsersSettingsPage; diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index 3b407825713..c955a485905 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -110,8 +110,7 @@ Project { "projectexplorerconstants.cpp", "projectexplorerconstants.h", "projectexplorericons.h", "projectexplorericons.cpp", - "projectexplorersettings.h", - "projectexplorersettingspage.cpp", "projectexplorersettingspage.h", + "projectexplorersettings.h", "projectexplorersettings.cpp", "projectexplorertr.h", "projectfilewizardextension.cpp", "projectfilewizardextension.h", "projectimporter.cpp", "projectimporter.h", diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.cpp b/src/plugins/projectexplorer/projectexplorersettings.cpp similarity index 99% rename from src/plugins/projectexplorer/projectexplorersettingspage.cpp rename to src/plugins/projectexplorer/projectexplorersettings.cpp index c8bc51b9675..14f66b885bf 100644 --- a/src/plugins/projectexplorer/projectexplorersettingspage.cpp +++ b/src/plugins/projectexplorer/projectexplorersettings.cpp @@ -1,7 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "projectexplorersettingspage.h" +#include "projectexplorersettings.h" #include "projectexplorer.h" #include "projectexplorerconstants.h" diff --git a/src/plugins/projectexplorer/projectexplorersettings.h b/src/plugins/projectexplorer/projectexplorersettings.h index bda218aef22..48cc85c414b 100644 --- a/src/plugins/projectexplorer/projectexplorersettings.h +++ b/src/plugins/projectexplorer/projectexplorersettings.h @@ -4,6 +4,8 @@ #pragma once #include +#include + #include #include @@ -72,12 +74,10 @@ public: int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; }; -class CompileOutputSettings +class ProjectExplorerSettingsPage : public Core::IOptionsPage { public: - bool popUp = false; - bool wrapOutput = false; - int maxCharCount = Core::Constants::DEFAULT_MAX_CHAR_COUNT; + ProjectExplorerSettingsPage(); }; } // namespace Internal diff --git a/src/plugins/projectexplorer/projectexplorersettingspage.h b/src/plugins/projectexplorer/projectexplorersettingspage.h deleted file mode 100644 index a924681daed..00000000000 --- a/src/plugins/projectexplorer/projectexplorersettingspage.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (C) 2016 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#pragma once - -#include - -namespace ProjectExplorer::Internal { - -class ProjectExplorerSettingsPage : public Core::IOptionsPage -{ -public: - ProjectExplorerSettingsPage(); -}; - -} // ProjectExplorer::Internal diff --git a/src/plugins/projectexplorer/taskmodel.cpp b/src/plugins/projectexplorer/taskmodel.cpp index c05614a445f..11591e707d7 100644 --- a/src/plugins/projectexplorer/taskmodel.cpp +++ b/src/plugins/projectexplorer/taskmodel.cpp @@ -273,6 +273,8 @@ QVariant TaskModel::data(const QModelIndex &index, int role) const return task.summary; case TaskModel::Description: return task.description(); + case TaskModel::Type: + return int(task.type); } return {}; } @@ -409,7 +411,8 @@ void TaskFilterModel::updateFilterProperties( bool TaskFilterModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const { - Q_UNUSED(source_parent) + if (source_parent.isValid()) + return true; return filterAcceptsTask(taskModel()->tasks().at(source_row)); } diff --git a/src/plugins/projectexplorer/taskmodel.h b/src/plugins/projectexplorer/taskmodel.h index e60d8a30a51..5fa37c02d1a 100644 --- a/src/plugins/projectexplorer/taskmodel.h +++ b/src/plugins/projectexplorer/taskmodel.h @@ -44,7 +44,7 @@ public: int sizeOfLineNumber(const QFont &font); void setFileNotFound(const QModelIndex &index, bool b); - enum Roles { Description = Qt::UserRole, }; + enum Roles { Description = Qt::UserRole, Type}; int taskCount(Utils::Id categoryId); int errorTaskCount(Utils::Id categoryId); diff --git a/src/plugins/projectexplorer/taskwindow.cpp b/src/plugins/projectexplorer/taskwindow.cpp index eb4b9de8ca5..9ae93c0bbf0 100644 --- a/src/plugins/projectexplorer/taskwindow.cpp +++ b/src/plugins/projectexplorer/taskwindow.cpp @@ -92,7 +92,7 @@ namespace Internal { class TaskView : public TreeView { public: - TaskView() { setMouseTracking(true); } + TaskView(); void resizeColumns(); private: @@ -169,6 +169,7 @@ TaskWindow::TaskWindow() : d(std::make_unique()) { d->m_model = new Internal::TaskModel(this); d->m_filter = new Internal::TaskFilterModel(d->m_model); + d->m_filter->setAutoAcceptChildRows(true); auto agg = new Aggregation::Aggregate; agg->add(&d->m_treeView); @@ -659,6 +660,12 @@ bool TaskDelegate::needsSpecialHandling(const QModelIndex &index) const return sourceIndex.internalId(); } +TaskView::TaskView() +{ + setMouseTracking(true); + setVerticalScrollMode(ScrollPerPixel); +} + void TaskView::resizeColumns() { setColumnWidth(0, width() * 0.85); @@ -694,7 +701,7 @@ void TaskView::mouseMoveEvent(QMouseEvent *e) void TaskView::mouseReleaseEvent(QMouseEvent *e) { - if (m_clickAnchor.isEmpty()) { + if (m_clickAnchor.isEmpty() || e->button() == Qt::RightButton) { TreeView::mouseReleaseEvent(e); return; } diff --git a/src/plugins/python/pipsupport.cpp b/src/plugins/python/pipsupport.cpp index 8e3f55dd11b..b420f24aa50 100644 --- a/src/plugins/python/pipsupport.cpp +++ b/src/plugins/python/pipsupport.cpp @@ -81,8 +81,10 @@ void PipInstallTask::cancel() m_process.stop(); m_process.waitForFinished(); Core::MessageManager::writeFlashing( - Tr::tr("The %1 installation was canceled by %2.") - .arg(packagesDisplayName(), m_killTimer.isActive() ? Tr::tr("user") : Tr::tr("time out"))); + m_killTimer.isActive() + ? Tr::tr("The installation of \"%1\" was canceled by timeout.").arg(packagesDisplayName()) + : Tr::tr("The installation of \"%1\" was canceled by the user.") + .arg(packagesDisplayName())); } void PipInstallTask::handleDone() diff --git a/src/plugins/python/pythonproject.cpp b/src/plugins/python/pythonproject.cpp index a29847ac7bc..e9d503037d0 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; @@ -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. @@ -268,25 +267,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 +303,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; } diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index d33346b8af7..0fe6576170f 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -634,7 +634,8 @@ static void addPythonsFromRegistry(QList &pythons) const FilePath &executable = FilePath::fromUserInput(regVal.toString()); if (executable.exists() && !alreadyRegistered(pythons, executable)) { pythons << Interpreter{QUuid::createUuid().toString(), - name + Tr::tr(" (Windowed)"), + //: (Windowed) + Tr::tr("%1 (Windowed)").arg(name), FilePath::fromUserInput(regVal.toString())}; } } diff --git a/src/plugins/python/pythonwizardpage.cpp b/src/plugins/python/pythonwizardpage.cpp index d132f3550c7..bcbca5a7072 100644 --- a/src/plugins/python/pythonwizardpage.cpp +++ b/src/plugins/python/pythonwizardpage.cpp @@ -102,7 +102,6 @@ PythonWizardPage::PythonWizardPage(const QList> &pySide m_createVenv.setLabelText(Tr::tr("Create new Virtual Environment")); m_venvPath.setLabelText(Tr::tr("Path to virtual environment")); - m_venvPath.setDisplayStyle(StringAspect::PathChooserDisplay); m_venvPath.setEnabler(&m_createVenv); m_venvPath.setExpectedKind(PathChooser::Directory); diff --git a/src/plugins/python/pythonwizardpage.h b/src/plugins/python/pythonwizardpage.h index 1d605b24176..6cf8a130c59 100644 --- a/src/plugins/python/pythonwizardpage.h +++ b/src/plugins/python/pythonwizardpage.h @@ -36,7 +36,7 @@ private: ProjectExplorer::InterpreterAspect m_interpreter; Utils::SelectionAspect m_pySideVersion; Utils::BoolAspect m_createVenv; - Utils::StringAspect m_venvPath; + Utils::FilePathAspect m_venvPath; Utils::InfoLabel *m_stateLabel = nullptr; }; diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index 173443958db..9da9f7f65a7 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -57,7 +57,8 @@ class RunSystemAspect : public TriStateAspect { Q_OBJECT public: - RunSystemAspect() : TriStateAspect(Tr::tr("Run"), Tr::tr("Ignore"), Tr::tr("Use global setting")) + RunSystemAspect() + : TriStateAspect(nullptr, Tr::tr("Run"), Tr::tr("Ignore"), Tr::tr("Use global setting")) { setSettingsKey("RunSystemFunction"); setDisplayName(Tr::tr("qmake system() behavior when parsing:")); diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 1a180bf833b..d7aefb50b8b 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1452,8 +1453,12 @@ void QmakeBuildSystem::testToolChain(ToolChain *tc, const FilePath &path) const QString QmakeBuildSystem::deviceRoot() const { - if (projectFilePath().needsDevice()) - return projectFilePath().withNewPath("/").toFSPathString(); + IDeviceConstPtr device = BuildDeviceKitAspect::device(target()->kit()); + QTC_ASSERT(device, return {}); + FilePath deviceRoot = device->rootPath(); + if (deviceRoot.needsDevice()) + return deviceRoot.toFSPathString(); + return {}; } diff --git a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp index d89393909ba..61493bf05c3 100644 --- a/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeprojectmanagerplugin.cpp @@ -357,8 +357,7 @@ void QmakeProjectManagerPluginPrivate::addLibraryImpl(const FilePath &filePath, // add extra \n in case the last line is not empty int line, column; editor->convertPosition(endOfDoc, &line, &column); - const int positionInBlock = column - 1; - if (!editor->textAt(endOfDoc - positionInBlock, positionInBlock).simplified().isEmpty()) + if (!editor->textAt(endOfDoc - column, column).simplified().isEmpty()) snippet = QLatin1Char('\n') + snippet; editor->insert(snippet); diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.cpp b/src/plugins/qmakeprojectmanager/qmakesettings.cpp index 292851e444b..e69526901f0 100644 --- a/src/plugins/qmakeprojectmanager/qmakesettings.cpp +++ b/src/plugins/qmakeprojectmanager/qmakesettings.cpp @@ -26,7 +26,6 @@ QmakeSettings::QmakeSettings() setCategory(ProjectExplorer::Constants::BUILD_AND_RUN_SETTINGS_CATEGORY); setSettingsGroup("QmakeProjectManager"); - registerAspect(&warnAgainstUnalignedBuildDir); warnAgainstUnalignedBuildDir.setSettingsKey("WarnAgainstUnalignedBuildDir"); warnAgainstUnalignedBuildDir.setDefaultValue(HostOsInfo::isWindowsHost()); warnAgainstUnalignedBuildDir.setLabelText(Tr::tr("Warn if a project's source and " @@ -34,13 +33,11 @@ QmakeSettings::QmakeSettings() warnAgainstUnalignedBuildDir.setToolTip(Tr::tr("Qmake has subtle bugs that " "can be triggered if source and build directory are not at the same level.")); - registerAspect(&alwaysRunQmake); alwaysRunQmake.setSettingsKey("AlwaysRunQmake"); alwaysRunQmake.setLabelText(Tr::tr("Run qmake on every build")); alwaysRunQmake.setToolTip(Tr::tr("This option can help to prevent failures on " "incremental builds, but might slow them down unnecessarily in the general case.")); - registerAspect(&ignoreSystemFunction); ignoreSystemFunction.setSettingsKey("RunSystemFunction"); ignoreSystemFunction.setLabelText(Tr::tr("Ignore qmake's system() function when parsing a project")); ignoreSystemFunction.setToolTip(Tr::tr("Checking this option avoids unwanted side effects, " @@ -51,14 +48,14 @@ QmakeSettings::QmakeSettings() ignoreSystemFunction.setFromSettingsTransformation(invertBoolVariant); ignoreSystemFunction.setToSettingsTransformation(invertBoolVariant); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { warnAgainstUnalignedBuildDir, alwaysRunQmake, ignoreSystemFunction, st - }.attachTo(widget); + }; }); readSettings(); diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.h b/src/plugins/qmakeprojectmanager/qmakesettings.h index de9a9ec0d37..9c2b277f538 100644 --- a/src/plugins/qmakeprojectmanager/qmakesettings.h +++ b/src/plugins/qmakeprojectmanager/qmakesettings.h @@ -14,9 +14,9 @@ public: bool runSystemFunction() { return !ignoreSystemFunction(); } - Utils::BoolAspect warnAgainstUnalignedBuildDir; - Utils::BoolAspect alwaysRunQmake; - Utils::BoolAspect ignoreSystemFunction; + Utils::BoolAspect warnAgainstUnalignedBuildDir{this}; + Utils::BoolAspect alwaysRunQmake{this}; + Utils::BoolAspect ignoreSystemFunction{this}; }; QmakeSettings &settings(); diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index c747f756581..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); @@ -455,21 +455,6 @@ bool QMakeStep::fromMap(const QVariantMap &map) { m_forced = map.value(QMAKE_FORCED_KEY, false).toBool(); m_selectedAbis = map.value(QMAKE_SELECTED_ABIS_KEY).toStringList(); - - // Backwards compatibility with < Creator 4.12. - const QVariant separateDebugInfo - = map.value("QtProjectManager.QMakeBuildStep.SeparateDebugInfo"); - if (separateDebugInfo.isValid()) - qmakeBuildConfiguration()->forceSeparateDebugInfo(separateDebugInfo.toBool()); - const QVariant qmlDebugging - = map.value("QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary"); - if (qmlDebugging.isValid()) - qmakeBuildConfiguration()->forceQmlDebugging(qmlDebugging.toBool()); - const QVariant useQtQuickCompiler - = map.value("QtProjectManager.QMakeBuildStep.UseQtQuickCompiler"); - if (useQtQuickCompiler.isValid()) - qmakeBuildConfiguration()->forceQtQuickCompiler(useQtQuickCompiler.toBool()); - return BuildStep::fromMap(map); } 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 -
-
- - -
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/qmljstools/qmljscodestylesettingswidget.cpp b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp index 34919f6144f..9fee7205b3d 100644 --- a/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp +++ b/src/plugins/qmljstools/qmljscodestylesettingswidget.cpp @@ -20,15 +20,17 @@ QmlJSCodeStyleSettingsWidget::QmlJSCodeStyleSettingsWidget(QWidget *parent) m_lineLengthSpinBox->setMaximum(999); using namespace Layouting; + // clang-format off Column { Group { - title(Tr::tr("Qml JS Code Style")), + title(Tr::tr("Other")), Form { Tr::tr("&Line length:"), m_lineLengthSpinBox, br, } }, noMargin }.attachTo(this); + // clang-format on connect(m_lineLengthSpinBox, &QSpinBox::valueChanged, this, &QmlJSCodeStyleSettingsWidget::slotSettingsChanged); 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); diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp index 0b6e74dca8b..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{optional, parallelLimit(MaxConcurrentStatCalls)}; + QList chmodList{finishAllAndDone, parallelLimit(MaxConcurrentStatCalls)}; for (const DeployableFile &file : std::as_const(filesToChmod)) { QTC_ASSERT(file.isValid(), continue); chmodList.append(chmodTask(file)); @@ -263,7 +263,7 @@ Group QnxDeployQtLibrariesDialogPrivate::deployRecipe() const Group root { onGroupSetup(setupHandler), Group { - optional, + finishAllAndDone, checkDirTask() }, Group { diff --git a/src/plugins/qtsupport/examplesparser.h b/src/plugins/qtsupport/examplesparser.h index bfe65721c22..54efaf58875 100644 --- a/src/plugins/qtsupport/examplesparser.h +++ b/src/plugins/qtsupport/examplesparser.h @@ -13,7 +13,7 @@ namespace QtSupport::Internal { enum InstructionalType { Example = 0, Demo, Tutorial }; -class QTSUPPORT_EXPORT ExampleItem : public Core::ListItem +class QTSUPPORT_TEST_EXPORT ExampleItem : public Core::ListItem { public: Utils::FilePath projectPath; @@ -31,13 +31,13 @@ public: QHash metaData; }; -QTSUPPORT_EXPORT Utils::expected_str> parseExamples( +QTSUPPORT_TEST_EXPORT Utils::expected_str> parseExamples( const Utils::FilePath &manifest, const Utils::FilePath &examplesInstallPath, const Utils::FilePath &demosInstallPath, bool examples); -QTSUPPORT_EXPORT Utils::expected_str> parseExamples( +QTSUPPORT_TEST_EXPORT Utils::expected_str> parseExamples( const QByteArray &manifestData, const Utils::FilePath &manifestPath, const Utils::FilePath &examplesInstallPath, diff --git a/src/plugins/remotelinux/filesystemaccess_test.cpp b/src/plugins/remotelinux/filesystemaccess_test.cpp index dda6e417615..2edb78fa7b3 100644 --- a/src/plugins/remotelinux/filesystemaccess_test.cpp +++ b/src/plugins/remotelinux/filesystemaccess_test.cpp @@ -503,8 +503,8 @@ void FileSystemAccessTest::testFileStreamer() } }; - TaskTree taskTree(root); - QVERIFY(taskTree.runBlocking(10000)); + using namespace std::chrono_literals; + QVERIFY(TaskTree::runBlocking(root, 10000ms)); QVERIFY(localData); QCOMPARE(*localData, data); diff --git a/src/plugins/remotelinux/genericdirectuploadstep.cpp b/src/plugins/remotelinux/genericdirectuploadstep.cpp index 9179ac87519..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{optional, 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{optional, 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/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp index ae85284ccb1..da7b6138dea 100644 --- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp +++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp @@ -79,12 +79,14 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget( m_gdbServerLineEdit->setPlaceholderText(hint); m_gdbServerLineEdit->setToolTip(hint); m_gdbServerLineEdit->setHistoryCompleter("GdbServer"); + m_gdbServerLineEdit->setAllowPathFromDevice(true); m_qmlRuntimeLineEdit = new PathChooser(this); m_qmlRuntimeLineEdit->setExpectedKind(PathChooser::ExistingCommand); m_qmlRuntimeLineEdit->setPlaceholderText(hint); m_qmlRuntimeLineEdit->setToolTip(hint); m_qmlRuntimeLineEdit->setHistoryCompleter("QmlRuntime"); + m_qmlRuntimeLineEdit->setAllowPathFromDevice(true); m_sourceProfileCheckBox = new QCheckBox(Tr::tr("Source %1 and %2").arg("/etc/profile").arg("$HOME/.profile")); diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index 59af192eaa2..cc44dddb3c4 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -978,6 +978,7 @@ 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) diff --git a/src/plugins/remotelinux/linuxdevicetester.cpp b/src/plugins/remotelinux/linuxdevicetester.cpp index ecb4296262d..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...")); @@ -131,12 +131,12 @@ TaskItem GenericLinuxDeviceTesterPrivate::unameTask() const emit q->errorMessage(Tr::tr("uname failed.") + '\n'); }; return Group { - optional, + finishAllAndDone, ProcessTask(setup, done, error) }; } -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...")); @@ -159,13 +159,13 @@ TaskItem GenericLinuxDeviceTesterPrivate::gathererTask() const }; return Group { - optional, + finishAllAndDone, DeviceUsedPortsGathererTask(setup, done, error) }; } -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/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index d1be1051452..42f0467c947 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -70,10 +70,9 @@ MakeInstallStep::MakeInstallStep(BuildStepList *parent, Id id) : MakeStep(parent connect(makeAspect, &ExecutableAspect::changed, this, &MakeInstallStep::updateCommandFromAspect); - const auto installRootAspect = addAspect(); + const auto installRootAspect = addAspect(); installRootAspect->setId(InstallRootAspectId); installRootAspect->setSettingsKey(InstallRootAspectId); - installRootAspect->setDisplayStyle(StringAspect::PathChooserDisplay); installRootAspect->setExpectedKind(PathChooser::Directory); installRootAspect->setLabelText(Tr::tr("Install root:")); installRootAspect->setFilePath(rootPath); diff --git a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp index 36e4b0bda24..fd6f75c0761 100644 --- a/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp +++ b/src/plugins/remotelinux/remotelinuxcustomrunconfiguration.cpp @@ -41,10 +41,9 @@ RemoteLinuxCustomRunConfiguration::RemoteLinuxCustomRunConfiguration(Target *tar exeAspect->setHistoryCompleter("RemoteLinux.CustomExecutable.History"); exeAspect->setExpectedKind(PathChooser::Any); - auto symbolsAspect = addAspect(); + auto symbolsAspect = addAspect(); symbolsAspect->setSettingsKey("RemoteLinux.CustomRunConfig.LocalExecutable"); symbolsAspect->setLabelText(Tr::tr("Local executable:")); - symbolsAspect->setDisplayStyle(SymbolFileAspect::PathChooserDisplay); addAspect(macroExpander()); addAspect(macroExpander(), envAspect); 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/silversearcher/CMakeLists.txt b/src/plugins/silversearcher/CMakeLists.txt index 7cc12ebb294..2af582d4a3e 100644 --- a/src/plugins/silversearcher/CMakeLists.txt +++ b/src/plugins/silversearcher/CMakeLists.txt @@ -2,12 +2,12 @@ add_qtc_plugin(SilverSearcher PLUGIN_DEPENDS Core TextEditor SOURCES findinfilessilversearcher.cpp findinfilessilversearcher.h - silversearcheroutputparser.cpp silversearcheroutputparser.h + silversearcherparser.cpp silversearcherparser.h silversearcherplugin.cpp silversearcherplugin.h silversearchertr.h ) extend_qtc_plugin(SilverSearcher CONDITION WITH_TESTS SOURCES - outputparser_test.cpp outputparser_test.h + silversearcherparser_test.cpp silversearcherparser_test.h ) diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp index 52e9f08a956..6cf428bb64f 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.cpp +++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp @@ -2,34 +2,27 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "findinfilessilversearcher.h" +#include "silversearcherparser.h" +#include "silversearchertr.h" -#include #include -#include #include -#include -#include #include #include -#include "silversearcheroutputparser.h" -#include "silversearchertr.h" - #include #include #include #include using namespace Core; +using namespace SilverSearcher; using namespace TextEditor; using namespace Utils; namespace { -const QLatin1String silverSearcherName("Silver Searcher"); - -const QString metacharacters = "+()^$.{}[]|\\"; - -const QString SearchOptionsString = "SearchOptionsString"; +const QLatin1String s_metaCharacters = QLatin1String("+()^$.{}[]|\\"); +const QLatin1String s_searchOptionsString = QLatin1String("SearchOptionsString"); class SilverSearcherSearchOptions { @@ -37,95 +30,80 @@ public: QString searchOptions; }; -QString convertWildcardToRegex(const QString &wildcard) +static QString convertWildcardToRegex(const QString &wildcard) { QString regex; const int wildcardSize = wildcard.size(); regex.append('^'); for (int i = 0; i < wildcardSize; ++i) { const QChar ch = wildcard[i]; - if (ch == '*') { + if (ch == '*') regex.append(".*"); - } else if (ch == '?') { + else if (ch == '?') regex.append('.'); - } else if (metacharacters.indexOf(ch) != -1) { - regex.append('\\'); + else if (s_metaCharacters.indexOf(ch) != -1) + regex.append('\\' + ch); + else regex.append(ch); - } else { - regex.append(ch); - } } regex.append('$'); - return regex; } -bool isSilverSearcherAvailable() +static bool isSilverSearcherAvailable() { Process silverSearcherProcess; silverSearcherProcess.setCommand({"ag", {"--version"}}); silverSearcherProcess.start(); - if (silverSearcherProcess.waitForFinished(1000)) { - if (silverSearcherProcess.cleanedStdOut().contains("ag version")) - return true; - } - - return false; + return silverSearcherProcess.waitForFinished(1000) + && silverSearcherProcess.cleanedStdOut().contains("ag version"); } -void runSilverSeacher(QPromise &promise, FileFindParameters parameters) +static void runSilverSeacher(QPromise &promise, + const FileFindParameters ¶meters) { - const FilePath directory = FilePath::fromUserInput(parameters.additionalParameters.toString()); - QStringList arguments = {"--parallel", "--ackmate"}; + const auto setupProcess = [parameters](Process &process) { + const FilePath directory + = FilePath::fromUserInput(parameters.additionalParameters.toString()); + QStringList arguments = {"--parallel", "--ackmate"}; - if (parameters.flags & FindCaseSensitively) - arguments << "-s"; - else - arguments << "-i"; + if (parameters.flags & FindCaseSensitively) + arguments << "-s"; + else + arguments << "-i"; - if (parameters.flags & FindWholeWords) - arguments << "-w"; + if (parameters.flags & FindWholeWords) + arguments << "-w"; - if (!(parameters.flags & FindRegularExpression)) - arguments << "-Q"; + if (!(parameters.flags & FindRegularExpression)) + arguments << "-Q"; - for (const QString &filter : std::as_const(parameters.exclusionFilters)) - arguments << "--ignore" << filter; + for (const QString &filter : std::as_const(parameters.exclusionFilters)) + arguments << "--ignore" << filter; - QString nameFiltersAsRegex; - for (const QString &filter : std::as_const(parameters.nameFilters)) - nameFiltersAsRegex += QString("(%1)|").arg(convertWildcardToRegex(filter)); - nameFiltersAsRegex.remove(nameFiltersAsRegex.length() - 1, 1); + QString nameFiltersAsRegExp; + for (const QString &filter : std::as_const(parameters.nameFilters)) + nameFiltersAsRegExp += QString("(%1)|").arg(convertWildcardToRegex(filter)); + nameFiltersAsRegExp.remove(nameFiltersAsRegExp.length() - 1, 1); - arguments << "-G" << nameFiltersAsRegex; + arguments << "-G" << nameFiltersAsRegExp; - SilverSearcherSearchOptions params = parameters.searchEngineParameters - .value(); - if (!params.searchOptions.isEmpty()) - arguments << params.searchOptions.split(' '); + const SilverSearcherSearchOptions params = parameters.searchEngineParameters + .value(); + if (!params.searchOptions.isEmpty()) + arguments << params.searchOptions.split(' '); - arguments << "--" << parameters.text << directory.normalizedPathName().toString(); + arguments << "--" << parameters.text << directory.normalizedPathName().toString(); + process.setCommand({"ag", arguments}); + }; - Process process; - process.setCommand({"ag", arguments}); - process.start(); - if (process.waitForFinished()) { - std::optional regExp; - if (parameters.flags & FindRegularExpression) { - regExp = QRegularExpression(); - const QRegularExpression::PatternOptions patternOptions - = (parameters.flags & FindCaseSensitively) - ? QRegularExpression::NoPatternOption - : QRegularExpression::CaseInsensitiveOption; - regExp->setPattern(parameters.text); - regExp->setPatternOptions(patternOptions); - } - const SearchResultItems items = SilverSearcher::parse(process.cleanedStdOut(), regExp); - if (!items.isEmpty()) - promise.addResult(items); - } else { - promise.future().cancel(); - } + FilePath lastFilePath; + const auto outputParser = [&lastFilePath](const QFuture &future, const QString &input, + const std::optional ®Exp) { + return SilverSearcher::parse(future, input, regExp, &lastFilePath); + }; + + TextEditor::searchInProcessOutput(promise, parameters, setupProcess, outputParser); } } // namespace @@ -150,6 +128,7 @@ FindInFilesSilverSearcher::FindInFilesSilverSearcher(QObject *parent) QTC_ASSERT(findInFiles, return); findInFiles->addSearchEngine(this); + // TODO: Make disabled by default and run isSilverSearcherAvailable asynchronously setEnabled(isSilverSearcherAvailable()); if (!isEnabled()) { QLabel *label = new QLabel(Tr::tr("Silver Searcher is not available on the system.")); @@ -158,10 +137,6 @@ FindInFilesSilverSearcher::FindInFilesSilverSearcher(QObject *parent) } } -FindInFilesSilverSearcher::~FindInFilesSilverSearcher() -{ -} - QVariant FindInFilesSilverSearcher::parameters() const { SilverSearcherSearchOptions silverSearcherSearchOptions; @@ -171,12 +146,12 @@ QVariant FindInFilesSilverSearcher::parameters() const QString FindInFilesSilverSearcher::title() const { - return silverSearcherName; + return "Silver Searcher"; } QString FindInFilesSilverSearcher::toolTip() const { - return QString(); + return {}; } QWidget *FindInFilesSilverSearcher::widget() const @@ -186,7 +161,7 @@ QWidget *FindInFilesSilverSearcher::widget() const void FindInFilesSilverSearcher::writeSettings(QSettings *settings) const { - settings->setValue(SearchOptionsString, m_searchOptionsLineEdit->text()); + settings->setValue(s_searchOptionsString, m_searchOptionsLineEdit->text()); } QFuture FindInFilesSilverSearcher::executeSearch( @@ -203,7 +178,7 @@ IEditor *FindInFilesSilverSearcher::openEditor(const SearchResultItem & /*item*/ void FindInFilesSilverSearcher::readSettings(QSettings *settings) { - m_searchOptionsLineEdit->setText(settings->value(SearchOptionsString).toString()); + m_searchOptionsLineEdit->setText(settings->value(s_searchOptionsString).toString()); } } // namespace SilverSearcher diff --git a/src/plugins/silversearcher/findinfilessilversearcher.h b/src/plugins/silversearcher/findinfilessilversearcher.h index 85b35192b8e..bc5e224c21f 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.h +++ b/src/plugins/silversearcher/findinfilessilversearcher.h @@ -3,10 +3,9 @@ #pragma once -#include #include -#include +#include #include @@ -14,6 +13,8 @@ QT_BEGIN_NAMESPACE class QLineEdit; QT_END_NAMESPACE +namespace Core { class IFindSupport; } + namespace SilverSearcher { class FindInFilesSilverSearcher : public TextEditor::SearchEngine @@ -22,7 +23,6 @@ class FindInFilesSilverSearcher : public TextEditor::SearchEngine public: explicit FindInFilesSilverSearcher(QObject *parent); - ~FindInFilesSilverSearcher() override; // TextEditor::FileFindExtension QString title() const override; diff --git a/src/plugins/silversearcher/silversearcher.qbs b/src/plugins/silversearcher/silversearcher.qbs index 8cf2cd15090..08a25367aad 100644 --- a/src/plugins/silversearcher/silversearcher.qbs +++ b/src/plugins/silversearcher/silversearcher.qbs @@ -9,14 +9,14 @@ QtcPlugin { files: [ "findinfilessilversearcher.cpp", "findinfilessilversearcher.h", - "silversearcheroutputparser.cpp", "silversearcheroutputparser.h", + "silversearcherparser.cpp", "silversearcherparser.h", "silversearcherplugin.cpp", "silversearcherplugin.h", ] QtcTestFiles { files: [ - "outputparser_test.cpp", - "outputparser_test.h", + "silversearcherparser_test.cpp", + "silversearcherparser_test.h", ] } } diff --git a/src/plugins/silversearcher/silversearcheroutputparser.cpp b/src/plugins/silversearcher/silversearcherparser.cpp similarity index 59% rename from src/plugins/silversearcher/silversearcheroutputparser.cpp rename to src/plugins/silversearcher/silversearcherparser.cpp index 65d773d0c68..7880bd78eba 100644 --- a/src/plugins/silversearcher/silversearcheroutputparser.cpp +++ b/src/plugins/silversearcher/silversearcherparser.cpp @@ -1,7 +1,9 @@ // Copyright (C) 2017 Przemyslaw Gorszkowski . // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "silversearcheroutputparser.h" +#include "silversearcherparser.h" + +#include using namespace Utils; @@ -19,16 +21,16 @@ namespace SilverSearcher { 10;8 2,35 2:PropertyAbstractContainer::PropertyAbstractContainer() */ -static QStringView nextLine(QStringView *remainingOutput) +static QStringView nextLine(QStringView *remainingInput) { - const int newLinePos = remainingOutput->indexOf('\n'); + const int newLinePos = remainingInput->indexOf('\n'); if (newLinePos < 0) { - QStringView ret = *remainingOutput; - *remainingOutput = QStringView(); + QStringView ret = *remainingInput; + *remainingInput = QStringView(); return ret; } - QStringView ret = remainingOutput->left(newLinePos); - *remainingOutput = remainingOutput->mid(newLinePos + 1); + QStringView ret = remainingInput->left(newLinePos); + *remainingInput = remainingInput->mid(newLinePos + 1); return ret; } @@ -45,16 +47,16 @@ static bool parseNumber(QStringView numberString, int *number) return ok; } -static bool parseLineNumber(QStringView *remainingOutput, int *lineNumber) +static bool parseLineNumber(QStringView *remainingInput, int *lineNumber) { - const int lineNumberDelimiterPos = remainingOutput->indexOf(';'); + const int lineNumberDelimiterPos = remainingInput->indexOf(';'); if (lineNumberDelimiterPos < 0) return false; - if (!parseNumber(remainingOutput->left(lineNumberDelimiterPos), lineNumber)) + if (!parseNumber(remainingInput->left(lineNumberDelimiterPos), lineNumber)) return false; - *remainingOutput = remainingOutput->mid(lineNumberDelimiterPos + 1); + *remainingInput = remainingInput->mid(lineNumberDelimiterPos + 1); return true; } @@ -76,13 +78,13 @@ static bool parseLineHit(QStringView hitString, QPair *hit) return true; } -static bool parseLineHits(QStringView *remainingOutput, QList> *hits) +static bool parseLineHits(QStringView *remainingInput, QList> *hits) { - const int hitsDelimiterPos = remainingOutput->indexOf(':'); + const int hitsDelimiterPos = remainingInput->indexOf(':'); if (hitsDelimiterPos < 0) return false; - const QStringView hitsString = remainingOutput->left(hitsDelimiterPos); + const QStringView hitsString = remainingInput->left(hitsDelimiterPos); const QList hitStrings = hitsString.split(',', Qt::SkipEmptyParts); for (const auto hitString : hitStrings) { QPair hit; @@ -90,30 +92,37 @@ static bool parseLineHits(QStringView *remainingOutput, QList> * return false; hits->append(hit); } - *remainingOutput = remainingOutput->mid(hitsDelimiterPos + 1); + *remainingInput = remainingInput->mid(hitsDelimiterPos + 1); return true; } -SearchResultItems parse(const QString &output, const std::optional ®Exp) +SearchResultItems parse(const QFuture &future, const QString &input, + const std::optional ®Exp, FilePath *lastFilePath) { + QTC_ASSERT(lastFilePath, return {}); SearchResultItems items; - QStringView remainingOutput(output); + QStringView remainingInput(input); while (true) { - if (remainingOutput.isEmpty()) + if (future.isCanceled()) + return {}; + + if (remainingInput.isEmpty()) break; - const QStringView filePathLine = nextLine(&remainingOutput); - if (filePathLine.isEmpty()) + const QStringView filePathLine = nextLine(&remainingInput); + if (filePathLine.isEmpty()) { + *lastFilePath = {}; // Clear the parser state continue; + } - if (!filePathLine.startsWith(':')) - continue; + if (filePathLine.startsWith(':')) + *lastFilePath = FilePath::fromPathPart(filePathLine.mid(1)); - const FilePath filePath = FilePath::fromPathPart(filePathLine.mid(1)); - - QStringView hitLine = nextLine(&remainingOutput); - while (!hitLine.isEmpty()) { + while (true) { + QStringView hitLine = nextLine(&remainingInput); + if (hitLine.isEmpty()) + break; int lineNumber = -1; if (!parseLineNumber(&hitLine, &lineNumber)) break; @@ -124,8 +133,9 @@ SearchResultItems parse(const QString &output, const std::optional &hit : hits) { item.setMainRange(lineNumber, hit.first, hit.second); item.setUserData( @@ -133,10 +143,17 @@ SearchResultItems parse(const QString &output, const std::optional ®Exp) +{ + QPromise promise; + promise.start(); + FilePath dummy; + return SilverSearcher::parse(promise.future(), input, regExp, &dummy); +} + } // namespace SilverSearcher diff --git a/src/plugins/silversearcher/silversearcheroutputparser.h b/src/plugins/silversearcher/silversearcherparser.h similarity index 54% rename from src/plugins/silversearcher/silversearcheroutputparser.h rename to src/plugins/silversearcher/silversearcherparser.h index f9d8502759a..67b3e124f42 100644 --- a/src/plugins/silversearcher/silversearcheroutputparser.h +++ b/src/plugins/silversearcher/silversearcherparser.h @@ -5,11 +5,18 @@ #include +#include #include +namespace Utils { class FilePath; } + namespace SilverSearcher { -Utils::SearchResultItems parse(const QString &output, +Utils::SearchResultItems parse(const QFuture &future, const QString &input, + const std::optional ®Exp, + Utils::FilePath *lastFilePath); + +Utils::SearchResultItems parse(const QString &input, const std::optional ®Exp = {}); } // namespace SilverSearcher diff --git a/src/plugins/silversearcher/outputparser_test.cpp b/src/plugins/silversearcher/silversearcherparser_test.cpp similarity index 91% rename from src/plugins/silversearcher/outputparser_test.cpp rename to src/plugins/silversearcher/silversearcherparser_test.cpp index 4aaacf37e68..b23002e191e 100644 --- a/src/plugins/silversearcher/outputparser_test.cpp +++ b/src/plugins/silversearcher/silversearcherparser_test.cpp @@ -1,8 +1,8 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "outputparser_test.h" -#include "silversearcheroutputparser.h" +#include "silversearcherparser.h" +#include "silversearcherparser_test.h" #include @@ -18,12 +18,13 @@ SearchResultItem searchResult(const FilePath &fileName, const QString &matchingL result.setFilePath(fileName); result.setLineText(matchingLine); result.setMainRange(lineNumber, matchStart, matchLength); + result.setUseTextEditorFont(true); return result; } void OutputParserTest::test_data() { - QTest::addColumn("parserOutput"); + QTest::addColumn("input"); QTest::addColumn("results"); QTest::addRow("nothing") << QString("\n") << SearchResultItems(); @@ -58,9 +59,9 @@ void OutputParserTest::test_data() void OutputParserTest::test() { - QFETCH(QString, parserOutput); + QFETCH(QString, input); QFETCH(SearchResultItems, results); - const SearchResultItems items = SilverSearcher::parse(parserOutput); + const SearchResultItems items = SilverSearcher::parse(input); QCOMPARE(items, results); } diff --git a/src/plugins/silversearcher/outputparser_test.h b/src/plugins/silversearcher/silversearcherparser_test.h similarity index 100% rename from src/plugins/silversearcher/outputparser_test.h rename to src/plugins/silversearcher/silversearcherparser_test.h diff --git a/src/plugins/silversearcher/silversearcherplugin.cpp b/src/plugins/silversearcher/silversearcherplugin.cpp index e16ed4a3498..1f143407d78 100644 --- a/src/plugins/silversearcher/silversearcherplugin.cpp +++ b/src/plugins/silversearcher/silversearcherplugin.cpp @@ -1,9 +1,9 @@ // Copyright (C) 2017 Przemyslaw Gorszkowski . // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include "silversearcherplugin.h" #include "findinfilessilversearcher.h" -#include "outputparser_test.h" +#include "silversearcherparser_test.h" +#include "silversearcherplugin.h" namespace SilverSearcher::Internal { diff --git a/src/plugins/squish/squishplugin.cpp b/src/plugins/squish/squishplugin.cpp index c8b779aac06..caeb787de69 100644 --- a/src/plugins/squish/squishplugin.cpp +++ b/src/plugins/squish/squishplugin.cpp @@ -43,7 +43,6 @@ public: bool initializeGlobalScripts(); SquishSettings m_squishSettings; - SquishSettingsPage m_settingsPage{&m_squishSettings}; SquishTestTreeModel m_treeModel; SquishNavigationWidgetFactory m_navigationWidgetFactory; ObjectsMapEditorFactory m_objectsMapEditorFactory; @@ -57,7 +56,6 @@ SquishPluginPrivate::SquishPluginPrivate() { qRegisterMetaType("SquishResultItem*"); - m_squishSettings.readSettings(ICore::settings()); m_outputPane = SquishOutputPane::instance(); m_squishTools = new SquishTools; initializeMenuEntries(); diff --git a/src/plugins/squish/squishsettings.cpp b/src/plugins/squish/squishsettings.cpp index 23ccc22dfec..f7758a844d3 100644 --- a/src/plugins/squish/squishsettings.cpp +++ b/src/plugins/squish/squishsettings.cpp @@ -33,6 +33,12 @@ namespace Internal { SquishSettings::SquishSettings() { + setId("A.Squish.General"); + setDisplayName(Tr::tr("General")); + setCategory(Constants::SQUISH_SETTINGS_CATEGORY); + setDisplayCategory("Squish"); + setCategoryIcon(Icon({{":/squish/images/settingscategory_squish.png", + Theme::PanelTextColorDark}}, Icon::Tint)); setSettingsGroup("Squish"); setAutoApply(false); @@ -85,6 +91,19 @@ SquishSettings::SquishSettings() serverHost.setEnabled(!checked); serverPort.setEnabled(!checked); }); + + setLayouter([this] { + using namespace Layouting; + return Form { + squishPath, br, + licensePath, br, + local, serverHost, serverPort, br, + verbose, br, + minimizeIDE, br, + }; + }); + + readSettings(); } Utils::FilePath SquishSettings::scriptsPath(Language language) const @@ -101,31 +120,6 @@ Utils::FilePath SquishSettings::scriptsPath(Language language) const return scripts.isReadableDir() ? scripts : Utils::FilePath(); } -SquishSettingsPage::SquishSettingsPage(SquishSettings *settings) -{ - setId("A.Squish.General"); - setDisplayName(Tr::tr("General")); - setCategory(Constants::SQUISH_SETTINGS_CATEGORY); - setDisplayCategory("Squish"); - setCategoryIcon(Icon({{":/squish/images/settingscategory_squish.png", - Theme::PanelTextColorDark}}, Icon::Tint)); - - setSettings(settings); - - setLayouter([settings](QWidget *widget) { - SquishSettings &s = *settings; - using namespace Layouting; - - Form { - s.squishPath, br, - s.licensePath, br, - s.local, s.serverHost, s.serverPort, br, - s.verbose, br, - s.minimizeIDE, br, - }.attachTo(widget); - }); -} - SquishServerSettings::SquishServerSettings() { autTimeout.setLabel(Tr::tr("Maximum startup time:")); diff --git a/src/plugins/squish/squishsettings.h b/src/plugins/squish/squishsettings.h index 180f2eb84fe..0e25a4215e3 100644 --- a/src/plugins/squish/squishsettings.h +++ b/src/plugins/squish/squishsettings.h @@ -5,8 +5,6 @@ #include -#include - #include #include @@ -31,7 +29,7 @@ public: Utils::BoolAspect animatedCursor{this}; }; -class SquishSettings : public Utils::AspectContainer +class SquishSettings : public Core::PagedSettings { public: SquishSettings(); @@ -47,12 +45,6 @@ public: Utils::BoolAspect minimizeIDE{this}; }; -class SquishSettingsPage final : public Core::IOptionsPage -{ -public: - SquishSettingsPage(SquishSettings *settings); -}; - class SquishServerSettingsDialog : public QDialog { public: 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); diff --git a/src/plugins/subversion/subversionclient.cpp b/src/plugins/subversion/subversionclient.cpp index 3146f066d6e..ac664a69b88 100644 --- a/src/plugins/subversion/subversionclient.cpp +++ b/src/plugins/subversion/subversionclient.cpp @@ -204,7 +204,7 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume Storage(diffInputStorage), parallel, Group { - optional, + finishAllAndDone, ProcessTask(setupDescription, onDescriptionDone, onDescriptionError) }, Group { diff --git a/src/plugins/subversion/subversionsettings.cpp b/src/plugins/subversion/subversionsettings.cpp index 092c4ee1c05..9b9225ccae7 100644 --- a/src/plugins/subversion/subversionsettings.cpp +++ b/src/plugins/subversion/subversionsettings.cpp @@ -68,10 +68,10 @@ SubversionSettings::SubversionSettings() password.setEnabled(useAuthentication()); }); - setLayouter([this](QWidget *widget) { + setLayouter([this] { using namespace Layouting; - Column { + return Column { Group { title(Tr::tr("Configuration")), Column { binaryPath } @@ -95,7 +95,7 @@ SubversionSettings::SubversionSettings() }, st - }.attachTo(widget); + }; }); } diff --git a/src/plugins/terminal/CMakeLists.txt b/src/plugins/terminal/CMakeLists.txt index 083ffa844e6..69da526cdc0 100644 --- a/src/plugins/terminal/CMakeLists.txt +++ b/src/plugins/terminal/CMakeLists.txt @@ -10,7 +10,8 @@ add_qtc_plugin(Terminal shellintegration.cpp shellintegration.h shellmodel.cpp shellmodel.h terminal.qrc - terminalcommands.cpp terminalcommands.h + terminalconstants.h + terminalicons.h terminalpane.cpp terminalpane.h terminalplugin.cpp terminalprocessimpl.cpp terminalprocessimpl.h 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) { diff --git a/src/plugins/terminal/terminal.qbs b/src/plugins/terminal/terminal.qbs index 0fa1ba46316..300c1acf19d 100644 --- a/src/plugins/terminal/terminal.qbs +++ b/src/plugins/terminal/terminal.qbs @@ -22,8 +22,8 @@ QtcPlugin { "shellintegration.cpp", "shellintegration.h", "terminal.qrc", - "terminalcommands.cpp", - "terminalcommands.h", + "terminalconstants.h", + "terminalicons.h", "terminalpane.cpp", "terminalpane.h", "terminalplugin.cpp", diff --git a/src/plugins/terminal/terminalcommands.cpp b/src/plugins/terminal/terminalcommands.cpp deleted file mode 100644 index 0e7cba8194c..00000000000 --- a/src/plugins/terminal/terminalcommands.cpp +++ /dev/null @@ -1,186 +0,0 @@ -// 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 "terminalcommands.h" -#include "terminaltr.h" - -#include -#include -#include -#include - -#include - -//#include - -using namespace Core; -using namespace Utils; - -namespace Terminal { - -constexpr char COPY[] = "Terminal.Copy"; -constexpr char PASTE[] = "Terminal.Paste"; -constexpr char COPY_LINK[] = "Terminal.CopyLink"; -constexpr char CLEARSELECTION[] = "Terminal.ClearSelection"; -constexpr char MOVECURSORWORDLEFT[] = "Terminal.MoveCursorWordLeft"; -constexpr char MOVECURSORWORDRIGHT[] = "Terminal.MoveCursorWordRight"; - -constexpr char NEWTERMINAL[] = "Terminal.NewTerminal"; -constexpr char CLOSETERMINAL[] = "Terminal.CloseTerminal"; -constexpr char NEXTTERMINAL[] = "Terminal.NextTerminal"; -constexpr char PREVTERMINAL[] = "Terminal.PrevTerminal"; -constexpr char MINMAX[] = "Terminal.MinMax"; - -TerminalCommands &TerminalCommands::instance() -{ - static TerminalCommands instance; - return instance; -} - -TerminalCommands::TerminalCommands() {} - -void TerminalCommands::init(const Core::Context &context) -{ - m_context = context; - initWidgetActions(); - initPaneActions(); - initGlobalCommands(); -} - -void TerminalCommands::registerAction(QAction &action, - const Utils::Id &id, - QList shortCuts) -{ - Command *cmd = ActionManager::instance()->registerAction(&action, id, m_context); - cmd->setKeySequences(shortCuts); - m_commands.push_back(cmd); -} - -void TerminalCommands::initWidgetActions() -{ - m_widgetActions.copy.setText(Tr::tr("Copy")); - m_widgetActions.paste.setText(Tr::tr("Paste")); - m_widgetActions.copyLink.setText(Tr::tr("Copy Link")); - m_widgetActions.clearSelection.setText(Tr::tr("Clear Selection")); - m_widgetActions.clearTerminal.setText(Tr::tr("Clear Terminal")); - m_widgetActions.moveCursorWordLeft.setText(Tr::tr("Move Cursor Word Left")); - m_widgetActions.moveCursorWordRight.setText(Tr::tr("Move Cursor Word Right")); - m_widgetActions.findNext.setText(Tr::tr("Find Next")); - m_widgetActions.findPrevious.setText(Tr::tr("Find Previous")); - - registerAction(m_widgetActions.copy, - COPY, - {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C") - : QLatin1String("Ctrl+Shift+C"))}); - - registerAction(m_widgetActions.paste, - PASTE, - {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") - : QLatin1String("Ctrl+Shift+V"))}); - - registerAction(m_widgetActions.copyLink, COPY_LINK); - - registerAction(m_widgetActions.clearSelection, CLEARSELECTION); - - registerAction(m_widgetActions.moveCursorWordLeft, - MOVECURSORWORDLEFT, - {QKeySequence("Alt+Left")}); - - registerAction(m_widgetActions.moveCursorWordRight, - MOVECURSORWORDRIGHT, - {QKeySequence("Alt+Right")}); -} - -void TerminalCommands::initPaneActions() -{ - m_paneActions.newTerminal.setText(Tr::tr("New Terminal")); - m_paneActions.closeTerminal.setText(Tr::tr("Close Terminal")); - m_paneActions.nextTerminal.setText(Tr::tr("Next Terminal")); - m_paneActions.prevTerminal.setText(Tr::tr("Previous Terminal")); - m_paneActions.minMax.setText(Tr::tr("Minimize/Maximize Terminal")); - - registerAction(m_paneActions.newTerminal, - NEWTERMINAL, - {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") - : QLatin1String("Ctrl+Shift+T"))}); - - registerAction(m_paneActions.closeTerminal, - CLOSETERMINAL, - {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+W") - : QLatin1String("Ctrl+Shift+W"))}); - - registerAction(m_paneActions.nextTerminal, - NEXTTERMINAL, - {QKeySequence("ALT+TAB"), - QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[") - : QLatin1String("Ctrl+PgUp"))}); - - registerAction(m_paneActions.prevTerminal, - PREVTERMINAL, - {QKeySequence("ALT+SHIFT+TAB"), - QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]") - : QLatin1String("Ctrl+PgDown"))}); - - registerAction(m_paneActions.minMax, - MINMAX, - {QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Return") - : QLatin1String("Alt+Return"))}); -} - -void TerminalCommands::initGlobalCommands() -{ - // Global commands we still want to allow - m_commands.push_back(ActionManager::command(Constants::ZOOM_IN)); - m_commands.push_back(ActionManager::command(Constants::ZOOM_OUT)); - m_commands.push_back(ActionManager::command(Constants::EXIT)); - m_commands.push_back(ActionManager::command(Constants::OPTIONS)); -} - -bool TerminalCommands::triggerAction(QKeyEvent *event) -{ - QKeyCombination combination = event->keyCombination(); - - // On macOS, the arrow keys include the KeypadModifier, which we don't want. - if (HostOsInfo::isMacHost() && combination.keyboardModifiers() & Qt::KeypadModifier) - combination = QKeyCombination(combination.keyboardModifiers() & ~Qt::KeypadModifier, - combination.key()); - - for (const auto &command : TerminalCommands::instance().m_commands) { - if (!command->action()->isEnabled()) - continue; - - for (const auto &shortcut : command->keySequences()) { - const auto result = shortcut.matches(QKeySequence(combination)); - if (result == QKeySequence::ExactMatch) { - command->action()->trigger(); - return true; - } - } - } - - return false; -} - -QAction *TerminalCommands::openSettingsAction() -{ - return ActionManager::command("Preferences.Terminal.General")->action(); -} - -void TerminalCommands::lazyInitCommand(const Utils::Id &id) -{ - Command *cmd = ActionManager::command(id); - QTC_ASSERT(cmd, return); - m_commands.append(cmd); -} - -void TerminalCommands::lazyInitCommands() -{ - static const Utils::Id terminalPaneCmd("QtCreator.Pane.Terminal"); - lazyInitCommand(terminalPaneCmd); - lazyInitCommand(Core::Constants::FIND_IN_DOCUMENT); - lazyInitCommand(Core::Constants::FIND_NEXT); - lazyInitCommand(Core::Constants::FIND_PREVIOUS); - lazyInitCommand(Core::Constants::LOCATE); -} - -} // namespace Terminal diff --git a/src/plugins/terminal/terminalcommands.h b/src/plugins/terminal/terminalcommands.h deleted file mode 100644 index 71bc7ffe0f8..00000000000 --- a/src/plugins/terminal/terminalcommands.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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 - -#include - -#include -#include -#include - -namespace Core { -class Command; -class Context; -} // namespace Core - -namespace Terminal { - -struct WidgetActions -{ - QAction copy; - QAction paste; - QAction copyLink; - QAction clearSelection; - QAction clearTerminal; - QAction moveCursorWordLeft; - QAction moveCursorWordRight; - QAction findNext; - QAction findPrevious; -}; - -struct PaneActions -{ - QAction newTerminal; - QAction closeTerminal; - QAction nextTerminal; - QAction prevTerminal; - QAction minMax; -}; - -class TerminalCommands -{ -public: - TerminalCommands(); - - void init(const Core::Context &context); - static TerminalCommands &instance(); - static WidgetActions &widgetActions() { return instance().m_widgetActions; } - static PaneActions &paneActions() { return instance().m_paneActions; } - - static QList shortcutsFor(QAction *action); - - static bool triggerAction(QKeyEvent *event); - - static QAction *openSettingsAction(); - - void lazyInitCommands(); - -protected: - void initWidgetActions(); - void initPaneActions(); - void initGlobalCommands(); - - void lazyInitCommand(const Utils::Id &id); - void registerAction(QAction &action, const Utils::Id &id, QList shortcuts = {}); - -private: - WidgetActions m_widgetActions; - PaneActions m_paneActions; - QList m_commands; - Core::Context m_context; -}; - -} // namespace Terminal diff --git a/src/plugins/terminal/terminalconstants.h b/src/plugins/terminal/terminalconstants.h new file mode 100644 index 00000000000..99700475e60 --- /dev/null +++ b/src/plugins/terminal/terminalconstants.h @@ -0,0 +1,19 @@ +// 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 + +namespace Terminal::Constants { +constexpr char NEWTERMINAL[] = "Terminal.NewTerminal"; +constexpr char NEXTTERMINAL[] = "Terminal.NextTerminal"; +constexpr char PREVTERMINAL[] = "Terminal.PrevTerminal"; +constexpr char MINMAX[] = "Terminal.MinMax"; + +constexpr char COPY[] = "Terminal.Copy"; +constexpr char PASTE[] = "Terminal.Paste"; +constexpr char CLEARSELECTION[] = "Terminal.ClearSelection"; +constexpr char MOVECURSORWORDLEFT[] = "Terminal.MoveCursorWordLeft"; +constexpr char MOVECURSORWORDRIGHT[] = "Terminal.MoveCursorWordRight"; +constexpr char CLEAR_TERMINAL[] = "Terminal.ClearTerminal"; + +} // namespace Terminal::Constants diff --git a/src/plugins/terminal/terminalicons.h b/src/plugins/terminal/terminalicons.h new file mode 100644 index 00000000000..ca503f50f96 --- /dev/null +++ b/src/plugins/terminal/terminalicons.h @@ -0,0 +1,18 @@ +// 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 + +namespace Terminal { + +static Utils::Icon NEW_TERMINAL_ICON( + {{":/terminal/images/terminal.png", Utils::Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_add_small.png", Utils::Theme::IconsRunToolBarColor}}); + +static Utils::Icon CLOSE_TERMINAL_ICON( + {{":/terminal/images/terminal.png", Utils::Theme::IconsBaseColor}, + {":/utils/images/iconoverlay_close_small.png", Utils::Theme::IconsStopToolBarColor}}); + +} // namespace Terminal diff --git a/src/plugins/terminal/terminalpane.cpp b/src/plugins/terminal/terminalpane.cpp index 34370f22bb2..965e3ba651c 100644 --- a/src/plugins/terminal/terminalpane.cpp +++ b/src/plugins/terminal/terminalpane.cpp @@ -4,14 +4,17 @@ #include "terminalpane.h" #include "shellmodel.h" -#include "terminalcommands.h" +#include "terminalconstants.h" +#include "terminalicons.h" #include "terminalsettings.h" #include "terminaltr.h" #include "terminalwidget.h" #include +#include #include #include +#include #include #include @@ -29,15 +32,15 @@ namespace Terminal { using namespace Utils; using namespace Utils::Terminal; +using namespace Core; TerminalPane::TerminalPane(QObject *parent) - : Core::IOutputPane(parent) + : IOutputPane(parent) + , m_context("Terminal.Pane", Core::Constants::C_GLOBAL_CUTOFF) { - setupContext("Terminal.Pane", &m_tabWidget); + setupContext(m_context, &m_tabWidget); setZoomButtonsEnabled(true); - TerminalCommands::instance().init(Core::Context("Terminal.Pane")); - connect(this, &IOutputPane::zoomInRequested, this, [this] { if (currentTerminal()) currentTerminal()->zoomIn(); @@ -47,89 +50,24 @@ TerminalPane::TerminalPane(QObject *parent) currentTerminal()->zoomOut(); }); - QAction &newTerminal = TerminalCommands::instance().paneActions().newTerminal; - QAction &closeTerminal = TerminalCommands::instance().paneActions().closeTerminal; - - newTerminal.setIcon( - Icon({{":/terminal/images/terminal.png", Theme::IconsBaseColor}, - {":/utils/images/iconoverlay_add_small.png", Theme::IconsRunToolBarColor}}) - .icon()); - newTerminal.setToolTip(Tr::tr("Create a new Terminal.")); - - connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); }); - - closeTerminal.setIcon( - Icon({{":/terminal/images/terminal.png", Theme::IconsBaseColor}, - {":/utils/images/iconoverlay_close_small.png", Theme::IconsStopToolBarColor}}) - .icon()); - closeTerminal.setToolTip(Tr::tr("Close the current Terminal.")); - - connect(&closeTerminal, &QAction::triggered, this, [this] { - removeTab(m_tabWidget.currentIndex()); - }); + initActions(); m_newTerminalButton = new QToolButton(); - - QMenu *shellMenu = new QMenu(m_newTerminalButton); - const Internal::ShellModel *shellModel = new Internal::ShellModel(shellMenu); - connect(shellMenu, &QMenu::aboutToShow, shellMenu, [shellMenu, shellModel, pane = this] { - shellMenu->clear(); - - const auto addItems = [shellMenu, pane](const QList &items) { - for (const Internal::ShellModelItem &item : items) { - QAction *action = new QAction(item.icon, item.name, shellMenu); - - connect(action, &QAction::triggered, action, [item, pane]() { - pane->openTerminal(item.openParameters); - }); - - shellMenu->addAction(action); - } - }; - - addItems(shellModel->local()); - shellMenu->addSection(Tr::tr("Devices")); - addItems(shellModel->remote()); - }); - - newTerminal.setMenu(shellMenu); - m_newTerminalButton->setDefaultAction(&newTerminal); m_closeTerminalButton = new QToolButton(); m_closeTerminalButton->setDefaultAction(&closeTerminal); - connect(&TerminalCommands::instance().paneActions().nextTerminal, - &QAction::triggered, - this, - [this] { - if (canNavigate()) - goToNext(); - }); - connect(&TerminalCommands::instance().paneActions().prevTerminal, - &QAction::triggered, - this, - [this] { - if (canPrevious()) - goToPrev(); - }); - - connect(&TerminalCommands::instance().paneActions().minMax, &QAction::triggered, this, []() { - Core::Command *minMaxCommand = Core::ActionManager::command("Coreplugin.OutputPane.minmax"); - if (minMaxCommand) - emit minMaxCommand->action()->triggered(); - }); - m_openSettingsButton = new QToolButton(); - m_openSettingsButton->setToolTip(Tr::tr("Open Terminal Settings")); + m_openSettingsButton->setToolTip(Tr::tr("Configure...")); m_openSettingsButton->setIcon(Icons::SETTINGS_TOOLBAR.icon()); connect(m_openSettingsButton, &QToolButton::clicked, m_openSettingsButton, []() { - TerminalCommands::openSettingsAction()->trigger(); + ICore::showOptionsDialog("Terminal.General"); }); const auto updateEscButton = [this] { - m_escSettingButton->setChecked(TerminalSettings::instance().sendEscapeToTerminal.value()); + m_escSettingButton->setChecked(TerminalSettings::instance().sendEscapeToTerminal()); static const QString escKey = QKeySequence(Qt::Key_Escape).toString(QKeySequence::NativeText); static const QString shiftEsc = QKeySequence( @@ -149,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(Core::ICore::settings()); + TerminalSettings::instance().writeSettings(ICore::settings()); + updateEscButton(); }); connect(&TerminalSettings::instance(), &TerminalSettings::applied, this, updateEscButton); @@ -225,6 +164,7 @@ TerminalWidget *TerminalPane::stoppedTerminalWithId(Id identifier) const QWidget *TerminalPane::outputWidget(QWidget *parent) { + Q_UNUSED(parent) if (!m_widgetInitialized) { m_widgetInitialized = true; m_tabWidget.setTabBarAutoHide(false); @@ -242,10 +182,6 @@ QWidget *TerminalPane::outputWidget(QWidget *parent) else emit hidePage(); }); - - const auto terminalWidget = new TerminalWidget(parent); - m_tabWidget.addTab(terminalWidget, Tr::tr("Terminal")); - setupTerminalWidget(terminalWidget); } return &m_tabWidget; @@ -289,6 +225,81 @@ void TerminalPane::setupTerminalWidget(TerminalWidget *terminal) setTabText(); } +void TerminalPane::initActions() +{ + createShellMenu(); + + newTerminal.setText(Tr::tr("New Terminal")); + newTerminal.setIcon(NEW_TERMINAL_ICON.icon()); + newTerminal.setToolTip(Tr::tr("Create a new Terminal.")); + newTerminal.setMenu(&m_shellMenu); + + nextTerminal.setText(Tr::tr("Next Terminal")); + prevTerminal.setText(Tr::tr("Previous Terminal")); + + closeTerminal.setIcon(CLOSE_TERMINAL_ICON.icon()); + closeTerminal.setToolTip(Tr::tr("Close the current Terminal.")); + + using namespace Constants; + + ActionManager::registerAction(&newTerminal, NEWTERMINAL, m_context) + ->setDefaultKeySequences({QKeySequence( + HostOsInfo::isMacHost() ? QLatin1String("Ctrl+T") : QLatin1String("Ctrl+Shift+T"))}); + + ActionManager::registerAction(&nextTerminal, NEXTTERMINAL, m_context) + ->setDefaultKeySequences( + {QKeySequence("Alt+Tab"), + QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+[") + : QLatin1String("Ctrl+PgUp"))}); + + ActionManager::registerAction(&prevTerminal, PREVTERMINAL, m_context) + ->setDefaultKeySequences( + {QKeySequence("Alt+Shift+Tab"), + QKeySequence(HostOsInfo::isMacHost() ? QLatin1String("Ctrl+Shift+]") + : QLatin1String("Ctrl+PgDown"))}); + + m_minMax = TerminalWidget::unlockGlobalAction("Coreplugin.OutputPane.minmax", m_context); + m_locate = TerminalWidget::unlockGlobalAction(Core::Constants::LOCATE, m_context); + + connect(&newTerminal, &QAction::triggered, this, [this] { openTerminal({}); }); + connect(&closeTerminal, &QAction::triggered, this, [this] { + removeTab(m_tabWidget.currentIndex()); + }); + connect(&nextTerminal, &QAction::triggered, this, [this] { + if (canNavigate()) + goToNext(); + }); + connect(&prevTerminal, &QAction::triggered, this, [this] { + if (canPrevious()) + goToPrev(); + }); +} + +void TerminalPane::createShellMenu() +{ + const Internal::ShellModel *shellModel = new Internal::ShellModel(&m_shellMenu); + + connect(&m_shellMenu, &QMenu::aboutToShow, &m_shellMenu, [shellModel, this] { + m_shellMenu.clear(); + + const auto addItems = [this](const QList &items) { + for (const Internal::ShellModelItem &item : items) { + QAction *action = new QAction(item.icon, item.name, &m_shellMenu); + + connect(action, &QAction::triggered, action, [item, this]() { + openTerminal(item.openParameters); + }); + + m_shellMenu.addAction(action); + } + }; + + addItems(shellModel->local()); + m_shellMenu.addSection(Tr::tr("Devices")); + addItems(shellModel->remote()); + }); +} + QList TerminalPane::toolBarWidgets() const { QList widgets = IOutputPane::toolBarWidgets(); diff --git a/src/plugins/terminal/terminalpane.h b/src/plugins/terminal/terminalpane.h index 6ef2d005357..21f15168f26 100644 --- a/src/plugins/terminal/terminalpane.h +++ b/src/plugins/terminal/terminalpane.h @@ -3,11 +3,14 @@ #pragma once +#include "terminalwidget.h" + #include #include #include +#include #include #include @@ -49,6 +52,8 @@ private: void removeTab(int index); void setupTerminalWidget(TerminalWidget *terminal); + void initActions(); + void createShellMenu(); private: QTabWidget m_tabWidget; @@ -58,6 +63,18 @@ private: QToolButton *m_openSettingsButton{nullptr}; QToolButton *m_escSettingButton{nullptr}; + UnlockedGlobalAction m_minMax; + UnlockedGlobalAction m_locate; + + QAction newTerminal; + QAction nextTerminal; + QAction prevTerminal; + QAction closeTerminal; + + QMenu m_shellMenu; + + Core::Context m_context; + bool m_widgetInitialized{false}; bool m_isVisible{false}; }; diff --git a/src/plugins/terminal/terminalplugin.cpp b/src/plugins/terminal/terminalplugin.cpp index 4738bf9b307..38b58b1722f 100644 --- a/src/plugins/terminal/terminalplugin.cpp +++ b/src/plugins/terminal/terminalplugin.cpp @@ -4,7 +4,7 @@ #include "terminalpane.h" #include "terminalprocessimpl.h" #include "terminalsettings.h" -#include "terminalcommands.h" +#include "terminalwidget.h" #include #include @@ -39,16 +39,15 @@ public: m_terminalPane = nullptr; } - void initialize() final - { - addManaged(); - } + void initialize() final { addManaged(); } void extensionsInitialized() final { m_terminalPane = new TerminalPane; ExtensionSystem::PluginManager::addObject(m_terminalPane); + TerminalWidget::initActions(); + auto enable = [this] { Utils::Terminal::Hooks::instance() .addCallbackSet("Internal", @@ -71,21 +70,18 @@ public: } }; - QObject::connect(&TerminalSettings::instance(), &Utils::AspectContainer::applied, this, settingsChanged); + QObject::connect(&TerminalSettings::instance(), + &Utils::AspectContainer::applied, + this, + settingsChanged); settingsChanged(); } - bool delayedInitialize() final - { - TerminalCommands::instance().lazyInitCommands(); - return true; - } - private: TerminalPane *m_terminalPane{nullptr}; }; -} // Terminal::Internal +} // namespace Terminal::Internal #include "terminalplugin.moc" diff --git a/src/plugins/terminal/terminalsearch.cpp b/src/plugins/terminal/terminalsearch.cpp index 341c6af402c..d1aa392e9f4 100644 --- a/src/plugins/terminal/terminalsearch.cpp +++ b/src/plugins/terminal/terminalsearch.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "terminalsearch.h" -#include "terminalcommands.h" #include #include @@ -30,15 +29,6 @@ TerminalSearch::TerminalSearch(TerminalSurface *surface) connect(surface, &TerminalSurface::invalidated, this, &TerminalSearch::updateHits); connect(&m_debounceTimer, &QTimer::timeout, this, &TerminalSearch::debouncedUpdateHits); - - connect(&TerminalCommands::widgetActions().findNext, - &QAction::triggered, - this, - &TerminalSearch::nextHit); - connect(&TerminalCommands::widgetActions().findPrevious, - &QAction::triggered, - this, - &TerminalSearch::previousHit); } void TerminalSearch::setCurrentSelection(std::optional selection) diff --git a/src/plugins/terminal/terminalsettings.cpp b/src/plugins/terminal/terminalsettings.cpp index 577367b6641..06903c4d4c5 100644 --- a/src/plugins/terminal/terminalsettings.cpp +++ b/src/plugins/terminal/terminalsettings.cpp @@ -365,13 +365,11 @@ TerminalSettings::TerminalSettings() { s_instance = this; - setAutoApply(false); setSettingsGroup("Terminal"); setId("Terminal.General"); setDisplayName("Terminal"); setCategory("ZY.Terminal"); setDisplayCategory("Terminal"); - setSettings(&TerminalSettings::instance()); setCategoryIconPath(":/terminal/images/settingscategory_terminal.png"); enableTerminal.setSettingsKey("EnableTerminal"); @@ -401,7 +399,6 @@ TerminalSettings::TerminalSettings() shell.setSettingsKey("ShellPath"); shell.setLabelText(Tr::tr("Shell path:")); shell.setExpectedKind(PathChooser::ExistingCommand); - shell.setDisplayStyle(StringAspect::PathChooserDisplay); shell.setHistoryCompleter("Terminal.Shell.History"); shell.setToolTip(Tr::tr("The shell executable to be started.")); shell.setDefaultValue(defaultShell()); @@ -483,7 +480,7 @@ TerminalSettings::TerminalSettings() auto loadThemeButton = new QPushButton(Tr::tr("Load Theme...")); auto resetTheme = new QPushButton(Tr::tr("Reset Theme")); - connect(loadThemeButton, &QPushButton::clicked, this, [this] { + connect(loadThemeButton, &QPushButton::clicked, this, [] { const FilePath path = FileUtils::getOpenFilePath( Core::ICore::dialogParent(), "Open Theme", diff --git a/src/plugins/terminal/terminalsettings.h b/src/plugins/terminal/terminalsettings.h index 30212b0c3de..4550bbce2a2 100644 --- a/src/plugins/terminal/terminalsettings.h +++ b/src/plugins/terminal/terminalsettings.h @@ -18,7 +18,7 @@ public: Utils::StringAspect font{this}; Utils::IntegerAspect fontSize{this}; - Utils::StringAspect shell{this}; + Utils::FilePathAspect shell{this}; Utils::StringAspect shellArguments{this}; Utils::ColorAspect foregroundColor; diff --git a/src/plugins/terminal/terminalsurface.cpp b/src/plugins/terminal/terminalsurface.cpp index ee8e571adb3..ea994302318 100644 --- a/src/plugins/terminal/terminalsurface.cpp +++ b/src/plugins/terminal/terminalsurface.cpp @@ -12,10 +12,10 @@ #include -Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg); - namespace Terminal::Internal { +Q_LOGGING_CATEGORY(log, "qtc.terminal.surface", QtWarningMsg); + QColor toQColor(const VTermColor &c) { return QColor(qRgb(c.rgb.red, c.rgb.green, c.rgb.blue)); diff --git a/src/plugins/terminal/terminalwidget.cpp b/src/plugins/terminal/terminalwidget.cpp index b63e66b78dd..12a0521857c 100644 --- a/src/plugins/terminal/terminalwidget.cpp +++ b/src/plugins/terminal/terminalwidget.cpp @@ -3,9 +3,10 @@ #include "terminalwidget.h" #include "glyphcache.h" -#include "terminalcommands.h" +#include "terminalconstants.h" #include "terminalsettings.h" #include "terminalsurface.h" +#include "terminaltr.h" #include @@ -20,6 +21,7 @@ #include #include #include +#include #include #include @@ -50,6 +52,7 @@ Q_LOGGING_CATEGORY(paintLog, "qtc.terminal.paint", QtWarningMsg) using namespace Utils; using namespace Utils::Terminal; +using namespace Core; namespace Terminal { @@ -69,10 +72,16 @@ static constexpr std::chrono::milliseconds minRefreshInterval = 1s / 30; TerminalWidget::TerminalWidget(QWidget *parent, const OpenTerminalParameters &openParameters) : QAbstractScrollArea(parent) + , 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()) { + auto contextObj = new IContext(this); + contextObj->setWidget(this); + contextObj->setContext(m_context); + ICore::addContextObject(contextObj); + setupSurface(); setupFont(); setupColors(); @@ -245,24 +254,31 @@ void TerminalWidget::setupColors() void TerminalWidget::setupActions() { - WidgetActions &a = TerminalCommands::widgetActions(); + 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); - auto ifHasFocus = [this](void (TerminalWidget::*f)()) { - return [this, f] { - if (hasFocus()) - (this->*f)(); - }; - }; + 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); - // clang-format off - connect(&a.copy, &QAction::triggered, this, ifHasFocus(&TerminalWidget::copyToClipboard)); - connect(&a.paste, &QAction::triggered, this, ifHasFocus(&TerminalWidget::pasteFromClipboard)); - connect(&a.copyLink, &QAction::triggered, this, ifHasFocus(&TerminalWidget::copyLinkToClipboard)); - connect(&a.clearSelection, &QAction::triggered, this, ifHasFocus(&TerminalWidget::clearSelection)); - connect(&a.clearTerminal, &QAction::triggered, this, ifHasFocus(&TerminalWidget::clearContents)); - connect(&a.moveCursorWordLeft, &QAction::triggered, this, ifHasFocus(&TerminalWidget::moveCursorWordLeft)); - connect(&a.moveCursorWordRight, &QAction::triggered, this, ifHasFocus(&TerminalWidget::moveCursorWordRight)); - // clang-format on + m_exit = unlockGlobalAction(Core::Constants::EXIT, m_context); + m_options = unlockGlobalAction(Core::Constants::OPTIONS, m_context); + m_settings = unlockGlobalAction("Preferences.Terminal.General", m_context); + m_findInDocument = unlockGlobalAction(Core::Constants::FIND_IN_DOCUMENT, m_context); +} + +void TerminalWidget::closeTerminal() +{ + deleteLater(); } void TerminalWidget::writeToPty(const QByteArray &data) @@ -385,8 +401,7 @@ void TerminalWidget::updateCopyState() if (!hasFocus()) return; - TerminalCommands::widgetActions().copy.setEnabled(m_selection.has_value()); - TerminalCommands::widgetActions().copyLink.setEnabled(m_linkSelection.has_value()); + m_copy.setEnabled(m_selection.has_value()); } void TerminalWidget::setFont(const QFont &font) @@ -511,6 +526,8 @@ QString TerminalWidget::textFromSelection() const Internal::CellIterator it = m_surface->iteratorAt(m_selection->start); Internal::CellIterator end = m_surface->iteratorAt(m_selection->end); + QTC_ASSERT(it.position() < end.position(), return {}); + std::u32string s; bool previousWasZero = false; for (; it != end; ++it) { @@ -1078,21 +1095,16 @@ void TerminalWidget::keyPressEvent(QKeyEvent *event) } if (m_selection) - TerminalCommands::widgetActions().clearSelection.trigger(); + m_clearSelection.trigger(); else { - QTC_ASSERT(Core::ActionManager::command(Core::Constants::S_RETURNTOEDITOR), return); - Core::ActionManager::command(Core::Constants::S_RETURNTOEDITOR)->action()->trigger(); + QAction *returnAction = ActionManager::command(Core::Constants::S_RETURNTOEDITOR) + ->actionForContext(Core::Constants::C_GLOBAL); + QTC_ASSERT(returnAction, return); + returnAction->trigger(); } return; } - auto oldSelection = m_selection; - if (TerminalCommands::triggerAction(event)) { - if (oldSelection && oldSelection == m_selection) - setSelection(std::nullopt); - return; - } - if (event->key() == Qt::Key_Control) { if (!m_linkSelection.has_value() && checkLinkAt(mapFromGlobal(QCursor::pos()))) { setCursor(Qt::PointingHandCursor); @@ -1236,7 +1248,7 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event) if (m_linkSelection->link.targetFilePath.isDir()) Core::FileUtils::showInFileSystemView(m_linkSelection->link.targetFilePath); else - Core::EditorManager::openEditorAt(m_linkSelection->link); + EditorManager::openEditorAt(m_linkSelection->link); } return; } @@ -1261,13 +1273,18 @@ void TerminalWidget::mousePressEvent(QMouseEvent *event) } else if (event->button() == Qt::RightButton) { if (event->modifiers() & Qt::ShiftModifier) { QMenu *contextMenu = new QMenu(this); - contextMenu->addAction(&TerminalCommands::widgetActions().copy); - contextMenu->addAction(&TerminalCommands::widgetActions().paste); - contextMenu->addAction(&TerminalCommands::widgetActions().copyLink); + QAction *configureAction = new QAction(contextMenu); + configureAction->setText(Tr::tr("Configure...")); + connect(configureAction, &QAction::triggered, this, [] { + ICore::showOptionsDialog("Terminal.General"); + }); + + contextMenu->addAction(ActionManager::command(Constants::COPY)->action()); + contextMenu->addAction(ActionManager::command(Constants::PASTE)->action()); contextMenu->addSeparator(); - contextMenu->addAction(&TerminalCommands::widgetActions().clearTerminal); + contextMenu->addAction(ActionManager::command(Constants::CLEAR_TERMINAL)->action()); contextMenu->addSeparator(); - contextMenu->addAction(TerminalCommands::openSettingsAction()); + contextMenu->addAction(configureAction); contextMenu->popup(event->globalPos()); } else if (m_selection) { @@ -1472,11 +1489,10 @@ void TerminalWidget::showEvent(QShowEvent *event) bool TerminalWidget::event(QEvent *event) { - if (event->type() == QEvent::ShortcutOverride) { - if (hasFocus()) { - event->accept(); - return true; - } + if (event->type() == QEvent::Paint) { + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]); + return true; } if (event->type() == QEvent::KeyPress) { @@ -1490,13 +1506,63 @@ bool TerminalWidget::event(QEvent *event) return true; } - if (event->type() == QEvent::Paint) { - QPainter p(this); - p.fillRect(QRect(QPoint(0, 0), size()), m_currentColors[ColorIndex::Background]); - return true; - } - return QAbstractScrollArea::event(event); } +void TerminalWidget::initActions() +{ + Core::Context context(Utils::Id("TerminalWidget")); + + static QAction copy; + static QAction paste; + static QAction clearSelection; + static QAction clearTerminal; + static QAction moveCursorWordLeft; + static QAction moveCursorWordRight; + static QAction close; + + copy.setText(Tr::tr("Copy")); + paste.setText(Tr::tr("Paste")); + clearSelection.setText(Tr::tr("Clear Selection")); + clearTerminal.setText(Tr::tr("Clear Terminal")); + moveCursorWordLeft.setText(Tr::tr("Move Cursor Word Left")); + moveCursorWordRight.setText(Tr::tr("Move Cursor Word Right")); + close.setText(Tr::tr("Close Terminal")); + + ActionManager::registerAction(©, Constants::COPY, context) + ->setDefaultKeySequences({QKeySequence( + HostOsInfo::isMacHost() ? QLatin1String("Ctrl+C") : QLatin1String("Ctrl+Shift+C"))}); + + ActionManager::registerAction(&paste, Constants::PASTE, context) + ->setDefaultKeySequences({QKeySequence( + HostOsInfo::isMacHost() ? QLatin1String("Ctrl+V") : QLatin1String("Ctrl+Shift+V"))}); + + ActionManager::registerAction(&clearSelection, Constants::CLEARSELECTION, context); + + ActionManager::registerAction(&moveCursorWordLeft, Constants::MOVECURSORWORDLEFT, context) + ->setDefaultKeySequences({QKeySequence("Alt+Left")}); + + ActionManager::registerAction(&moveCursorWordRight, Constants::MOVECURSORWORDRIGHT, context) + ->setDefaultKeySequences({QKeySequence("Alt+Right")}); + + ActionManager::registerAction(&clearTerminal, Constants::CLEAR_TERMINAL, context); +} + +UnlockedGlobalAction TerminalWidget::unlockGlobalAction(const Utils::Id &commandId, + const Context &context) +{ + QAction *srcAction = ActionManager::command(commandId)->actionForContext( + Core::Constants::C_GLOBAL); + + ProxyAction *proxy = ProxyAction::proxyActionWithIcon(srcAction, srcAction->icon()); + ActionManager::registerAction(proxy, commandId, context); + + UnlockedGlobalAction registeredAction(proxy, [commandId](QAction *a) { + ActionManager::unregisterAction(a, commandId); + delete a; + }); + + return registeredAction; +} + } // namespace Terminal diff --git a/src/plugins/terminal/terminalwidget.h b/src/plugins/terminal/terminalwidget.h index f44a721bd31..4b82e4355a1 100644 --- a/src/plugins/terminal/terminalwidget.h +++ b/src/plugins/terminal/terminalwidget.h @@ -8,6 +8,8 @@ #include +#include + #include #include #include @@ -22,6 +24,8 @@ namespace Terminal { +using UnlockedGlobalAction = std::unique_ptr>; + class TerminalWidget : public QAbstractScrollArea { friend class CellIterator; @@ -46,6 +50,8 @@ public: void clearContents(); + void closeTerminal(); + TerminalSearch *search() { return m_search.get(); } struct Selection @@ -82,6 +88,11 @@ public: void restart(const Utils::Terminal::OpenTerminalParameters &openParameters); + static void initActions(); + + [[nodiscard]] static UnlockedGlobalAction unlockGlobalAction(const Utils::Id &commandId, + const Core::Context &context); + signals: void started(qint64 pid); void cwdChanged(const Utils::FilePath &cwd); @@ -178,6 +189,7 @@ protected: void updateCopyState(); private: + Core::Context m_context; std::unique_ptr m_process; std::unique_ptr m_surface; std::unique_ptr m_shellIntegration; @@ -226,6 +238,19 @@ 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; + + UnlockedGlobalAction m_findInDocument; + UnlockedGlobalAction m_exit; + UnlockedGlobalAction m_options; + UnlockedGlobalAction m_settings; }; } // namespace Terminal diff --git a/src/plugins/texteditor/CMakeLists.txt b/src/plugins/texteditor/CMakeLists.txt index eebe8bf0bed..048cd40b1d5 100644 --- a/src/plugins/texteditor/CMakeLists.txt +++ b/src/plugins/texteditor/CMakeLists.txt @@ -113,5 +113,7 @@ add_qtc_plugin(TextEditor extend_qtc_plugin(TextEditor CONDITION WITH_TESTS - SOURCES texteditor_test.cpp + SOURCES + codeassist/codeassist_test.cpp codeassist/codeassist_test.h + texteditor_test.cpp ) diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index a98cd210826..66e44e714b9 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +40,98 @@ using namespace Utils; using namespace Core; namespace TextEditor { + +static std::optional regExpFromParameters(const FileFindParameters ¶meters) +{ + if (!(parameters.flags & FindRegularExpression)) + return {}; + + const QRegularExpression::PatternOptions options = parameters.flags & FindCaseSensitively + ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption; + QRegularExpression regExp; + regExp.setPattern(parameters.text); + regExp.setPatternOptions(options); + return regExp; +} + +void searchInProcessOutput(QPromise &promise, + const FileFindParameters ¶meters, + const ProcessSetupHandler &processSetupHandler, + const ProcessOutputParser &processOutputParser) +{ + if (promise.isCanceled()) + return; + + QEventLoop loop; + + Process process; + processSetupHandler(process); + + const std::optional regExp = regExpFromParameters(parameters); + QStringList outputBuffer; + // The states transition exactly in this order: + enum State { BelowLimit, AboveLimit, Paused, Resumed }; + State state = BelowLimit; + int reportedItemsCount = 0; + QFuture future(promise.future()); + process.setStdOutCallback([&](const QString &output) { + if (promise.isCanceled()) { + process.close(); + loop.quit(); + return; + } + // The SearchResultWidget is going to pause the search anyway, so start buffering + // the output. + if (state == AboveLimit || state == Paused) { + outputBuffer.append(output); + } else { + const SearchResultItems items = processOutputParser(future, output, regExp); + if (!items.isEmpty()) + promise.addResult(items); + reportedItemsCount += items.size(); + if (state == BelowLimit && reportedItemsCount > 200000) + state = AboveLimit; + } + }); + QObject::connect(&process, &Process::done, &loop, [&loop, &promise, &state] { + if (state == BelowLimit || state == Resumed || promise.isCanceled()) + loop.quit(); + }); + + if (promise.isCanceled()) + return; + + process.start(); + if (process.state() == QProcess::NotRunning) + return; + + QFutureWatcher watcher; + QObject::connect(&watcher, &QFutureWatcherBase::canceled, &loop, [&process, &loop] { + process.close(); + loop.quit(); + }); + QObject::connect(&watcher, &QFutureWatcherBase::paused, &loop, [&state] { state = Paused; }); + QObject::connect(&watcher, &QFutureWatcherBase::resumed, &loop, [&] { + state = Resumed; + for (const QString &output : outputBuffer) { + if (promise.isCanceled()) { + process.close(); + loop.quit(); + } + const SearchResultItems items = processOutputParser(future, output, regExp); + if (!items.isEmpty()) + promise.addResult(items); + } + outputBuffer.clear(); + if (process.state() == QProcess::NotRunning) + loop.quit(); + }); + watcher.setFuture(future); + if (promise.isCanceled()) + return; + loop.exec(QEventLoop::ExcludeUserInputEvents); +} + namespace Internal { class InternalEngine : public TextEditor::SearchEngine diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index 7da5bec9038..538314c42f8 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -20,6 +20,7 @@ class SearchResult; namespace Utils { class FileIterator; +class Process; } namespace TextEditor { @@ -41,6 +42,16 @@ public: Utils::FindFlags flags; }; +using ProcessSetupHandler = std::function; +using ProcessOutputParser = std::function &, const QString &, const std::optional &)>; + +// Call it from a non-main thread only, it's a blocking invocation. +void TEXTEDITOR_EXPORT searchInProcessOutput(QPromise &promise, + const FileFindParameters ¶meters, + const ProcessSetupHandler &processSetupHandler, + const ProcessOutputParser &processOutputParser); + class BaseFileFind; class TEXTEDITOR_EXPORT SearchEngine : public QObject 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/codeassist_test.cpp b/src/plugins/texteditor/codeassist/codeassist_test.cpp new file mode 100644 index 00000000000..8b724dbe166 --- /dev/null +++ b/src/plugins/texteditor/codeassist/codeassist_test.cpp @@ -0,0 +1,152 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifdef WITH_TESTS + +#include "codeassist_test.h" + +#include "../texteditor.h" + +#include "assistinterface.h" +#include "assistproposaliteminterface.h" +#include "asyncprocessor.h" +#include "completionassistprovider.h" +#include "genericproposal.h" +#include "genericproposalwidget.h" + +#include +#include +#include + +#include + +namespace TextEditor::Internal { + +class TestProposalItem : public AssistProposalItemInterface +{ +public: + QString m_text = "test"; + bool m_implicitlyApplies = false; + bool m_prematurelyApplies = false; + QIcon m_icon; + QString m_detail = "detail"; + bool m_isSnippet = false; + bool m_isValid = true; + + QString text() const override { return m_text; } + bool implicitlyApplies() const override { return m_implicitlyApplies; } + bool prematurelyApplies(const QChar &) const override { return m_prematurelyApplies; } + QIcon icon() const override { return m_icon; } + QString detail() const override { return m_detail; } + bool isSnippet() const override { return m_isSnippet; } + bool isValid() const override { return m_isValid; } + quint64 hash() const override { return 0; } // used to remove duplicates +}; + +class OpenEditorItem : public TestProposalItem +{ +public: + void apply(TextDocumentManipulatorInterface &, int) const override + { + m_openedEditor = Core::EditorManager::openEditor(m_filePath, + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID); + } + + mutable Core::IEditor *m_openedEditor = nullptr; + Utils::FilePath m_filePath; +}; + +class TestProposalWidget : public GenericProposalWidget +{ +public: + void showProposal(const QString &prefix) override + { + GenericProposalModelPtr proposalModel = model(); + if (proposalModel && proposalModel->size() == 1) { + emit proposalItemActivated(proposalModel->proposalItem(0)); + deleteLater(); + return; + } + GenericProposalWidget::showProposal(prefix); + } +}; + +class TestProposal : public GenericProposal +{ +public: + TestProposal(int pos, const QList &items) + : GenericProposal(pos, items) + {} + IAssistProposalWidget *createWidget() const override { return new TestProposalWidget; } +}; + +class TestProcessor : public AsyncProcessor +{ +public: + TestProcessor(const QList &items) + : m_items(items) + {} + IAssistProposal *performAsync() override + { return new TestProposal(interface()->position(), m_items); } + QList m_items; +}; + +class TestProvider : public CompletionAssistProvider +{ +public: + IAssistProcessor *createProcessor(const AssistInterface *assistInterface) const override + { + Q_UNUSED(assistInterface); + return new TestProcessor(m_items); + } + QList m_items; +}; + +void CodeAssistTests::initTestCase() +{ + Core::IEditor *editor = Core::EditorManager::openEditorWithContents( + Core::Constants::K_DEFAULT_TEXT_EDITOR_ID); + QVERIFY(editor); + m_editor = qobject_cast(editor); + QVERIFY(m_editor); + m_editorsToClose << m_editor; + m_testProvider = new TestProvider(); +} + +static Utils::FilePath createBigFile() +{ + constexpr int textChunkSize = 65536; // from utils/textfileformat.cpp + + const Utils::FilePath result = Utils::TemporaryDirectory::masterDirectoryFilePath() / "BigFile"; + QByteArray data; + data.reserve(textChunkSize); + while (data.size() < textChunkSize) + data.append("bigfile line\n"); + result.writeFileContents(data); + return result; +} + +void CodeAssistTests::testFollowSymbolBigFile() +{ + auto item = new OpenEditorItem; + item->m_filePath = createBigFile(); + m_testProvider->m_items = {item}; + auto editorWidget = m_editor->editorWidget(); + + editorWidget->invokeAssist(FollowSymbol, m_testProvider); + QSignalSpy spy(editorWidget, &TextEditorWidget::assistFinished); + QVERIFY(spy.wait(1000)); + QVERIFY(item->m_openedEditor); + m_editorsToClose << item->m_openedEditor; +} + +void CodeAssistTests::cleanupTestCase() +{ + m_testProvider->m_items.clear(); + Core::EditorManager::closeEditors(m_editorsToClose); + QVERIFY(Core::EditorManager::currentEditor() == nullptr); +} + +} // namespace TextEditor::Internal + +#endif // ifdef WITH_TESTS diff --git a/src/plugins/texteditor/codeassist/codeassist_test.h b/src/plugins/texteditor/codeassist/codeassist_test.h new file mode 100644 index 00000000000..3bf734aadfb --- /dev/null +++ b/src/plugins/texteditor/codeassist/codeassist_test.h @@ -0,0 +1,37 @@ +// Copyright (C) 2016 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 + +#ifdef WITH_TESTS + +#include + +namespace Core { class IEditor; } +namespace TextEditor { class BaseTextEditor; } + +namespace TextEditor::Internal { + +class TestProvider; + +class CodeAssistTests : public QObject +{ + Q_OBJECT +public: + +private slots: + void initTestCase(); + + void testFollowSymbolBigFile(); + + void cleanupTestCase(); + +private: + TextEditor::BaseTextEditor *m_editor = nullptr; + QList m_editorsToClose; + TestProvider *m_testProvider = nullptr; +}; + +} // namespace TextEditor::Internal + +#endif 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/markdowneditor.cpp b/src/plugins/texteditor/markdowneditor.cpp index 12e60f027af..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)); @@ -245,6 +245,8 @@ public: if (obj == m_widget && ev->type() == QEvent::FocusIn) { if (m_splitter->focusWidget()) m_splitter->focusWidget()->setFocus(); + else if (m_textEditorWidget->isVisible()) + m_textEditorWidget->setFocus(); else m_splitter->widget(0)->setFocus(); return true; diff --git a/src/plugins/texteditor/semantichighlighter.cpp b/src/plugins/texteditor/semantichighlighter.cpp index 68b1792ca84..30cb9cf729a 100644 --- a/src/plugins/texteditor/semantichighlighter.cpp +++ b/src/plugins/texteditor/semantichighlighter.cpp @@ -82,26 +82,28 @@ void SemanticHighlighter::incrementalApplyExtraAdditionalFormats(SyntaxHighlight if (to <= from) return; - const int firstResultBlockNumber = int(future.resultAt(from).line) - 1; + const int resultStartLine = future.resultAt(from).line; + int formattingStartLine = 1; - // blocks between currentBlockNumber and the last block with results will - // be cleaned of additional extra formats if they have no results - int currentBlockNumber = 0; + // Find the line on which to start formatting, where "formatting" means to either + // clear out formats from outdated document versions (if there is no current result + // on that line), or apply the format corresponding to the respective result. + // Note that if there are earlier results on the same line, we have to make sure they + // get re-applied by adapting the from variable accordingly. for (int i = from - 1; i >= 0; --i) { const HighlightingResult &result = future.resultAt(i); - const int blockNumber = int(result.line) - 1; - if (blockNumber < firstResultBlockNumber) { - // stop! found where last format stopped - currentBlockNumber = blockNumber + 1; - // add previous results for the same line to avoid undoing their formats + if (result.line == resultStartLine) { + from = i; + } else if (result.line < resultStartLine) { + formattingStartLine = result.line + 1; from = i + 1; break; } } QTextDocument *doc = highlighter->document(); - QTC_ASSERT(currentBlockNumber < doc->blockCount(), return); - QTextBlock currentBlock = doc->findBlockByNumber(currentBlockNumber); + QTC_ASSERT(formattingStartLine <= doc->blockCount(), return); + QTextBlock currentBlock = doc->findBlockByNumber(formattingStartLine - 1); std::map> formatRanges; for (int i = from; i < to; ++i) { diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 7388a3ae393..97c878e4425 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -51,7 +51,6 @@ #include #include #include -#include #include #include #include @@ -92,9 +91,10 @@ #include #include #include -#include +#include #include #include +#include #include #include #include @@ -1426,7 +1426,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); @@ -5275,7 +5275,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); diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 1b94811a563..071e0b5fc2a 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -224,6 +224,8 @@ Project { QtcTestFiles { files: [ + "codeassist/codeassist_test.cpp", + "codeassist/codeassist_test.h", "texteditor_test.cpp", ] } diff --git a/src/plugins/texteditor/texteditor_test.cpp b/src/plugins/texteditor/texteditor_test.cpp index e939e515681..3b88ac5efb5 100644 --- a/src/plugins/texteditor/texteditor_test.cpp +++ b/src/plugins/texteditor/texteditor_test.cpp @@ -3,20 +3,13 @@ #ifdef WITH_TESTS -#include -#include -#include +#include "tabsettings.h" +#include "texteditorplugin.h" + +#include #include -#include -#include - -#include "texteditor.h" -#include "texteditorplugin.h" -#include "textdocument.h" -#include "tabsettings.h" - -using namespace TextEditor; +namespace TextEditor { QString tabPolicyToString(TabSettings::TabPolicy policy) { @@ -149,4 +142,6 @@ void Internal::TextEditorPlugin::testIndentationClean() QCOMPARE(settings.isIndentationClean(block, indentSize), clean); } +} // namespace TextEditor + #endif // ifdef WITH_TESTS diff --git a/src/plugins/texteditor/texteditorplugin.cpp b/src/plugins/texteditor/texteditorplugin.cpp index a66ddf23c32..4d5c2268d1e 100644 --- a/src/plugins/texteditor/texteditorplugin.cpp +++ b/src/plugins/texteditor/texteditorplugin.cpp @@ -20,6 +20,10 @@ #include "texteditorsettings.h" #include "texteditortr.h" +#ifdef WITH_TESTS +#include "codeassist/codeassist_test.h" +#endif + #include #include #include @@ -142,6 +146,10 @@ void TextEditorPlugin::initialize() Tr::tr("Text", "SnippetProvider")); d->createStandardContextMenu(); + +#ifdef WITH_TESTS + addTest(); +#endif } void TextEditorPluginPrivate::extensionsInitialized() 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/vcpkg/vcpkgsettings.cpp b/src/plugins/vcpkg/vcpkgsettings.cpp index a450373ab52..3542ec47910 100644 --- a/src/plugins/vcpkg/vcpkgsettings.cpp +++ b/src/plugins/vcpkg/vcpkgsettings.cpp @@ -4,6 +4,7 @@ #include "vcpkgsettings.h" #include "vcpkgconstants.h" +#include "vcpkgtr.h" #include @@ -50,10 +51,10 @@ VcpkgSettings::VcpkgSettings() using namespace Layouting; return Column { Group { - title(tr("Vcpkg installation")), + title(Tr::tr("Vcpkg installation")), Form { Utils::PathChooser::label(), - Span{ 2, Row{ vcpkgRoot, websiteButton} }, + Span { 2, Row { vcpkgRoot, websiteButton } }, }, }, st, diff --git a/src/plugins/vcsbase/commonvcssettings.cpp b/src/plugins/vcsbase/commonvcssettings.cpp index e33de123790..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,13 +28,26 @@ 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.setDisplayStyle(StringAspect::PathChooserDisplay); nickNameMailMap.setExpectedKind(PathChooser::File); nickNameMailMap.setHistoryCompleter("Vcs.NickMap.History"); nickNameMailMap.setLabelText(Tr::tr("User/&alias configuration file:")); @@ -47,7 +55,6 @@ CommonVcsSettings::CommonVcsSettings() "'name alias '.")); nickNameFieldListFile.setSettingsKey("NickNameFieldListFile"); - nickNameFieldListFile.setDisplayStyle(StringAspect::PathChooserDisplay); nickNameFieldListFile.setExpectedKind(PathChooser::File); nickNameFieldListFile.setHistoryCompleter("Vcs.NickFields.History"); nickNameFieldListFile.setLabelText(Tr::tr("User &fields configuration file:")); @@ -55,7 +62,6 @@ CommonVcsSettings::CommonVcsSettings() "\"Reviewed-By:\" which will be added below the submit editor.")); submitMessageCheckScript.setSettingsKey("SubmitMessageCheckScript"); - submitMessageCheckScript.setDisplayStyle(StringAspect::PathChooserDisplay); submitMessageCheckScript.setExpectedKind(PathChooser::ExistingCommand); submitMessageCheckScript.setHistoryCompleter("Vcs.MessageCheckScript.History"); submitMessageCheckScript.setLabelText(Tr::tr("Submit message &check script:")); @@ -64,7 +70,6 @@ CommonVcsSettings::CommonVcsSettings() "on standard error to indicate failure.")); sshPasswordPrompt.setSettingsKey("SshPasswordPrompt"); - sshPasswordPrompt.setDisplayStyle(StringAspect::PathChooserDisplay); sshPasswordPrompt.setExpectedKind(PathChooser::ExistingCommand); sshPasswordPrompt.setHistoryCompleter("Vcs.SshPrompt.History"); sshPasswordPrompt.setDefaultValue(sshPasswordPromptDefault()); @@ -80,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 44bf03c5776..0f591b27111 100644 --- a/src/plugins/vcsbase/commonvcssettings.h +++ b/src/plugins/vcsbase/commonvcssettings.h @@ -4,42 +4,27 @@ #pragma once #include - -#include +#include namespace VcsBase::Internal { -class CommonVcsSettings : public Utils::AspectContainer +class CommonVcsSettings : public Core::PagedSettings { - Q_OBJECT - public: CommonVcsSettings(); - Utils::StringAspect nickNameMailMap{this}; - Utils::StringAspect nickNameFieldListFile{this}; + Utils::FilePathAspect nickNameMailMap{this}; + Utils::FilePathAspect nickNameFieldListFile{this}; - Utils::StringAspect submitMessageCheckScript{this}; + Utils::FilePathAspect submitMessageCheckScript{this}; // Executable run to graphically prompt for a SSH-password. - Utils::StringAspect sshPasswordPrompt{this}; + Utils::FilePathAspect sshPasswordPrompt{this}; 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/submiteditorwidget.cpp b/src/plugins/vcsbase/submiteditorwidget.cpp index 63a75e7ac06..00b4200580a 100644 --- a/src/plugins/vcsbase/submiteditorwidget.cpp +++ b/src/plugins/vcsbase/submiteditorwidget.cpp @@ -619,15 +619,16 @@ void SubmitEditorWidget::verifyDescription() d->descriptionHint->setText(hints.join("
")); if (!d->descriptionHint->text().isEmpty()) { + static_assert(MaxSubjectLength == 72); // change the translated message below when changing d->descriptionHint->setToolTip( Tr::tr("

Writing good commit messages

" "
    " "
  • Avoid very short commit messages.
  • " - "
  • Consider the first line as subject (like in email) " - "and keep it shorter than %n characters.
  • " + "
  • Consider the first line as a subject (like in emails) " + "and keep it shorter than 72 characters.
  • " "
  • After an empty second line, a longer description can be added.
  • " "
  • Describe why the change was done, not how it was done.
  • " - "
", nullptr, MaxSubjectLength)); + "")); } } 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/src/plugins/vcsbase/vcsbaseplugin.cpp b/src/plugins/vcsbase/vcsbaseplugin.cpp index 517ffbdf48f..b73f97dd79c 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.cpp +++ b/src/plugins/vcsbase/vcsbaseplugin.cpp @@ -593,9 +593,27 @@ bool VcsBasePluginPrivate::enableMenuAction(ActionState as, QAction *menuAction) QString VcsBasePluginPrivate::commitDisplayName() const { + //: Name of the "commit" action of the VCS return Tr::tr("Commit", "name of \"commit\" action of the VCS."); } +QString VcsBasePluginPrivate::commitAbortTitle() const +{ + return Tr::tr("Close Commit Editor"); +} + +QString VcsBasePluginPrivate::commitAbortMessage() const +{ + return Tr::tr("Closing this editor will abort the commit."); +} + +QString VcsBasePluginPrivate::commitErrorMessage(const QString &error) const +{ + if (error.isEmpty()) + return Tr::tr("Cannot commit."); + return Tr::tr("Cannot commit: %1.").arg(error); +} + void VcsBasePluginPrivate::commitFromEditor() { QTC_ASSERT(m_submitEditor, return); @@ -732,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/vcsbaseplugin.h b/src/plugins/vcsbase/vcsbaseplugin.h index af96e12631a..5a4c9ae6b30 100644 --- a/src/plugins/vcsbase/vcsbaseplugin.h +++ b/src/plugins/vcsbase/vcsbaseplugin.h @@ -135,6 +135,9 @@ public: const QStringList &extraArgs); // Display name of the commit action virtual QString commitDisplayName() const; + virtual QString commitAbortTitle() const; + virtual QString commitAbortMessage() const; + virtual QString commitErrorMessage(const QString &error) const; void commitFromEditor(); virtual bool activateCommit() = 0; diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index 1895a64f4d1..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 @@ -453,11 +447,7 @@ void VcsBaseSubmitEditor::accept(VcsBasePluginPrivate *plugin) QString errorMessage; const bool canCommit = checkSubmitMessage(&errorMessage) && submitWidget->canSubmit(&errorMessage); if (!canCommit) { - VcsOutputWindow::appendError( - Tr::tr("Cannot %1%2.", - "%2 is an optional error message with ': ' prefix. Don't add space in front.") - .arg(plugin->commitDisplayName().toLower(), - errorMessage.isEmpty() ? errorMessage : ": " + errorMessage)); + VcsOutputWindow::appendError(plugin->commitErrorMessage(errorMessage)); } else if (plugin->activateCommit()) { close(); } @@ -480,12 +470,10 @@ bool VcsBaseSubmitEditor::promptSubmit(VcsBasePluginPrivate *plugin) if (!submitWidget->isEnabled() || !submitWidget->isEdited()) return true; - const QString commitName = plugin->commitDisplayName(); QMessageBox mb(Core::ICore::dialogParent()); - mb.setWindowTitle(Tr::tr("Close %1 %2 Editor").arg(plugin->displayName(), commitName)); + mb.setWindowTitle(plugin->commitAbortTitle()); mb.setIcon(QMessageBox::Warning); - mb.setText(Tr::tr("Closing this editor will abort the %1.") - .arg(commitName.toLower())); + mb.setText(plugin->commitAbortMessage()); mb.setStandardButtons(QMessageBox::Close | QMessageBox::Cancel); // On Windows there is no mnemonic for Close. Set it explicitly. mb.button(QMessageBox::Close)->setText(Tr::tr("&Close")); @@ -533,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: diff --git a/src/plugins/webassembly/webassemblysettings.cpp b/src/plugins/webassembly/webassemblysettings.cpp index 1af1d30c911..fb9293f2c84 100644 --- a/src/plugins/webassembly/webassemblysettings.cpp +++ b/src/plugins/webassembly/webassemblysettings.cpp @@ -20,6 +20,7 @@ #include #include +#include using namespace Utils; @@ -66,7 +67,7 @@ WebAssemblySettings::WebAssemblySettings() connect(this, &Utils::AspectContainer::applied, &WebAssemblyToolChain::registerToolChains); - setLayouter([this](QWidget *widget) { + setLayouter([this] { auto instruction = new QLabel( Tr::tr("Select the root directory of an installed %1. " "Ensure that the activated SDK version is compatible with the %2 " @@ -96,7 +97,7 @@ WebAssemblySettings::WebAssemblySettings() // _clang-format off using namespace Layouting; - Column { + Column col { Group { title(Tr::tr("Emscripten SDK path:")), Column { @@ -113,12 +114,17 @@ WebAssemblySettings::WebAssemblySettings() }, }, m_qtVersionDisplay, - }.attachTo(widget); + }; // _clang-format on - updateStatus(); connect(emSdk.pathChooser(), &Utils::PathChooser::textChanged, this, &WebAssemblySettings::updateStatus); + + // updateStatus() uses m_emSdkEnvGroupBox which only exists + // after this here emerges. So delay the update a bit. + QTimer::singleShot(0, this, &WebAssemblySettings::updateStatus); + + return col; }); readSettings(); @@ -131,6 +137,8 @@ void WebAssemblySettings::updateStatus() const Utils::FilePath newEmSdk = emSdk.pathChooser()->filePath(); const bool sdkValid = newEmSdk.exists() && WebAssemblyEmSdk::isValid(newEmSdk); + QTC_ASSERT(m_emSdkVersionDisplay, return); + QTC_ASSERT(m_emSdkEnvGroupBox, return); m_emSdkVersionDisplay->setVisible(sdkValid); m_emSdkEnvGroupBox->setEnabled(sdkValid); diff --git a/src/shared/qbs b/src/shared/qbs index eaeba4a45e7..43af7ba7fa1 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit eaeba4a45e7d95b7bb3f4fe907e862d947faf092 +Subproject commit 43af7ba7fa12de25268d2cc5582b963e360022e5 diff --git a/tests/auto/solutions/tasking/CMakeLists.txt b/tests/auto/solutions/tasking/CMakeLists.txt index 534d215ccb1..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 Utils + 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 173c1fc5752..d6ffa884920 100644 --- a/tests/auto/solutions/tasking/tasking.qbs +++ b/tests/auto/solutions/tasking/tasking.qbs @@ -1,7 +1,8 @@ QtcAutotest { name: "Tasking autotest" - Depends { name: "Utils" } + Depends { name: "Qt"; submodules: ["network"] } + Depends { name: "Tasking" } files: "tst_tasking.cpp" } diff --git a/tests/auto/solutions/tasking/tst_tasking.cpp b/tests/auto/solutions/tasking/tst_tasking.cpp index 17335d29f41..69d87e8188e 100644 --- a/tests/auto/solutions/tasking/tst_tasking.cpp +++ b/tests/auto/solutions/tasking/tst_tasking.cpp @@ -1,21 +1,17 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 -#include - -#include +#include #include -#include - -using namespace std::literals::chrono_literals; - -using namespace Utils; using namespace Tasking; -using TestTask = Async; -using Test = AsyncTask; +using namespace std::chrono; +using namespace std::chrono_literals; + +using TaskObject = milliseconds; +using TestTask = TimeoutTask; namespace PrintableEnums { @@ -29,7 +25,8 @@ enum class Handler { GroupDone, GroupError, Sync, - BarrierAdvance + BarrierAdvance, + Timeout }; Q_ENUM_NS(Handler); @@ -55,8 +52,6 @@ private: int CustomStorage::s_count = 0; static const char s_taskIdProperty[] = "__taskId"; -static FutureSynchronizer *s_futureSynchronizer = nullptr; - struct TestData { TreeStorage storage; Group root; @@ -70,35 +65,20 @@ class tst_Tasking : public QObject Q_OBJECT private slots: - void initTestCase(); - void validConstructs(); // compile test void testTree_data(); void testTree(); void storageOperators(); void storageDestructor(); - - void cleanupTestCase(); }; -void tst_Tasking::initTestCase() -{ - s_futureSynchronizer = new FutureSynchronizer; -} - -void tst_Tasking::cleanupTestCase() -{ - delete s_futureSynchronizer; - s_futureSynchronizer = nullptr; -} - void tst_Tasking::validConstructs() { const Group task { parallel, - Test([](TestTask &) {}, [](const TestTask &) {}), - Test([](TestTask &) {}, [](const TestTask &) {}), - Test([](TestTask &) {}, [](const TestTask &) {}) + TestTask([](TaskObject &) {}, [](const TaskObject &) {}), + TestTask([](TaskObject &) {}, [](const TaskObject &) {}), + TestTask([](TaskObject &) {}, [](const TaskObject &) {}) }; const Group group1 { @@ -109,18 +89,18 @@ void tst_Tasking::validConstructs() parallel, Group { parallel, - Test([](TestTask &) {}, [](const TestTask &) {}), + TestTask([](TaskObject &) {}, [](const TaskObject &) {}), Group { parallel, - Test([](TestTask &) {}, [](const TestTask &) {}), + TestTask([](TaskObject &) {}, [](const TaskObject &) {}), Group { parallel, - Test([](TestTask &) {}, [](const TestTask &) {}) + TestTask([](TaskObject &) {}, [](const TaskObject &) {}) } }, Group { parallel, - Test([](TestTask &) {}, [](const TestTask &) {}), + TestTask([](TaskObject &) {}, [](const TaskObject &) {}), onGroupDone([] {}) } }, @@ -129,32 +109,32 @@ void tst_Tasking::validConstructs() onGroupError([] {}) }; - const auto setupHandler = [](TestTask &) {}; - const auto doneHandler = [](const TestTask &) {}; - const auto errorHandler = [](const TestTask &) {}; + const auto setupHandler = [](TaskObject &) {}; + const auto doneHandler = [](const TaskObject &) {}; + const auto errorHandler = [](const TaskObject &) {}; // Not fluent interface const Group task2 { parallel, - Test(setupHandler), - Test(setupHandler, doneHandler), - Test(setupHandler, doneHandler, errorHandler), + TestTask(setupHandler), + TestTask(setupHandler, doneHandler), + TestTask(setupHandler, doneHandler, errorHandler), // need to explicitly pass empty handler for done - Test(setupHandler, {}, errorHandler) + TestTask(setupHandler, {}, errorHandler) }; // Fluent interface const Group fluent { parallel, - Test().onSetup(setupHandler), - Test().onSetup(setupHandler).onDone(doneHandler), - Test().onSetup(setupHandler).onDone(doneHandler).onError(errorHandler), + TestTask().onSetup(setupHandler), + TestTask().onSetup(setupHandler).onDone(doneHandler), + TestTask().onSetup(setupHandler).onDone(doneHandler).onError(errorHandler), // possible to skip the empty done - Test().onSetup(setupHandler).onError(errorHandler), + TestTask().onSetup(setupHandler).onError(errorHandler), // possible to set handlers in a different order - Test().onError(errorHandler).onDone(doneHandler).onSetup(setupHandler), + TestTask().onError(errorHandler).onDone(doneHandler).onSetup(setupHandler), }; @@ -185,43 +165,53 @@ void tst_Tasking::validConstructs() #endif } -static void runTask(QPromise &promise, bool success, std::chrono::milliseconds sleep) +class TickAndDone : public QObject { - QDeadlineTimer deadline(sleep); - while (!deadline.hasExpired()) { - QThread::msleep(1); - if (promise.isCanceled()) - return; - } - if (!success) - promise.future().cancel(); -} + Q_OBJECT -static void reportAndSleep(QPromise &promise) -{ - promise.addResult(false); - QThread::msleep(5); +public: + void setInterval(const milliseconds &interval) { m_interval = interval; } + void start() { + QTimer::singleShot(0, this, [this] { + emit tick(); + QTimer::singleShot(m_interval, this, &TickAndDone::done); + }); + } + +signals: + void tick(); + void done(); + +private: + milliseconds m_interval; }; -template -auto setupBarrierAdvance(const TreeStorage &storage, - const SharedBarrierType &barrier, int taskId) +class TickAndDoneTaskAdapter : public TaskAdapter { - return [storage, barrier, taskId](Async &async) { - async.setFutureSynchronizer(s_futureSynchronizer); - async.setConcurrentCallData(reportAndSleep); - async.setProperty(s_taskIdProperty, taskId); +public: + TickAndDoneTaskAdapter() { connect(task(), &TickAndDone::done, this, + [this] { emit done(true); }); } + void start() final { task()->start(); } +}; + +TASKING_DECLARE_TASK(TickAndDoneTask, TickAndDoneTaskAdapter); + +template +GroupItem createBarrierAdvance(const TreeStorage &storage, + const SharedBarrierType &barrier, int taskId) +{ + return TickAndDoneTask([storage, barrier, taskId](TickAndDone &tickAndDone) { + tickAndDone.setInterval(1ms); storage->m_log.append({taskId, Handler::Setup}); CustomStorage *currentStorage = storage.activeStorage(); Barrier *sharedBarrier = barrier->barrier(); - QObject::connect(&async, &TestTask::resultReadyAt, sharedBarrier, - [currentStorage, sharedBarrier, taskId](int index) { - Q_UNUSED(index) + QObject::connect(&tickAndDone, &TickAndDone::tick, sharedBarrier, + [currentStorage, sharedBarrier, taskId] { currentStorage->m_log.append({taskId, Handler::BarrierAdvance}); sharedBarrier->advance(); }); - }; + }); } void tst_Tasking::testTree_data() @@ -230,102 +220,103 @@ void tst_Tasking::testTree_data() TreeStorage storage; - const auto setupTaskHelper = [storage](TestTask &task, int taskId, bool success = true, - std::chrono::milliseconds sleep = 0ms) { - task.setFutureSynchronizer(s_futureSynchronizer); - task.setConcurrentCallData(runTask, success, sleep); - task.setProperty(s_taskIdProperty, taskId); - storage->m_log.append({taskId, Handler::Setup}); + const auto setupTask = [storage](int taskId, milliseconds timeout) { + return [storage, taskId, timeout](TaskObject &taskObject) { + taskObject = timeout; + storage->m_log.append({taskId, Handler::Setup}); + }; }; - const auto setupTask = [setupTaskHelper](int taskId) { - return [=](TestTask &task) { setupTaskHelper(task, taskId); }; - }; - const auto setupFailingTask = [setupTaskHelper](int taskId) { - return [=](TestTask &task) { setupTaskHelper(task, taskId, false); }; - }; - const auto setupSleepingTask = [setupTaskHelper](int taskId, bool success, - std::chrono::milliseconds sleep) { - return [=](TestTask &task) { setupTaskHelper(task, taskId, success, sleep); }; - }; - const auto setupDynamicTask = [setupTaskHelper](int taskId, TaskAction action) { - return [=](TestTask &task) { - setupTaskHelper(task, taskId); + + const auto setupDynamicTask = [storage](int taskId, TaskAction action) { + return [storage, taskId, action](TaskObject &) { + storage->m_log.append({taskId, Handler::Setup}); return action; }; }; - const auto logDone = [storage](const TestTask &task) { - storage->m_log.append({task.property(s_taskIdProperty).toInt(), Handler::Done}); - }; - const auto logError = [storage](const TestTask &task) { - storage->m_log.append({task.property(s_taskIdProperty).toInt(), Handler::Error}); - }; - const auto groupSetup = [storage](int taskId) { - return [=] { storage->m_log.append({taskId, Handler::GroupSetup}); }; - }; - const auto groupDone = [storage](int taskId) { - return [=] { storage->m_log.append({taskId, Handler::GroupDone}); }; - }; - const auto groupError = [storage](int taskId) { - return [=] { storage->m_log.append({taskId, Handler::GroupError}); }; - }; - const auto setupSync = [storage](int taskId) { - return [=] { storage->m_log.append({taskId, Handler::Sync}); }; - }; - const auto setupSyncWithReturn = [storage](int taskId, bool success) { - return [=] { storage->m_log.append({taskId, Handler::Sync}); return success; }; + + const auto setupDone = [storage](int taskId) { + return [storage, taskId](const TaskObject &) { + storage->m_log.append({taskId, Handler::Done}); + }; }; - const auto constructSimpleSequence = [=](WorkflowPolicy policy) { - return Group { - Storage(storage), - workflowPolicy(policy), - Test(setupTask(1), logDone), - Test(setupFailingTask(2), logDone, logError), - Test(setupTask(3), logDone), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + const auto setupError = [storage](int taskId) { + return [storage, taskId](const TaskObject &) { + storage->m_log.append({taskId, Handler::Error}); }; }; - const auto constructDynamicHierarchy = [=](TaskAction taskAction) { - return Group { - Storage(storage), - Group { - Test(setupTask(1), logDone) - }, - Group { - onGroupSetup([=] { return taskAction; }), - Test(setupTask(2), logDone), - Test(setupTask(3), logDone), - Test(setupTask(4), logDone) - }, - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + + const auto setupTimeout = [storage](int taskId) { + return [storage, taskId] { + storage->m_log.append({taskId, Handler::Timeout}); }; }; + const auto createTask = [storage, setupTask, setupDone, setupError]( + int taskId, bool successTask, milliseconds timeout = 0ms) -> GroupItem { + if (successTask) + return TestTask(setupTask(taskId, timeout), setupDone(taskId), setupError(taskId)); + const Group root { + finishAllAndError, + TestTask(setupTask(taskId, timeout)), + onGroupDone([storage, taskId] { storage->m_log.append({taskId, Handler::Done}); }), + onGroupError([storage, taskId] { storage->m_log.append({taskId, Handler::Error}); }) + }; + return root; + }; + + const auto createSuccessTask = [createTask](int taskId, milliseconds timeout = 0ms) { + return createTask(taskId, true, timeout); + }; + + const auto createFailingTask = [createTask](int taskId, milliseconds timeout = 0ms) { + return createTask(taskId, false, timeout); + }; + + const auto createDynamicTask = [storage, setupDynamicTask, setupDone, setupError]( + int taskId, TaskAction action) { + return TestTask(setupDynamicTask(taskId, action), setupDone(taskId), setupError(taskId)); + }; + + const auto groupSetup = [storage](int taskId) { + return onGroupSetup([=] { storage->m_log.append({taskId, Handler::GroupSetup}); }); + }; + const auto groupDone = [storage](int taskId) { + return onGroupDone([=] { storage->m_log.append({taskId, Handler::GroupDone}); }); + }; + const auto groupError = [storage](int taskId) { + return onGroupError([=] { storage->m_log.append({taskId, Handler::GroupError}); }); + }; + const auto createSync = [storage](int taskId) { + return Sync([=] { storage->m_log.append({taskId, Handler::Sync}); }); + }; + const auto createSyncWithReturn = [storage](int taskId, bool success) { + return Sync([=] { storage->m_log.append({taskId, Handler::Sync}); return success; }); + }; + { const Group root1 { Storage(storage), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + groupDone(0), + groupError(0) }; const Group root2 { Storage(storage), onGroupSetup([] { return TaskAction::Continue; }), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + groupDone(0), + groupError(0) }; const Group root3 { Storage(storage), onGroupSetup([] { return TaskAction::StopWithDone; }), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + groupDone(0), + groupError(0) }; const Group root4 { Storage(storage), onGroupSetup([] { return TaskAction::StopWithError; }), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + groupDone(0), + groupError(0) }; const Log logDone {{0, Handler::GroupDone}}; const Log logError {{0, Handler::GroupError}}; @@ -338,8 +329,8 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Test(setupDynamicTask(1, TaskAction::StopWithDone), logDone, logError), - Test(setupDynamicTask(2, TaskAction::StopWithDone), logDone, logError) + createDynamicTask(1, TaskAction::StopWithDone), + createDynamicTask(2, TaskAction::StopWithDone) }; const Log log {{1, Handler::Setup}, {2, Handler::Setup}}; QTest::newRow("DynamicTaskDone") << TestData{storage, root, log, 2, OnDone::Success}; @@ -348,8 +339,8 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Test(setupDynamicTask(1, TaskAction::StopWithError), logDone, logError), - Test(setupDynamicTask(2, TaskAction::StopWithError), logDone, logError) + createDynamicTask(1, TaskAction::StopWithError), + createDynamicTask(2, TaskAction::StopWithError) }; const Log log {{1, Handler::Setup}}; QTest::newRow("DynamicTaskError") << TestData{storage, root, log, 2, OnDone::Failure}; @@ -358,10 +349,10 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError), - Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError), - Test(setupDynamicTask(3, TaskAction::StopWithError), logDone, logError), - Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError) + createDynamicTask(1, TaskAction::Continue), + createDynamicTask(2, TaskAction::Continue), + createDynamicTask(3, TaskAction::StopWithError), + createDynamicTask(4, TaskAction::Continue) }; const Log log { {1, Handler::Setup}, @@ -377,10 +368,10 @@ void tst_Tasking::testTree_data() const Group root { parallel, Storage(storage), - Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError), - Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError), - Test(setupDynamicTask(3, TaskAction::StopWithError), logDone, logError), - Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError) + createDynamicTask(1, TaskAction::Continue), + createDynamicTask(2, TaskAction::Continue), + createDynamicTask(3, TaskAction::StopWithError), + createDynamicTask(4, TaskAction::Continue) }; const Log log { {1, Handler::Setup}, @@ -396,12 +387,12 @@ void tst_Tasking::testTree_data() const Group root { parallel, Storage(storage), - Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError), - Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError), + createDynamicTask(1, TaskAction::Continue), + createDynamicTask(2, TaskAction::Continue), Group { - Test(setupDynamicTask(3, TaskAction::StopWithError), logDone, logError) + createDynamicTask(3, TaskAction::StopWithError) }, - Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError) + createDynamicTask(4, TaskAction::Continue) }; const Log log { {1, Handler::Setup}, @@ -417,16 +408,16 @@ void tst_Tasking::testTree_data() const Group root { parallel, Storage(storage), - Test(setupDynamicTask(1, TaskAction::Continue), logDone, logError), - Test(setupDynamicTask(2, TaskAction::Continue), logDone, logError), + createDynamicTask(1, TaskAction::Continue), + createDynamicTask(2, TaskAction::Continue), Group { onGroupSetup([storage] { storage->m_log.append({0, Handler::GroupSetup}); return TaskAction::StopWithError; }), - Test(setupDynamicTask(3, TaskAction::Continue), logDone, logError) + createDynamicTask(3, TaskAction::Continue) }, - Test(setupDynamicTask(4, TaskAction::Continue), logDone, logError) + createDynamicTask(4, TaskAction::Continue) }; const Log log { {1, Handler::Setup}, @@ -447,23 +438,23 @@ void tst_Tasking::testTree_data() Group { Group { Group { - Test(setupTask(5), logDone, logError), - onGroupSetup(groupSetup(5)), - onGroupDone(groupDone(5)) + createSuccessTask(5), + groupSetup(5), + groupDone(5) }, - onGroupSetup(groupSetup(4)), - onGroupDone(groupDone(4)) + groupSetup(4), + groupDone(4) }, - onGroupSetup(groupSetup(3)), - onGroupDone(groupDone(3)) + groupSetup(3), + groupDone(3) }, - onGroupSetup(groupSetup(2)), - onGroupDone(groupDone(2)) + groupSetup(2), + groupDone(2) }, - onGroupSetup(groupSetup(1)), - onGroupDone(groupDone(1)) + groupSetup(1), + groupDone(1) }, - onGroupDone(groupDone(0)) + groupDone(0) }; const Log log { {1, Handler::GroupSetup}, @@ -484,18 +475,15 @@ void tst_Tasking::testTree_data() } { - const auto logDoneAnonymously = [=](const TestTask &) { - storage->m_log.append({0, Handler::Done}); - }; const Group root { Storage(storage), parallel, - Test(setupTask(1), logDoneAnonymously), - Test(setupTask(2), logDoneAnonymously), - Test(setupTask(3), logDoneAnonymously), - Test(setupTask(4), logDoneAnonymously), - Test(setupTask(5), logDoneAnonymously), - onGroupDone(groupDone(0)) + createSuccessTask(1), + createSuccessTask(2), + createSuccessTask(3), + createSuccessTask(4), + createSuccessTask(5), + groupDone(0) }; const Log log { {1, Handler::Setup}, // Setup order is determined in parallel mode @@ -503,23 +491,23 @@ void tst_Tasking::testTree_data() {3, Handler::Setup}, {4, Handler::Setup}, {5, Handler::Setup}, - {0, Handler::Done}, // Done order isn't determined in parallel mode - {0, Handler::Done}, - {0, Handler::Done}, - {0, Handler::Done}, - {0, Handler::Done}, + {1, Handler::Done}, + {2, Handler::Done}, + {3, Handler::Done}, + {4, Handler::Done}, + {5, Handler::Done}, {0, Handler::GroupDone} }; QTest::newRow("Parallel") << TestData{storage, root, log, 5, OnDone::Success}; } { - auto setupSubTree = [=](TaskTree &taskTree) { + auto setupSubTree = [storage, createSuccessTask](TaskTree &taskTree) { const Group nestedRoot { Storage(storage), - Test(setupTask(2), logDone), - Test(setupTask(3), logDone), - Test(setupTask(4), logDone) + createSuccessTask(2), + createSuccessTask(3), + createSuccessTask(4) }; taskTree.setupRoot(nestedRoot); CustomStorage *activeStorage = storage.activeStorage(); @@ -530,28 +518,28 @@ void tst_Tasking::testTree_data() }; const Group root1 { Storage(storage), - Test(setupTask(1), logDone), - Test(setupTask(2), logDone), - Test(setupTask(3), logDone), - Test(setupTask(4), logDone), - Test(setupTask(5), logDone), - onGroupDone(groupDone(0)) + createSuccessTask(1), + createSuccessTask(2), + createSuccessTask(3), + createSuccessTask(4), + createSuccessTask(5), + groupDone(0) }; const Group root2 { Storage(storage), - Group { Test(setupTask(1), logDone) }, - Group { Test(setupTask(2), logDone) }, - Group { Test(setupTask(3), logDone) }, - Group { Test(setupTask(4), logDone) }, - Group { Test(setupTask(5), logDone) }, - onGroupDone(groupDone(0)) + Group { createSuccessTask(1) }, + Group { createSuccessTask(2) }, + Group { createSuccessTask(3) }, + Group { createSuccessTask(4) }, + Group { createSuccessTask(5) }, + groupDone(0) }; const Group root3 { Storage(storage), - Test(setupTask(1), logDone), + createSuccessTask(1), TaskTreeTask(setupSubTree), - Test(setupTask(5), logDone), - onGroupDone(groupDone(0)) + createSuccessTask(5), + groupDone(0) }; const Log log { {1, Handler::Setup}, @@ -576,26 +564,26 @@ void tst_Tasking::testTree_data() const Group root { Storage(storage), Group { - Test(setupTask(1), logDone), + createSuccessTask(1), Group { - Test(setupTask(2), logDone), + createSuccessTask(2), Group { - Test(setupTask(3), logDone), + createSuccessTask(3), Group { - Test(setupTask(4), logDone), + createSuccessTask(4), Group { - Test(setupTask(5), logDone), - onGroupDone(groupDone(5)) + createSuccessTask(5), + groupDone(5) }, - onGroupDone(groupDone(4)) + groupDone(4) }, - onGroupDone(groupDone(3)) + groupDone(3) }, - onGroupDone(groupDone(2)) + groupDone(2) }, - onGroupDone(groupDone(1)) + groupDone(1) }, - onGroupDone(groupDone(0)) + groupDone(0) }; const Log log { {1, Handler::Setup}, @@ -621,13 +609,13 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Test(setupTask(1), logDone), - Test(setupTask(2), logDone), - Test(setupFailingTask(3), logDone, logError), - Test(setupTask(4), logDone), - Test(setupTask(5), logDone), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + createSuccessTask(1), + createSuccessTask(2), + createFailingTask(3), + createSuccessTask(4), + createSuccessTask(5), + groupDone(0), + groupError(0) }; const Log log { {1, Handler::Setup}, @@ -642,129 +630,569 @@ void tst_Tasking::testTree_data() } { - const auto constructEmptyWorkflow = [=](WorkflowPolicy policy) { + const auto createRoot = [storage, groupDone, groupError](WorkflowPolicy policy) { return Group { Storage(storage), workflowPolicy(policy), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + groupDone(0), + groupError(0) }; }; - const Log log = {{0, Handler::GroupDone}}; + const Log doneLog = {{0, Handler::GroupDone}}; + const Log errorLog = {{0, Handler::GroupError}}; - const Group root1 = constructEmptyWorkflow(WorkflowPolicy::StopOnError); - QTest::newRow("EmptyStopOnError") << TestData{storage, root1, log, 0, OnDone::Success}; + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("EmptyStopOnError") << TestData{storage, root1, doneLog, 0, + OnDone::Success}; - const Group root2 = constructEmptyWorkflow(WorkflowPolicy::ContinueOnError); - QTest::newRow("EmptyContinueOnError") << TestData{storage, root2, log, 0, OnDone::Success}; + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("EmptyContinueOnError") << TestData{storage, root2, doneLog, 0, + OnDone::Success}; - const Group root3 = constructEmptyWorkflow(WorkflowPolicy::StopOnDone); - QTest::newRow("EmptyStopOnDone") << TestData{storage, root3, log, 0, OnDone::Success}; + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("EmptyStopOnDone") << TestData{storage, root3, errorLog, 0, + OnDone::Failure}; - const Group root4 = constructEmptyWorkflow(WorkflowPolicy::ContinueOnDone); - QTest::newRow("EmptyContinueOnDone") << TestData{storage, root4, log, 0, OnDone::Success}; + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("EmptyContinueOnDone") << TestData{storage, root4, errorLog, 0, + OnDone::Failure}; - const Group root5 = constructEmptyWorkflow(WorkflowPolicy::StopOnFinished); - QTest::newRow("EmptyStopOnFinished") << TestData{storage, root5, log, 0, OnDone::Success}; + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("EmptyStopOnFinished") << TestData{storage, root5, errorLog, 0, + OnDone::Failure}; - const Group root6 = constructEmptyWorkflow(WorkflowPolicy::Optional); - QTest::newRow("EmptyOptional") << TestData{storage, root6, log, 0, OnDone::Success}; + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("EmptyFinishAllAndDone") << TestData{storage, root6, doneLog, 0, + OnDone::Success}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("EmptyFinishAllAndError") << TestData{storage, root7, errorLog, 0, + OnDone::Failure}; } { - const auto constructDoneWorkflow = [=](WorkflowPolicy policy) { + const auto createRoot = [storage, createSuccessTask, groupDone, groupError]( + WorkflowPolicy policy) { return Group { Storage(storage), workflowPolicy(policy), - Test(setupTask(1), logDone, logError), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + createSuccessTask(1), + groupDone(0), + groupError(0) }; }; - const Log log = { + const Log doneLog = { {1, Handler::Setup}, {1, Handler::Done}, {0, Handler::GroupDone} }; - const Group root1 = constructDoneWorkflow(WorkflowPolicy::StopOnError); - QTest::newRow("DoneStopOnError") << TestData{storage, root1, log, 1, OnDone::Success}; - - const Group root2 = constructDoneWorkflow(WorkflowPolicy::ContinueOnError); - QTest::newRow("DoneContinueOnError") << TestData{storage, root2, log, 1, OnDone::Success}; - - const Group root3 = constructDoneWorkflow(WorkflowPolicy::StopOnDone); - QTest::newRow("DoneStopOnDone") << TestData{storage, root3, log, 1, OnDone::Success}; - - const Group root4 = constructDoneWorkflow(WorkflowPolicy::ContinueOnDone); - QTest::newRow("DoneContinueOnDone") << TestData{storage, root4, log, 1, OnDone::Success}; - - const Group root5 = constructDoneWorkflow(WorkflowPolicy::StopOnFinished); - QTest::newRow("DoneStopOnFinished") << TestData{storage, root5, log, 1, OnDone::Success}; - - const Group root6 = constructDoneWorkflow(WorkflowPolicy::Optional); - QTest::newRow("DoneOptional") << TestData{storage, root6, log, 1, OnDone::Success}; - } - - { - const auto constructErrorWorkflow = [=](WorkflowPolicy policy) { - return Group { - Storage(storage), - workflowPolicy(policy), - Test(setupFailingTask(1), logDone, logError), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) - }; - }; - - const Log log = { + const Log errorLog = { {1, Handler::Setup}, - {1, Handler::Error}, + {1, Handler::Done}, {0, Handler::GroupError} }; - const Log optionalLog = { + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("DoneStopOnError") << TestData{storage, root1, doneLog, 1, + OnDone::Success}; + + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("DoneContinueOnError") << TestData{storage, root2, doneLog, 1, + OnDone::Success}; + + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("DoneStopOnDone") << TestData{storage, root3, doneLog, 1, + OnDone::Success}; + + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("DoneContinueOnDone") << TestData{storage, root4, doneLog, 1, + OnDone::Success}; + + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("DoneStopOnFinished") << TestData{storage, root5, doneLog, 1, + OnDone::Success}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("DoneFinishAllAndDone") << TestData{storage, root6, doneLog, 1, + OnDone::Success}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("DoneFinishAllAndError") << TestData{storage, root7, errorLog, 1, + OnDone::Failure}; + } + + { + const auto createRoot = [storage, createFailingTask, groupDone, groupError]( + WorkflowPolicy policy) { + return Group { + Storage(storage), + workflowPolicy(policy), + createFailingTask(1), + groupDone(0), + groupError(0) + }; + }; + + const Log doneLog = { {1, Handler::Setup}, {1, Handler::Error}, {0, Handler::GroupDone} }; - const Group root1 = constructErrorWorkflow(WorkflowPolicy::StopOnError); - QTest::newRow("ErrorStopOnError") << TestData{storage, root1, log, 1, OnDone::Failure}; + const Log errorLog = { + {1, Handler::Setup}, + {1, Handler::Error}, + {0, Handler::GroupError} + }; - const Group root2 = constructErrorWorkflow(WorkflowPolicy::ContinueOnError); - QTest::newRow("ErrorContinueOnError") << TestData{storage, root2, log, 1, OnDone::Failure}; + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("ErrorStopOnError") << TestData{storage, root1, errorLog, 1, + OnDone::Failure}; - const Group root3 = constructErrorWorkflow(WorkflowPolicy::StopOnDone); - QTest::newRow("ErrorStopOnDone") << TestData{storage, root3, log, 1, OnDone::Failure}; + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("ErrorContinueOnError") << TestData{storage, root2, errorLog, 1, + OnDone::Failure}; - const Group root4 = constructErrorWorkflow(WorkflowPolicy::ContinueOnDone); - QTest::newRow("ErrorContinueOnDone") << TestData{storage, root4, log, 1, OnDone::Failure}; + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("ErrorStopOnDone") << TestData{storage, root3, errorLog, 1, + OnDone::Failure}; - const Group root5 = constructErrorWorkflow(WorkflowPolicy::StopOnFinished); - QTest::newRow("ErrorStopOnFinished") << TestData{storage, root5, log, 1, OnDone::Failure}; + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("ErrorContinueOnDone") << TestData{storage, root4, errorLog, 1, + OnDone::Failure}; - const Group root6 = constructErrorWorkflow(WorkflowPolicy::Optional); - QTest::newRow("ErrorOptional") << TestData{storage, root6, optionalLog, 1, OnDone::Success}; + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("ErrorStopOnFinished") << TestData{storage, root5, errorLog, 1, + OnDone::Failure}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("ErrorFinishAllAndDone") << TestData{storage, root6, doneLog, 1, + OnDone::Success}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("ErrorFinishAllAndError") << TestData{storage, root7, errorLog, 1, + OnDone::Failure}; } { - const Group root = constructSimpleSequence(WorkflowPolicy::StopOnError); - const Log log { + // These tests check whether the proper root's group end handler is called + // when the group is stopped. Test it with different workflow policies. + // The root starts one short failing task together with one long task. + const auto createRoot = [storage, createSuccessTask, createFailingTask, groupDone, + groupError](WorkflowPolicy policy) { + return Group { + Storage(storage), + parallel, + workflowPolicy(policy), + createFailingTask(1, 1ms), + createSuccessTask(2, 1ms), + groupDone(0), + groupError(0) + }; + }; + + const Log errorErrorLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {1, Handler::Error}, + {2, Handler::Error}, + {0, Handler::GroupError} + }; + + const Log errorDoneLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {1, Handler::Error}, + {2, Handler::Done}, + {0, Handler::GroupError} + }; + + const Log doneLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {1, Handler::Error}, + {2, Handler::Done}, + {0, Handler::GroupDone} + }; + + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("StopRootWithStopOnError") + << TestData{storage, root1, errorErrorLog, 2, OnDone::Failure}; + + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("StopRootWithContinueOnError") + << TestData{storage, root2, errorDoneLog, 2, OnDone::Failure}; + + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("StopRootWithStopOnDone") + << TestData{storage, root3, doneLog, 2, OnDone::Success}; + + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("StopRootWithContinueOnDone") + << TestData{storage, root4, doneLog, 2, OnDone::Success}; + + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("StopRootWithStopOnFinished") + << TestData{storage, root5, errorErrorLog, 2, OnDone::Failure}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("StopRootWithFinishAllAndDone") + << TestData{storage, root6, doneLog, 2, OnDone::Success}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("StopRootWithFinishAllAndError") + << TestData{storage, root7, errorDoneLog, 2, OnDone::Failure}; + } + + { + // These tests check whether the proper root's group end handler is called + // when the group is stopped. Test it with different workflow policies. + // The root starts in parallel: one very short successful task, one short failing task + // and one long task. + const auto createRoot = [storage, createSuccessTask, createFailingTask, groupDone, + groupError](WorkflowPolicy policy) { + return Group { + Storage(storage), + parallel, + workflowPolicy(policy), + createSuccessTask(1), + createFailingTask(2, 1ms), + createSuccessTask(3, 1ms), + groupDone(0), + groupError(0) + }; + }; + + const Log errorErrorLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {2, Handler::Error}, + {3, Handler::Error}, + {0, Handler::GroupError} + }; + + const Log errorDoneLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {2, Handler::Error}, + {3, Handler::Done}, + {0, Handler::GroupError} + }; + + const Log doneErrorLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {2, Handler::Error}, + {3, Handler::Error}, + {0, Handler::GroupDone} + }; + + const Log doneDoneLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {2, Handler::Error}, + {3, Handler::Done}, + {0, Handler::GroupDone} + }; + + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("StopRootAfterDoneWithStopOnError") + << TestData{storage, root1, errorErrorLog, 3, OnDone::Failure}; + + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("StopRootAfterDoneWithContinueOnError") + << TestData{storage, root2, errorDoneLog, 3, OnDone::Failure}; + + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("StopRootAfterDoneWithStopOnDone") + << TestData{storage, root3, doneErrorLog, 3, OnDone::Success}; + + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("StopRootAfterDoneWithContinueOnDone") + << TestData{storage, root4, doneDoneLog, 3, OnDone::Success}; + + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("StopRootAfterDoneWithStopOnFinished") + << TestData{storage, root5, doneErrorLog, 3, OnDone::Success}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("StopRootAfterDoneWithFinishAllAndDone") + << TestData{storage, root6, doneDoneLog, 3, OnDone::Success}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("StopRootAfterDoneWithFinishAllAndError") + << TestData{storage, root7, errorDoneLog, 3, OnDone::Failure}; + } + + { + // These tests check whether the proper subgroup's end handler is called + // when the group is stopped. Test it with different workflow policies. + // The subgroup starts one long task. + const auto createRoot = [storage, createSuccessTask, createFailingTask, groupDone, + groupError](WorkflowPolicy policy) { + return Group { + Storage(storage), + parallel, + Group { + workflowPolicy(policy), + createSuccessTask(1, 1000ms), + groupDone(1), + groupError(1) + }, + createFailingTask(2, 1ms), + groupDone(2), + groupError(2) + }; + }; + + const Log errorLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {2, Handler::Error}, + {1, Handler::Error}, + {1, Handler::GroupError}, + {2, Handler::GroupError} + }; + + const Log doneLog = { + {1, Handler::Setup}, + {2, Handler::Setup}, + {2, Handler::Error}, + {1, Handler::Error}, + {1, Handler::GroupDone}, + {2, Handler::GroupError} + }; + + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("StopGroupWithStopOnError") + << TestData{storage, root1, errorLog, 2, OnDone::Failure}; + + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("StopGroupWithContinueOnError") + << TestData{storage, root2, errorLog, 2, OnDone::Failure}; + + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("StopGroupWithStopOnDone") + << TestData{storage, root3, errorLog, 2, OnDone::Failure}; + + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("StopGroupWithContinueOnDone") + << TestData{storage, root4, errorLog, 2, OnDone::Failure}; + + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("StopGroupWithStopOnFinished") + << TestData{storage, root5, errorLog, 2, OnDone::Failure}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("StopGroupWithFinishAllAndDone") + << TestData{storage, root6, doneLog, 2, OnDone::Failure}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("StopGroupWithFinishAllAndError") + << TestData{storage, root7, errorLog, 2, OnDone::Failure}; + } + + { + // These tests check whether the proper subgroup's end handler is called + // when the group is stopped. Test it with different workflow policies. + // The sequential subgroup starts one short successful task followed by one long task. + const auto createRoot = [storage, createSuccessTask, createFailingTask, groupDone, + groupError](WorkflowPolicy policy) { + return Group { + Storage(storage), + parallel, + Group { + workflowPolicy(policy), + createSuccessTask(1), + createSuccessTask(2, 1000ms), + groupDone(1), + groupError(1) + }, + createFailingTask(3, 1ms), + groupDone(2), + groupError(2) + }; + }; + + const Log errorLog = { + {1, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {2, Handler::Setup}, + {3, Handler::Error}, + {2, Handler::Error}, + {1, Handler::GroupError}, + {2, Handler::GroupError} + }; + + const Log shortDoneLog = { + {1, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {1, Handler::GroupDone}, + {3, Handler::Error}, + {2, Handler::GroupError} + }; + + const Log longDoneLog = { + {1, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Done}, + {2, Handler::Setup}, + {3, Handler::Error}, + {2, Handler::Error}, + {1, Handler::GroupDone}, + {2, Handler::GroupError} + }; + + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("StopGroupAfterDoneWithStopOnError") + << TestData{storage, root1, errorLog, 3, OnDone::Failure}; + + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("StopGroupAfterDoneWithContinueOnError") + << TestData{storage, root2, errorLog, 3, OnDone::Failure}; + + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("StopGroupAfterDoneWithStopOnDone") + << TestData{storage, root3, shortDoneLog, 3, OnDone::Failure}; + + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("StopGroupAfterDoneWithContinueOnDone") + << TestData{storage, root4, longDoneLog, 3, OnDone::Failure}; + + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("StopGroupAfterDoneWithStopOnFinished") + << TestData{storage, root5, shortDoneLog, 3, OnDone::Failure}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("StopGroupAfterDoneWithFinishAllAndDone") + << TestData{storage, root6, longDoneLog, 3, OnDone::Failure}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("StopGroupAfterDoneWithFinishAllAndError") + << TestData{storage, root7, errorLog, 3, OnDone::Failure}; + } + + { + // These tests check whether the proper subgroup's end handler is called + // when the group is stopped. Test it with different workflow policies. + // The sequential subgroup starts one short failing task followed by one long task. + const auto createRoot = [storage, createSuccessTask, createFailingTask, groupDone, + groupError](WorkflowPolicy policy) { + return Group { + Storage(storage), + parallel, + Group { + workflowPolicy(policy), + createFailingTask(1), + createSuccessTask(2, 1000ms), + groupDone(1), + groupError(1) + }, + createFailingTask(3, 1ms), + groupDone(2), + groupError(2) + }; + }; + + const Log shortErrorLog = { + {1, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Error}, + {1, Handler::GroupError}, + {3, Handler::Error}, + {2, Handler::GroupError} + }; + + const Log longErrorLog = { + {1, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Error}, + {2, Handler::Setup}, + {3, Handler::Error}, + {2, Handler::Error}, + {1, Handler::GroupError}, + {2, Handler::GroupError} + }; + + const Log doneLog = { + {1, Handler::Setup}, + {3, Handler::Setup}, + {1, Handler::Error}, + {2, Handler::Setup}, + {3, Handler::Error}, + {2, Handler::Error}, + {1, Handler::GroupDone}, + {2, Handler::GroupError} + }; + + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + QTest::newRow("StopGroupAfterErrorWithStopOnError") + << TestData{storage, root1, shortErrorLog, 3, OnDone::Failure}; + + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + QTest::newRow("StopGroupAfterErrorWithContinueOnError") + << TestData{storage, root2, longErrorLog, 3, OnDone::Failure}; + + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + QTest::newRow("StopGroupAfterErrorWithStopOnDone") + << TestData{storage, root3, longErrorLog, 3, OnDone::Failure}; + + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + QTest::newRow("StopGroupAfterErrorWithContinueOnDone") + << TestData{storage, root4, longErrorLog, 3, OnDone::Failure}; + + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + QTest::newRow("StopGroupAfterErrorWithStopOnFinished") + << TestData{storage, root5, shortErrorLog, 3, OnDone::Failure}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("StopGroupAfterErrorWithFinishAllAndDone") + << TestData{storage, root6, doneLog, 3, OnDone::Failure}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("StopGroupAfterErrorWithFinishAllAndError") + << TestData{storage, root7, longErrorLog, 3, OnDone::Failure}; + } + + { + const auto createRoot = [storage, createSuccessTask, createFailingTask, groupDone, + groupError](WorkflowPolicy policy) { + return Group { + Storage(storage), + workflowPolicy(policy), + createSuccessTask(1), + createFailingTask(2), + createSuccessTask(3), + groupDone(0), + groupError(0) + }; + }; + + const Group root1 = createRoot(WorkflowPolicy::StopOnError); + const Log log1 { {1, Handler::Setup}, {1, Handler::Done}, {2, Handler::Setup}, {2, Handler::Error}, {0, Handler::GroupError} }; - QTest::newRow("StopOnError") << TestData{storage, root, log, 3, OnDone::Failure}; - } + QTest::newRow("StopOnError") << TestData{storage, root1, log1, 3, OnDone::Failure}; - { - const Group root = constructSimpleSequence(WorkflowPolicy::ContinueOnError); - const Log log { + const Group root2 = createRoot(WorkflowPolicy::ContinueOnError); + const Log errorLog { {1, Handler::Setup}, {1, Handler::Done}, {2, Handler::Setup}, @@ -773,22 +1201,18 @@ void tst_Tasking::testTree_data() {3, Handler::Done}, {0, Handler::GroupError} }; - QTest::newRow("ContinueOnError") << TestData{storage, root, log, 3, OnDone::Failure}; - } + QTest::newRow("ContinueOnError") << TestData{storage, root2, errorLog, 3, OnDone::Failure}; - { - const Group root = constructSimpleSequence(WorkflowPolicy::StopOnDone); - const Log log { + const Group root3 = createRoot(WorkflowPolicy::StopOnDone); + const Log log3 { {1, Handler::Setup}, {1, Handler::Done}, {0, Handler::GroupDone} }; - QTest::newRow("StopOnDone") << TestData{storage, root, log, 3, OnDone::Success}; - } + QTest::newRow("StopOnDone") << TestData{storage, root3, log3, 3, OnDone::Success}; - { - const Group root = constructSimpleSequence(WorkflowPolicy::ContinueOnDone); - const Log log { + const Group root4 = createRoot(WorkflowPolicy::ContinueOnDone); + const Log doneLog { {1, Handler::Setup}, {1, Handler::Done}, {2, Handler::Setup}, @@ -797,36 +1221,41 @@ void tst_Tasking::testTree_data() {3, Handler::Done}, {0, Handler::GroupDone} }; - QTest::newRow("ContinueOnDone") << TestData{storage, root, log, 3, OnDone::Success}; - } + QTest::newRow("ContinueOnDone") << TestData{storage, root4, doneLog, 3, OnDone::Success}; - { - const Group root = constructSimpleSequence(WorkflowPolicy::StopOnFinished); - const Log log { + const Group root5 = createRoot(WorkflowPolicy::StopOnFinished); + const Log log5 { {1, Handler::Setup}, {1, Handler::Done}, {0, Handler::GroupDone} }; - QTest::newRow("StopOnFinished") << TestData{storage, root, log, 3, OnDone::Success}; + QTest::newRow("StopOnFinished") << TestData{storage, root5, log5, 3, OnDone::Success}; + + const Group root6 = createRoot(WorkflowPolicy::FinishAllAndDone); + QTest::newRow("FinishAllAndDone") << TestData{storage, root6, doneLog, 3, OnDone::Success}; + + const Group root7 = createRoot(WorkflowPolicy::FinishAllAndError); + QTest::newRow("FinishAllAndError") << TestData{storage, root7, errorLog, 3, OnDone::Failure}; } { - const auto setupRoot = [=](bool firstSuccess, bool secondSuccess) { + const auto createRoot = [storage, createTask, groupDone, groupError]( + bool firstSuccess, bool secondSuccess) { return Group { parallel, stopOnFinished, Storage(storage), - Test(setupSleepingTask(1, firstSuccess, 1000ms), logDone, logError), - Test(setupSleepingTask(2, secondSuccess, 5ms), logDone, logError), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + createTask(1, firstSuccess, 1000ms), + createTask(2, secondSuccess, 1ms), + groupDone(0), + groupError(0) }; }; - const Group root1 = setupRoot(true, true); - const Group root2 = setupRoot(true, false); - const Group root3 = setupRoot(false, true); - const Group root4 = setupRoot(false, false); + const Group root1 = createRoot(true, true); + const Group root2 = createRoot(true, false); + const Group root3 = createRoot(false, true); + const Group root4 = createRoot(false, false); const Log success { {1, Handler::Setup}, @@ -850,47 +1279,42 @@ void tst_Tasking::testTree_data() } { - const Group root { - Storage(storage), - optional, - Test(setupFailingTask(1), logDone, logError), - Test(setupFailingTask(2), logDone, logError), - onGroupDone(groupDone(0)), - onGroupError(groupError(0)) + const auto createRoot = [storage, createSuccessTask, groupDone, groupError]( + TaskAction taskAction) { + return Group { + Storage(storage), + Group { + createSuccessTask(1) + }, + Group { + onGroupSetup([=] { return taskAction; }), + createSuccessTask(2), + createSuccessTask(3), + createSuccessTask(4) + }, + groupDone(0), + groupError(0) + }; }; - const Log log { - {1, Handler::Setup}, - {1, Handler::Error}, - {2, Handler::Setup}, - {2, Handler::Error}, - {0, Handler::GroupDone} - }; - QTest::newRow("Optional") << TestData{storage, root, log, 2, OnDone::Success}; - } - { - const Group root = constructDynamicHierarchy(TaskAction::StopWithDone); - const Log log { + const Group root1 = createRoot(TaskAction::StopWithDone); + const Log log1 { {1, Handler::Setup}, {1, Handler::Done}, {0, Handler::GroupDone} }; - QTest::newRow("DynamicSetupDone") << TestData{storage, root, log, 4, OnDone::Success}; - } + QTest::newRow("DynamicSetupDone") << TestData{storage, root1, log1, 4, OnDone::Success}; - { - const Group root = constructDynamicHierarchy(TaskAction::StopWithError); - const Log log { + const Group root2 = createRoot(TaskAction::StopWithError); + const Log log2 { {1, Handler::Setup}, {1, Handler::Done}, {0, Handler::GroupError} }; - QTest::newRow("DynamicSetupError") << TestData{storage, root, log, 4, OnDone::Failure}; - } + QTest::newRow("DynamicSetupError") << TestData{storage, root2, log2, 4, OnDone::Failure}; - { - const Group root = constructDynamicHierarchy(TaskAction::Continue); - const Log log { + const Group root3 = createRoot(TaskAction::Continue); + const Log log3 { {1, Handler::Setup}, {1, Handler::Done}, {2, Handler::Setup}, @@ -901,7 +1325,7 @@ void tst_Tasking::testTree_data() {4, Handler::Done}, {0, Handler::GroupDone} }; - QTest::newRow("DynamicSetupContinue") << TestData{storage, root, log, 4, OnDone::Success}; + QTest::newRow("DynamicSetupContinue") << TestData{storage, root3, log3, 4, OnDone::Success}; } { @@ -909,20 +1333,20 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - onGroupSetup(groupSetup(1)), - Test(setupTask(1)) + groupSetup(1), + createSuccessTask(1) }, Group { - onGroupSetup(groupSetup(2)), - Test(setupTask(2)) + groupSetup(2), + createSuccessTask(2) }, Group { - onGroupSetup(groupSetup(3)), - Test(setupTask(3)) + groupSetup(3), + createSuccessTask(3) }, Group { - onGroupSetup(groupSetup(4)), - Test(setupTask(4)) + groupSetup(4), + createSuccessTask(4) } }; const Log log { @@ -930,10 +1354,14 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {2, Handler::GroupSetup}, {2, Handler::Setup}, + {1, Handler::Done}, {3, Handler::GroupSetup}, {3, Handler::Setup}, + {2, Handler::Done}, {4, Handler::GroupSetup}, - {4, Handler::Setup} + {4, Handler::Setup}, + {3, Handler::Done}, + {4, Handler::Done} }; QTest::newRow("NestedParallel") << TestData{storage, root, log, 4, OnDone::Success}; } @@ -943,24 +1371,24 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - onGroupSetup(groupSetup(1)), - Test(setupTask(1)) + groupSetup(1), + createSuccessTask(1) }, Group { - onGroupSetup(groupSetup(2)), - Test(setupTask(2)) + groupSetup(2), + createSuccessTask(2) }, Group { - onGroupSetup(groupSetup(3)), - Test(setupDynamicTask(3, TaskAction::StopWithDone)) + groupSetup(3), + createDynamicTask(3, TaskAction::StopWithDone) }, Group { - onGroupSetup(groupSetup(4)), - Test(setupTask(4)) + groupSetup(4), + createSuccessTask(4) }, Group { - onGroupSetup(groupSetup(5)), - Test(setupTask(5)) + groupSetup(5), + createSuccessTask(5) } }; const Log log { @@ -968,12 +1396,16 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {2, Handler::GroupSetup}, {2, Handler::Setup}, + {1, Handler::Done}, {3, Handler::GroupSetup}, {3, Handler::Setup}, {4, Handler::GroupSetup}, {4, Handler::Setup}, + {2, Handler::Done}, {5, Handler::GroupSetup}, - {5, Handler::Setup} + {5, Handler::Setup}, + {4, Handler::Done}, + {5, Handler::Done} }; QTest::newRow("NestedParallelDone") << TestData{storage, root, log, 5, OnDone::Success}; } @@ -983,26 +1415,36 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - onGroupSetup(groupSetup(1)), - Test(setupTask(1)) + groupSetup(1), + createSuccessTask(1) }, Group { - onGroupSetup(groupSetup(2)), - Test(setupTask(2)) + groupSetup(2), + createSuccessTask(2) }, Group { - onGroupSetup(groupSetup(3)), - Test(setupDynamicTask(3, TaskAction::StopWithError)) + groupSetup(3), + createDynamicTask(3, TaskAction::StopWithError) }, Group { - onGroupSetup(groupSetup(4)), - Test(setupTask(4)) + groupSetup(4), + createSuccessTask(4) }, Group { - onGroupSetup(groupSetup(5)), - Test(setupTask(5)) + groupSetup(5), + createSuccessTask(5) } }; + const Log log1 { + {1, Handler::GroupSetup}, + {1, Handler::Setup}, + {2, Handler::GroupSetup}, + {2, Handler::Setup}, + {1, Handler::Done}, + {3, Handler::GroupSetup}, + {3, Handler::Setup}, + {2, Handler::Error} + }; // Inside this test the task 2 should finish first, then synchonously: // - task 3 should exit setup with error @@ -1012,26 +1454,36 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - onGroupSetup(groupSetup(1)), - Test(setupSleepingTask(1, true, 10ms)) + groupSetup(1), + createSuccessTask(1, 10ms) }, Group { - onGroupSetup(groupSetup(2)), - Test(setupTask(2)) + groupSetup(2), + createSuccessTask(2) }, Group { - onGroupSetup(groupSetup(3)), - Test(setupDynamicTask(3, TaskAction::StopWithError)) + groupSetup(3), + createDynamicTask(3, TaskAction::StopWithError) }, Group { - onGroupSetup(groupSetup(4)), - Test(setupTask(4)) + groupSetup(4), + createSuccessTask(4) }, Group { - onGroupSetup(groupSetup(5)), - Test(setupTask(5)) + groupSetup(5), + createSuccessTask(5) } }; + const Log log2 { + {1, Handler::GroupSetup}, + {1, Handler::Setup}, + {2, Handler::GroupSetup}, + {2, Handler::Setup}, + {2, Handler::Done}, + {3, Handler::GroupSetup}, + {3, Handler::Setup}, + {1, Handler::Error} + }; // This test ensures that the task 1 doesn't invoke its done handler, // being ready while sleeping in the task's 2 done handler. @@ -1047,42 +1499,46 @@ void tst_Tasking::testTree_data() Group { parallelLimit(2), Group { - onGroupSetup(groupSetup(1)), - Test(setupSleepingTask(1, true, 20ms)) + groupSetup(1), + createSuccessTask(1, 10ms) }, Group { - onGroupSetup(groupSetup(2)), - Test(setupSleepingTask(2, true, 10ms)) + groupSetup(2), + createSuccessTask(2, 1ms) }, Group { - onGroupSetup(groupSetup(3)), - Test(setupDynamicTask(3, TaskAction::StopWithError)) + groupSetup(3), + createDynamicTask(3, TaskAction::StopWithError) }, Group { - onGroupSetup(groupSetup(4)), - Test(setupTask(4)) + groupSetup(4), + createSuccessTask(4) } }, Group { - onGroupSetup(groupSetup(5)), - Test(setupTask(5)) + groupSetup(5), + createSuccessTask(5) } }; - const Log shortLog { + const Log log3 { {1, Handler::GroupSetup}, {1, Handler::Setup}, {2, Handler::GroupSetup}, {2, Handler::Setup}, + {2, Handler::Done}, {3, Handler::GroupSetup}, - {3, Handler::Setup} + {3, Handler::Setup}, + {1, Handler::Error}, + {5, Handler::GroupSetup}, + {5, Handler::Setup}, + {5, Handler::Done} }; - const Log longLog = shortLog + Log {{5, Handler::GroupSetup}, {5, Handler::Setup}}; QTest::newRow("NestedParallelError1") - << TestData{storage, root1, shortLog, 5, OnDone::Failure}; + << TestData{storage, root1, log1, 5, OnDone::Failure}; QTest::newRow("NestedParallelError2") - << TestData{storage, root2, shortLog, 5, OnDone::Failure}; + << TestData{storage, root2, log2, 5, OnDone::Failure}; QTest::newRow("NestedParallelError3") - << TestData{storage, root3, longLog, 5, OnDone::Failure}; + << TestData{storage, root3, log3, 5, OnDone::Failure}; } { @@ -1090,35 +1546,31 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(1)), + groupSetup(1), Group { parallel, - Test(setupTask(1)) + createSuccessTask(1) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(2)), + groupSetup(2), Group { parallel, - Test(setupTask(2)) + createSuccessTask(2) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(3)), + groupSetup(3), Group { parallel, - Test(setupTask(3)) + createSuccessTask(3) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(4)), + groupSetup(4), Group { parallel, - Test(setupTask(4)) + createSuccessTask(4) } } }; @@ -1127,10 +1579,14 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {2, Handler::GroupSetup}, {2, Handler::Setup}, + {1, Handler::Done}, {3, Handler::GroupSetup}, {3, Handler::Setup}, + {2, Handler::Done}, {4, Handler::GroupSetup}, - {4, Handler::Setup} + {4, Handler::Setup}, + {3, Handler::Done}, + {4, Handler::Done} }; QTest::newRow("DeeplyNestedParallel") << TestData{storage, root, log, 4, OnDone::Success}; } @@ -1140,29 +1596,24 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(1)), - Group { Test(setupTask(1)) } + groupSetup(1), + Group { createSuccessTask(1) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(2)), - Group { Test(setupTask(2)) } + groupSetup(2), + Group { createSuccessTask(2) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(3)), - Group { Test(setupDynamicTask(3, TaskAction::StopWithDone)) } + groupSetup(3), + Group { createDynamicTask(3, TaskAction::StopWithDone) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(4)), - Group { Test(setupTask(4)) } + groupSetup(4), + Group { createSuccessTask(4) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(5)), - Group { Test(setupTask(5)) } + groupSetup(5), + Group { createSuccessTask(5) } } }; const Log log { @@ -1170,12 +1621,16 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {2, Handler::GroupSetup}, {2, Handler::Setup}, + {1, Handler::Done}, {3, Handler::GroupSetup}, {3, Handler::Setup}, {4, Handler::GroupSetup}, {4, Handler::Setup}, + {2, Handler::Done}, {5, Handler::GroupSetup}, - {5, Handler::Setup} + {5, Handler::Setup}, + {4, Handler::Done}, + {5, Handler::Done} }; QTest::newRow("DeeplyNestedParallelDone") << TestData{storage, root, log, 5, OnDone::Success}; @@ -1186,29 +1641,24 @@ void tst_Tasking::testTree_data() parallelLimit(2), Storage(storage), Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(1)), - Group { Test(setupTask(1)) } + groupSetup(1), + Group { createSuccessTask(1) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(2)), - Group { Test(setupTask(2)) } + groupSetup(2), + Group { createSuccessTask(2) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(3)), - Group { Test(setupDynamicTask(3, TaskAction::StopWithError)) } + groupSetup(3), + Group { createDynamicTask(3, TaskAction::StopWithError) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(4)), - Group { Test(setupTask(4)) } + groupSetup(4), + Group { createSuccessTask(4) } }, Group { - Storage(TreeStorage()), - onGroupSetup(groupSetup(5)), - Group { Test(setupTask(5)) } + groupSetup(5), + Group { createSuccessTask(5) } } }; const Log log { @@ -1216,8 +1666,10 @@ void tst_Tasking::testTree_data() {1, Handler::Setup}, {2, Handler::GroupSetup}, {2, Handler::Setup}, + {1, Handler::Done}, {3, Handler::GroupSetup}, - {3, Handler::Setup} + {3, Handler::Setup}, + {2, Handler::Error} }; QTest::newRow("DeeplyNestedParallelError") << TestData{storage, root, log, 5, OnDone::Failure}; @@ -1226,11 +1678,11 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Sync(setupSync(1)), - Sync(setupSync(2)), - Sync(setupSync(3)), - Sync(setupSync(4)), - Sync(setupSync(5)) + createSync(1), + createSync(2), + createSync(3), + createSync(4), + createSync(5) }; const Log log { {1, Handler::Sync}, @@ -1245,11 +1697,11 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Sync(setupSyncWithReturn(1, true)), - Sync(setupSyncWithReturn(2, true)), - Sync(setupSyncWithReturn(3, true)), - Sync(setupSyncWithReturn(4, true)), - Sync(setupSyncWithReturn(5, true)) + createSyncWithReturn(1, true), + createSyncWithReturn(2, true), + createSyncWithReturn(3, true), + createSyncWithReturn(4, true), + createSyncWithReturn(5, true) }; const Log log { {1, Handler::Sync}, @@ -1265,11 +1717,11 @@ void tst_Tasking::testTree_data() const Group root { Storage(storage), parallel, - Sync(setupSync(1)), - Sync(setupSync(2)), - Sync(setupSync(3)), - Sync(setupSync(4)), - Sync(setupSync(5)) + createSync(1), + createSync(2), + createSync(3), + createSync(4), + createSync(5) }; const Log log { {1, Handler::Sync}, @@ -1285,11 +1737,11 @@ void tst_Tasking::testTree_data() const Group root { Storage(storage), parallel, - Sync(setupSync(1)), - Sync(setupSync(2)), - Sync(setupSyncWithReturn(3, false)), - Sync(setupSync(4)), - Sync(setupSync(5)) + createSync(1), + createSync(2), + createSyncWithReturn(3, false), + createSync(4), + createSync(5) }; const Log log { {1, Handler::Sync}, @@ -1302,18 +1754,20 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Sync(setupSync(1)), - Test(setupTask(2)), - Sync(setupSync(3)), - Test(setupTask(4)), - Sync(setupSync(5)), - onGroupDone(groupDone(0)) + createSync(1), + createSuccessTask(2), + createSync(3), + createSuccessTask(4), + createSync(5), + groupDone(0) }; const Log log { {1, Handler::Sync}, {2, Handler::Setup}, + {2, Handler::Done}, {3, Handler::Sync}, {4, Handler::Setup}, + {4, Handler::Done}, {5, Handler::Sync}, {0, Handler::GroupDone} }; @@ -1323,16 +1777,17 @@ void tst_Tasking::testTree_data() { const Group root { Storage(storage), - Sync(setupSync(1)), - Test(setupTask(2)), - Sync(setupSyncWithReturn(3, false)), - Test(setupTask(4)), - Sync(setupSync(5)), - onGroupError(groupError(0)) + createSync(1), + createSuccessTask(2), + createSyncWithReturn(3, false), + createSuccessTask(4), + createSync(5), + groupError(0) }; const Log log { {1, Handler::Sync}, {2, Handler::Setup}, + {2, Handler::Done}, {3, Handler::Sync}, {0, Handler::GroupError} }; @@ -1349,12 +1804,12 @@ void tst_Tasking::testTree_data() Storage(storage), Storage(barrier), sequential, - AsyncTask(setupBarrierAdvance(storage, barrier, 1)), + createBarrierAdvance(storage, barrier, 1), Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(2)), - Test(setupTask(3)) + createSuccessTask(2), + createSuccessTask(3) } }; const Log log1 { @@ -1362,7 +1817,9 @@ void tst_Tasking::testTree_data() {1, Handler::BarrierAdvance}, {2, Handler::GroupSetup}, {2, Handler::Setup}, - {3, Handler::Setup} + {2, Handler::Done}, + {3, Handler::Setup}, + {3, Handler::Done} }; // Test that barrier advance, triggered from inside the task described by @@ -1372,12 +1829,12 @@ void tst_Tasking::testTree_data() Storage(storage), Storage(barrier), parallel, - AsyncTask(setupBarrierAdvance(storage, barrier, 1)), + createBarrierAdvance(storage, barrier, 1), Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(2)), - Test(setupTask(3)) + createSuccessTask(2), + createSuccessTask(3) } }; const Log log2 { @@ -1385,7 +1842,9 @@ void tst_Tasking::testTree_data() {2, Handler::GroupSetup}, {1, Handler::BarrierAdvance}, {2, Handler::Setup}, - {3, Handler::Setup} + {2, Handler::Done}, + {3, Handler::Setup}, + {3, Handler::Done} }; // Test that barrier advance, triggered from inside the task described by @@ -1403,19 +1862,21 @@ void tst_Tasking::testTree_data() Storage(barrier), parallel, Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(2)), - Test(setupTask(3)) + createSuccessTask(2), + createSuccessTask(3) }, - AsyncTask(setupBarrierAdvance(storage, barrier, 1)) + createBarrierAdvance(storage, barrier, 1) }; const Log log3 { {2, Handler::GroupSetup}, {1, Handler::Setup}, {1, Handler::BarrierAdvance}, {2, Handler::Setup}, - {3, Handler::Setup} + {2, Handler::Done}, + {3, Handler::Setup}, + {3, Handler::Done} }; // Test that barrier advance, triggered from inside the task described by @@ -1425,16 +1886,16 @@ void tst_Tasking::testTree_data() Storage(storage), Storage(barrier), parallel, - AsyncTask(setupBarrierAdvance(storage, barrier, 1)), + createBarrierAdvance(storage, barrier, 1), Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(4)) + createSuccessTask(4) }, Group { - onGroupSetup(groupSetup(3)), + groupSetup(3), WaitForBarrierTask(barrier), - Test(setupTask(5)) + createSuccessTask(5) } }; const Log log4 { @@ -1443,7 +1904,9 @@ void tst_Tasking::testTree_data() {3, Handler::GroupSetup}, {1, Handler::BarrierAdvance}, {4, Handler::Setup}, - {5, Handler::Setup} + {5, Handler::Setup}, + {4, Handler::Done}, + {5, Handler::Done} }; // Test two separate single barriers. @@ -1455,25 +1918,26 @@ void tst_Tasking::testTree_data() Storage(barrier), Storage(barrier2), parallel, - AsyncTask(setupBarrierAdvance(storage, barrier, 0)), - AsyncTask(setupBarrierAdvance(storage, barrier2, 0)), + createBarrierAdvance(storage, barrier, 1), + createBarrierAdvance(storage, barrier2, 2), Group { Group { parallel, - onGroupSetup(groupSetup(1)), + groupSetup(1), WaitForBarrierTask(barrier), WaitForBarrierTask(barrier2) }, - Test(setupTask(2)) + createSuccessTask(3) }, }; const Log log5 { - {0, Handler::Setup}, - {0, Handler::Setup}, + {1, Handler::Setup}, + {2, Handler::Setup}, {1, Handler::GroupSetup}, - {0, Handler::BarrierAdvance}, - {0, Handler::BarrierAdvance}, - {2, Handler::Setup} + {1, Handler::BarrierAdvance}, + {2, Handler::BarrierAdvance}, + {3, Handler::Setup}, + {3, Handler::Done} }; // Notice the different log order for each scenario. @@ -1499,13 +1963,13 @@ void tst_Tasking::testTree_data() Storage(storage), Storage(barrier), sequential, - AsyncTask(setupBarrierAdvance(storage, barrier, 1)), - AsyncTask(setupBarrierAdvance(storage, barrier, 2)), + createBarrierAdvance(storage, barrier, 1), + createBarrierAdvance(storage, barrier, 2), Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(2)), - Test(setupTask(3)) + createSuccessTask(2), + createSuccessTask(3) } }; const Log log1 { @@ -1515,7 +1979,9 @@ void tst_Tasking::testTree_data() {2, Handler::BarrierAdvance}, {2, Handler::GroupSetup}, {2, Handler::Setup}, - {3, Handler::Setup} + {2, Handler::Done}, + {3, Handler::Setup}, + {3, Handler::Done} }; // Test that multi barrier advance, triggered from inside the tasks described by @@ -1525,23 +1991,25 @@ void tst_Tasking::testTree_data() Storage(storage), Storage(barrier), parallel, - AsyncTask(setupBarrierAdvance(storage, barrier, 0)), - AsyncTask(setupBarrierAdvance(storage, barrier, 0)), + createBarrierAdvance(storage, barrier, 1), + createBarrierAdvance(storage, barrier, 2), Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(2)), - Test(setupTask(3)) + createSuccessTask(3), + createSuccessTask(4) } }; const Log log2 { - {0, Handler::Setup}, - {0, Handler::Setup}, - {2, Handler::GroupSetup}, - {0, Handler::BarrierAdvance}, // Barrier advances may come in different order in - {0, Handler::BarrierAdvance}, // parallel mode, that's why id = 0 (same for both). + {1, Handler::Setup}, {2, Handler::Setup}, - {3, Handler::Setup} + {2, Handler::GroupSetup}, + {1, Handler::BarrierAdvance}, + {2, Handler::BarrierAdvance}, + {3, Handler::Setup}, + {3, Handler::Done}, + {4, Handler::Setup}, + {4, Handler::Done} }; // Test that multi barrier advance, triggered from inside the tasks described by @@ -1559,22 +2027,24 @@ void tst_Tasking::testTree_data() Storage(barrier), parallel, Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(2)), - Test(setupTask(3)) + createSuccessTask(3), + createSuccessTask(4) }, - AsyncTask(setupBarrierAdvance(storage, barrier, 0)), - AsyncTask(setupBarrierAdvance(storage, barrier, 0)) + createBarrierAdvance(storage, barrier, 1), + createBarrierAdvance(storage, barrier, 2) }; const Log log3 { {2, Handler::GroupSetup}, - {0, Handler::Setup}, - {0, Handler::Setup}, - {0, Handler::BarrierAdvance}, // Barrier advances may come in different order in - {0, Handler::BarrierAdvance}, // parallel mode, that's why id = 0 (same for both). + {1, Handler::Setup}, {2, Handler::Setup}, - {3, Handler::Setup} + {1, Handler::BarrierAdvance}, + {2, Handler::BarrierAdvance}, + {3, Handler::Setup}, + {3, Handler::Done}, + {4, Handler::Setup}, + {4, Handler::Done} }; // Test that multi barrier advance, triggered from inside the task described by @@ -1584,28 +2054,30 @@ void tst_Tasking::testTree_data() Storage(storage), Storage(barrier), parallel, - AsyncTask(setupBarrierAdvance(storage, barrier, 0)), - AsyncTask(setupBarrierAdvance(storage, barrier, 0)), + createBarrierAdvance(storage, barrier, 1), + createBarrierAdvance(storage, barrier, 2), Group { - onGroupSetup(groupSetup(2)), + groupSetup(2), WaitForBarrierTask(barrier), - Test(setupTask(4)) + createSuccessTask(3) }, Group { - onGroupSetup(groupSetup(3)), + groupSetup(3), WaitForBarrierTask(barrier), - Test(setupTask(5)) + createSuccessTask(4) } }; const Log log4 { - {0, Handler::Setup}, - {0, Handler::Setup}, + {1, Handler::Setup}, + {2, Handler::Setup}, {2, Handler::GroupSetup}, {3, Handler::GroupSetup}, - {0, Handler::BarrierAdvance}, - {0, Handler::BarrierAdvance}, + {1, Handler::BarrierAdvance}, + {2, Handler::BarrierAdvance}, + {3, Handler::Setup}, {4, Handler::Setup}, - {5, Handler::Setup} + {3, Handler::Done}, + {4, Handler::Done} }; // Notice the different log order for each scenario. @@ -1618,21 +2090,127 @@ void tst_Tasking::testTree_data() QTest::newRow("MultiBarrierParallelMultiWaitFor") << TestData{storage, root4, log4, 6, OnDone::Success}; } + + { + // Test CustomTask::withTimeout() combinations: + // 1. When the timeout has triggered or not. + // 2. With and without timeout handler. + const Group root1 { + Storage(storage), + TestTask(setupTask(1, 1000ms), setupDone(1), setupError(1)) + .withTimeout(1ms) + }; + const Log log1 { + {1, Handler::Setup}, + {1, Handler::Error} + }; + QTest::newRow("TaskErrorWithTimeout") << TestData{storage, root1, log1, 2, + OnDone::Failure}; + + const Group root2 { + Storage(storage), + TestTask(setupTask(1, 1000ms), setupDone(1), setupError(1)) + .withTimeout(1ms, setupTimeout(1)) + }; + const Log log2 { + {1, Handler::Setup}, + {1, Handler::Timeout}, + {1, Handler::Error} + }; + QTest::newRow("TaskErrorWithTimeoutHandler") << TestData{storage, root2, log2, 2, + OnDone::Failure}; + + const Group root3 { + Storage(storage), + TestTask(setupTask(1, 1ms), setupDone(1), setupError(1)) + .withTimeout(1000ms) + }; + const Log doneLog { + {1, Handler::Setup}, + {1, Handler::Done} + }; + QTest::newRow("TaskDoneWithTimeout") << TestData{storage, root3, doneLog, 2, + OnDone::Success}; + + const Group root4 { + Storage(storage), + TestTask(setupTask(1, 1ms), setupDone(1), setupError(1)) + .withTimeout(1000ms, setupTimeout(1)) + }; + QTest::newRow("TaskDoneWithTimeoutHandler") << TestData{storage, root4, doneLog, 2, + OnDone::Success}; + } + + { + // Test Group::withTimeout() combinations: + // 1. When the timeout has triggered or not. + // 2. With and without timeout handler. + const Group root1 { + Storage(storage), + Group { + createSuccessTask(1, 1000ms) + }.withTimeout(1ms) + }; + const Log log1 { + {1, Handler::Setup}, + {1, Handler::Error} + }; + QTest::newRow("GroupErrorWithTimeout") << TestData{storage, root1, log1, 2, + OnDone::Failure}; + + // Test Group::withTimeout(), passing custom handler + const Group root2 { + Storage(storage), + Group { + createSuccessTask(1, 1000ms) + }.withTimeout(1ms, setupTimeout(1)) + }; + const Log log2 { + {1, Handler::Setup}, + {1, Handler::Timeout}, + {1, Handler::Error} + }; + QTest::newRow("GroupErrorWithTimeoutHandler") << TestData{storage, root2, log2, 2, + OnDone::Failure}; + + const Group root3 { + Storage(storage), + Group { + createSuccessTask(1, 1ms) + }.withTimeout(1000ms) + }; + const Log doneLog { + {1, Handler::Setup}, + {1, Handler::Done} + }; + QTest::newRow("GroupDoneWithTimeout") << TestData{storage, root3, doneLog, 2, + OnDone::Success}; + + // Test Group::withTimeout(), passing custom handler + const Group root4 { + Storage(storage), + Group { + createSuccessTask(1, 1ms) + }.withTimeout(1000ms, setupTimeout(1)) + }; + QTest::newRow("GroupDoneWithTimeoutHandler") << TestData{storage, root4, doneLog, 2, + OnDone::Success}; + } } void tst_Tasking::testTree() { QFETCH(TestData, testData); - TaskTree taskTree(testData.root); - QCOMPARE(taskTree.taskCount(), testData.taskCount); + TaskTree taskTree({testData.root.withTimeout(1000ms)}); + QCOMPARE(taskTree.taskCount() - 1, testData.taskCount); // -1 for the timeout task above Log actualLog; const auto collectLog = [&actualLog](CustomStorage *storage) { actualLog = storage->m_log; }; taskTree.onStorageDone(testData.storage, collectLog); - const OnDone result = taskTree.runBlocking(2000) ? OnDone::Success : OnDone::Failure; + const OnDone result = taskTree.runBlocking() ? OnDone::Success : OnDone::Failure; QCOMPARE(taskTree.isRunning(), false); - QCOMPARE(taskTree.progressValue(), testData.taskCount); + QCOMPARE(taskTree.progressValue(), taskTree.progressMaximum()); QCOMPARE(actualLog, testData.expectedLog); QCOMPARE(CustomStorage::instanceCount(), 0); @@ -1667,13 +2245,12 @@ void tst_Tasking::storageDestructor() QCOMPARE(CustomStorage::instanceCount(), 0); { TreeStorage storage; - const auto setupSleepingTask = [](TestTask &task) { - task.setFutureSynchronizer(s_futureSynchronizer); - task.setConcurrentCallData(runTask, true, 1000ms); + const auto setupSleepingTask = [](TaskObject &taskObject) { + taskObject = 1000ms; }; const Group root { Storage(storage), - Test(setupSleepingTask) + TestTask(setupSleepingTask) }; TaskTree taskTree(root); @@ -1682,7 +2259,6 @@ void tst_Tasking::storageDestructor() taskTree.onStorageDone(storage, doneHandler); taskTree.start(); QCOMPARE(CustomStorage::instanceCount(), 1); - QThread::msleep(5); // Give the sleeping task a change to start } QCOMPARE(CustomStorage::instanceCount(), 0); QVERIFY(setupCalled); diff --git a/tests/auto/texteditor/highlighter/tst_highlighter.cpp b/tests/auto/texteditor/highlighter/tst_highlighter.cpp index 163699c0cb1..e3f4e3dced6 100644 --- a/tests/auto/texteditor/highlighter/tst_highlighter.cpp +++ b/tests/auto/texteditor/highlighter/tst_highlighter.cpp @@ -301,6 +301,41 @@ void tst_highlighter::test_incrementalApplyAdditionalFormats() // should have no results since the new results do not contain a highlight at that position formats = emptyLineBlock.layout()->formats(); QVERIFY(formats.isEmpty()); + + // QTCREATORBUG-29218 + highlighter->clearAllExtraFormats(); + const HighlightingResults bug29218Results{HighlightingResult(1, 1, 2, 0), + HighlightingResult(1, 3, 2, 1)}; + QFutureInterface fi29218; + fi29218.reportResults(bug29218Results); + formats = firstBlock.layout()->formats(); + QVERIFY(formats.isEmpty()); + SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter, + fi29218.future(), + 0, + 1, + formatHash); + formats = firstBlock.layout()->formats(); + QCOMPARE(formats.size(), 1); + QCOMPARE(formats.at(0).format.fontItalic(), true); + QCOMPARE(formats.at(0).format.fontOverline(), false); + QCOMPARE(formats.at(0).start, 0); + QCOMPARE(formats.at(0).length, 2); + SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter, + fi29218.future(), + 1, + 2, + formatHash); + formats = firstBlock.layout()->formats(); + QCOMPARE(formats.size(), 2); + QCOMPARE(formats.at(0).format.fontItalic(), true); + QCOMPARE(formats.at(0).format.fontOverline(), false); + QCOMPARE(formats.at(0).start, 0); + QCOMPARE(formats.at(0).length, 2); + QCOMPARE(formats.at(1).format.fontItalic(), false); + QCOMPARE(formats.at(1).format.fontOverline(), true); + QCOMPARE(formats.at(1).start, 2); + QCOMPARE(formats.at(1).length, 2); } void tst_highlighter::cleanup() diff --git a/tests/auto/utils/async/tst_async.cpp b/tests/auto/utils/async/tst_async.cpp index 01c3306a3bf..0e8bc1e2b03 100644 --- a/tests/auto/utils/async/tst_async.cpp +++ b/tests/auto/utils/async/tst_async.cpp @@ -8,6 +8,8 @@ using namespace Utils; +using namespace std::chrono_literals; + class tst_Async : public QObject { Q_OBJECT @@ -438,8 +440,8 @@ void tst_Async::taskTree() AsyncTask(setupIntAsync, handleIntAsync), }; - TaskTree tree(root); - QVERIFY(tree.runBlocking(1000)); + + QVERIFY(TaskTree::runBlocking(root, 1000ms)); QCOMPARE(value, 16); } @@ -498,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 { @@ -575,8 +577,7 @@ void tst_Async::mapReduce() QFETCH(double, sum); QFETCH(QList, results); - TaskTree tree(root); - QVERIFY(tree.runBlocking(1000)); + QVERIFY(TaskTree::runBlocking(root, 1000ms)); QCOMPARE(s_results, results); QCOMPARE(s_sum, sum); } diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index f4596cb1a5c..198abf3398b 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -136,6 +136,9 @@ void tst_fileutils::filePathInfoFromTriple_data() FilePathInfo::ReadOwnerPerm | FilePathInfo::WriteOwnerPerm | FilePathInfo::ExeOwnerPerm + | FilePathInfo::ReadUserPerm + | FilePathInfo::WriteUserPerm + | FilePathInfo::ExeUserPerm | FilePathInfo::ReadGroupPerm | FilePathInfo::ExeGroupPerm | FilePathInfo::ReadOtherPerm @@ -149,10 +152,11 @@ void tst_fileutils::filePathInfoFromTriple_data() << FilePathInfo{808104, FilePathInfo::FileFlags( FilePathInfo::ReadOwnerPerm | FilePathInfo::WriteOwnerPerm - | FilePathInfo::ExeOwnerPerm | FilePathInfo::ReadGroupPerm - | FilePathInfo::ExeGroupPerm | FilePathInfo::ReadOtherPerm - | FilePathInfo::ExeOtherPerm | FilePathInfo::FileType - | FilePathInfo::ExistsFlag), + | FilePathInfo::ExeOwnerPerm | FilePathInfo::ReadUserPerm + | FilePathInfo::WriteUserPerm | FilePathInfo::ExeUserPerm + | FilePathInfo::ReadGroupPerm | FilePathInfo::ExeGroupPerm + | FilePathInfo::ReadOtherPerm | FilePathInfo::ExeOtherPerm + | FilePathInfo::FileType | FilePathInfo::ExistsFlag), QDateTime::fromSecsSinceEpoch(1668852790)}; QTest::newRow("linux-disk") << QString("61b0 1651167746 0") @@ -160,6 +164,8 @@ void tst_fileutils::filePathInfoFromTriple_data() FilePathInfo::FileFlags( FilePathInfo::ReadOwnerPerm | FilePathInfo::WriteOwnerPerm + | FilePathInfo::ReadUserPerm + | FilePathInfo::WriteUserPerm | FilePathInfo::ReadGroupPerm | FilePathInfo::WriteGroupPerm | FilePathInfo::LocalDiskFlag diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index fb0b611f4f7..fde3cb962ea 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -17,5 +17,5 @@ add_subdirectory(proparser) # add_subdirectory(search) add_subdirectory(shootout) add_subdirectory(subdirfileiterator) -add_subdirectory(tasktree) +add_subdirectory(tasking) add_subdirectory(widgets) diff --git a/tests/manual/manual.qbs b/tests/manual/manual.qbs index 8e157a9216b..35d08815d26 100644 --- a/tests/manual/manual.qbs +++ b/tests/manual/manual.qbs @@ -14,7 +14,8 @@ Project { "proparser/testreader.qbs", "shootout/shootout.qbs", "subdirfileiterator/subdirfileiterator.qbs", - "tasktree/tasktree.qbs", + "tasking/demo/demo.qbs", + "tasking/imagescaling/imagescaling.qbs", "widgets/widgets.qbs", ] } diff --git a/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp b/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp index 55312208e8a..e3f81388a32 100644 --- a/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp +++ b/tests/manual/subdirfileiterator/tst_subdirfileiterator.cpp @@ -148,15 +148,14 @@ private slots: }; // Parallelize tree generation - QList tasks{parallelLimit(m_threadsCount)}; + QList tasks{parallelLimit(m_threadsCount)}; for (int i = 1; i < s_topLevelSubDirsCount; ++i) { const QString destDirName = dirName(i); QVERIFY(parentDir.mkdir(destDirName)); tasks.append(AsyncTask(onCopySetup(parentDir.filePath(sourceDirName), parentDir.filePath(destDirName)))); } - TaskTree taskTree(tasks); - QVERIFY(taskTree.runBlocking()); + QVERIFY(TaskTree::runBlocking(tasks)); } void cleanupTestCase() @@ -174,12 +173,11 @@ private slots: }; }; const QDir parentDir(m_tempDir->path()); - QList tasks {parallelLimit(m_threadsCount)}; + QList tasks {parallelLimit(m_threadsCount)}; for (int i = 0; i < s_topLevelSubDirsCount; ++i) tasks.append(AsyncTask(onSetup(parentDir.filePath(dirName(i))))); - TaskTree taskTree(tasks); - QVERIFY(taskTree.runBlocking()); + QVERIFY(TaskTree::runBlocking(tasks)); m_tempDir.reset(); Singleton::deleteAll(); diff --git a/tests/manual/tasking/CMakeLists.txt b/tests/manual/tasking/CMakeLists.txt new file mode 100644 index 00000000000..a27004eb3ec --- /dev/null +++ b/tests/manual/tasking/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(demo) +add_subdirectory(imagescaling) diff --git a/tests/manual/tasking/demo/CMakeLists.txt b/tests/manual/tasking/demo/CMakeLists.txt new file mode 100644 index 00000000000..7597e74d608 --- /dev/null +++ b/tests/manual/tasking/demo/CMakeLists.txt @@ -0,0 +1,11 @@ +add_qtc_test(tst_tasking_demo + MANUALTEST + DEPENDS Tasking Qt::Widgets Qt::Network + SOURCES + demo.qrc + main.cpp + progressindicator.h + progressindicator.cpp + taskwidget.h + taskwidget.cpp +) diff --git a/tests/manual/tasking/demo/demo.qbs b/tests/manual/tasking/demo/demo.qbs new file mode 100644 index 00000000000..9cf856bca21 --- /dev/null +++ b/tests/manual/tasking/demo/demo.qbs @@ -0,0 +1,18 @@ +import qbs.FileInfo + +QtcManualtest { + name: "Tasking demo" + type: ["application"] + + Depends { name: "Qt"; submodules: ["network", "widgets"] } + Depends { name: "Tasking" } + + files: [ + "demo.qrc", + "main.cpp", + "progressindicator.h", + "progressindicator.cpp", + "taskwidget.h", + "taskwidget.cpp", + ] +} diff --git a/tests/manual/tasking/demo/demo.qrc b/tests/manual/tasking/demo/demo.qrc new file mode 100644 index 00000000000..5ea09fe25ec --- /dev/null +++ b/tests/manual/tasking/demo/demo.qrc @@ -0,0 +1,6 @@ + + + icons/progressindicator.png + icons/progressindicator@2x.png + + diff --git a/tests/manual/tasking/demo/icons/progressindicator.png b/tests/manual/tasking/demo/icons/progressindicator.png new file mode 100644 index 00000000000..c968ae8ac9f Binary files /dev/null and b/tests/manual/tasking/demo/icons/progressindicator.png differ diff --git a/tests/manual/tasking/demo/icons/progressindicator@2x.png b/tests/manual/tasking/demo/icons/progressindicator@2x.png new file mode 100644 index 00000000000..36821a92e19 Binary files /dev/null and b/tests/manual/tasking/demo/icons/progressindicator@2x.png differ diff --git a/tests/manual/tasktree/main.cpp b/tests/manual/tasking/demo/main.cpp similarity index 55% rename from tests/manual/tasktree/main.cpp rename to tests/manual/tasking/demo/main.cpp index ac5df4b0f6c..807def8e454 100644 --- a/tests/manual/tasktree/main.cpp +++ b/tests/manual/tasking/demo/main.cpp @@ -1,53 +1,66 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "taskwidget.h" -#include -#include -#include -#include +#include #include -#include +#include +#include #include #include #include #include -using namespace Utils; +using namespace Tasking; -// TODO: make tasks cancellable -static void sleepInThread(QPromise &promise, int seconds, bool reportSuccess) +using namespace std::chrono; + +static QWidget *hr() { - QThread::sleep(seconds); - if (!reportSuccess) - promise.future().cancel(); + auto frame = new QFrame; + frame->setFrameShape(QFrame::HLine); + frame->setFrameShadow(QFrame::Sunken); + return frame; +} + +QWidget *taskGroup(QWidget *groupWidget, const QList &widgets) +{ + QWidget *widget = new QWidget; + QBoxLayout *layout = new QHBoxLayout(widget); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(groupWidget); + QGroupBox *groupBox = new QGroupBox; + QBoxLayout *subLayout = new QVBoxLayout(groupBox); + for (int i = 0; i < widgets.size(); ++i) { + if (i > 0) + subLayout->addWidget(hr()); + subLayout->addWidget(widgets.at(i)); + } + layout->addWidget(groupBox); + return widget; } int main(int argc, char *argv[]) { QApplication app(argc, argv); - setCreatorTheme(new Theme("default", &app)); - QWidget mainWidget; mainWidget.setWindowTitle("Task Tree Demo"); // Non-task GUI - QToolButton *startButton = new QToolButton(); + QToolButton *startButton = new QToolButton; startButton->setText("Start"); - QToolButton *stopButton = new QToolButton(); + QToolButton *stopButton = new QToolButton; stopButton->setText("Stop"); - QToolButton *resetButton = new QToolButton(); + QToolButton *resetButton = new QToolButton; resetButton->setText("Reset"); - QProgressBar *progressBar = new QProgressBar(); - QCheckBox *synchronizerCheckBox = new QCheckBox("Use Future Synchronizer"); - synchronizerCheckBox->setChecked(true); - QScrollArea *scrollArea = new QScrollArea(); + QProgressBar *progressBar = new QProgressBar; + QScrollArea *scrollArea = new QScrollArea; scrollArea->setWidgetResizable(true); - QWidget *scrollAreaWidget = new QWidget(); + QWidget *scrollAreaWidget = new QWidget; // Task GUI @@ -100,120 +113,124 @@ int main(int argc, char *argv[]) task_4_4->setBusyTime(6); task_4_4->setBusyTime(3); - groupTask_1->setWorkflowPolicy(Tasking::WorkflowPolicy::ContinueOnDone); - groupTask_4->setWorkflowPolicy(Tasking::WorkflowPolicy::Optional); + groupTask_1->setWorkflowPolicy(WorkflowPolicy::ContinueOnDone); + groupTask_4->setWorkflowPolicy(WorkflowPolicy::FinishAllAndDone); groupTask_4_3->setExecuteMode(ExecuteMode::Parallel); - groupTask_4_3->setWorkflowPolicy(Tasking::WorkflowPolicy::StopOnError); + groupTask_4_3->setWorkflowPolicy(WorkflowPolicy::StopOnError); // Task layout { - using namespace Layouting; - - Column { - TaskGroup { rootGroup, { - TaskGroup { groupTask_1, { - task_1_1, hr, - task_1_2, hr, - task_1_3, - }}, hr, - task_2, hr, - task_3, hr, - TaskGroup { groupTask_4, { - task_4_1, hr, - task_4_2, hr, - TaskGroup { groupTask_4_3, { - task_4_3_1, hr, - task_4_3_2, hr, - task_4_3_3, hr, - task_4_3_4, - }}, hr, - task_4_4, hr, - task_4_5, - }}, hr, - task_5 - }}, st - }.attachTo(scrollAreaWidget); - + QWidget *taskTree = taskGroup(rootGroup, { + taskGroup(groupTask_1, { + task_1_1, + task_1_2, + task_1_3 + }), + task_2, + task_3, + taskGroup(groupTask_4, { + task_4_1, + task_4_2, + taskGroup(groupTask_4_3, { + task_4_3_1, + task_4_3_2, + task_4_3_3, + task_4_3_4, + }), + task_4_4, + task_4_5 + }), + task_5 + }); + QBoxLayout *scrollLayout = new QVBoxLayout(scrollAreaWidget); + scrollLayout->addWidget(taskTree); + scrollLayout->addStretch(); scrollArea->setWidget(scrollAreaWidget); - Column { - Row { startButton, stopButton, resetButton, synchronizerCheckBox, progressBar }, - hr, - scrollArea - }.attachTo(&mainWidget); + QBoxLayout *mainLayout = new QVBoxLayout(&mainWidget); + QBoxLayout *subLayout = new QHBoxLayout; + subLayout->addWidget(startButton); + subLayout->addWidget(stopButton); + subLayout->addWidget(resetButton); + subLayout->addWidget(progressBar); + mainLayout->addLayout(subLayout); + mainLayout->addWidget(hr()); + mainLayout->addWidget(scrollArea); } - // Task tree creator (takes configuation from GUI) - - using namespace Tasking; + // Task tree (takes initial configuation from GUI) std::unique_ptr taskTree; - FutureSynchronizer synchronizer; + + const auto createTask = [](TaskWidget *widget) -> GroupItem { + const auto setupTask = [](TaskWidget *widget) { + return [widget](milliseconds &taskObject) { + taskObject = milliseconds{widget->busyTime() * 1000}; + widget->setState(State::Running); + }; + }; + if (widget->isSuccess()) { + return TimeoutTask(setupTask(widget), + [widget](const milliseconds &) { widget->setState(State::Done); }, + [widget](const milliseconds &) { widget->setState(State::Error); }); + } + const Group root { + finishAllAndError, + TimeoutTask(setupTask(widget)), + onGroupDone([widget] { widget->setState(State::Done); }), + onGroupError([widget] { widget->setState(State::Error); }) + }; + return root; + }; auto treeRoot = [&] { - auto taskItem = [sync = &synchronizer, synchronizerCheckBox](TaskWidget *widget) { - const auto setupHandler = [=](Async &task) { - task.setConcurrentCallData(sleepInThread, widget->busyTime(), widget->isSuccess()); - if (synchronizerCheckBox->isChecked()) - task.setFutureSynchronizer(sync); - widget->setState(State::Running); - }; - const auto doneHandler = [widget](const Async &) { - widget->setState(State::Done); - }; - const auto errorHandler = [widget](const Async &) { - widget->setState(State::Error); - }; - return AsyncTask(setupHandler, doneHandler, errorHandler); - }; - const Group root { rootGroup->executeMode(), - workflowPolicy(rootGroup->workflowPolicy()), + rootGroup->workflowPolicy(), onGroupSetup([rootGroup] { rootGroup->setState(State::Running); }), onGroupDone([rootGroup] { rootGroup->setState(State::Done); }), onGroupError([rootGroup] { rootGroup->setState(State::Error); }), Group { groupTask_1->executeMode(), - workflowPolicy(groupTask_1->workflowPolicy()), + groupTask_1->workflowPolicy(), onGroupSetup([groupTask_1] { groupTask_1->setState(State::Running); }), onGroupDone([groupTask_1] { groupTask_1->setState(State::Done); }), onGroupError([groupTask_1] { groupTask_1->setState(State::Error); }), - taskItem(task_1_1), - taskItem(task_1_2), - taskItem(task_1_3) + createTask(task_1_1), + createTask(task_1_2), + createTask(task_1_3) }, - taskItem(task_2), - taskItem(task_3), + createTask(task_2), + createTask(task_3), Group { groupTask_4->executeMode(), - workflowPolicy(groupTask_4->workflowPolicy()), + groupTask_4->workflowPolicy(), onGroupSetup([groupTask_4] { groupTask_4->setState(State::Running); }), onGroupDone([groupTask_4] { groupTask_4->setState(State::Done); }), onGroupError([groupTask_4] { groupTask_4->setState(State::Error); }), - taskItem(task_4_1), - taskItem(task_4_2), + createTask(task_4_1), + createTask(task_4_2), Group { groupTask_4_3->executeMode(), - workflowPolicy(groupTask_4_3->workflowPolicy()), + groupTask_4_3->workflowPolicy(), onGroupSetup([groupTask_4_3] { groupTask_4_3->setState(State::Running); }), onGroupDone([groupTask_4_3] { groupTask_4_3->setState(State::Done); }), onGroupError([groupTask_4_3] { groupTask_4_3->setState(State::Error); }), - taskItem(task_4_3_1), - taskItem(task_4_3_2), - taskItem(task_4_3_3), - taskItem(task_4_3_4) + createTask(task_4_3_1), + createTask(task_4_3_2), + createTask(task_4_3_3), + createTask(task_4_3_4) }, - taskItem(task_4_4), - taskItem(task_4_5) + createTask(task_4_4), + createTask(task_4_5) }, - taskItem(task_5) + createTask(task_5) }; return root; }; @@ -258,7 +275,7 @@ int main(int argc, char *argv[]) // Hack in order to show initial size minimal, but without scrollbars. // Apparently setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow) doesn't work. - const int margin = 2; + const int margin = 4; scrollArea->setMinimumSize(scrollAreaWidget->minimumSizeHint().grownBy({0, 0, margin, margin})); QTimer::singleShot(0, scrollArea, [&] { scrollArea->setMinimumSize({0, 0}); }); diff --git a/tests/manual/tasking/demo/progressindicator.cpp b/tests/manual/tasking/demo/progressindicator.cpp new file mode 100644 index 00000000000..38f577f4ed7 --- /dev/null +++ b/tests/manual/tasking/demo/progressindicator.cpp @@ -0,0 +1,149 @@ +// 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 "progressindicator.h" + +#include +#include +#include +#include + +class OverlayWidget : public QWidget +{ +public: + using PaintFunction = std::function; + + explicit OverlayWidget(QWidget *parent = nullptr) + { + setAttribute(Qt::WA_TransparentForMouseEvents); + if (parent) + attachToWidget(parent); + } + + void attachToWidget(QWidget *parent) + { + if (parentWidget()) + parentWidget()->removeEventFilter(this); + setParent(parent); + if (parent) { + parent->installEventFilter(this); + resizeToParent(); + raise(); + } + } + void setPaintFunction(const PaintFunction &paint) { m_paint = paint; } + +protected: + bool eventFilter(QObject *obj, QEvent *ev) override + { + if (obj == parent() && ev->type() == QEvent::Resize) + resizeToParent(); + return QWidget::eventFilter(obj, ev); + } + void paintEvent(QPaintEvent *ev) override + { + if (m_paint) { + QPainter p(this); + m_paint(this, p, ev); + } + } + +private: + void resizeToParent() { setGeometry(QRect(QPoint(0, 0), parentWidget()->size())); } + + PaintFunction m_paint; +}; + +class ProgressIndicatorPainter +{ +public: + using UpdateCallback = std::function; + + ProgressIndicatorPainter(); + virtual ~ProgressIndicatorPainter() = default; + + void setUpdateCallback(const UpdateCallback &cb) { m_callback = cb; } + + QSize size() const { return m_pixmap.size() / m_pixmap.devicePixelRatio(); } + + void paint(QPainter &painter, const QRect &rect) const; + void startAnimation() { m_timer.start(); } + void stopAnimation() { m_timer.stop(); } + +protected: + void nextAnimationStep() { m_rotation = (m_rotation + m_rotationStep + 360) % 360; } + +private: + const int m_rotationStep = 45; + int m_rotation = 0; + QTimer m_timer; + QPixmap m_pixmap; + UpdateCallback m_callback; +}; + +ProgressIndicatorPainter::ProgressIndicatorPainter() +{ + m_timer.setSingleShot(false); + QObject::connect(&m_timer, &QTimer::timeout, &m_timer, [this] { + nextAnimationStep(); + if (m_callback) + m_callback(); + }); + + m_timer.setInterval(100); + m_pixmap = QPixmap(":/icons/progressindicator.png"); +} + +void ProgressIndicatorPainter::paint(QPainter &painter, const QRect &rect) const +{ + painter.save(); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + QPoint translate(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); + QTransform t; + t.translate(translate.x(), translate.y()); + t.rotate(m_rotation); + t.translate(-translate.x(), -translate.y()); + painter.setTransform(t); + QSize pixmapUserSize(m_pixmap.size() / m_pixmap.devicePixelRatio()); + painter.drawPixmap(QPoint(rect.x() + ((rect.width() - pixmapUserSize.width()) / 2), + rect.y() + ((rect.height() - pixmapUserSize.height()) / 2)), + m_pixmap); + painter.restore(); +} + +class ProgressIndicatorWidget : public OverlayWidget +{ +public: + explicit ProgressIndicatorWidget(QWidget *parent = nullptr) + : OverlayWidget(parent) + { + setPaintFunction( + [this](QWidget *w, QPainter &p, QPaintEvent *) { m_paint.paint(p, w->rect()); }); + m_paint.setUpdateCallback([this] { update(); }); + updateGeometry(); + } + + QSize sizeHint() const final { return m_paint.size(); } + +protected: + void showEvent(QShowEvent *) final { m_paint.startAnimation(); } + void hideEvent(QHideEvent *) final { m_paint.stopAnimation(); } + +private: + ProgressIndicatorPainter m_paint; +}; + +ProgressIndicator::ProgressIndicator(QWidget *parent) + : QObject(parent) + , m_widget(new ProgressIndicatorWidget(parent)) {} + + +void ProgressIndicator::show() +{ + m_widget->show(); +} + +void ProgressIndicator::hide() +{ + m_widget->hide(); +} diff --git a/tests/manual/tasking/demo/progressindicator.h b/tests/manual/tasking/demo/progressindicator.h new file mode 100644 index 00000000000..406ebc9e831 --- /dev/null +++ b/tests/manual/tasking/demo/progressindicator.h @@ -0,0 +1,21 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef PROGRESSINDICATOR_H +#define PROGRESSINDICATOR_H + +#include + +class ProgressIndicator : public QObject +{ +public: + ProgressIndicator(QWidget *parent = nullptr); + + void show(); + void hide(); + +private: + class ProgressIndicatorWidget *m_widget = nullptr; +}; + +#endif // PROGRESSINDICATOR_H diff --git a/tests/manual/tasktree/taskwidget.cpp b/tests/manual/tasking/demo/taskwidget.cpp similarity index 59% rename from tests/manual/tasktree/taskwidget.cpp rename to tests/manual/tasking/demo/taskwidget.cpp index e969dbf2d73..97d3d2b9ef7 100644 --- a/tests/manual/tasktree/taskwidget.cpp +++ b/tests/manual/tasking/demo/taskwidget.cpp @@ -1,20 +1,18 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#include "progressindicator.h" #include "taskwidget.h" -#include -#include -#include - +#include #include #include #include #include +#include #include -using namespace Utils; -using namespace Layouting; +using namespace Tasking; static QString colorButtonStyleSheet(const QColor &bgColor) { @@ -37,10 +35,10 @@ static QColor stateToColor(State state) { class StateIndicator : public QLabel { public: - StateIndicator() + StateIndicator(QWidget *parent = nullptr) + : QLabel(parent) { - m_progressIndicator = new ProgressIndicator(ProgressIndicatorSize::Small); - m_progressIndicator->attachToWidget(this); + m_progressIndicator = new ProgressIndicator(this); m_progressIndicator->hide(); updateState(); } @@ -67,9 +65,7 @@ private: }; StateWidget::StateWidget() - : m_stateIndicator(new StateIndicator) -{ -} + : m_stateIndicator(new StateIndicator(this)) {} void StateWidget::setState(State state) { @@ -86,14 +82,13 @@ TaskWidget::TaskWidget() setBusyTime(1); setSuccess(true); - Row { - m_stateIndicator, - m_infoLabel, - m_spinBox, - m_checkBox, - st, - noMargin, - }.attachTo(this); + QBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_stateIndicator); + layout->addWidget(m_infoLabel); + layout->addWidget(m_spinBox); + layout->addWidget(m_checkBox); + layout->addStretch(); + layout->setContentsMargins(0, 0, 0, 0); } void TaskWidget::setBusyTime(int seconds) @@ -122,34 +117,32 @@ GroupWidget::GroupWidget() { m_stateIndicator->setFixedWidth(30); - m_executeCombo->addItem("Sequential", (int)ExecuteMode::Sequential); - m_executeCombo->addItem("Parallel", (int)ExecuteMode::Parallel); + m_executeCombo->addItem("Sequential", int(ExecuteMode::Sequential)); + m_executeCombo->addItem("Parallel", int(ExecuteMode::Parallel)); updateExecuteMode(); connect(m_executeCombo, &QComboBox::currentIndexChanged, this, [this](int index) { m_executeMode = (ExecuteMode)m_executeCombo->itemData(index).toInt(); }); - m_workflowCombo->addItem("Stop On Error", (int)Tasking::WorkflowPolicy::StopOnError); - m_workflowCombo->addItem("Cont On Error", (int)Tasking::WorkflowPolicy::ContinueOnError); - m_workflowCombo->addItem("Stop On Done", (int)Tasking::WorkflowPolicy::StopOnDone); - m_workflowCombo->addItem("Cont On Done", (int)Tasking::WorkflowPolicy::ContinueOnDone); - m_workflowCombo->addItem("Optional", (int)Tasking::WorkflowPolicy::Optional); + const QMetaEnum workflow = QMetaEnum::fromType(); + for (int i = 0; i < workflow.keyCount(); ++i) + m_workflowCombo->addItem(workflow.key(i), workflow.value(i)); + updateWorkflowPolicy(); connect(m_workflowCombo, &QComboBox::currentIndexChanged, this, [this](int index) { - m_workflowPolicy = (Tasking::WorkflowPolicy)m_workflowCombo->itemData(index).toInt(); + m_workflowPolicy = (WorkflowPolicy)m_workflowCombo->itemData(index).toInt(); }); - Row { - m_stateIndicator, - Column { - new QLabel("Execute:"), - m_executeCombo, - new QLabel("Workflow:"), - m_workflowCombo, - st, - noMargin - } - }.attachTo(this); + QBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_stateIndicator); + QBoxLayout *subLayout = new QVBoxLayout; + subLayout->addWidget(new QLabel("Execute Mode:")); + subLayout->addWidget(m_executeCombo); + subLayout->addWidget(new QLabel("Workflow Policy:")); + subLayout->addWidget(m_workflowCombo); + subLayout->addStretch(); + layout->addLayout(subLayout); + layout->setContentsMargins(0, 0, 0, 0); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred); } @@ -165,12 +158,12 @@ void GroupWidget::updateExecuteMode() m_executeCombo->setCurrentIndex(m_executeCombo->findData((int)m_executeMode)); } -Tasking::TaskItem GroupWidget::executeMode() const +GroupItem GroupWidget::executeMode() const { - return m_executeMode == ExecuteMode::Sequential ? Tasking::sequential : Tasking::parallel; + return m_executeMode == ExecuteMode::Sequential ? sequential : parallel; } -void GroupWidget::setWorkflowPolicy(Tasking::WorkflowPolicy policy) +void GroupWidget::setWorkflowPolicy(WorkflowPolicy policy) { m_workflowPolicy = policy; updateWorkflowPolicy(); @@ -181,13 +174,7 @@ void GroupWidget::updateWorkflowPolicy() m_workflowCombo->setCurrentIndex(m_workflowCombo->findData((int)m_workflowPolicy)); } -Tasking::WorkflowPolicy GroupWidget::workflowPolicy() const +GroupItem GroupWidget::workflowPolicy() const { - return m_workflowPolicy; + return Tasking::workflowPolicy(m_workflowPolicy); } - -void createItem(Layouting::LayoutItem *item, const TaskGroup &taskGroup) -{ - item->addItems({taskGroup.group, Group { taskGroup.items }, br}); -} - diff --git a/tests/manual/tasktree/taskwidget.h b/tests/manual/tasking/demo/taskwidget.h similarity index 72% rename from tests/manual/tasktree/taskwidget.h rename to tests/manual/tasking/demo/taskwidget.h index c425f23965f..a737e315a4e 100644 --- a/tests/manual/tasktree/taskwidget.h +++ b/tests/manual/tasking/demo/taskwidget.h @@ -1,9 +1,10 @@ -// Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause -#include +#ifndef TASKWIDGET_H +#define TASKWIDGET_H -#include +#include #include @@ -61,10 +62,10 @@ public: GroupWidget(); void setExecuteMode(ExecuteMode mode); - Tasking::TaskItem executeMode() const; + Tasking::GroupItem executeMode() const; void setWorkflowPolicy(Tasking::WorkflowPolicy policy); - Tasking::WorkflowPolicy workflowPolicy() const; + Tasking::GroupItem workflowPolicy() const; private: void updateExecuteMode(); @@ -77,11 +78,4 @@ private: Tasking::WorkflowPolicy m_workflowPolicy = Tasking::WorkflowPolicy::StopOnError; }; -class TaskGroup -{ -public: - QWidget *group; - Layouting::Column items; -}; - -void createItem(Layouting::LayoutItem *item, const TaskGroup &taskGroup); +#endif // TASKWIDGET_H 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..362a30287e6 --- /dev/null +++ b/tests/manual/tasking/imagescaling/imagescaling.cpp @@ -0,0 +1,204 @@ +// 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")); + connect(addUrlsButton, &QPushButton::clicked, this, &Images::process); + + cancelButton = new QPushButton(tr("Cancel")); + cancelButton->setEnabled(false); + connect(cancelButton, &QPushButton::clicked, this, &Images::cancel); + + 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); + + connect(&scalingWatcher, &QFutureWatcher>::finished, + this, &Images::scaleFinished); +} + +Images::~Images() +{ + cancel(); +} + +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...")); + + 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); + }); + } +} + +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(); + }); + } + + 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])); + } +} + +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..d3409e60a0f --- /dev/null +++ b/tests/manual/tasking/imagescaling/imagescaling.h @@ -0,0 +1,52 @@ +// 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: + using OptionalImages = std::optional>; + 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(); +} diff --git a/tests/manual/tasktree/CMakeLists.txt b/tests/manual/tasktree/CMakeLists.txt deleted file mode 100644 index 19bd81bfb2b..00000000000 --- a/tests/manual/tasktree/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -add_qtc_test(tst_manual_tasktree - MANUALTEST - DEPENDS Utils - SOURCES - main.cpp taskwidget.h taskwidget.cpp -) diff --git a/tests/manual/tasktree/tasktree.qbs b/tests/manual/tasktree/tasktree.qbs deleted file mode 100644 index dad8e14763c..00000000000 --- a/tests/manual/tasktree/tasktree.qbs +++ /dev/null @@ -1,14 +0,0 @@ -import qbs.FileInfo - -QtcManualtest { - name: "Manual TaskTree test" - type: ["application"] - - Depends { name: "Utils" } - - files: [ - "main.cpp", - "taskwidget.h", - "taskwidget.cpp", - ] -} diff --git a/tests/system/objects.map b/tests/system/objects.map index 591b671e539..97d44b7ed26 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -62,6 +62,8 @@ :Dialog.componentNameEdit_QLineEdit {name='componentNameEdit' type='Utils::ClassNameValidatingLineEdit' visible='1' window=':Dialog_QmlJSEditor::Internal::ComponentNameDialog'} :Dialog_Debugger::Internal::SymbolPathsDialog {name='Debugger__Internal__SymbolPathsDialog' type='Debugger::Internal::SymbolPathsDialog' visible='1' windowTitle='Dialog'} :Dialog_QmlJSEditor::Internal::ComponentNameDialog {type='QmlJSEditor::Internal::ComponentNameDialog' unnamed='1' visible='1' windowTitle='Move Component into Separate File'} +:EnableQMLDebugger_ComboBox {buddy=':EnableQMLDebugger_Label' type='QComboBox' unnamed='1' visible='1'} +:EnableQMLDebugger_Label {text='QML debugger:' type='QLabel' window=':Qt Creator_Core::Internal::MainWindow'} :Events.QmlProfilerEventsTable_QmlProfiler::Internal::QmlProfilerStatisticsMainView {container=':Qt Creator.Events_QDockWidget' name='QmlProfilerEventsTable' type='QmlProfiler::Internal::QmlProfilerStatisticsMainView' visible='1'} :Failed to start application_QMessageBox {type='QMessageBox' unnamed='1' visible='1' windowTitle='Failed to start application'} :File has been removed.Close_QPushButton {text='Close' type='QPushButton' unnamed='1' visible='1' window=':File has been removed_QMessageBox'} @@ -124,7 +126,7 @@ :Qt Creator.DragDoc_QToolButton {toolTip='Drag to drag documents between splits' type='QToolButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.Events_QDockWidget {name='QmlProfiler.Statistics.DockDockWidget' type='QDockWidget' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator.Events_QTabBar {aboveWidget=':Qt Creator.Events_QDockWidget' type='QTabBar' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:Qt Creator.Issues_QListView {type='QListView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'} +:Qt Creator.Issues_QListView {type='Utils::TreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Issues'} :Qt Creator.Project.Menu.File_QMenu {name='Project.Menu.File' type='QMenu'} :Qt Creator.Project.Menu.Folder_QMenu {name='Project.Menu.Folder' type='QMenu' visible='1'} :Qt Creator.QML debugging and profiling:_QComboBox {leftWidget=':Qt Creator.QML debugging and profiling:_QLabel' type='QComboBox' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} diff --git a/tests/system/shared/build_utils.py b/tests/system/shared/build_utils.py index 18ae360fca0..6a2904286a3 100644 --- a/tests/system/shared/build_utils.py +++ b/tests/system/shared/build_utils.py @@ -47,7 +47,7 @@ def checkLastBuild(expectedToFail=False): test.log("checkLastBuild called without a build") return buildIssues = getBuildIssues() - types = [i[5] for i in buildIssues] + types = [i[1] for i in buildIssues] errors = types.count("1") warnings = types.count("2") gotErrors = errors != 0 @@ -89,7 +89,7 @@ def dumpBuildIssues(listModel): issueDump = [] for index in dumpIndices(listModel): issueDump.extend([[str(index.data(role).toString()) for role - in range(Qt.UserRole, Qt.UserRole + 6)]]) + in range(Qt.UserRole, Qt.UserRole + 2)]]) return issueDump diff --git a/tests/system/shared/debugger.py b/tests/system/shared/debugger.py index 424785b3a37..7176afc8e59 100644 --- a/tests/system/shared/debugger.py +++ b/tests/system/shared/debugger.py @@ -113,8 +113,7 @@ def doSimpleDebugging(currentKit, currentConfigName, expectedBPOrder=[], enableQ expectedLabelTexts.append("Running\.") switchViewTo(ViewConstants.PROJECTS) switchToBuildOrRunSettingsFor(currentKit, ProjectSettings.RUN) - ensureChecked(waitForObject("{container=':Qt Creator_Core::Internal::MainWindow' text='Enable QML' " - "type='QCheckBox' unnamed='1' visible='1'}"), enableQml) + selectFromCombo(":EnableQMLDebugger_ComboBox", "Enabled" if enableQml else "Disabled") switchViewTo(ViewConstants.EDIT) if not __startDebugger__(currentKit, currentConfigName): return False diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index 2e541ddf920..d1e44102bcc 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -128,7 +128,7 @@ def __createProjectHandleQtQuickSelection__(minimumQtVersion): comboBox = waitForObject("{name?='*QtVersion' type='QComboBox' visible='1'" " window=':New_ProjectExplorer::JsonWizard'}") try: - selectFromCombo(comboBox, minimumQtVersion) + selectFromCombo(comboBox, "Qt " + minimumQtVersion) except: t,v = sys.exc_info()[:2] test.fatal("Exception while trying to select Qt version", "%s: %s" % (t.__name__, str(v))) @@ -277,7 +277,12 @@ def createNewQtQuickApplication(workingDir, projectName=None, buildSystem=None): available = __createProjectOrFileSelectType__(" Application (Qt)", template, fromWelcome) projectName = __createProjectSetNameAndPath__(workingDir, projectName) - __handleBuildSystem__(buildSystem) + if template == "Qt Quick Application": + if buildSystem: + test.warning("Build system set explicitly for a template which can't change it.", + "Template: %s, Build System: %s" % (template, buildSystem)) + else: + __handleBuildSystem__(buildSystem) requiredQt = __createProjectHandleQtQuickSelection__(minimumQtVersion) __modifyAvailableTargets__(available, requiredQt) checkedTargets = __chooseTargets__(targets, available) diff --git a/tests/system/shared/suites_qtta.py b/tests/system/shared/suites_qtta.py index 2c63e009e83..97032ae0638 100644 --- a/tests/system/shared/suites_qtta.py +++ b/tests/system/shared/suites_qtta.py @@ -20,9 +20,9 @@ def checkSyntaxError(issuesView, expectedTextsArray, warnIfMoreIssues = True): if(warnIfMoreIssues and issuesModel.rowCount() > 1): test.warning("More than one expected issues reported") # iterate issues and check if there exists "Unexpected token" message - for description, type in zip(dumpItems(issuesModel, role=Qt.UserRole + 3), - dumpItems(issuesModel, role=Qt.UserRole + 5)): - # enum Roles { File = Qt::UserRole, Line, MovedLine, Description, FileNotFound, Type, Category, Icon, Task_t }; + for description, type in zip(dumpItems(issuesModel, role=Qt.UserRole), + dumpItems(issuesModel, role=Qt.UserRole + 1)): + # enum Roles { Description = Qt::UserRole, Type}; # check if at least one of expected texts found in issue text for expectedText in expectedTextsArray: if expectedText in description: diff --git a/tests/system/suite_APTW/tst_APTW02/test.py b/tests/system/suite_APTW/tst_APTW02/test.py index 716145a6401..0a5ff2bd72e 100644 --- a/tests/system/suite_APTW/tst_APTW02/test.py +++ b/tests/system/suite_APTW/tst_APTW02/test.py @@ -8,7 +8,7 @@ def main(): startQC() if not startedWithoutPluginError(): return - createNewQtQuickApplication(tempDir(), "SampleApp", buildSystem="qmake") + createNewQtQuickApplication(tempDir(), "SampleApp") # run project for debug and release and verify results runVerify() #close Qt Creator 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 diff --git a/tests/system/suite_debugger/tst_qml_js_console/test.py b/tests/system/suite_debugger/tst_qml_js_console/test.py index 6580e4988a4..4760296cf69 100644 --- a/tests/system/suite_debugger/tst_qml_js_console/test.py +++ b/tests/system/suite_debugger/tst_qml_js_console/test.py @@ -114,8 +114,7 @@ def main(): # make sure QML Debugging is enabled switchViewTo(ViewConstants.PROJECTS) switchToBuildOrRunSettingsFor(Targets.getDefaultKit(), ProjectSettings.RUN) - ensureChecked("{container=':Qt Creator.scrollArea_QScrollArea' text='Enable QML' " - "type='QCheckBox' unnamed='1' visible='1'}") + selectFromCombo(":EnableQMLDebugger_ComboBox", "Enabled") switchViewTo(ViewConstants.EDIT) # start debugging clickButton(fancyDebugButton) diff --git a/tests/system/suite_debugger/tst_qml_locals/test.py b/tests/system/suite_debugger/tst_qml_locals/test.py index 70256ee2d44..37ecb515cc6 100644 --- a/tests/system/suite_debugger/tst_qml_locals/test.py +++ b/tests/system/suite_debugger/tst_qml_locals/test.py @@ -35,8 +35,7 @@ def main(): return switchViewTo(ViewConstants.PROJECTS) switchToBuildOrRunSettingsFor(Targets.getDefaultKit(), ProjectSettings.RUN) - ensureChecked("{container=':Qt Creator_Core::Internal::MainWindow' text='Enable QML' " - "type='QCheckBox' unnamed='1' visible='1'}") + selectFromCombo(":EnableQMLDebugger_ComboBox", "Enabled") switchViewTo(ViewConstants.EDIT) clickButton(fancyDebugButton) locAndExprTV = waitForObject(":Locals and Expressions_Debugger::Internal::WatchTreeView") diff --git a/tests/system/suite_editors/tst_memberoperator/test.py b/tests/system/suite_editors/tst_memberoperator/test.py index ba1f1476850..4d11465b719 100644 --- a/tests/system/suite_editors/tst_memberoperator/test.py +++ b/tests/system/suite_editors/tst_memberoperator/test.py @@ -27,11 +27,11 @@ def __noBuildIssues__(): def __syntaxErrorDetected__(): buildIssues = getBuildIssues(False) for issue in buildIssues: - if issue[3] in ["Expected ';' after expression (fix available)", + if issue[0] in ["Expected ';' after expression (fix available)", "Expected ';' at end of declaration (fix available)", "Use of undeclared identifier 'syntaxError'"]: return True - if re.match(issue[3], "Declaration of reference variable '.+' requires an initializer"): + if re.match(issue[0], "Declaration of reference variable '.+' requires an initializer"): return True return False diff --git a/tests/system/suite_general/tst_create_proj_wizard/test.py b/tests/system/suite_general/tst_create_proj_wizard/test.py index 643e8054fa1..8579111b02c 100644 --- a/tests/system/suite_general/tst_create_proj_wizard/test.py +++ b/tests/system/suite_general/tst_create_proj_wizard/test.py @@ -54,18 +54,15 @@ def main(): with TestSection("Testing project template %s -> %s" % (category, template)): displayedPlatforms = __createProject__(category, template) if template.startswith("Qt Quick Application"): - if "(compat)" in template: # QTCREATORBUG-29126 - qtVersionsForQuick = ["Qt 5.14", "Qt 6.2"] - else: - qtVersionsForQuick = ["6.2"] + qtVersionsForQuick = ["6.2"] + if "(compat)" in template: + qtVersionsForQuick += ["5.14"] for counter, qtVersion in enumerate(qtVersionsForQuick): + def additionalFunc(displayedPlatforms, qtVersion): requiredQtVersion = __createProjectHandleQtQuickSelection__(qtVersion) - if sys.version_info.major > 2: - requiredQtVersion = requiredQtVersion.removeprefix("Qt ") - else: - requiredQtVersion = requiredQtVersion.lstrip("Qt ") __modifyAvailableTargets__(displayedPlatforms, requiredQtVersion, True) + handleBuildSystemVerifyKits(category, template, kits, displayedPlatforms, additionalFunc, qtVersion) # are there more Quick combinations - then recreate this project @@ -90,13 +87,15 @@ def verifyKitCheckboxes(kits, displayedPlatforms): unexpectedShownKits = availableCheckboxes.difference(displayedPlatforms) missingKits = displayedPlatforms.difference(availableCheckboxes) - test.log("Expected kits shown on 'Kit Selection' page:\n%s" % "\n".join(expectedShownKits)) - if len(unexpectedShownKits): - test.fail("Kits found on 'Kit Selection' page but not expected:\n%s" - % "\n".join(unexpectedShownKits)) - if len(missingKits): - test.fail("Expected kits missing on 'Kit Selection' page:\n%s" - % "\n".join(missingKits)) + if not test.verify(len(unexpectedShownKits) == 0 and len(missingKits) == 0, + "No missing or unexpected kits found on 'Kit Selection' page?\n" + "Found expected kits:\n%s" % "\n".join(expectedShownKits)): + if len(unexpectedShownKits): + test.log("Kits found on 'Kit Selection' page but not expected:\n%s" + % "\n".join(unexpectedShownKits)) + if len(missingKits): + test.log("Expected kits missing on 'Kit Selection' page:\n%s" + % "\n".join(missingKits)) def handleBuildSystemVerifyKits(category, template, kits, displayedPlatforms, specialHandlingFunc = None, *args): diff --git a/tests/system/suite_general/tst_opencreator_qbs/test.py b/tests/system/suite_general/tst_opencreator_qbs/test.py index 555e1437582..ca941547c90 100644 --- a/tests/system/suite_general/tst_opencreator_qbs/test.py +++ b/tests/system/suite_general/tst_opencreator_qbs/test.py @@ -24,7 +24,7 @@ def main(): else: test.warning("Parsing project timed out") compareProjectTree(rootNodeTemplate % "Qt Creator", "projecttree_creator.tsv") - buildIssuesTexts = map(lambda i: str(i[3]), getBuildIssues()) + buildIssuesTexts = map(lambda i: str(i[0]), getBuildIssues()) deprecationWarnings = filter(lambda s: "deprecated" in s, buildIssuesTexts) if deprecationWarnings: test.warning("Creator claims that the .qbs file uses deprecated features.", diff --git a/tests/system/suite_general/tst_save_before_build/test.py b/tests/system/suite_general/tst_save_before_build/test.py index d6b9b5f8574..87c2d56d39a 100644 --- a/tests/system/suite_general/tst_save_before_build/test.py +++ b/tests/system/suite_general/tst_save_before_build/test.py @@ -29,7 +29,7 @@ def main(): if not startedWithoutPluginError(): return verifySaveBeforeBuildChecked(False) - for buildSystem in ["CMake", "Qbs"]: + for buildSystem in ["CMake"]: projectName = "SampleApp-" + buildSystem ensureSaveBeforeBuildChecked(False) # create qt quick application diff --git a/tests/system/suite_general/tst_tasks_handling/test.py b/tests/system/suite_general/tst_tasks_handling/test.py index 053d5d24c74..865d22a2485 100644 --- a/tests/system/suite_general/tst_tasks_handling/test.py +++ b/tests/system/suite_general/tst_tasks_handling/test.py @@ -31,7 +31,7 @@ def generateRandomTaskType(): return 2 def generateMockTasksFile(): - descriptions = ["", "dummy information", "unknown error", "not found", "syntax error", + descriptions = ["dummy information", "unknown error", "not found", "syntax error", "missing information", "unused"] tasks = ["warn", "error", "other"] isWin = platform.system() in ('Microsoft', 'Windows') @@ -44,7 +44,7 @@ def generateMockTasksFile(): tasksType = generateRandomTaskType() tasksCount[tasksType] += 1 tData = tasks[tasksType] - dData = descriptions[random.randint(0, 6)] + dData = descriptions[random.randint(0, len(descriptions) - 1)] tFile.write("%s\t%d\t%s\t%s\n" % (fData, lData, tData, dData)) tFile.close() test.log("Wrote tasks file with %d warnings, %d errors and %d other tasks." % tuple(tasksCount)) @@ -58,7 +58,7 @@ def checkOrUncheckMyTasks(): "My Tasks")) def getBuildIssuesTypeCounts(model): - issueTypes = list(map(lambda x: x.data(Qt.UserRole + 5).toInt(), dumpIndices(model))) + issueTypes = list(map(lambda x: x.data(Qt.UserRole + 1).toInt(), dumpIndices(model))) result = [issueTypes.count(0), issueTypes.count(1), issueTypes.count(2)] if len(issueTypes) != sum(result): test.fatal("Found unexpected value(s) for TaskType...")