forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/11.0'
Change-Id: Icb3ed8a1aaf31e8201a61d04221bfcb23a78562a
This commit is contained in:
27
dist/changelog/changes-11.0.0.md
vendored
27
dist/changelog/changes-11.0.0.md
vendored
@@ -42,7 +42,7 @@ To use an external terminal, deselect the `Use internal terminal` check box in
|
||||
|
||||
### Copilot
|
||||
|
||||
The experimental Copilot plugin integrates
|
||||
The Copilot plugin (disabled by default) integrates
|
||||
[GitHub Copilot](https://github.com/features/copilot), which uses OpenAI to
|
||||
suggest code in the `Edit` mode.
|
||||
|
||||
@@ -87,6 +87,8 @@ General
|
||||
([QTCREATORBUG-26128](https://bugreports.qt.io/browse/QTCREATORBUG-26128),
|
||||
[QTCREATORBUG-27006](https://bugreports.qt.io/browse/QTCREATORBUG-27006),
|
||||
[QTCREATORBUG-27506](https://bugreports.qt.io/browse/QTCREATORBUG-27506))
|
||||
* Fixed a crash with a large number of search hits from Silver Searcher
|
||||
([QTCREATORBUG-29130](https://bugreports.qt.io/browse/QTCREATORBUG-29130))
|
||||
* Locator
|
||||
* Improved performance
|
||||
* Added the creation of directories to the `Files in File System` filter
|
||||
@@ -101,6 +103,8 @@ Editing
|
||||
([QTCREATORBUG-19651](https://bugreports.qt.io/browse/QTCREATORBUG-19651))
|
||||
* Fixed an issue of copy and paste with multiple cursors
|
||||
([QTCREATORBUG-29117](https://bugreports.qt.io/browse/QTCREATORBUG-29117))
|
||||
* Fixed the handling of pre-edit text for input methods
|
||||
([QTCREATORBUG-29134](https://bugreports.qt.io/browse/QTCREATORBUG-29134))
|
||||
|
||||
### C++
|
||||
|
||||
@@ -111,12 +115,22 @@ Editing
|
||||
* Extended the `Add Class Member` refactoring action to create class
|
||||
members from assignments
|
||||
([QTCREATORBUG-1918](https://bugreports.qt.io/browse/QTCREATORBUG-1918))
|
||||
* Fixed that generated functions did not have a `const` qualifier when
|
||||
required
|
||||
([QTCREATORBUG-29274](https://bugreports.qt.io/browse/QTCREATORBUG-29274))
|
||||
* Fixed that locator showed both the declaration and the definition of symbols
|
||||
([QTCREATORBUG-13894](https://bugreports.qt.io/browse/QTCREATORBUG-13894))
|
||||
* Fixed the handling of C++20 keywords and concepts
|
||||
* Clangd
|
||||
* Fixed that the index could be outdated after VCS operations
|
||||
* Fixed the highlighting of labels
|
||||
([QTCREATORBUG-27338](https://bugreports.qt.io/browse/QTCREATORBUG-27338))
|
||||
* Built-in
|
||||
* Fixed support for `if`-statements with initializer
|
||||
([QTCREATORBUG-29182](https://bugreports.qt.io/browse/QTCREATORBUG-29182))
|
||||
* Clang Format
|
||||
* Fixed the conversion of tab indentation settings to Clang Format
|
||||
([QTCREATORBUG-29185](https://bugreports.qt.io/browse/QTCREATORBUG-29185))
|
||||
|
||||
### Language Server Protocol
|
||||
|
||||
@@ -135,12 +149,16 @@ Editing
|
||||
([QTCREATORBUG-29123](https://bugreports.qt.io/browse/QTCREATORBUG-29123))
|
||||
* Fixed the completion for Qt Quick Controls
|
||||
([QTCREATORBUG-28648](https://bugreports.qt.io/browse/QTCREATORBUG-28648))
|
||||
* Fixed that `qmllint` issues were not shown in the `Issues` view
|
||||
([QTCREATORBUG-28720](https://bugreports.qt.io/browse/QTCREATORBUG-28720))
|
||||
|
||||
### Python
|
||||
|
||||
* Added the option to create a virtual environment (`venv`) to the Python
|
||||
interpreter selector and the wizard
|
||||
([PYSIDE-2152](https://bugreports.qt.io/browse/PYSIDE-2152))
|
||||
* Fixed that too many progress indicators could be created
|
||||
([QTCREATORBUG-29224](https://bugreports.qt.io/browse/QTCREATORBUG-29224))
|
||||
|
||||
([Documentation](https://doc-snapshots.qt.io/qtcreator-11.0/creator-python-development.html))
|
||||
|
||||
@@ -168,6 +186,7 @@ Projects
|
||||
[QTCREATORBUG-29006](https://bugreports.qt.io/browse/QTCREATORBUG-29006))
|
||||
* Added `Build > Reload CMake Presets` to reload CMake presets after making
|
||||
changes to them
|
||||
* Added support for `block()` and `endblock()`
|
||||
* Fixed that CMake Presets were not visible in `Projects` view
|
||||
([QTCREATORBUG-28966](https://bugreports.qt.io/browse/QTCREATORBUG-28966))
|
||||
* Fixed issues with detecting a configured Qt version when importing a build
|
||||
@@ -184,11 +203,17 @@ Debugging
|
||||
* Improved the UI for enabling and disabling debuggers in `Projects > Run >
|
||||
Debugger settings`
|
||||
([QTCREATORBUG-28627](https://bugreports.qt.io/browse/QTCREATORBUG-28627))
|
||||
* Fixed the automatic source mapping for Qt versions from an installer
|
||||
([QTCREATORBUG-28950](https://bugreports.qt.io/browse/QTCREATORBUG-28950))
|
||||
* Fixed pretty printer for `std::string` for recent `libc++`
|
||||
([QTCREATORBUG-29230](https://bugreports.qt.io/browse/QTCREATORBUG-29230))
|
||||
|
||||
### C++
|
||||
|
||||
* Added an option for the default number of array elements to show
|
||||
(`Preferences > Debugger > Locals & Expressions > Default array size`)
|
||||
* Fixed debugging in a terminal as the root user
|
||||
([QTCREATORBUG-27519](https://bugreports.qt.io/browse/QTCREATORBUG-27519))
|
||||
* CDB
|
||||
* Added automatic source file mapping for Qt packages
|
||||
* Fixed the variables view on remote Windows devices
|
||||
|
||||
BIN
doc/qtcreator/images/qtquick-debugger-settings.webp
Normal file
BIN
doc/qtcreator/images/qtquick-debugger-settings.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -42,9 +42,11 @@
|
||||
\section2 Debugging Qt Quick UI Projects
|
||||
\endif
|
||||
|
||||
To debug Qt Quick UI projects (.qmlproject), select the
|
||||
\uicontrol {Enable QML} check box in \uicontrol {Debugger settings}
|
||||
in \uicontrol Projects mode \uicontrol {Run Settings}.
|
||||
To debug Qt Quick UI projects (.qmlproject), select \uicontrol Automatic
|
||||
or \uicontrol Enabled in \uicontrol{Run Settings} >
|
||||
\uicontrol {Debugger Settings} > \uicontrol {QML debugger}.
|
||||
|
||||
\image qtquick-debugger-settings.webp {Debugger settings section in Run Settings}
|
||||
|
||||
\if defined(qtcreator)
|
||||
\section2 Debugging Qt Quick Applications
|
||||
@@ -65,9 +67,13 @@
|
||||
functions. Therefore, you must make sure that the port is properly
|
||||
protected by a firewall.
|
||||
|
||||
\li In \uicontrol {Run Settings} > \uicontrol {Debugger settings}, select
|
||||
the \uicontrol {Enable QML} check box to enable QML debugging for
|
||||
running applications.
|
||||
\li In \uicontrol {Run Settings} > \uicontrol {Debugger settings} >
|
||||
\uicontrol {QML debugger}, select \uicontrol Automatic or
|
||||
\uicontrol Enabled to enable QML debugging for running applications.
|
||||
|
||||
To debug both the C++ and QML parts of your application at the same
|
||||
time, also select \uicontrol Automatic or \uicontrol Enabled in
|
||||
\uicontrol {C++ debugger}.
|
||||
|
||||
\li Select \uicontrol Build > \uicontrol {Rebuild Project} to clean and
|
||||
rebuild the project.
|
||||
@@ -119,17 +125,8 @@
|
||||
For example, for qmake the global setting only affects build configurations
|
||||
that are automatically created when enabling a kit. Also, CMake ignores the
|
||||
global setting.
|
||||
|
||||
\section1 Mixed C++/QML Debugging
|
||||
|
||||
To debug both the C++ and QML parts of your application at the same time,
|
||||
select the \uicontrol {Enable C++} and \uicontrol {Enable QML} checkboxes for both
|
||||
languages in the \uicontrol {Debugger Settings} section in the project
|
||||
\uicontrol{Run Settings}.
|
||||
\endif
|
||||
|
||||
\image qtquick-debugging-settings.png {Debugger settings section in Run Settings}
|
||||
|
||||
\section1 Starting QML Debugging
|
||||
|
||||
To start the application, choose \uicontrol Debug > \uicontrol {Start Debugging}
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
\li \l {Using GitHub Copilot}
|
||||
|
||||
The experimental Copilot plugin integrates
|
||||
The Copilot plugin (disabled by default) integrates
|
||||
\l{https://github.com/features/copilot}{GitHub Copilot} into \QC.
|
||||
You can view suggestions from Copilot in the code editor.
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
\title Using GitHub Copilot
|
||||
|
||||
The experimental Copilot plugin integrates
|
||||
The Copilot plugin (disabled by default) integrates
|
||||
\l{https://github.com/features/copilot}{GitHub Copilot} into \QC.
|
||||
You can view suggestions from Copilot in the \uicontrol Edit mode.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
|
||||
|
||||
/*!
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
\section1 Enabling Debugging
|
||||
|
||||
\image qtquick-debugging-settings.png "Debugger Settings"
|
||||
\image qtquick-debugger-settings.webp "Debugger Settings"
|
||||
|
||||
To select the languages to debug, select the \uicontrol {Enable C++} and
|
||||
\uicontrol {Enable QML} check boxes.
|
||||
To select the languages to debug, select \uicontrol Automatic
|
||||
or \uicontrol Enabled in \uicontrol {Debugger Settings} >
|
||||
\uicontrol {C++ debugger} and \uicontrol {QML debugger}.
|
||||
|
||||
\note Opening a socket at a well-known port presents a security risk. Anyone
|
||||
on the Internet could connect to the application that you are debugging and
|
||||
@@ -28,6 +29,11 @@
|
||||
|
||||
However, you can usually leave this field empty.
|
||||
|
||||
\note To create a build configuration that supports debugging for a
|
||||
Qt Quick application project, you also need to \l {Using Default Values}
|
||||
{enable QML debugging} either globally or in the \uicontrol {Build Settings}
|
||||
of the project.
|
||||
|
||||
For more information about debugging, see \l{Debugging}.
|
||||
|
||||
//! [run settings debugger]
|
||||
|
||||
@@ -16,7 +16,7 @@ headerdirs = . \
|
||||
../src \
|
||||
../../../src/libs/aggregation \
|
||||
../../../src/libs/extensionsystem \
|
||||
../../../src/libs/solutions/tasking \
|
||||
../../../src/libs/solutions \
|
||||
../../../src/libs/utils \
|
||||
../../../src/plugins/coreplugin
|
||||
|
||||
@@ -24,7 +24,7 @@ sourcedirs = . \
|
||||
../src \
|
||||
../../../src/libs/aggregation \
|
||||
../../../src/libs/extensionsystem \
|
||||
../../../src/libs/solutions/tasking \
|
||||
../../../src/libs/solutions \
|
||||
../../../src/libs/utils \
|
||||
../../../src/plugins/coreplugin
|
||||
|
||||
@@ -42,7 +42,8 @@ sources.fileextensions = "*.cpp *.qdoc"
|
||||
|
||||
imagedirs = ../images \
|
||||
../../config/images \
|
||||
../../qtcreator/images
|
||||
../../qtcreator/images \
|
||||
../../../src/libs/solutions
|
||||
exampledirs = ../examples
|
||||
|
||||
depends += qtwidgets \
|
||||
|
||||
@@ -119,6 +119,11 @@
|
||||
\li Solution Name
|
||||
\li Description
|
||||
|
||||
\row
|
||||
\li \l{Spinner Solution}{Spinner}
|
||||
\li Renders a circular, endlessly animated progress indicator,
|
||||
which may be attached to any widget as an overlay.
|
||||
|
||||
\row
|
||||
\li \l{Tasking Solution}{Tasking}
|
||||
\li Enables you to build extensible, declarative task tree structures
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (C) 2016 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import locale
|
||||
import shutil
|
||||
@@ -196,13 +197,13 @@ def is_not_debug(path, filenames):
|
||||
files = [fn for fn in filenames if os.path.isfile(os.path.join(path, fn))]
|
||||
return [fn for fn in files if not is_debug_file(os.path.join(path, fn))]
|
||||
|
||||
def codesign_call():
|
||||
signing_identity = os.environ.get('SIGNING_IDENTITY')
|
||||
def codesign_call(identity=None, flags=None):
|
||||
signing_identity = identity or os.environ.get('SIGNING_IDENTITY')
|
||||
if not signing_identity:
|
||||
return None
|
||||
codesign_call = ['codesign', '-o', 'runtime', '--force', '-s', signing_identity,
|
||||
'-v']
|
||||
signing_flags = os.environ.get('SIGNING_FLAGS')
|
||||
signing_flags = flags or os.environ.get('SIGNING_FLAGS')
|
||||
if signing_flags:
|
||||
codesign_call.extend(signing_flags.split())
|
||||
return codesign_call
|
||||
@@ -228,8 +229,8 @@ def conditional_sign_recursive(path, filter):
|
||||
if is_mac_platform():
|
||||
os_walk(path, filter, lambda fp: codesign_executable(fp))
|
||||
|
||||
def codesign(app_path):
|
||||
codesign = codesign_call()
|
||||
def codesign(app_path, identity=None, flags=None):
|
||||
codesign = codesign_call(identity, flags)
|
||||
if not codesign or not is_mac_platform():
|
||||
return
|
||||
# sign all executables in Resources
|
||||
@@ -243,3 +244,20 @@ def codesign(app_path):
|
||||
entitlements_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), '..', 'dist',
|
||||
'installer', 'mac', 'entitlements.plist')
|
||||
subprocess.check_call(codesign + ['--deep', app_path, '--entitlements', entitlements_path])
|
||||
|
||||
def codesign_main(args):
|
||||
codesign(args.app_bundle, args.identity, args.flags)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Qt Creator build tools')
|
||||
subparsers = parser.add_subparsers(title='subcommands', required=True)
|
||||
parser_codesign = subparsers.add_parser('codesign', description='Codesign macOS app bundle')
|
||||
parser_codesign.add_argument('app_bundle')
|
||||
parser_codesign.add_argument('-s', '--identity', help='Codesign identity')
|
||||
parser_codesign.add_argument('--flags', help='Additional flags')
|
||||
parser_codesign.set_defaults(func=codesign_main)
|
||||
args = parser.parse_args()
|
||||
args.func(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1509,9 +1509,10 @@ class CliDumper(Dumper):
|
||||
self.setupDumpers({})
|
||||
|
||||
def put(self, line):
|
||||
if self.output.endswith('\n'):
|
||||
self.output = self.output[0:-1]
|
||||
self.output += line
|
||||
if self.output:
|
||||
if self.output[-1].endswith('\n'):
|
||||
self.output[-1] = self.output[-1][0:-1]
|
||||
self.output.append(line)
|
||||
|
||||
def putNumChild(self, numchild):
|
||||
pass
|
||||
|
||||
@@ -504,6 +504,21 @@ int main(int argc, char **argv)
|
||||
{{"LD_PRELOAD", "", Utils::EnvironmentItem::Unset}});
|
||||
}
|
||||
|
||||
auto restoreEnvVarFromSquish = [](const QByteArray &squishVar, const QString &var) {
|
||||
if (qEnvironmentVariableIsSet(squishVar)) {
|
||||
Utils::Environment::modifySystemEnvironment(
|
||||
{{var, "", Utils::EnvironmentItem::Unset}});
|
||||
const QString content = qEnvironmentVariable(squishVar);
|
||||
if (!content.isEmpty()) {
|
||||
Utils::Environment::modifySystemEnvironment(
|
||||
{{var, content, Utils::EnvironmentItem::Prepend}});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
restoreEnvVarFromSquish("SQUISH_SHELL_ORIG_DYLD_LIBRARY_PATH", "DYLD_LIBRARY_PATH");
|
||||
restoreEnvVarFromSquish("SQUISH_ORIG_DYLD_FRAMEWORK_PATH", "DYLD_FRAMEWORK_PATH");
|
||||
|
||||
if (options.userLibraryPath) {
|
||||
if ((*options.userLibraryPath).isEmpty()) {
|
||||
Utils::Environment::modifySystemEnvironment(
|
||||
|
||||
@@ -370,7 +370,7 @@ QByteArray LibraryInfo::calculateFingerprint() const
|
||||
{
|
||||
QCryptographicHash hash(QCryptographicHash::Sha1);
|
||||
auto addData = [&hash](auto p, size_t len) {
|
||||
hash.addData(QByteArrayView(reinterpret_cast<const char *>(p), len));
|
||||
hash.addData(reinterpret_cast<const char *>(p), len);
|
||||
};
|
||||
|
||||
addData(&_status, sizeof(_status));
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
add_subdirectory(spinner)
|
||||
add_subdirectory(tasking)
|
||||
|
||||
@@ -2,6 +2,7 @@ Project {
|
||||
name: "Solutions"
|
||||
|
||||
references: [
|
||||
"spinner/spinner.qbs",
|
||||
"tasking/tasking.qbs",
|
||||
].concat(project.additionalLibs)
|
||||
}
|
||||
|
||||
9
src/libs/solutions/spinner/CMakeLists.txt
Normal file
9
src/libs/solutions/spinner/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
add_qtc_library(Spinner OBJECT
|
||||
# Never add dependencies to non-Qt libraries for this library
|
||||
DEPENDS Qt::Core Qt::Widgets
|
||||
PUBLIC_DEFINES SPINNER_LIBRARY
|
||||
SOURCES
|
||||
spinner.cpp spinner.h
|
||||
spinner.qrc
|
||||
spinner_global.h
|
||||
)
|
||||
BIN
src/libs/solutions/spinner/icons/spinner_large.png
Normal file
BIN
src/libs/solutions/spinner/icons/spinner_large.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/libs/solutions/spinner/icons/spinner_medium.png
Normal file
BIN
src/libs/solutions/spinner/icons/spinner_medium.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 765 B |
BIN
src/libs/solutions/spinner/icons/spinner_small.png
Normal file
BIN
src/libs/solutions/spinner/icons/spinner_small.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 315 B |
249
src/libs/solutions/spinner/spinner.cpp
Normal file
249
src/libs/solutions/spinner/spinner.cpp
Normal file
@@ -0,0 +1,249 @@
|
||||
// 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 "spinner.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
namespace SpinnerSolution {
|
||||
|
||||
class OverlayWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
using PaintFunction = std::function<void(QWidget *, QPainter &, QPaintEvent *)>;
|
||||
|
||||
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({}, parentWidget()->size())); }
|
||||
|
||||
PaintFunction m_paint;
|
||||
};
|
||||
|
||||
class SpinnerPainter
|
||||
{
|
||||
public:
|
||||
using UpdateCallback = std::function<void()>;
|
||||
|
||||
SpinnerPainter(SpinnerSize size);
|
||||
|
||||
void setSize(SpinnerSize size);
|
||||
|
||||
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:
|
||||
SpinnerSize m_size = SpinnerSize::Small;
|
||||
int m_rotationStep = 45;
|
||||
int m_rotation = 0;
|
||||
QTimer m_timer;
|
||||
QPixmap m_pixmap;
|
||||
UpdateCallback m_callback;
|
||||
};
|
||||
|
||||
static QString imageFileNameForSpinnerSize(SpinnerSize size)
|
||||
{
|
||||
switch (size) {
|
||||
case SpinnerSize::Large:
|
||||
return ":/icons/spinner_large.png";
|
||||
case SpinnerSize::Medium:
|
||||
return ":/icons/spinner_medium.png";
|
||||
case SpinnerSize::Small:
|
||||
return ":/icons/spinner_small.png";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
SpinnerPainter::SpinnerPainter(SpinnerSize size)
|
||||
{
|
||||
m_timer.setSingleShot(false);
|
||||
QObject::connect(&m_timer, &QTimer::timeout, &m_timer, [this] {
|
||||
nextAnimationStep();
|
||||
if (m_callback)
|
||||
m_callback();
|
||||
});
|
||||
setSize(size);
|
||||
}
|
||||
|
||||
void SpinnerPainter::setSize(SpinnerSize size)
|
||||
{
|
||||
m_size = size;
|
||||
m_rotationStep = size == SpinnerSize::Small ? 45 : 30;
|
||||
m_timer.setInterval(size == SpinnerSize::Small ? 100 : 80);
|
||||
m_pixmap = QPixmap(imageFileNameForSpinnerSize(size));
|
||||
}
|
||||
|
||||
void SpinnerPainter::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 SpinnerWidget : public OverlayWidget
|
||||
{
|
||||
public:
|
||||
explicit SpinnerWidget(SpinnerSize size, QWidget *parent = nullptr)
|
||||
: OverlayWidget(parent)
|
||||
, m_paint(size)
|
||||
{
|
||||
setPaintFunction(
|
||||
[this](QWidget *w, QPainter &p, QPaintEvent *) { m_paint.paint(p, w->rect()); });
|
||||
m_paint.setUpdateCallback([this] { update(); });
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void setSize(SpinnerSize size)
|
||||
{
|
||||
m_paint.setSize(size);
|
||||
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:
|
||||
SpinnerPainter m_paint;
|
||||
};
|
||||
|
||||
/*!
|
||||
\module SpinnerSolution
|
||||
\title Spinner Solution
|
||||
\ingroup solutions-modules
|
||||
\brief Contains a Spinner solution.
|
||||
|
||||
The Spinner solution depends on Qt only, and doesn't depend on any \QC specific code.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\namespace SpinnerSolution
|
||||
\inmodule SpinnerSolution
|
||||
\brief The SpinnerSolution namespace encloses the Spinner class.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum SpinnerSolution::SpinnerSize
|
||||
|
||||
This enum describes the possible spinner sizes.
|
||||
|
||||
\value Small \inlineimage spinner/icons/spinner_small.png
|
||||
\value Medium \inlineimage spinner/icons/spinner_medium.png
|
||||
\value Large \inlineimage spinner/icons/spinner_large.png
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class SpinnerSolution::Spinner
|
||||
\inheaderfile solutions/spinner/spinner.h
|
||||
\inmodule SpinnerSolution
|
||||
\brief The Spinner class renders a circular, endlessly animated progress indicator,
|
||||
that may be attached to any widget as an overlay.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Creates a spinner overlay with a given \a size for the passed \a parent widget.
|
||||
|
||||
The \a parent widget takes the ownership of the created spinner.
|
||||
*/
|
||||
Spinner::Spinner(SpinnerSize size, QWidget *parent)
|
||||
: QObject(parent)
|
||||
, m_widget(new SpinnerWidget(size, parent)) {}
|
||||
|
||||
/*!
|
||||
Sets the size of the spinner to the given \a size.
|
||||
*/
|
||||
void Spinner::setSize(SpinnerSize size)
|
||||
{
|
||||
m_widget->setSize(size);
|
||||
}
|
||||
|
||||
/*!
|
||||
Shows the animated spinner as an overlay for the parent widget
|
||||
set previously in the constructor.
|
||||
*/
|
||||
void Spinner::show()
|
||||
{
|
||||
m_widget->show();
|
||||
}
|
||||
|
||||
/*!
|
||||
Hides the spinner.
|
||||
*/
|
||||
void Spinner::hide()
|
||||
{
|
||||
m_widget->hide();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if the spinner is visible; otherwise, returns \c false.
|
||||
*/
|
||||
bool Spinner::isVisible() const
|
||||
{
|
||||
return m_widget->isVisible();
|
||||
}
|
||||
|
||||
/*!
|
||||
Shows or hides the spinner depending on the value of \a visible.
|
||||
By default, the spinner is visible.
|
||||
*/
|
||||
void Spinner::setVisible(bool visible)
|
||||
{
|
||||
m_widget->setVisible(visible);
|
||||
}
|
||||
|
||||
} // namespace SpinnerSolution
|
||||
37
src/libs/solutions/spinner/spinner.h
Normal file
37
src/libs/solutions/spinner/spinner.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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 SPINNER_H
|
||||
#define SPINNER_H
|
||||
|
||||
#include "spinner_global.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
namespace SpinnerSolution {
|
||||
|
||||
Q_NAMESPACE_EXPORT(SPINNER_EXPORT)
|
||||
|
||||
enum class SpinnerSize { Small, Medium, Large };
|
||||
Q_ENUM_NS(SpinnerSize);
|
||||
|
||||
// TODO: SpinnerOverlay and SpinnerWidget?
|
||||
|
||||
class SPINNER_EXPORT Spinner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit Spinner(SpinnerSize size, QWidget *parent = nullptr);
|
||||
void setSize(SpinnerSize size);
|
||||
void show();
|
||||
void hide();
|
||||
bool isVisible() const;
|
||||
void setVisible(bool visible);
|
||||
|
||||
private:
|
||||
class SpinnerWidget *m_widget = nullptr;
|
||||
};
|
||||
|
||||
} // namespace SpinnerSolution
|
||||
|
||||
#endif // SPINNER_H
|
||||
13
src/libs/solutions/spinner/spinner.qbs
Normal file
13
src/libs/solutions/spinner/spinner.qbs
Normal file
@@ -0,0 +1,13 @@
|
||||
QtcLibrary {
|
||||
name: "Spinner"
|
||||
Depends { name: "Qt"; submodules: ["core", "widgets"] }
|
||||
cpp.defines: base.concat("SPINNER_LIBRARY")
|
||||
|
||||
files: [
|
||||
"spinner.cpp",
|
||||
"spinner.h",
|
||||
"spinner.qrc",
|
||||
"spinner_global.h",
|
||||
]
|
||||
}
|
||||
|
||||
7
src/libs/solutions/spinner/spinner.qrc
Normal file
7
src/libs/solutions/spinner/spinner.qrc
Normal file
@@ -0,0 +1,7 @@
|
||||
<RCC>
|
||||
<qresource prefix="/" >
|
||||
<file>icons/spinner_large.png</file>
|
||||
<file>icons/spinner_medium.png</file>
|
||||
<file>icons/spinner_small.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
14
src/libs/solutions/spinner/spinner_global.h
Normal file
14
src/libs/solutions/spinner/spinner_global.h
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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 <qglobal.h>
|
||||
|
||||
#if defined(SPINNER_LIBRARY)
|
||||
# define SPINNER_EXPORT Q_DECL_EXPORT
|
||||
#elif defined(SPINNER_STATIC_LIBRARY)
|
||||
# define SPINNER_EXPORT
|
||||
#else
|
||||
# define SPINNER_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
@@ -83,14 +83,14 @@ public:
|
||||
"It is possible that no barrier was added to the tree, "
|
||||
"or the storage is not reachable from where it is referenced. "
|
||||
"The WaitForBarrier task will finish with error. ");
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
Barrier *activeSharedBarrier = activeBarrier->barrier();
|
||||
const std::optional<bool> result = activeSharedBarrier->result();
|
||||
if (result.has_value())
|
||||
return result.value() ? TaskAction::StopWithDone : TaskAction::StopWithError;
|
||||
return result.value() ? SetupResult::StopWithDone : SetupResult::StopWithError;
|
||||
QObject::connect(activeSharedBarrier, &Barrier::done, &barrier, &Barrier::stopWithResult);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
}) {}
|
||||
};
|
||||
|
||||
|
||||
@@ -57,6 +57,107 @@ private:
|
||||
\brief The Tasking namespace encloses all classes and global functions of the Tasking solution.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class Tasking::TaskInterface
|
||||
\inheaderfile solutions/tasking/tasktree.h
|
||||
\inmodule TaskingSolution
|
||||
\brief TaskInterface is the abstract base class for implementing custom task adapters.
|
||||
|
||||
To implement a custom task adapter, derive your adapter from the
|
||||
\c TaskAdapter<Task> class template. TaskAdapter automatically creates and destroys
|
||||
the custom task instance and associates the adapter with a given \c Task type.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn virtual void TaskInterface::start()
|
||||
|
||||
This method is called by the running TaskTree for starting the \c Task instance.
|
||||
Reimplement this method in \c TaskAdapter<Task>'s subclass in order to start the
|
||||
associated task.
|
||||
|
||||
Use TaskAdapter::task() to access the associated \c Task instance.
|
||||
|
||||
\sa done(), TaskAdapter::task()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void TaskInterface::done(bool success)
|
||||
|
||||
Emit this signal from the \c TaskAdapter<Task>'s subclass, when the \c Task is finished.
|
||||
Pass \c true as a \a success argument when the task finishes with success;
|
||||
otherwise, when an error occurs, pass \c false.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class Tasking::TaskAdapter
|
||||
\inheaderfile solutions/tasking/tasktree.h
|
||||
\inmodule TaskingSolution
|
||||
\brief A class template for implementing custom task adapters.
|
||||
|
||||
The TaskAdapter class template is responsible for creating a task of the \c Task type,
|
||||
starting it, and reporting success or an error when the task is finished.
|
||||
It also associates the adapter with a given \c Task type.
|
||||
|
||||
Reimplement this class with the actual \c Task type to adapt the task's interface
|
||||
into the general TaskTree's interface for managing the \c Task instances.
|
||||
|
||||
Each subclass needs to provide a public default constructor,
|
||||
implement the start() method, and emit the done() signal when the task is finished.
|
||||
Use task() to access the associated \c Task instance.
|
||||
|
||||
For more information on implementing the custom task adapters, refer to \l {Task Adapters}.
|
||||
|
||||
\sa start(), done(), task()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typealias TaskAdapter::Type
|
||||
|
||||
Type alias for the \c Task type.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Task> TaskAdapter<Task>::TaskAdapter<Task>()
|
||||
|
||||
Creates a task adapter for the given \c Task type. Internally, it creates
|
||||
an instance of \c Task, which is accessible via the task() method.
|
||||
|
||||
\sa task()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Task> Task *TaskAdapter<Task>::task()
|
||||
|
||||
Returns the pointer to the associated \c Task instance.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn template <typename Task> Task *TaskAdapter<Task>::task() const
|
||||
\overload
|
||||
|
||||
Returns the const pointer to the associated \c Task instance.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\macro TASKING_DECLARE_TASK(CustomTaskName, TaskAdapterClass)
|
||||
\relates Tasking
|
||||
|
||||
Registers the new custom task type under a \a CustomTaskName name inside the
|
||||
Tasking namespace for the passed \a TaskAdapterClass adapter class.
|
||||
|
||||
For more information on implementing the custom task adapters, refer to \l {Task Adapters}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\macro TASKING_DECLARE_TEMPLATE_TASK(CustomTaskName, TaskAdapterClass)
|
||||
\relates Tasking
|
||||
|
||||
Registers the new custom task template type under a \a CustomTaskName name inside the
|
||||
Tasking namespace for the passed \a TaskAdapterClass adapter class template.
|
||||
|
||||
For more information on implementing the custom task adapters, refer to \l {Task Adapters}.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class Tasking::GroupItem
|
||||
\inheaderfile solutions/tasking/tasktree.h
|
||||
@@ -236,25 +337,27 @@ private:
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum Tasking::TaskAction
|
||||
\enum Tasking::SetupResult
|
||||
|
||||
This enum is optionally returned from the group's or task's setup handler function.
|
||||
It instructs the running task tree on how to proceed after the setup handler's execution
|
||||
finished.
|
||||
\value Continue
|
||||
Default. The group's or task's execution continues nomally.
|
||||
Default. The group's or task's execution continues normally.
|
||||
When a group's or task's setup handler returns void, it's assumed that
|
||||
it returned Continue.
|
||||
\value StopWithDone
|
||||
The group's or task's execution stops immediately with success.
|
||||
When returned from the group's setup handler, all child tasks are skipped,
|
||||
and the group's onGroupDone() handler is invoked (if provided).
|
||||
The group reports success to its parent. The group's workflow policy is ignored.
|
||||
When returned from the task's setup handler, the task isn't started,
|
||||
its done handler isn't invoked, and the task reports success to its parent.
|
||||
\value StopWithError
|
||||
The group's or task's execution stops immediately with an error.
|
||||
When returned from the group's setup handler, all child tasks are skipped,
|
||||
and the group's onGroupError() handler is invoked (if provided).
|
||||
The group reports an error to its parent. The group's workflow policy is ignored.
|
||||
When returned from the task's setup handler, the task isn't started,
|
||||
its error handler isn't invoked, and the task reports an error to its parent.
|
||||
*/
|
||||
@@ -262,21 +365,21 @@ private:
|
||||
/*!
|
||||
\typealias GroupItem::GroupSetupHandler
|
||||
|
||||
Type alias for \c std::function<TaskAction()>.
|
||||
Type alias for \c std::function<SetupResult()>.
|
||||
|
||||
The GroupSetupHandler is used when constructing the onGroupSetup() element.
|
||||
Any function with the above signature, when passed as a group setup handler,
|
||||
will be called by the running task tree when the group execution starts.
|
||||
|
||||
The return value of the handler instructs the running group on how to proceed
|
||||
after the handler's invocation is finished. The default return value of TaskAction::Continue
|
||||
after the handler's invocation is finished. The default return value of SetupResult::Continue
|
||||
instructs the group to continue running, i.e. to start executing its child tasks.
|
||||
The return value of TaskAction::StopWithDone or TaskAction::StopWithError
|
||||
The return value of SetupResult::StopWithDone or SetupResult::StopWithError
|
||||
instructs the group to skip the child tasks' execution and finish immediately with
|
||||
success or an error, respectively.
|
||||
|
||||
When the return type is either TaskAction::StopWithDone
|
||||
of TaskAction::StopWithError, the group's done or error handler (if provided)
|
||||
When the return type is either SetupResult::StopWithDone
|
||||
of SetupResult::StopWithError, the group's done or error handler (if provided)
|
||||
is called synchronously immediately afterwards.
|
||||
|
||||
\note Even if the group setup handler returns StopWithDone or StopWithError,
|
||||
@@ -285,7 +388,7 @@ private:
|
||||
|
||||
The onGroupSetup() accepts also functions in the shortened form of \c std::function<void()>,
|
||||
i.e. the return value is void. In this case it's assumed that the return value
|
||||
is TaskAction::Continue by default.
|
||||
is SetupResult::Continue by default.
|
||||
|
||||
\sa onGroupSetup()
|
||||
*/
|
||||
@@ -293,7 +396,7 @@ private:
|
||||
/*!
|
||||
\typealias GroupItem::GroupEndHandler
|
||||
|
||||
Type alias for \c std::function\<void()\>.
|
||||
Type alias for \c std::function<void()>.
|
||||
|
||||
The GroupEndHandler is used when constructing the onGroupDone() and onGroupError() elements.
|
||||
Any function with the above signature, when passed as a group done or error handler,
|
||||
@@ -309,7 +412,7 @@ private:
|
||||
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<TaskAction()> or \c std::function<void()>
|
||||
The passed \a handler is either of \c std::function<SetupResult()> or \c std::function<void()>
|
||||
type. For more information on possible argument type, refer to
|
||||
\l {GroupItem::GroupSetupHandler}.
|
||||
|
||||
@@ -430,9 +533,9 @@ const GroupItem stopOnFinished = workflowPolicy(WorkflowPolicy::StopOnFinished);
|
||||
const GroupItem finishAllAndDone = workflowPolicy(WorkflowPolicy::FinishAllAndDone);
|
||||
const GroupItem finishAllAndError = workflowPolicy(WorkflowPolicy::FinishAllAndError);
|
||||
|
||||
static TaskAction toTaskAction(bool success)
|
||||
static SetupResult toSetupResult(bool success)
|
||||
{
|
||||
return success ? TaskAction::StopWithDone : TaskAction::StopWithError;
|
||||
return success ? SetupResult::StopWithDone : SetupResult::StopWithError;
|
||||
}
|
||||
|
||||
bool TreeStorageBase::isValid() const
|
||||
@@ -658,10 +761,10 @@ public:
|
||||
TaskContainer(TaskTreePrivate *taskTreePrivate, const GroupItem &task,
|
||||
TaskNode *parentNode, TaskContainer *parentContainer)
|
||||
: m_constData(taskTreePrivate, task, parentNode, parentContainer, this) {}
|
||||
TaskAction start();
|
||||
TaskAction continueStart(TaskAction startAction, int nextChild);
|
||||
TaskAction startChildren(int nextChild);
|
||||
TaskAction childDone(bool success);
|
||||
SetupResult start();
|
||||
SetupResult continueStart(SetupResult startAction, int nextChild);
|
||||
SetupResult startChildren(int nextChild);
|
||||
SetupResult childDone(bool success);
|
||||
void stop();
|
||||
void invokeEndHandler();
|
||||
bool isRunning() const { return m_runtimeData.has_value(); }
|
||||
@@ -716,7 +819,7 @@ public:
|
||||
|
||||
// If returned value != Continue, childDone() needs to be called in parent container (in caller)
|
||||
// in order to unwind properly.
|
||||
TaskAction start();
|
||||
SetupResult start();
|
||||
void stop();
|
||||
void invokeEndHandler(bool success);
|
||||
bool isRunning() const { return m_task || m_container.isRunning(); }
|
||||
@@ -945,31 +1048,34 @@ int TaskContainer::RuntimeData::currentLimit() const
|
||||
? qMin(m_doneCount + m_constData.m_parallelLimit, childCount) : childCount;
|
||||
}
|
||||
|
||||
TaskAction TaskContainer::start()
|
||||
SetupResult TaskContainer::start()
|
||||
{
|
||||
QTC_CHECK(!isRunning());
|
||||
m_runtimeData.emplace(m_constData);
|
||||
|
||||
TaskAction startAction = TaskAction::Continue;
|
||||
SetupResult startAction = SetupResult::Continue;
|
||||
if (m_constData.m_groupHandler.m_setupHandler) {
|
||||
startAction = invokeHandler(this, m_constData.m_groupHandler.m_setupHandler);
|
||||
if (startAction != TaskAction::Continue)
|
||||
if (startAction != SetupResult::Continue) {
|
||||
m_constData.m_taskTreePrivate->advanceProgress(m_constData.m_taskCount);
|
||||
// Non-Continue SetupResult takes precedence over the workflow policy.
|
||||
m_runtimeData->m_successBit = startAction == SetupResult::StopWithDone;
|
||||
}
|
||||
if (startAction == TaskAction::Continue) {
|
||||
}
|
||||
if (startAction == SetupResult::Continue) {
|
||||
if (m_constData.m_children.isEmpty())
|
||||
startAction = toTaskAction(m_runtimeData->m_successBit);
|
||||
startAction = toSetupResult(m_runtimeData->m_successBit);
|
||||
}
|
||||
return continueStart(startAction, 0);
|
||||
}
|
||||
|
||||
TaskAction TaskContainer::continueStart(TaskAction startAction, int nextChild)
|
||||
SetupResult TaskContainer::continueStart(SetupResult startAction, int nextChild)
|
||||
{
|
||||
const TaskAction groupAction = startAction == TaskAction::Continue ? startChildren(nextChild)
|
||||
const SetupResult groupAction = startAction == SetupResult::Continue ? startChildren(nextChild)
|
||||
: startAction;
|
||||
QTC_CHECK(isRunning()); // TODO: superfluous
|
||||
if (groupAction != TaskAction::Continue) {
|
||||
const bool success = m_runtimeData->updateSuccessBit(groupAction == TaskAction::StopWithDone);
|
||||
if (groupAction != SetupResult::Continue) {
|
||||
const bool success = m_runtimeData->updateSuccessBit(groupAction == SetupResult::StopWithDone);
|
||||
invokeEndHandler();
|
||||
if (TaskContainer *parentContainer = m_constData.m_parentContainer) {
|
||||
QTC_CHECK(parentContainer->isRunning());
|
||||
@@ -984,7 +1090,7 @@ TaskAction TaskContainer::continueStart(TaskAction startAction, int nextChild)
|
||||
return groupAction;
|
||||
}
|
||||
|
||||
TaskAction TaskContainer::startChildren(int nextChild)
|
||||
SetupResult TaskContainer::startChildren(int nextChild)
|
||||
{
|
||||
QTC_CHECK(isRunning());
|
||||
GuardLocker locker(m_runtimeData->m_startGuard);
|
||||
@@ -993,12 +1099,12 @@ TaskAction TaskContainer::startChildren(int nextChild)
|
||||
if (i >= limit)
|
||||
break;
|
||||
|
||||
const TaskAction startAction = m_constData.m_children.at(i)->start();
|
||||
if (startAction == TaskAction::Continue)
|
||||
const SetupResult startAction = m_constData.m_children.at(i)->start();
|
||||
if (startAction == SetupResult::Continue)
|
||||
continue;
|
||||
|
||||
const TaskAction finalizeAction = childDone(startAction == TaskAction::StopWithDone);
|
||||
if (finalizeAction == TaskAction::Continue)
|
||||
const SetupResult finalizeAction = childDone(startAction == SetupResult::StopWithDone);
|
||||
if (finalizeAction == SetupResult::Continue)
|
||||
continue;
|
||||
|
||||
int skippedTaskCount = 0;
|
||||
@@ -1008,10 +1114,10 @@ TaskAction TaskContainer::startChildren(int nextChild)
|
||||
m_constData.m_taskTreePrivate->advanceProgress(skippedTaskCount);
|
||||
return finalizeAction;
|
||||
}
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
}
|
||||
|
||||
TaskAction TaskContainer::childDone(bool success)
|
||||
SetupResult TaskContainer::childDone(bool success)
|
||||
{
|
||||
QTC_CHECK(isRunning());
|
||||
const int limit = m_runtimeData->currentLimit(); // Read before bumping m_doneCount and stop()
|
||||
@@ -1023,9 +1129,9 @@ TaskAction TaskContainer::childDone(bool success)
|
||||
|
||||
++m_runtimeData->m_doneCount;
|
||||
const bool updatedSuccess = m_runtimeData->updateSuccessBit(success);
|
||||
const TaskAction startAction
|
||||
const SetupResult startAction
|
||||
= (shouldStop || m_runtimeData->m_doneCount == m_constData.m_children.size())
|
||||
? toTaskAction(updatedSuccess) : TaskAction::Continue;
|
||||
? toSetupResult(updatedSuccess) : SetupResult::Continue;
|
||||
|
||||
if (isStarting())
|
||||
return startAction;
|
||||
@@ -1059,30 +1165,30 @@ void TaskContainer::invokeEndHandler()
|
||||
m_runtimeData.reset();
|
||||
}
|
||||
|
||||
TaskAction TaskNode::start()
|
||||
SetupResult TaskNode::start()
|
||||
{
|
||||
QTC_CHECK(!isRunning());
|
||||
if (!isTask())
|
||||
return m_container.start();
|
||||
|
||||
m_task.reset(m_taskHandler.m_createHandler());
|
||||
const TaskAction startAction = m_taskHandler.m_setupHandler
|
||||
const SetupResult startAction = m_taskHandler.m_setupHandler
|
||||
? invokeHandler(parentContainer(), m_taskHandler.m_setupHandler, *m_task.get())
|
||||
: TaskAction::Continue;
|
||||
if (startAction != TaskAction::Continue) {
|
||||
: SetupResult::Continue;
|
||||
if (startAction != SetupResult::Continue) {
|
||||
m_container.m_constData.m_taskTreePrivate->advanceProgress(1);
|
||||
m_task.reset();
|
||||
return startAction;
|
||||
}
|
||||
const std::shared_ptr<TaskAction> unwindAction
|
||||
= std::make_shared<TaskAction>(TaskAction::Continue);
|
||||
const std::shared_ptr<SetupResult> unwindAction
|
||||
= std::make_shared<SetupResult>(SetupResult::Continue);
|
||||
QObject::connect(m_task.get(), &TaskInterface::done, taskTree(), [=](bool success) {
|
||||
invokeEndHandler(success);
|
||||
QObject::disconnect(m_task.get(), &TaskInterface::done, taskTree(), nullptr);
|
||||
m_task.release()->deleteLater();
|
||||
QTC_ASSERT(parentContainer() && parentContainer()->isRunning(), return);
|
||||
if (parentContainer()->isStarting())
|
||||
*unwindAction = toTaskAction(success);
|
||||
*unwindAction = toSetupResult(success);
|
||||
else
|
||||
parentContainer()->childDone(success);
|
||||
});
|
||||
@@ -1321,18 +1427,18 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
as the task tree calls it when needed. The setup handler is optional. When used,
|
||||
it must be the first argument of the task's constructor.
|
||||
|
||||
Optionally, the setup handler may return a TaskAction. The returned
|
||||
TaskAction influences the further start behavior of a given task. The
|
||||
Optionally, the setup handler may return a SetupResult. The returned
|
||||
SetupResult influences the further start behavior of a given task. The
|
||||
possible values are:
|
||||
|
||||
\table
|
||||
\header
|
||||
\li TaskAction Value
|
||||
\li SetupResult Value
|
||||
\li Brief Description
|
||||
\row
|
||||
\li Continue
|
||||
\li The task will be started normally. This is the default behavior when the
|
||||
setup handler doesn't return TaskAction (that is, its return type is
|
||||
setup handler doesn't return SetupResult (that is, its return type is
|
||||
void).
|
||||
\row
|
||||
\li StopWithDone
|
||||
@@ -1404,12 +1510,12 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
handler. If you add more than one onGroupSetup() element to a group, an assert
|
||||
is triggered at runtime that includes an error message.
|
||||
|
||||
Like the task's start handler, the group start handler may return TaskAction.
|
||||
The returned TaskAction value affects the start behavior of the
|
||||
Like the task's start handler, the group start handler may return SetupResult.
|
||||
The returned SetupResult value affects the start behavior of the
|
||||
whole group. If you do not specify a group start handler or its return type
|
||||
is void, the default group's action is TaskAction::Continue, so that all
|
||||
is void, the default group's action is SetupResult::Continue, so that all
|
||||
tasks are started normally. Otherwise, when the start handler returns
|
||||
TaskAction::StopWithDone or TaskAction::StopWithError, the tasks are not
|
||||
SetupResult::StopWithDone or SetupResult::StopWithError, the tasks are not
|
||||
started (they are skipped) and the group itself reports success or failure,
|
||||
depending on the returned value, respectively.
|
||||
|
||||
@@ -1417,15 +1523,15 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
const Group root {
|
||||
onGroupSetup([] { qDebug() << "Root setup"; }),
|
||||
Group {
|
||||
onGroupSetup([] { qDebug() << "Group 1 setup"; return TaskAction::Continue; }),
|
||||
onGroupSetup([] { qDebug() << "Group 1 setup"; return SetupResult::Continue; }),
|
||||
ProcessTask(...) // Process 1
|
||||
},
|
||||
Group {
|
||||
onGroupSetup([] { qDebug() << "Group 2 setup"; return TaskAction::StopWithDone; }),
|
||||
onGroupSetup([] { qDebug() << "Group 2 setup"; return SetupResult::StopWithDone; }),
|
||||
ProcessTask(...) // Process 2
|
||||
},
|
||||
Group {
|
||||
onGroupSetup([] { qDebug() << "Group 3 setup"; return TaskAction::StopWithError; }),
|
||||
onGroupSetup([] { qDebug() << "Group 3 setup"; return SetupResult::StopWithError; }),
|
||||
ProcessTask(...) // Process 3
|
||||
},
|
||||
ProcessTask(...) // Process 4
|
||||
@@ -1441,7 +1547,7 @@ void TaskNode::invokeEndHandler(bool success)
|
||||
\li Comment
|
||||
\row
|
||||
\li Root Group starts
|
||||
\li Doesn't return TaskAction, so its tasks are executed.
|
||||
\li Doesn't return SetupResult, so its tasks are executed.
|
||||
\row
|
||||
\li Group 1 starts
|
||||
\li Returns Continue, so its tasks are executed.
|
||||
@@ -1833,7 +1939,7 @@ void TaskTree::setRecipe(const Group &recipe)
|
||||
Otherwise, the task tree is started.
|
||||
|
||||
The started task tree may finish synchronously,
|
||||
for example when the main group's start handler returns TaskAction::StopWithError.
|
||||
for example when the main group's start handler returns SetupResult::StopWithError.
|
||||
For this reason, the connections to the done and errorOccurred signals should be
|
||||
established before calling start. Use isRunning() in order to detect whether
|
||||
the task tree is still running after a call to start().
|
||||
|
||||
@@ -26,12 +26,14 @@ class TASKING_EXPORT TaskInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TaskInterface() = default;
|
||||
virtual void start() = 0;
|
||||
|
||||
signals:
|
||||
void done(bool success);
|
||||
|
||||
private:
|
||||
template <typename Task> friend class TaskAdapter;
|
||||
friend class TaskNode;
|
||||
TaskInterface() = default;
|
||||
virtual void start() = 0;
|
||||
};
|
||||
|
||||
class TASKING_EXPORT TreeStorageBase
|
||||
@@ -115,13 +117,13 @@ enum class WorkflowPolicy {
|
||||
};
|
||||
Q_ENUM_NS(WorkflowPolicy);
|
||||
|
||||
enum class TaskAction
|
||||
enum class SetupResult
|
||||
{
|
||||
Continue,
|
||||
StopWithDone,
|
||||
StopWithError
|
||||
};
|
||||
Q_ENUM_NS(TaskAction);
|
||||
Q_ENUM_NS(SetupResult);
|
||||
|
||||
class TASKING_EXPORT GroupItem
|
||||
{
|
||||
@@ -129,11 +131,11 @@ public:
|
||||
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
|
||||
using TaskCreateHandler = std::function<TaskInterface *(void)>;
|
||||
// Called prior to task start, just after createHandler
|
||||
using TaskSetupHandler = std::function<TaskAction(TaskInterface &)>;
|
||||
using TaskSetupHandler = std::function<SetupResult(TaskInterface &)>;
|
||||
// Called on task done / error
|
||||
using TaskEndHandler = std::function<void(const TaskInterface &)>;
|
||||
// Called when group entered
|
||||
using GroupSetupHandler = std::function<TaskAction()>;
|
||||
using GroupSetupHandler = std::function<SetupResult()>;
|
||||
// Called when group done / error
|
||||
using GroupEndHandler = std::function<void()>;
|
||||
|
||||
@@ -228,17 +230,17 @@ private:
|
||||
static GroupSetupHandler wrapGroupSetup(SetupHandler &&handler)
|
||||
{
|
||||
static constexpr bool isDynamic
|
||||
= std::is_same_v<TaskAction, std::invoke_result_t<std::decay_t<SetupHandler>>>;
|
||||
= std::is_same_v<SetupResult, std::invoke_result_t<std::decay_t<SetupHandler>>>;
|
||||
constexpr bool isVoid
|
||||
= std::is_same_v<void, std::invoke_result_t<std::decay_t<SetupHandler>>>;
|
||||
static_assert(isDynamic || isVoid,
|
||||
"Group setup handler needs to take no arguments and has to return "
|
||||
"void or TaskAction. The passed handler doesn't fulfill these requirements.");
|
||||
"void or SetupResult. The passed handler doesn't fulfill these requirements.");
|
||||
return [=] {
|
||||
if constexpr (isDynamic)
|
||||
return std::invoke(handler);
|
||||
std::invoke(handler);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -290,22 +292,24 @@ private:
|
||||
static_assert(isBool || isVoid,
|
||||
"Sync element: The synchronous function has to return void or bool.");
|
||||
if constexpr (isBool) {
|
||||
return {onGroupSetup([function] { return function() ? TaskAction::StopWithDone
|
||||
: TaskAction::StopWithError; })};
|
||||
return {onGroupSetup([function] { return function() ? SetupResult::StopWithDone
|
||||
: SetupResult::StopWithError; })};
|
||||
}
|
||||
return {onGroupSetup([function] { function(); return TaskAction::StopWithDone; })};
|
||||
return {onGroupSetup([function] { function(); return SetupResult::StopWithDone; })};
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Task>
|
||||
class TaskAdapter : public TaskInterface
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
using Type = Task;
|
||||
TaskAdapter() = default;
|
||||
Task *task() { return &m_task; }
|
||||
const Task *task() const { return &m_task; }
|
||||
|
||||
private:
|
||||
template <typename Adapter> friend class CustomTask;
|
||||
Task m_task;
|
||||
};
|
||||
|
||||
@@ -344,19 +348,19 @@ public:
|
||||
private:
|
||||
template<typename SetupFunction>
|
||||
static GroupItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
|
||||
static constexpr bool isDynamic = std::is_same_v<TaskAction,
|
||||
static constexpr bool isDynamic = std::is_same_v<SetupResult,
|
||||
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
||||
constexpr bool isVoid = std::is_same_v<void,
|
||||
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
||||
static_assert(isDynamic || isVoid,
|
||||
"Task setup handler needs to take (Task &) as an argument and has to return "
|
||||
"void or TaskAction. The passed handler doesn't fulfill these requirements.");
|
||||
"void or SetupResult. The passed handler doesn't fulfill these requirements.");
|
||||
return [=](TaskInterface &taskInterface) {
|
||||
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
||||
if constexpr (isDynamic)
|
||||
return std::invoke(function, *adapter.task());
|
||||
std::invoke(function, *adapter.task());
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1474,8 +1474,13 @@ void BoolAspect::addToLayout(Layouting::LayoutItem &parent)
|
||||
d->m_button = createSubWidget<QCheckBox>();
|
||||
}
|
||||
switch (d->m_labelPlacement) {
|
||||
case LabelPlacement::AtCheckBoxWithoutDummyLabel:
|
||||
d->m_button->setText(labelText());
|
||||
parent.addItem(d->m_button.data());
|
||||
break;
|
||||
case LabelPlacement::AtCheckBox:
|
||||
d->m_button->setText(labelText());
|
||||
parent.addItem(empty());
|
||||
parent.addItem(d->m_button.data());
|
||||
break;
|
||||
case LabelPlacement::InExtraLabel:
|
||||
|
||||
@@ -237,7 +237,7 @@ public:
|
||||
bool defaultValue() const;
|
||||
void setDefaultValue(bool val);
|
||||
|
||||
enum class LabelPlacement { AtCheckBox, InExtraLabel };
|
||||
enum class LabelPlacement { AtCheckBox, AtCheckBoxWithoutDummyLabel, InExtraLabel };
|
||||
void setLabel(const QString &labelText,
|
||||
LabelPlacement labelPlacement = LabelPlacement::InExtraLabel);
|
||||
void setLabelPlacement(LabelPlacement labelPlacement);
|
||||
|
||||
@@ -1444,16 +1444,15 @@ CommandLine CommandLine::fromUserInput(const QString &cmdline, MacroExpander *ex
|
||||
|
||||
QString input = cmdline.trimmed();
|
||||
|
||||
QStringList result = ProcessArgs::splitArgs(cmdline, HostOsInfo::hostOs());
|
||||
if (expander)
|
||||
input = expander->expand(input);
|
||||
|
||||
const QStringList result = ProcessArgs::splitArgs(input, HostOsInfo::hostOs());
|
||||
|
||||
if (result.isEmpty())
|
||||
return {};
|
||||
|
||||
auto cmd = CommandLine(FilePath::fromUserInput(result.value(0)), result.mid(1));
|
||||
if (expander)
|
||||
cmd.m_arguments = expander->expand(cmd.m_arguments);
|
||||
|
||||
return cmd;
|
||||
return {FilePath::fromUserInput(result.value(0)), result.mid(1)};
|
||||
}
|
||||
|
||||
void CommandLine::addArg(const QString &arg)
|
||||
|
||||
@@ -229,6 +229,7 @@ struct ResultItem
|
||||
int space = -1;
|
||||
int stretch = -1;
|
||||
int span = 1;
|
||||
bool empty = false;
|
||||
};
|
||||
|
||||
struct Slice
|
||||
@@ -287,6 +288,8 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
|
||||
layout->addSpacing(item.space);
|
||||
} else if (!item.text.isEmpty()) {
|
||||
layout->addWidget(createLabel(item.text));
|
||||
} else if (item.empty) {
|
||||
// Nothing to do, but no reason to warn, either.
|
||||
} else {
|
||||
QTC_CHECK(false);
|
||||
}
|
||||
@@ -654,8 +657,8 @@ LayoutItem empty()
|
||||
LayoutItem item;
|
||||
item.onAdd = [](LayoutBuilder &builder) {
|
||||
ResultItem ri;
|
||||
ri.span = 1;
|
||||
builder.stack.last().pendingItems.append(ResultItem());
|
||||
ri.empty = true;
|
||||
builder.stack.last().pendingItems.append(ri);
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA
|
||||
proc.setCommand(avdManager);
|
||||
proc.start();
|
||||
if (!proc.waitForStarted()) {
|
||||
result.error = Tr::tr("Could not start process \"%1\"").arg(avdManager.toUserOutput());
|
||||
result.error = Tr::tr("Could not start process \"%1\".").arg(avdManager.toUserOutput());
|
||||
return result;
|
||||
}
|
||||
QTC_CHECK(proc.isRunning());
|
||||
|
||||
@@ -389,7 +389,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder,
|
||||
rccProcess.setCommand({rccBinary, args});
|
||||
rccProcess.start();
|
||||
if (!rccProcess.waitForStarted()) {
|
||||
appendMessage(Tr::tr("Could not create file for %1 \"%2\"").
|
||||
appendMessage(Tr::tr("Could not create file for %1 \"%2\".").
|
||||
arg(apkInfo()->name, rccProcess.commandLine().toUserOutput()),
|
||||
StdErrFormat);
|
||||
qrcPath.removeFile();
|
||||
@@ -400,7 +400,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder,
|
||||
if (!rccProcess.readDataFromProcess(&stdOut, &stdErr)) {
|
||||
rccProcess.stop();
|
||||
rccProcess.waitForFinished();
|
||||
appendMessage(Tr::tr("A timeout occurred running \"%1\"").
|
||||
appendMessage(Tr::tr("A timeout occurred running \"%1\".").
|
||||
arg(rccProcess.commandLine().toUserOutput()), StdErrFormat);
|
||||
qrcPath.removeFile();
|
||||
return {};
|
||||
@@ -412,7 +412,7 @@ FilePath AndroidQmlPreviewWorker::createQmlrcFile(const FilePath &workFolder,
|
||||
appendMessage(QString::fromLocal8Bit(stdErr), StdErrFormat);
|
||||
|
||||
if (rccProcess.exitStatus() != QProcess::NormalExit) {
|
||||
appendMessage(Tr::tr("Crash while creating file for %1 \"%2\"").
|
||||
appendMessage(Tr::tr("Crash while creating file for %1 \"%2\".").
|
||||
arg(apkInfo()->name, rccProcess.commandLine().toUserOutput()),
|
||||
StdErrFormat);
|
||||
qrcPath.removeFile();
|
||||
|
||||
@@ -158,20 +158,20 @@ void AndroidSdkDownloader::downloadAndExtractSdk()
|
||||
m_progressDialog->setRange(0, 0);
|
||||
m_progressDialog->setLabelText(Tr::tr("Unarchiving SDK Tools package..."));
|
||||
if (!*storage)
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
const FilePath sdkFileName = **storage;
|
||||
if (!verifyFileIntegrity(sdkFileName, m_androidConfig.getSdkToolsSha256())) {
|
||||
logError(Tr::tr("Verifying the integrity of the downloaded file has failed."));
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
const auto sourceAndCommand = Unarchiver::sourceAndCommand(sdkFileName);
|
||||
if (!sourceAndCommand) {
|
||||
logError(sourceAndCommand.error());
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
unarchiver.setSourceAndCommand(*sourceAndCommand);
|
||||
unarchiver.setDestDir(sdkFileName.parentDir());
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onUnarchiverDone = [this, storage](const Unarchiver &) {
|
||||
m_androidConfig.setTemporarySdkToolsPath(
|
||||
|
||||
@@ -102,8 +102,13 @@ QStringList BoostTestConfiguration::argumentsForTestRunner(QStringList *omitted)
|
||||
arguments << "--detect_memory_leaks=0";
|
||||
|
||||
// TODO improve the test case gathering and arguments building to avoid too long command lines
|
||||
if (isDebugRunMode()) { // debugger has its own quoting
|
||||
for (const QString &test : testCases())
|
||||
arguments << "-t" << test;
|
||||
} else {
|
||||
for (const QString &test : testCases())
|
||||
arguments << "-t" << "\"" + test + "\"";
|
||||
}
|
||||
|
||||
if (TestSettings::instance()->processArgs()) {
|
||||
arguments << filterInterfering(runnable().command.arguments().split(
|
||||
|
||||
@@ -31,12 +31,6 @@ BoostTestOutputReader::BoostTestOutputReader(Process *testApplication,
|
||||
, m_logLevel(log)
|
||||
, m_reportLevel(report)
|
||||
{
|
||||
if (!testApplication)
|
||||
return;
|
||||
|
||||
connect(testApplication, &Process::done, this, [this, testApplication] {
|
||||
onDone(testApplication->exitCode());
|
||||
});
|
||||
}
|
||||
|
||||
// content of "error:..." / "info:..." / ... messages
|
||||
@@ -147,7 +141,7 @@ void BoostTestOutputReader::handleMessageMatch(const QRegularExpressionMatch &ma
|
||||
if (m_currentTest != match.captured(11) && m_currentTest.isEmpty())
|
||||
m_currentTest = match.captured(11);
|
||||
m_result = ResultType::TestEnd;
|
||||
m_description = Tr::tr("Test execution took %1").arg(match.captured(12));
|
||||
m_description = Tr::tr("Test execution took %1.").arg(match.captured(12));
|
||||
} else if (type == "suite") {
|
||||
if (!m_currentSuite.isEmpty()) {
|
||||
int index = m_currentSuite.lastIndexOf('/');
|
||||
@@ -163,7 +157,7 @@ void BoostTestOutputReader::handleMessageMatch(const QRegularExpressionMatch &ma
|
||||
}
|
||||
m_currentTest.clear();
|
||||
m_result = ResultType::TestEnd;
|
||||
m_description = Tr::tr("Test suite execution took %1").arg(match.captured(12));
|
||||
m_description = Tr::tr("Test suite execution took %1.").arg(match.captured(12));
|
||||
}
|
||||
} else if (content.startsWith("Test case ")) {
|
||||
m_currentTest = match.captured(4);
|
||||
@@ -247,7 +241,7 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
} else {
|
||||
QTC_CHECK(m_currentModule == match.captured(3));
|
||||
BoostTestResult result(id(), m_currentModule, m_projectFile);
|
||||
result.setDescription(Tr::tr("Test module execution took %1").arg(match.captured(4)));
|
||||
result.setDescription(Tr::tr("Test module execution took %1.").arg(match.captured(4)));
|
||||
result.setResult(ResultType::TestEnd);
|
||||
reportResult(result);
|
||||
|
||||
@@ -394,17 +388,17 @@ void BoostTestOutputReader::onDone(int exitCode)
|
||||
if (m_logLevel == LogLevel::Nothing && m_reportLevel == ReportLevel::No) {
|
||||
switch (exitCode) {
|
||||
case 0:
|
||||
reportNoOutputFinish(Tr::tr("Running tests exited with %1").arg("boost::exit_success."),
|
||||
reportNoOutputFinish(Tr::tr("Running tests exited with %1.").arg("boost::exit_success"),
|
||||
ResultType::Pass);
|
||||
break;
|
||||
case 200:
|
||||
reportNoOutputFinish(
|
||||
Tr::tr("Running tests exited with %1").arg("boost::exit_test_exception."),
|
||||
Tr::tr("Running tests exited with %1.").arg("boost::exit_test_exception"),
|
||||
ResultType::MessageFatal);
|
||||
break;
|
||||
case 201:
|
||||
reportNoOutputFinish(Tr::tr("Running tests exited with %1")
|
||||
.arg("boost::exit_test_failure."), ResultType::Fail);
|
||||
reportNoOutputFinish(Tr::tr("Running tests exited with %1.")
|
||||
.arg("boost::exit_test_failure"), ResultType::Fail);
|
||||
break;
|
||||
}
|
||||
} else if (exitCode != 0 && exitCode != 201 && !m_description.isEmpty()) {
|
||||
|
||||
@@ -23,7 +23,7 @@ protected:
|
||||
TestResult createDefaultResult() const override;
|
||||
|
||||
private:
|
||||
void onDone(int exitCode);
|
||||
void onDone(int exitCode) override;
|
||||
void sendCompleteInformation();
|
||||
void handleMessageMatch(const QRegularExpressionMatch &match);
|
||||
void reportNoOutputFinish(const QString &description, ResultType type);
|
||||
|
||||
@@ -90,7 +90,7 @@ void CTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
m_project = match.captured(1);
|
||||
TestResult testResult = createDefaultResult();
|
||||
testResult.setResult(ResultType::TestStart);
|
||||
testResult.setDescription(Tr::tr("Running tests for %1").arg(m_project));
|
||||
testResult.setDescription(Tr::tr("Running tests for \"%1\".").arg(m_project));
|
||||
reportResult(testResult);
|
||||
} else if (ExactMatch match = testCase1.match(line)) {
|
||||
int current = match.captured("current").toInt();
|
||||
|
||||
@@ -23,18 +23,6 @@ GTestOutputReader::GTestOutputReader(Process *testApplication,
|
||||
: TestOutputReader(testApplication, buildDirectory)
|
||||
, m_projectFile(projectFile)
|
||||
{
|
||||
if (testApplication) {
|
||||
connect(testApplication, &Process::done, this, [this, testApplication] {
|
||||
const int exitCode = testApplication->exitCode();
|
||||
if (exitCode == 1 && !m_description.isEmpty()) {
|
||||
createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2")
|
||||
.arg(m_description).arg(id()), ResultType::MessageFatal);
|
||||
}
|
||||
// on Windows abort() will result in normal termination, but exit code will be set to 3
|
||||
if (HostOsInfo::isWindowsHost() && exitCode == 3)
|
||||
reportCrash();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
@@ -81,7 +69,7 @@ void GTestOutputReader::processOutputLine(const QByteArray &outputLine)
|
||||
if (ExactMatch match = testEnds.match(line)) {
|
||||
TestResult testResult = createDefaultResult();
|
||||
testResult.setResult(ResultType::TestEnd);
|
||||
testResult.setDescription(Tr::tr("Test execution took %1").arg(match.captured(2)));
|
||||
testResult.setDescription(Tr::tr("Test execution took %1.").arg(match.captured(2)));
|
||||
reportResult(testResult);
|
||||
m_currentTestSuite.clear();
|
||||
m_currentTestCase.clear();
|
||||
@@ -182,6 +170,17 @@ TestResult GTestOutputReader::createDefaultResult() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void GTestOutputReader::onDone(int exitCode)
|
||||
{
|
||||
if (exitCode == 1 && !m_description.isEmpty()) {
|
||||
createAndReportResult(Tr::tr("Running tests failed.\n %1\nExecutable: %2")
|
||||
.arg(m_description).arg(id()), ResultType::MessageFatal);
|
||||
}
|
||||
// on Windows abort() will result in normal termination, but exit code will be set to 3
|
||||
if (HostOsInfo::isWindowsHost() && exitCode == 3)
|
||||
reportCrash();
|
||||
}
|
||||
|
||||
void GTestOutputReader::setCurrentTestCase(const QString &testCase)
|
||||
{
|
||||
m_currentTestCase = testCase;
|
||||
|
||||
@@ -19,6 +19,7 @@ protected:
|
||||
TestResult createDefaultResult() const override;
|
||||
|
||||
private:
|
||||
void onDone(int exitCode) override;
|
||||
void setCurrentTestCase(const QString &testCase);
|
||||
void setCurrentTestSuite(const QString &testSuite);
|
||||
void handleDescriptionAndReportResult(const TestResult &testResult);
|
||||
|
||||
@@ -28,6 +28,8 @@ public:
|
||||
void setId(const QString &id) { m_id = id; }
|
||||
QString id() const { return m_id; }
|
||||
|
||||
virtual void onDone(int exitCode) { Q_UNUSED(exitCode) }
|
||||
|
||||
void resetCommandlineColor();
|
||||
signals:
|
||||
void newResult(const TestResult &result);
|
||||
|
||||
@@ -355,13 +355,13 @@ void TestRunner::runTestsHelper()
|
||||
|
||||
const auto onSetup = [this, config] {
|
||||
if (!config->project())
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
if (config->testExecutable().isEmpty()) {
|
||||
reportResult(ResultType::MessageFatal,
|
||||
Tr::tr("Executable path is empty. (%1)").arg(config->displayName()));
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onProcessSetup = [this, config, storage](Process &process) {
|
||||
TestStorage *testStorage = storage.activeStorage();
|
||||
@@ -419,6 +419,9 @@ void TestRunner::runTestsHelper()
|
||||
+ processInformation(&process) + rcInfo(config));
|
||||
}
|
||||
|
||||
if (testStorage->m_outputReader)
|
||||
testStorage->m_outputReader->onDone(process.exitCode());
|
||||
|
||||
if (process.exitStatus() == QProcess::CrashExit) {
|
||||
if (testStorage->m_outputReader)
|
||||
testStorage->m_outputReader->reportCrash();
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QLabel>
|
||||
#include <QScrollArea>
|
||||
#include <QStackedWidget>
|
||||
@@ -28,7 +29,8 @@ public:
|
||||
private:
|
||||
QLabel *m_project = nullptr;
|
||||
QLabel *m_loc = nullptr;
|
||||
QFormLayout *m_formLayout = nullptr;
|
||||
QLabel *m_timestamp = nullptr;
|
||||
QGridLayout *m_gridLayout = nullptr;
|
||||
};
|
||||
|
||||
DashboardWidget::DashboardWidget(QWidget *parent)
|
||||
@@ -41,58 +43,98 @@ DashboardWidget::DashboardWidget(QWidget *parent)
|
||||
projectLayout->addRow(Tr::tr("Project:"), m_project);
|
||||
m_loc = new QLabel(this);
|
||||
projectLayout->addRow(Tr::tr("Lines of code:"), m_loc);
|
||||
m_timestamp = new QLabel(this);
|
||||
projectLayout->addRow(Tr::tr("Analysis timestamp:"), m_timestamp);
|
||||
layout->addLayout(projectLayout);
|
||||
m_formLayout = new QFormLayout;
|
||||
layout->addLayout(m_formLayout);
|
||||
layout->addSpacing(10);
|
||||
auto row = new QHBoxLayout;
|
||||
m_gridLayout = new QGridLayout;
|
||||
row->addLayout(m_gridLayout);
|
||||
row->addStretch(1);
|
||||
layout->addLayout(row);
|
||||
layout->addStretch(1);
|
||||
setWidget(widget);
|
||||
setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
setWidgetResizable(true);
|
||||
}
|
||||
|
||||
static QPixmap trendIcon(int added, int removed)
|
||||
{
|
||||
static const QPixmap unchanged = Utils::Icons::NEXT.pixmap();
|
||||
static const QPixmap increased = Utils::Icon(
|
||||
{ {":/utils/images/arrowup.png", Utils::Theme::IconsErrorColor} }).pixmap();
|
||||
static const QPixmap decreased = Utils::Icon(
|
||||
{ {":/utils/images/arrowdown.png", Utils::Theme::IconsRunColor} }).pixmap();
|
||||
if (added == removed)
|
||||
return unchanged;
|
||||
return added < removed ? decreased : increased;
|
||||
}
|
||||
|
||||
void DashboardWidget::updateUi()
|
||||
{
|
||||
const ProjectInfo &info = AxivionPlugin::projectInfo();
|
||||
m_project->setText(info.name);
|
||||
m_loc->setText({});
|
||||
while (m_formLayout->rowCount())
|
||||
m_formLayout->removeRow(0);
|
||||
m_timestamp->setText({});
|
||||
QLayoutItem *child;
|
||||
while ((child = m_gridLayout->takeAt(0)) != nullptr) {
|
||||
delete child->widget();
|
||||
delete child;
|
||||
}
|
||||
|
||||
if (info.versions.isEmpty())
|
||||
return;
|
||||
|
||||
const ResultVersion &last = info.versions.last();
|
||||
m_loc->setText(QString::number(last.linesOfCode));
|
||||
const QDateTime timeStamp = QDateTime::fromString(last.timeStamp, Qt::ISODate);
|
||||
m_timestamp->setText(timeStamp.isValid() ? timeStamp.toString("yyyy-MM-dd HH::mm::ss")
|
||||
: Tr::tr("unknown"));
|
||||
|
||||
const QString tmpl("%1 %2 +%3 / -%4");
|
||||
auto apply = [&tmpl](int t, int a, int r){
|
||||
QChar tr = (a == r ? '=' : (a < r ? '^' : 'v'));
|
||||
return tmpl.arg(t, 10, 10, QLatin1Char(' ')).arg(tr).arg(a, 5, 10, QLatin1Char(' '))
|
||||
.arg(r, 5, 10, QLatin1Char(' '));
|
||||
};
|
||||
const QList<IssueKind> &issueKinds = info.issueKinds;
|
||||
auto toolTip = [issueKinds](const QString &prefix){
|
||||
for (const IssueKind &kind : issueKinds) {
|
||||
if (kind.prefix == prefix)
|
||||
return kind.nicePlural;
|
||||
}
|
||||
return QString();
|
||||
return prefix;
|
||||
};
|
||||
int allTotal = 0, allAdded = 0, allRemoved = 0;
|
||||
auto addValuesWidgets = [this, &toolTip](const IssueCount &issueCount, int row){
|
||||
const QString currentToolTip = toolTip(issueCount.issueKind);
|
||||
QLabel *label = new QLabel(issueCount.issueKind, this);
|
||||
label->setToolTip(currentToolTip);
|
||||
m_gridLayout->addWidget(label, row, 0);
|
||||
label = new QLabel(QString::number(issueCount.total), this);
|
||||
label->setToolTip(currentToolTip);
|
||||
label->setAlignment(Qt::AlignRight);
|
||||
m_gridLayout->addWidget(label, row, 1);
|
||||
label = new QLabel(this);
|
||||
label->setPixmap(trendIcon(issueCount.added, issueCount.removed));
|
||||
label->setToolTip(currentToolTip);
|
||||
m_gridLayout->addWidget(label, row, 2);
|
||||
label = new QLabel('+' + QString::number(issueCount.added));
|
||||
label->setAlignment(Qt::AlignRight);
|
||||
label->setToolTip(currentToolTip);
|
||||
m_gridLayout->addWidget(label, row, 3);
|
||||
label = new QLabel("/");
|
||||
label->setToolTip(currentToolTip);
|
||||
m_gridLayout->addWidget(label, row, 4);
|
||||
label = new QLabel('-' + QString::number(issueCount.removed));
|
||||
label->setAlignment(Qt::AlignRight);
|
||||
label->setToolTip(currentToolTip);
|
||||
m_gridLayout->addWidget(label, row, 5);
|
||||
};
|
||||
int allTotal = 0, allAdded = 0, allRemoved = 0, row = 0;
|
||||
for (auto issueCount : std::as_const(last.issueCounts)) {
|
||||
allTotal += issueCount.total;
|
||||
allAdded += issueCount.added;
|
||||
allRemoved += issueCount.removed;
|
||||
const QString txt = apply(issueCount.total, issueCount.added, issueCount.removed);
|
||||
const QString currentToolTip = toolTip(issueCount.issueKind);
|
||||
QLabel *label = new QLabel(issueCount.issueKind, this);
|
||||
label->setToolTip(currentToolTip);
|
||||
QLabel *values = new QLabel(txt, this);
|
||||
values->setToolTip(currentToolTip);
|
||||
m_formLayout->addRow(label, values);
|
||||
addValuesWidgets(issueCount, row);
|
||||
++row;
|
||||
}
|
||||
|
||||
QLabel *label = new QLabel(apply(allTotal, allAdded, allRemoved), this);
|
||||
m_formLayout->addRow(Tr::tr("Total:"), label);
|
||||
const IssueCount total{{}, Tr::tr("Total:"), allTotal, allAdded, allRemoved};
|
||||
addValuesWidgets(total, row);
|
||||
}
|
||||
|
||||
AxivionOutputPane::AxivionOutputPane(QObject *parent)
|
||||
|
||||
@@ -43,7 +43,7 @@ Group QdbStopApplicationStep::deployRecipe()
|
||||
const auto device = DeviceKitAspect::device(target()->kit());
|
||||
if (!device) {
|
||||
addErrorMessage(Tr::tr("No device to stop the application on."));
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
QTC_CHECK(device);
|
||||
process.setCommand({device->filePath(Constants::AppcontrollerFilepath), {"--stop"}});
|
||||
@@ -52,7 +52,7 @@ Group QdbStopApplicationStep::deployRecipe()
|
||||
connect(proc, &Process::readyReadStandardOutput, this, [this, proc] {
|
||||
handleStdOutData(proc->readAllStandardOutput());
|
||||
});
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto doneHandler = [this](const Process &) {
|
||||
addProgressMessage(Tr::tr("Stopped the running application."));
|
||||
|
||||
@@ -128,23 +128,23 @@ GroupItem clangToolTask(const AnalyzeInputData &input,
|
||||
|
||||
const auto onSetup = [=] {
|
||||
if (setupHandler && !setupHandler())
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
|
||||
ClangToolStorage *data = storage.activeStorage();
|
||||
data->name = clangToolName(input.tool);
|
||||
data->executable = toolExecutable(input.tool);
|
||||
if (!data->executable.isExecutableFile()) {
|
||||
qWarning() << "Can't start:" << data->executable << "as" << data->name;
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
|
||||
QTC_CHECK(!input.unit.arguments.contains(QLatin1String("-o")));
|
||||
QTC_CHECK(!input.unit.arguments.contains(input.unit.file.nativePath()));
|
||||
QTC_ASSERT(input.unit.file.exists(), return TaskAction::StopWithError);
|
||||
QTC_ASSERT(input.unit.file.exists(), return SetupResult::StopWithError);
|
||||
data->outputFilePath = createOutputFilePath(input.outputDirPath, input.unit.file);
|
||||
QTC_ASSERT(!data->outputFilePath.isEmpty(), return TaskAction::StopWithError);
|
||||
QTC_ASSERT(!data->outputFilePath.isEmpty(), return SetupResult::StopWithError);
|
||||
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onProcessSetup = [=](Process &process) {
|
||||
process.setEnvironment(input.environment);
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include "cmakeprojectmanagertr.h"
|
||||
#include "cmaketool.h"
|
||||
|
||||
#include <android/androidconstants.h>
|
||||
|
||||
#include <ios/iosconstants.h>
|
||||
|
||||
#include <coreplugin/find/itemviewfind.h>
|
||||
#include <projectexplorer/buildsteplist.h>
|
||||
#include <projectexplorer/devicesupport/idevice.h>
|
||||
@@ -173,13 +177,15 @@ static QString initialStagingDir(Kit *kit)
|
||||
return QString::fromUtf8("/tmp/Qt-Creator-staging-" + ba);
|
||||
}
|
||||
|
||||
static bool buildAndRunOnSameDevice(Kit *kit)
|
||||
static bool supportsStageForInstallation(const Kit *kit)
|
||||
{
|
||||
IDeviceConstPtr runDevice = DeviceKitAspect::device(kit);
|
||||
IDeviceConstPtr buildDevice = BuildDeviceKitAspect::device(kit);
|
||||
QTC_ASSERT(runDevice, return false);
|
||||
QTC_ASSERT(buildDevice, return false);
|
||||
return runDevice->id() == buildDevice->id();
|
||||
return runDevice->id() != buildDevice->id()
|
||||
&& runDevice->type() != Android::Constants::ANDROID_DEVICE_TYPE
|
||||
&& runDevice->type() != Ios::Constants::IOS_DEVICE_TYPE;
|
||||
}
|
||||
|
||||
CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) :
|
||||
@@ -198,7 +204,7 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Id id) :
|
||||
m_useStaging = addAspect<BoolAspect>();
|
||||
m_useStaging->setSettingsKey(USE_STAGING_KEY);
|
||||
m_useStaging->setLabel(Tr::tr("Stage for installation"), BoolAspect::LabelPlacement::AtCheckBox);
|
||||
m_useStaging->setDefaultValue(!buildAndRunOnSameDevice(kit()));
|
||||
m_useStaging->setDefaultValue(supportsStageForInstallation(kit()));
|
||||
|
||||
m_stagingDir = addAspect<FilePathAspect>();
|
||||
m_stagingDir->setSettingsKey(STAGING_DIR_KEY);
|
||||
|
||||
@@ -30,6 +30,8 @@ AuthWidget::AuthWidget(QWidget *parent)
|
||||
m_progressIndicator->setVisible(false);
|
||||
m_statusLabel = new QLabel();
|
||||
m_statusLabel->setVisible(false);
|
||||
m_statusLabel->setTextInteractionFlags(Qt::TextInteractionFlag::TextSelectableByMouse
|
||||
| Qt::TextInteractionFlag::TextSelectableByKeyboard);
|
||||
|
||||
// clang-format off
|
||||
Column {
|
||||
|
||||
@@ -12,6 +12,8 @@ const char COPILOT_TOGGLE[] = "Copilot.Toggle";
|
||||
const char COPILOT_ENABLE[] = "Copilot.Enable";
|
||||
const char COPILOT_DISABLE[] = "Copilot.Disable";
|
||||
const char COPILOT_REQUEST_SUGGESTION[] = "Copilot.RequestSuggestion";
|
||||
const char COPILOT_NEXT_SUGGESTION[] = "Copilot.NextSuggestion";
|
||||
const char COPILOT_PREVIOUS_SUGGESTION[] = "Copilot.PreviousSuggestion";
|
||||
|
||||
const char COPILOT_GENERAL_OPTIONS_ID[] = "Copilot.General";
|
||||
const char COPILOT_GENERAL_OPTIONS_CATEGORY[] = "ZY.Copilot";
|
||||
|
||||
@@ -41,16 +41,15 @@ public:
|
||||
});
|
||||
|
||||
// clang-format off
|
||||
helpLabel->setText(Tr::tr(R"(
|
||||
The Copilot plugin requires node.js and the Copilot neovim plugin.
|
||||
If you install the neovim plugin as described in the
|
||||
[README.md](https://github.com/github/copilot.vim),
|
||||
the plugin will find the agent.js file automatically.
|
||||
|
||||
Otherwise you need to specify the path to the
|
||||
[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)
|
||||
file from the Copilot neovim plugin.
|
||||
)", "Markdown text for the copilot instruction label"));
|
||||
helpLabel->setText(Tr::tr(
|
||||
"The Copilot plugin requires node.js and the Copilot neovim plugin. "
|
||||
"If you install the neovim plugin as described in %1, "
|
||||
"the plugin will find the agent.js file automatically.\n\n"
|
||||
"Otherwise you need to specify the path to the %2 "
|
||||
"file from the Copilot neovim plugin.",
|
||||
"Markdown text for the copilot instruction label").arg(
|
||||
"[README.md](https://github.com/github/copilot.vim)",
|
||||
"[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)"));
|
||||
|
||||
Column {
|
||||
authWidget, br,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "copilotoptionspage.h"
|
||||
#include "copilotprojectpanel.h"
|
||||
#include "copilotsettings.h"
|
||||
#include "copilotsuggestion.h"
|
||||
#include "copilottr.h"
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
@@ -20,6 +21,7 @@
|
||||
|
||||
#include <projectexplorer/projectpanelfactory.h>
|
||||
|
||||
#include <texteditor/textdocumentlayout.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <QAction>
|
||||
@@ -32,6 +34,28 @@ using namespace ProjectExplorer;
|
||||
namespace Copilot {
|
||||
namespace Internal {
|
||||
|
||||
enum Direction { Previous, Next };
|
||||
void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction direction)
|
||||
{
|
||||
QTextBlock block = editor->textCursor().block();
|
||||
if (auto *suggestion = dynamic_cast<CopilotSuggestion *>(
|
||||
TextEditor::TextDocumentLayout::suggestion(block))) {
|
||||
int index = suggestion->currentCompletion();
|
||||
if (direction == Previous)
|
||||
--index;
|
||||
else
|
||||
++index;
|
||||
if (index < 0)
|
||||
index = suggestion->completions().count() - 1;
|
||||
else if (index >= suggestion->completions().count())
|
||||
index = 0;
|
||||
suggestion->reset();
|
||||
editor->insertSuggestion(std::make_unique<CopilotSuggestion>(suggestion->completions(),
|
||||
editor->document(),
|
||||
index));
|
||||
}
|
||||
}
|
||||
|
||||
void CopilotPlugin::initialize()
|
||||
{
|
||||
CopilotSettings::instance().readSettings(ICore::settings());
|
||||
@@ -57,6 +81,30 @@ void CopilotPlugin::initialize()
|
||||
|
||||
ActionManager::registerAction(requestAction, Constants::COPILOT_REQUEST_SUGGESTION);
|
||||
|
||||
QAction *nextSuggestionAction = new QAction(this);
|
||||
nextSuggestionAction->setText(Tr::tr("Show next Copilot Suggestion"));
|
||||
nextSuggestionAction->setToolTip(Tr::tr(
|
||||
"Cycles through the received Copilot Suggestions showing the next available Suggestion."));
|
||||
|
||||
connect(nextSuggestionAction, &QAction::triggered, this, [] {
|
||||
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget())
|
||||
cycleSuggestion(editor, Next);
|
||||
});
|
||||
|
||||
ActionManager::registerAction(nextSuggestionAction, Constants::COPILOT_NEXT_SUGGESTION);
|
||||
|
||||
QAction *previousSuggestionAction = new QAction(this);
|
||||
previousSuggestionAction->setText(Tr::tr("Show previos Copilot Suggestion"));
|
||||
previousSuggestionAction->setToolTip(Tr::tr("Cycles through the received Copilot Suggestions "
|
||||
"showing the previous available Suggestion."));
|
||||
|
||||
connect(previousSuggestionAction, &QAction::triggered, this, [] {
|
||||
if (auto editor = TextEditor::TextEditorWidget::currentTextEditorWidget())
|
||||
cycleSuggestion(editor, Previous);
|
||||
});
|
||||
|
||||
ActionManager::registerAction(previousSuggestionAction, Constants::COPILOT_PREVIOUS_SUGGESTION);
|
||||
|
||||
QAction *disableAction = new QAction(this);
|
||||
disableAction->setText(Tr::tr("Disable Copilot"));
|
||||
disableAction->setToolTip(Tr::tr("Disable Copilot."));
|
||||
|
||||
@@ -190,11 +190,11 @@ LocatorMatcherTasks ActionsFilter::matchers()
|
||||
collectEntriesForCommands();
|
||||
if (storage->input().simplified().isEmpty()) {
|
||||
storage->reportOutput(m_entries);
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
|
||||
async.setConcurrentCallData(matches, *storage, m_entries);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
|
||||
return {{AsyncTask<void>(onSetup), storage}};
|
||||
|
||||
@@ -82,9 +82,9 @@ DirectoryFilter::DirectoryFilter(Id id)
|
||||
using namespace Tasking;
|
||||
const auto groupSetup = [this] {
|
||||
if (!m_directories.isEmpty())
|
||||
return TaskAction::Continue; // Async task will run
|
||||
return SetupResult::Continue; // Async task will run
|
||||
m_cache.setFilePaths({});
|
||||
return TaskAction::StopWithDone; // Group stops, skips async task
|
||||
return SetupResult::StopWithDone; // Group stops, skips async task
|
||||
};
|
||||
const auto asyncSetup = [this](Async<FilePaths> &async) {
|
||||
async.setConcurrentCallData(&refresh, m_directories, m_filters, m_exclusionFilters,
|
||||
|
||||
@@ -1503,16 +1503,16 @@ LocatorMatcherTask LocatorFileCache::matcher() const
|
||||
const auto onSetup = [storage, weak](Async<LocatorFileCachePrivate> &async) {
|
||||
auto that = weak.lock();
|
||||
if (!that) // LocatorMatcher is running after *this LocatorFileCache was destructed.
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
|
||||
if (!that->ensureValidated())
|
||||
return TaskAction::StopWithDone; // The cache is invalid and
|
||||
return SetupResult::StopWithDone; // The cache is invalid and
|
||||
// no provider is set or it returned empty generator
|
||||
that->bumpExecutionId();
|
||||
|
||||
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
|
||||
async.setConcurrentCallData(&filter, *storage, *that);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onDone = [weak](const Async<LocatorFileCachePrivate> &async) {
|
||||
auto that = weak.lock();
|
||||
|
||||
@@ -372,7 +372,7 @@ LocatorMatcherTasks JavaScriptFilter::matchers()
|
||||
|
||||
const auto onSetup = [storage, engine] {
|
||||
if (!engine)
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
if (storage->input().trimmed().isEmpty()) {
|
||||
LocatorFilterEntry entry;
|
||||
entry.displayName = Tr::tr("Reset Engine");
|
||||
@@ -385,9 +385,9 @@ LocatorMatcherTasks JavaScriptFilter::matchers()
|
||||
return AcceptResult();
|
||||
};
|
||||
storage->reportOutput({entry});
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
|
||||
const auto onJavaScriptSetup = [storage, engine](JavaScriptRequest &request) {
|
||||
|
||||
@@ -179,7 +179,7 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers()
|
||||
const Link link = Link::fromString(storage->input(), true);
|
||||
const FilePath input = link.targetFilePath;
|
||||
if (input.isEmpty())
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
|
||||
// only pass the file name part to allow searches like "somepath/*foo"
|
||||
const std::unique_ptr<MacroExpander> expander(createMacroExpander(input.fileName()));
|
||||
@@ -189,7 +189,7 @@ LocatorMatcherTasks SpotlightLocatorFilter::matchers()
|
||||
CommandLine::Raw);
|
||||
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
|
||||
async.setConcurrentCallData(matches, *storage, cmd);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
|
||||
return {{AsyncTask<void>(onSetup), storage}};
|
||||
|
||||
@@ -236,11 +236,11 @@ public:
|
||||
|
||||
const auto onCheckerSetup = [this](Async<ArchiveIssue> &async) {
|
||||
if (!m_tempDir)
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
|
||||
async.setConcurrentCallData(checkContents, m_tempDir->path());
|
||||
async.setFutureSynchronizer(PluginManager::futureSynchronizer());
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onCheckerDone = [this](const Async<ArchiveIssue> &async) {
|
||||
m_isComplete = !async.isResultAvailable();
|
||||
|
||||
@@ -310,12 +310,12 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
|
||||
|
||||
const auto setupStaged = [this, stagedFiles](Process &process) {
|
||||
if (stagedFiles.isEmpty())
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), stagedFiles));
|
||||
setupCommand(process, addConfigurationArguments(
|
||||
QStringList({"diff", "--cached", "--"}) + stagedFiles));
|
||||
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onStagedDone = [storage](const Process &process) {
|
||||
storage->m_stagedOutput = process.cleanedStdOut();
|
||||
@@ -323,12 +323,12 @@ FileListDiffController::FileListDiffController(IDocument *document, const QStrin
|
||||
|
||||
const auto setupUnstaged = [this, unstagedFiles](Process &process) {
|
||||
if (unstagedFiles.isEmpty())
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
process.setCodec(VcsBaseEditor::getCodec(workingDirectory(), unstagedFiles));
|
||||
setupCommand(process, addConfigurationArguments(
|
||||
QStringList({"diff", "--"}) + unstagedFiles));
|
||||
VcsOutputWindow::appendCommand(process.workingDirectory(), process.commandLine());
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onUnstagedDone = [storage](const Process &process) {
|
||||
storage->m_unstagedOutput = process.cleanedStdOut();
|
||||
@@ -421,8 +421,8 @@ ShowController::ShowController(IDocument *document, const QString &id)
|
||||
|
||||
const auto desciptionDetailsSetup = [storage] {
|
||||
if (!storage->m_postProcessDescription)
|
||||
return TaskAction::StopWithDone;
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::StopWithDone;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
|
||||
const auto setupBranches = [this, storage](Process &process) {
|
||||
|
||||
@@ -1698,27 +1698,35 @@ void ClientPrivate::log(const ShowMessageParams &message)
|
||||
LanguageClientValue<MessageActionItem> ClientPrivate::showMessageBox(
|
||||
const ShowMessageRequestParams &message)
|
||||
{
|
||||
auto box = new QMessageBox();
|
||||
box->setText(message.toString());
|
||||
box->setAttribute(Qt::WA_DeleteOnClose);
|
||||
QMessageBox box;
|
||||
box.setText(message.toString());
|
||||
switch (message.type()) {
|
||||
case Error: box->setIcon(QMessageBox::Critical); break;
|
||||
case Warning: box->setIcon(QMessageBox::Warning); break;
|
||||
case Info: box->setIcon(QMessageBox::Information); break;
|
||||
case Log: box->setIcon(QMessageBox::NoIcon); break;
|
||||
case Error:
|
||||
box.setIcon(QMessageBox::Critical);
|
||||
break;
|
||||
case Warning:
|
||||
box.setIcon(QMessageBox::Warning);
|
||||
break;
|
||||
case Info:
|
||||
box.setIcon(QMessageBox::Information);
|
||||
break;
|
||||
case Log:
|
||||
box.setIcon(QMessageBox::NoIcon);
|
||||
break;
|
||||
}
|
||||
|
||||
QHash<QAbstractButton *, MessageActionItem> itemForButton;
|
||||
if (const std::optional<QList<MessageActionItem>> actions = message.actions()) {
|
||||
auto button = box->addButton(QMessageBox::Close);
|
||||
connect(button, &QPushButton::clicked, box, &QMessageBox::reject);
|
||||
for (const MessageActionItem &action : *actions) {
|
||||
connect(button, &QPushButton::clicked, box, &QMessageBox::accept);
|
||||
auto button = box.addButton(action.title(), QMessageBox::ActionRole);
|
||||
connect(button, &QPushButton::clicked, &box, &QMessageBox::accept);
|
||||
itemForButton.insert(button, action);
|
||||
}
|
||||
}
|
||||
if (box->exec() == QDialog::Rejected)
|
||||
|
||||
if (box.exec() == QDialog::Rejected || itemForButton.isEmpty())
|
||||
return {};
|
||||
const MessageActionItem &item = itemForButton.value(box->clickedButton());
|
||||
const MessageActionItem &item = itemForButton.value(box.clickedButton());
|
||||
return item.isValid() ? LanguageClientValue<MessageActionItem>(item)
|
||||
: LanguageClientValue<MessageActionItem>();
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ LocatorMatcherTask locatorMatcher(Client *client, int maxResultCount,
|
||||
const auto onFilterSetup = [storage, resultStorage, client, filter](Async<void> &async) {
|
||||
const QList<SymbolInformation> results = *resultStorage;
|
||||
if (results.isEmpty())
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer());
|
||||
async.setConcurrentCallData(filterResults, *storage, client, results, filter);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
|
||||
const Group root {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
add_qtc_plugin(McuSupport
|
||||
DEPENDS Qt::Core
|
||||
DEPENDS Qt::Core QmlJS
|
||||
PLUGIN_DEPENDS Core BareMetal ProjectExplorer Debugger CMakeProjectManager QtSupport
|
||||
SOURCES
|
||||
mcukitinformation.cpp mcukitinformation.h
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "test/unittest.h"
|
||||
#endif
|
||||
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
@@ -34,9 +35,14 @@
|
||||
|
||||
#include <cmakeprojectmanager/cmakeprojectconstants.h>
|
||||
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
#include <qmljstools/qmljstoolsconstants.h>
|
||||
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/infobar.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace Core;
|
||||
@@ -119,6 +125,41 @@ void McuSupportPlugin::initialize()
|
||||
&ProjectManager::projectFinishedParsing,
|
||||
updateMCUProjectTree);
|
||||
|
||||
// Temporary fix for CodeModel/Checker race condition
|
||||
// Remove after https://bugreports.qt.io/browse/QTCREATORBUG-29269 is closed
|
||||
connect(QmlJS::ModelManagerInterface::instance(),
|
||||
&QmlJS::ModelManagerInterface::documentUpdated,
|
||||
[lasttime = QTime::currentTime()](QmlJS::Document::Ptr doc) mutable {
|
||||
// Prevent inifinite recall loop
|
||||
auto currenttime = QTime::currentTime();
|
||||
if (lasttime.msecsTo(currenttime) < 1000) {
|
||||
lasttime = currenttime;
|
||||
return;
|
||||
}
|
||||
lasttime = currenttime;
|
||||
|
||||
if (!doc)
|
||||
return;
|
||||
//Reset code model only for QtMCUs documents
|
||||
const Project *project = ProjectManager::projectForFile(doc->path());
|
||||
if (!project)
|
||||
return;
|
||||
const QList<Target *> targets = project->targets();
|
||||
bool isMcuDocument
|
||||
= std::any_of(std::begin(targets), std::end(targets), [](const Target *target) {
|
||||
if (!target || !target->kit()
|
||||
|| !target->kit()->hasValue(Constants::KIT_MCUTARGET_KITVERSION_KEY))
|
||||
return false;
|
||||
return true;
|
||||
});
|
||||
if (!isMcuDocument)
|
||||
return;
|
||||
|
||||
Core::ActionManager::command(QmlJSTools::Constants::RESET_CODEMODEL)
|
||||
->action()
|
||||
->trigger();
|
||||
});
|
||||
|
||||
dd->m_options.registerQchFiles();
|
||||
dd->m_options.registerExamples();
|
||||
ProjectExplorer::JsonWizardFactory::addWizardPath(":/mcusupport/wizards/");
|
||||
|
||||
@@ -70,6 +70,7 @@ public:
|
||||
{
|
||||
if (m_widget)
|
||||
return m_widget->displayIcon();
|
||||
QTC_ASSERT(m_kit, return {});
|
||||
return m_kit->displayIcon();
|
||||
}
|
||||
|
||||
@@ -77,6 +78,7 @@ public:
|
||||
{
|
||||
if (m_widget)
|
||||
return m_widget->displayName();
|
||||
QTC_ASSERT(m_kit, return {});
|
||||
return m_kit->displayName();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ void TerminalAspect::addToLayout(LayoutItem &parent)
|
||||
m_checkBox = createSubWidget<QCheckBox>(Tr::tr("Run in terminal"));
|
||||
m_checkBox->setChecked(m_useTerminal);
|
||||
m_checkBox->setEnabled(isEnabled());
|
||||
parent.addItems({{}, m_checkBox.data()});
|
||||
parent.addItems({empty(), m_checkBox.data()});
|
||||
connect(m_checkBox.data(), &QAbstractButton::clicked, this, [this] {
|
||||
m_userSet = true;
|
||||
m_useTerminal = m_checkBox->isChecked();
|
||||
|
||||
@@ -671,7 +671,6 @@ TaskView::TaskView()
|
||||
void TaskView::resizeColumns()
|
||||
{
|
||||
setColumnWidth(0, width() * 0.85);
|
||||
setColumnWidth(1, width() * 0.15);
|
||||
}
|
||||
|
||||
void TaskView::resizeEvent(QResizeEvent *e)
|
||||
|
||||
@@ -149,10 +149,10 @@ GroupItem QnxDeployQtLibrariesDialogPrivate::removeDirTask()
|
||||
{
|
||||
const auto setupHandler = [this](Process &process) {
|
||||
if (m_checkResult != CheckResult::RemoveDir)
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
m_deployLogWindow->appendPlainText(Tr::tr("Removing \"%1\"").arg(fullRemoteDirectory()));
|
||||
process.setCommand({m_device->filePath("rm"), {"-rf", fullRemoteDirectory()}});
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto errorHandler = [this](const Process &process) {
|
||||
QTC_ASSERT(process.exitCode() == 0, return);
|
||||
@@ -167,7 +167,7 @@ GroupItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
|
||||
const auto setupHandler = [this](FileTransfer &transfer) {
|
||||
if (m_deployableFiles.isEmpty()) {
|
||||
emitProgressMessage(Tr::tr("No files need to be uploaded."));
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
emitProgressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
|
||||
m_deployableFiles.size()));
|
||||
@@ -177,18 +177,18 @@ GroupItem QnxDeployQtLibrariesDialogPrivate::uploadTask()
|
||||
const QString message = Tr::tr("Local file \"%1\" does not exist.")
|
||||
.arg(file.localFilePath().toUserOutput());
|
||||
emitErrorMessage(message);
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
files.append({file.localFilePath(), m_device->filePath(file.remoteFilePath())});
|
||||
}
|
||||
if (files.isEmpty()) {
|
||||
emitProgressMessage(Tr::tr("No files need to be uploaded."));
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
transfer.setFilesToTransfer(files);
|
||||
QObject::connect(&transfer, &FileTransfer::progress,
|
||||
this, &QnxDeployQtLibrariesDialogPrivate::emitProgressMessage);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto errorHandler = [this](const FileTransfer &transfer) {
|
||||
emitErrorMessage(transfer.resultData().m_errorString);
|
||||
@@ -238,7 +238,7 @@ Group QnxDeployQtLibrariesDialogPrivate::deployRecipe()
|
||||
const auto setupHandler = [this] {
|
||||
if (!m_device) {
|
||||
emitErrorMessage(Tr::tr("No device configuration set."));
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
QList<DeployableFile> collected;
|
||||
for (int i = 0; i < m_deployableFiles.count(); ++i)
|
||||
@@ -247,18 +247,18 @@ Group QnxDeployQtLibrariesDialogPrivate::deployRecipe()
|
||||
QTC_CHECK(collected.size() >= m_deployableFiles.size());
|
||||
m_deployableFiles = collected;
|
||||
if (!m_deployableFiles.isEmpty())
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
|
||||
emitProgressMessage(Tr::tr("No deployment action necessary. Skipping."));
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
};
|
||||
const auto doneHandler = [this] {
|
||||
emitProgressMessage(Tr::tr("All files successfully deployed."));
|
||||
};
|
||||
const auto subGroupSetupHandler = [this] {
|
||||
if (m_checkResult == CheckResult::Abort)
|
||||
return TaskAction::StopWithError;
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::StopWithError;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const Group root {
|
||||
onGroupSetup(setupHandler),
|
||||
|
||||
@@ -19,12 +19,13 @@ using namespace ProjectExplorer;
|
||||
namespace QtSupport {
|
||||
|
||||
// opt. drive letter + filename: (2 brackets)
|
||||
#define FILE_PATTERN R"(^(?<file>(?:[A-Za-z]:)?[^:\(]+\.[^:\(]+))"
|
||||
#define FILE_PATTERN R"((?<file>(?:[A-Za-z]:)?[^:\(]+\.[^:\(]+))"
|
||||
|
||||
QtParser::QtParser() :
|
||||
m_mocRegExp(FILE_PATTERN R"([:\(](?<line>\d+)?(?::(?<column>\d+))?\)?:\s(?<level>[Ww]arning|[Ee]rror|[Nn]ote):\s(?<description>.+?)$)"),
|
||||
m_uicRegExp(FILE_PATTERN R"(: Warning:\s(?<msg>.+?)$)"),
|
||||
m_translationRegExp(R"(^(?<level>[Ww]arning|[Ee]rror):\s+(?<description>.*?) in '(?<file>.*?)'$)")
|
||||
m_mocRegExp("^" FILE_PATTERN R"([:\(](?<line>\d+)?(?::(?<column>\d+))?\)?:\s(?<level>[Ww]arning|[Ee]rror|[Nn]ote):\s(?<description>.+?)$)"),
|
||||
m_uicRegExp("^" FILE_PATTERN R"(: Warning:\s(?<msg>.+?)$)"),
|
||||
m_translationRegExp(R"(^(?<level>[Ww]arning|[Ee]rror):\s+(?<description>.*?) in '(?<file>.*?)'$)"),
|
||||
m_qmlToolsRegExp(R"(^(?<level>Warning|Error):\s*)" FILE_PATTERN R"([:\(](?<line>\d+)?(?::(?<column>\d+))?\)?:\s(?<description>.+?)$)")
|
||||
{
|
||||
setObjectName(QLatin1String("QtParser"));
|
||||
}
|
||||
@@ -89,19 +90,22 @@ Utils::OutputLineParser::Result QtParser::handleLine(const QString &line, Utils:
|
||||
scheduleTask(task, 1);
|
||||
return {Status::Done, linkSpecs};
|
||||
}
|
||||
|
||||
if (lne.startsWith(QLatin1String("Error:"))) {
|
||||
constexpr int matchLength = 6;
|
||||
CompileTask task(Task::TaskType::Error, line.mid(matchLength).trimmed());
|
||||
match = m_qmlToolsRegExp.match(line);
|
||||
if (match.hasMatch()) {
|
||||
const Task::TaskType type = match.captured("level") == "Error" ? Task::Error
|
||||
: Task::Warning;
|
||||
const Utils::FilePath file
|
||||
= absoluteFilePath(Utils::FilePath::fromUserInput(match.captured("file")));
|
||||
bool ok;
|
||||
int lineno = match.captured("line").toInt(&ok);
|
||||
if (!ok)
|
||||
lineno = -1;
|
||||
LinkSpecs linkSpecs;
|
||||
addLinkSpecForAbsoluteFilePath(linkSpecs, file, lineno, match, "file");
|
||||
CompileTask task(type, match.captured("description"), file, lineno,
|
||||
match.captured("column").toInt());
|
||||
scheduleTask(task, 1);
|
||||
return Status::Done;
|
||||
}
|
||||
|
||||
if (lne.startsWith(QLatin1String("Warning:"))) {
|
||||
constexpr int matchLength = 8;
|
||||
CompileTask task(Task::TaskType::Warning, line.mid(matchLength).trimmed());
|
||||
scheduleTask(task, 1);
|
||||
return Status::Done;
|
||||
return {Status::Done, linkSpecs};
|
||||
}
|
||||
|
||||
return Status::NotHandled;
|
||||
@@ -220,14 +224,21 @@ void QtSupportPlugin::testQtOutputParser_data()
|
||||
QLatin1String("dropping duplicate messages"),
|
||||
Utils::FilePath::fromUserInput(QLatin1String("/some/place/qtcreator_fr.qm")), -1))
|
||||
<< QString();
|
||||
QTest::newRow("qmlsc warning") // QTCREATORBUG-28720
|
||||
<< QString::fromUtf8("Warning: Main.qml:4:1: Warnings occurred while importing module "
|
||||
QTest::newRow("qmlsc/qmllint warning") // QTCREATORBUG-28720
|
||||
<< QString::fromLatin1("Warning: Main.qml:4:1: Warnings occurred while importing module "
|
||||
"\"QtQuick.Controls\": [import]\"")
|
||||
<< OutputParserTester::STDERR << QString() << QString()
|
||||
<< (Tasks() << CompileTask(Task::Warning,
|
||||
QString::fromUtf8(
|
||||
"Main.qml:4:1: Warnings occurred while importing module "
|
||||
"\"QtQuick.Controls\": [import]\"")))
|
||||
"Warnings occurred while importing module \"QtQuick.Controls\": [import]\"",
|
||||
Utils::FilePath::fromUserInput("Main.qml"), 4, 1))
|
||||
<< QString();
|
||||
QTest::newRow("qmlsc/qmllint error") // QTCREATORBUG-28720
|
||||
<< QString::fromLatin1("Error: E:/foo/PerfProfilerFlameGraphView.qml:10:5: "
|
||||
"Could not compile binding for model: Cannot resolve property type for binding on model")
|
||||
<< OutputParserTester::STDERR << QString() << QString()
|
||||
<< (Tasks() << CompileTask(Task::Error,
|
||||
"Could not compile binding for model: Cannot resolve property type for binding on model",
|
||||
Utils::FilePath::fromUserInput("E:/foo/PerfProfilerFlameGraphView.qml"), 10, 5))
|
||||
<< QString();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ private:
|
||||
QRegularExpression m_mocRegExp;
|
||||
QRegularExpression m_uicRegExp;
|
||||
QRegularExpression m_translationRegExp;
|
||||
const QRegularExpression m_qmlToolsRegExp;
|
||||
};
|
||||
|
||||
} // namespace QtSupport
|
||||
|
||||
@@ -194,7 +194,7 @@ GroupItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> &
|
||||
const auto setupHandler = [this, storage](FileTransfer &transfer) {
|
||||
if (storage->filesToUpload.isEmpty()) {
|
||||
addProgressMessage(Tr::tr("No files need to be uploaded."));
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
addProgressMessage(Tr::tr("%n file(s) need to be uploaded.", "",
|
||||
storage->filesToUpload.size()));
|
||||
@@ -208,19 +208,19 @@ GroupItem GenericDirectUploadStep::uploadTask(const TreeStorage<UploadStorage> &
|
||||
continue;
|
||||
}
|
||||
addErrorMessage(message);
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}
|
||||
files.append({file.localFilePath(),
|
||||
deviceConfiguration()->filePath(file.remoteFilePath())});
|
||||
}
|
||||
if (files.isEmpty()) {
|
||||
addProgressMessage(Tr::tr("No files need to be uploaded."));
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
}
|
||||
transfer.setFilesToTransfer(files);
|
||||
QObject::connect(&transfer, &FileTransfer::progress,
|
||||
this, &GenericDirectUploadStep::addProgressMessage);
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto errorHandler = [this](const FileTransfer &transfer) {
|
||||
addErrorMessage(transfer.resultData().m_errorString);
|
||||
|
||||
@@ -167,13 +167,13 @@ SubversionDiffEditorController::SubversionDiffEditorController(IDocument *docume
|
||||
|
||||
const auto setupDescription = [this](Process &process) {
|
||||
if (m_changeNumber == 0)
|
||||
return TaskAction::StopWithDone;
|
||||
return SetupResult::StopWithDone;
|
||||
setupCommand(process, {"log", "-r", QString::number(m_changeNumber)});
|
||||
CommandLine command = process.commandLine();
|
||||
command << SubversionClient::AddAuthOptions();
|
||||
process.setCommand(command);
|
||||
setDescription(Tr::tr("Waiting for data..."));
|
||||
return TaskAction::Continue;
|
||||
return SetupResult::Continue;
|
||||
};
|
||||
const auto onDescriptionDone = [this](const Process &process) {
|
||||
setDescription(process.cleanedStdOut());
|
||||
|
||||
@@ -227,7 +227,7 @@ void tst_Tasking::testTree_data()
|
||||
};
|
||||
};
|
||||
|
||||
const auto setupDynamicTask = [storage](int taskId, TaskAction action) {
|
||||
const auto setupDynamicTask = [storage](int taskId, SetupResult action) {
|
||||
return [storage, taskId, action](TaskObject &) {
|
||||
storage->m_log.append({taskId, Handler::Setup});
|
||||
return action;
|
||||
@@ -274,7 +274,7 @@ void tst_Tasking::testTree_data()
|
||||
};
|
||||
|
||||
const auto createDynamicTask = [storage, setupDynamicTask, setupDone, setupError](
|
||||
int taskId, TaskAction action) {
|
||||
int taskId, SetupResult action) {
|
||||
return TestTask(setupDynamicTask(taskId, action), setupDone(taskId), setupError(taskId));
|
||||
};
|
||||
|
||||
@@ -302,35 +302,74 @@ void tst_Tasking::testTree_data()
|
||||
};
|
||||
const Group root2 {
|
||||
Storage(storage),
|
||||
onGroupSetup([] { return TaskAction::Continue; }),
|
||||
onGroupSetup([] { return SetupResult::Continue; }),
|
||||
groupDone(0),
|
||||
groupError(0)
|
||||
};
|
||||
const Group root3 {
|
||||
Storage(storage),
|
||||
onGroupSetup([] { return TaskAction::StopWithDone; }),
|
||||
onGroupSetup([] { return SetupResult::StopWithDone; }),
|
||||
groupDone(0),
|
||||
groupError(0)
|
||||
};
|
||||
const Group root4 {
|
||||
Storage(storage),
|
||||
onGroupSetup([] { return TaskAction::StopWithError; }),
|
||||
onGroupSetup([] { return SetupResult::StopWithError; }),
|
||||
groupDone(0),
|
||||
groupError(0)
|
||||
};
|
||||
|
||||
const Log logDone {{0, Handler::GroupDone}};
|
||||
const Log logError {{0, Handler::GroupError}};
|
||||
|
||||
QTest::newRow("Empty") << TestData{storage, root1, logDone, 0, OnDone::Success};
|
||||
QTest::newRow("EmptyContinue") << TestData{storage, root2, logDone, 0, OnDone::Success};
|
||||
QTest::newRow("EmptyDone") << TestData{storage, root3, logDone, 0, OnDone::Success};
|
||||
QTest::newRow("EmptyError") << TestData{storage, root4, logError, 0, OnDone::Failure};
|
||||
}
|
||||
|
||||
{
|
||||
const auto setupGroup = [=](SetupResult setupResult, WorkflowPolicy policy) {
|
||||
return Group {
|
||||
Storage(storage),
|
||||
workflowPolicy(policy),
|
||||
onGroupSetup([setupResult] { return setupResult; }),
|
||||
groupDone(0),
|
||||
groupError(0)
|
||||
};
|
||||
};
|
||||
|
||||
const auto doneData = [storage, setupGroup](WorkflowPolicy policy) {
|
||||
return TestData{storage, setupGroup(SetupResult::StopWithDone, policy),
|
||||
Log{{0, Handler::GroupDone}}, 0, OnDone::Success};
|
||||
};
|
||||
const auto errorData = [storage, setupGroup](WorkflowPolicy policy) {
|
||||
return TestData{storage, setupGroup(SetupResult::StopWithError, policy),
|
||||
Log{{0, Handler::GroupError}}, 0, OnDone::Failure};
|
||||
};
|
||||
|
||||
QTest::newRow("DoneAndStopOnError") << doneData(WorkflowPolicy::StopOnError);
|
||||
QTest::newRow("DoneAndContinueOnError") << doneData(WorkflowPolicy::ContinueOnError);
|
||||
QTest::newRow("DoneAndStopOnDone") << doneData(WorkflowPolicy::StopOnDone);
|
||||
QTest::newRow("DoneAndContinueOnDone") << doneData(WorkflowPolicy::ContinueOnDone);
|
||||
QTest::newRow("DoneAndStopOnFinished") << doneData(WorkflowPolicy::StopOnFinished);
|
||||
QTest::newRow("DoneAndFinishAllAndDone") << doneData(WorkflowPolicy::FinishAllAndDone);
|
||||
QTest::newRow("DoneAndFinishAllAndError") << doneData(WorkflowPolicy::FinishAllAndError);
|
||||
|
||||
QTest::newRow("ErrorAndStopOnError") << errorData(WorkflowPolicy::StopOnError);
|
||||
QTest::newRow("ErrorAndContinueOnError") << errorData(WorkflowPolicy::ContinueOnError);
|
||||
QTest::newRow("ErrorAndStopOnDone") << errorData(WorkflowPolicy::StopOnDone);
|
||||
QTest::newRow("ErrorAndContinueOnDone") << errorData(WorkflowPolicy::ContinueOnDone);
|
||||
QTest::newRow("ErrorAndStopOnFinished") << errorData(WorkflowPolicy::StopOnFinished);
|
||||
QTest::newRow("ErrorAndFinishAllAndDone") << errorData(WorkflowPolicy::FinishAllAndDone);
|
||||
QTest::newRow("ErrorAndFinishAllAndError") << errorData(WorkflowPolicy::FinishAllAndError);
|
||||
}
|
||||
|
||||
{
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
createDynamicTask(1, TaskAction::StopWithDone),
|
||||
createDynamicTask(2, TaskAction::StopWithDone)
|
||||
createDynamicTask(1, SetupResult::StopWithDone),
|
||||
createDynamicTask(2, SetupResult::StopWithDone)
|
||||
};
|
||||
const Log log {{1, Handler::Setup}, {2, Handler::Setup}};
|
||||
QTest::newRow("DynamicTaskDone") << TestData{storage, root, log, 2, OnDone::Success};
|
||||
@@ -339,8 +378,8 @@ void tst_Tasking::testTree_data()
|
||||
{
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
createDynamicTask(1, TaskAction::StopWithError),
|
||||
createDynamicTask(2, TaskAction::StopWithError)
|
||||
createDynamicTask(1, SetupResult::StopWithError),
|
||||
createDynamicTask(2, SetupResult::StopWithError)
|
||||
};
|
||||
const Log log {{1, Handler::Setup}};
|
||||
QTest::newRow("DynamicTaskError") << TestData{storage, root, log, 2, OnDone::Failure};
|
||||
@@ -349,10 +388,10 @@ void tst_Tasking::testTree_data()
|
||||
{
|
||||
const Group root {
|
||||
Storage(storage),
|
||||
createDynamicTask(1, TaskAction::Continue),
|
||||
createDynamicTask(2, TaskAction::Continue),
|
||||
createDynamicTask(3, TaskAction::StopWithError),
|
||||
createDynamicTask(4, TaskAction::Continue)
|
||||
createDynamicTask(1, SetupResult::Continue),
|
||||
createDynamicTask(2, SetupResult::Continue),
|
||||
createDynamicTask(3, SetupResult::StopWithError),
|
||||
createDynamicTask(4, SetupResult::Continue)
|
||||
};
|
||||
const Log log {
|
||||
{1, Handler::Setup},
|
||||
@@ -368,10 +407,10 @@ void tst_Tasking::testTree_data()
|
||||
const Group root {
|
||||
parallel,
|
||||
Storage(storage),
|
||||
createDynamicTask(1, TaskAction::Continue),
|
||||
createDynamicTask(2, TaskAction::Continue),
|
||||
createDynamicTask(3, TaskAction::StopWithError),
|
||||
createDynamicTask(4, TaskAction::Continue)
|
||||
createDynamicTask(1, SetupResult::Continue),
|
||||
createDynamicTask(2, SetupResult::Continue),
|
||||
createDynamicTask(3, SetupResult::StopWithError),
|
||||
createDynamicTask(4, SetupResult::Continue)
|
||||
};
|
||||
const Log log {
|
||||
{1, Handler::Setup},
|
||||
@@ -387,12 +426,12 @@ void tst_Tasking::testTree_data()
|
||||
const Group root {
|
||||
parallel,
|
||||
Storage(storage),
|
||||
createDynamicTask(1, TaskAction::Continue),
|
||||
createDynamicTask(2, TaskAction::Continue),
|
||||
createDynamicTask(1, SetupResult::Continue),
|
||||
createDynamicTask(2, SetupResult::Continue),
|
||||
Group {
|
||||
createDynamicTask(3, TaskAction::StopWithError)
|
||||
createDynamicTask(3, SetupResult::StopWithError)
|
||||
},
|
||||
createDynamicTask(4, TaskAction::Continue)
|
||||
createDynamicTask(4, SetupResult::Continue)
|
||||
};
|
||||
const Log log {
|
||||
{1, Handler::Setup},
|
||||
@@ -408,16 +447,16 @@ void tst_Tasking::testTree_data()
|
||||
const Group root {
|
||||
parallel,
|
||||
Storage(storage),
|
||||
createDynamicTask(1, TaskAction::Continue),
|
||||
createDynamicTask(2, TaskAction::Continue),
|
||||
createDynamicTask(1, SetupResult::Continue),
|
||||
createDynamicTask(2, SetupResult::Continue),
|
||||
Group {
|
||||
onGroupSetup([storage] {
|
||||
storage->m_log.append({0, Handler::GroupSetup});
|
||||
return TaskAction::StopWithError;
|
||||
return SetupResult::StopWithError;
|
||||
}),
|
||||
createDynamicTask(3, TaskAction::Continue)
|
||||
createDynamicTask(3, SetupResult::Continue)
|
||||
},
|
||||
createDynamicTask(4, TaskAction::Continue)
|
||||
createDynamicTask(4, SetupResult::Continue)
|
||||
};
|
||||
const Log log {
|
||||
{1, Handler::Setup},
|
||||
@@ -1280,14 +1319,14 @@ void tst_Tasking::testTree_data()
|
||||
|
||||
{
|
||||
const auto createRoot = [storage, createSuccessTask, groupDone, groupError](
|
||||
TaskAction taskAction) {
|
||||
SetupResult setupResult) {
|
||||
return Group {
|
||||
Storage(storage),
|
||||
Group {
|
||||
createSuccessTask(1)
|
||||
},
|
||||
Group {
|
||||
onGroupSetup([=] { return taskAction; }),
|
||||
onGroupSetup([=] { return setupResult; }),
|
||||
createSuccessTask(2),
|
||||
createSuccessTask(3),
|
||||
createSuccessTask(4)
|
||||
@@ -1297,7 +1336,7 @@ void tst_Tasking::testTree_data()
|
||||
};
|
||||
};
|
||||
|
||||
const Group root1 = createRoot(TaskAction::StopWithDone);
|
||||
const Group root1 = createRoot(SetupResult::StopWithDone);
|
||||
const Log log1 {
|
||||
{1, Handler::Setup},
|
||||
{1, Handler::Done},
|
||||
@@ -1305,7 +1344,7 @@ void tst_Tasking::testTree_data()
|
||||
};
|
||||
QTest::newRow("DynamicSetupDone") << TestData{storage, root1, log1, 4, OnDone::Success};
|
||||
|
||||
const Group root2 = createRoot(TaskAction::StopWithError);
|
||||
const Group root2 = createRoot(SetupResult::StopWithError);
|
||||
const Log log2 {
|
||||
{1, Handler::Setup},
|
||||
{1, Handler::Done},
|
||||
@@ -1313,7 +1352,7 @@ void tst_Tasking::testTree_data()
|
||||
};
|
||||
QTest::newRow("DynamicSetupError") << TestData{storage, root2, log2, 4, OnDone::Failure};
|
||||
|
||||
const Group root3 = createRoot(TaskAction::Continue);
|
||||
const Group root3 = createRoot(SetupResult::Continue);
|
||||
const Log log3 {
|
||||
{1, Handler::Setup},
|
||||
{1, Handler::Done},
|
||||
@@ -1380,7 +1419,7 @@ void tst_Tasking::testTree_data()
|
||||
},
|
||||
Group {
|
||||
groupSetup(3),
|
||||
createDynamicTask(3, TaskAction::StopWithDone)
|
||||
createDynamicTask(3, SetupResult::StopWithDone)
|
||||
},
|
||||
Group {
|
||||
groupSetup(4),
|
||||
@@ -1424,7 +1463,7 @@ void tst_Tasking::testTree_data()
|
||||
},
|
||||
Group {
|
||||
groupSetup(3),
|
||||
createDynamicTask(3, TaskAction::StopWithError)
|
||||
createDynamicTask(3, SetupResult::StopWithError)
|
||||
},
|
||||
Group {
|
||||
groupSetup(4),
|
||||
@@ -1463,7 +1502,7 @@ void tst_Tasking::testTree_data()
|
||||
},
|
||||
Group {
|
||||
groupSetup(3),
|
||||
createDynamicTask(3, TaskAction::StopWithError)
|
||||
createDynamicTask(3, SetupResult::StopWithError)
|
||||
},
|
||||
Group {
|
||||
groupSetup(4),
|
||||
@@ -1508,7 +1547,7 @@ void tst_Tasking::testTree_data()
|
||||
},
|
||||
Group {
|
||||
groupSetup(3),
|
||||
createDynamicTask(3, TaskAction::StopWithError)
|
||||
createDynamicTask(3, SetupResult::StopWithError)
|
||||
},
|
||||
Group {
|
||||
groupSetup(4),
|
||||
@@ -1605,7 +1644,7 @@ void tst_Tasking::testTree_data()
|
||||
},
|
||||
Group {
|
||||
groupSetup(3),
|
||||
Group { createDynamicTask(3, TaskAction::StopWithDone) }
|
||||
Group { createDynamicTask(3, SetupResult::StopWithDone) }
|
||||
},
|
||||
Group {
|
||||
groupSetup(4),
|
||||
@@ -1650,7 +1689,7 @@ void tst_Tasking::testTree_data()
|
||||
},
|
||||
Group {
|
||||
groupSetup(3),
|
||||
Group { createDynamicTask(3, TaskAction::StopWithError) }
|
||||
Group { createDynamicTask(3, SetupResult::StopWithError) }
|
||||
},
|
||||
Group {
|
||||
groupSetup(4),
|
||||
|
||||
@@ -196,8 +196,7 @@ private slots:
|
||||
|
||||
QTest::newRow("simple") << "command %{hello}"
|
||||
<< "command"
|
||||
<< (HostOsInfo::isWindowsHost() ? "\"hello world\""
|
||||
: "'hello world'");
|
||||
<< "hello world";
|
||||
|
||||
QTest::newRow("simple-quoted")
|
||||
<< "command \"%{hello}\""
|
||||
@@ -226,15 +225,11 @@ private slots:
|
||||
CommandLine cmd = CommandLine::fromUserInput(input, &expander);
|
||||
QCOMPARE(cmd.executable().toUserOutput(), expectedExecutable);
|
||||
|
||||
// TODO: Fix (macro) escaping on windows
|
||||
if (HostOsInfo::isWindowsHost())
|
||||
QEXPECT_FAIL("simple", "Windows does not correctly quote macro arguments", Continue);
|
||||
if (HostOsInfo::isWindowsHost())
|
||||
QEXPECT_FAIL("simple-quoted", "Windows removes quotes from macro arguments", Continue);
|
||||
if (HostOsInfo::isWindowsHost())
|
||||
if (HostOsInfo::isWindowsHost()) {
|
||||
QEXPECT_FAIL("convert-to-quote-win",
|
||||
"Windows should convert single to double quotes",
|
||||
Continue);
|
||||
}
|
||||
|
||||
QCOMPARE(cmd.arguments(), expectedArguments);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ add_subdirectory(proparser)
|
||||
# add_subdirectory(qt4projectmanager)
|
||||
# add_subdirectory(search)
|
||||
add_subdirectory(shootout)
|
||||
add_subdirectory(spinner)
|
||||
add_subdirectory(subdirfileiterator)
|
||||
add_subdirectory(tasking)
|
||||
add_subdirectory(widgets)
|
||||
|
||||
23
tests/manual/docker/Dockerfile-qt-6-fedora-37-build
Normal file
23
tests/manual/docker/Dockerfile-qt-6-fedora-37-build
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
FROM fedora:37
|
||||
|
||||
RUN yum update
|
||||
|
||||
RUN yum -y install \
|
||||
yum-utils \
|
||||
qt-creator \
|
||||
gdb \
|
||||
git \
|
||||
vim \
|
||||
cmake \
|
||||
qt \
|
||||
qt-devel \
|
||||
qt6-qtbase-devel \
|
||||
qt6-qtdeclarative-devel \
|
||||
qt6-qtquicktimeline-devel \
|
||||
qt6-qtquick3d-devel \
|
||||
ninja-build \
|
||||
valgrind \
|
||||
xclock
|
||||
|
||||
# && rm -rf /var/lib/apt/lists/*
|
||||
@@ -3,3 +3,4 @@
|
||||
docker build -t qt-5-ubuntu-20.04-build -f Dockerfile-qt-5-ubuntu-20.04-build .
|
||||
docker build -t qt-5-ubuntu-20.04-run -f Dockerfile-qt-5-ubuntu-20.04-run .
|
||||
docker build -t qt-5-ubuntu-20.04-clang-lldb-build -f Dockerfile-qt-5-ubuntu-20.04-clang-lldb-build .
|
||||
docker build -t qt-6-fedora-37-build -f Dockerfile-qt-6-fedora-37-build .
|
||||
|
||||
@@ -13,6 +13,7 @@ Project {
|
||||
"pluginview/pluginview.qbs",
|
||||
"proparser/testreader.qbs",
|
||||
"shootout/shootout.qbs",
|
||||
"spinner/spinner.qbs",
|
||||
"subdirfileiterator/subdirfileiterator.qbs",
|
||||
"tasking/demo/demo.qbs",
|
||||
"tasking/imagescaling/imagescaling.qbs",
|
||||
|
||||
6
tests/manual/spinner/CMakeLists.txt
Normal file
6
tests/manual/spinner/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
add_qtc_test(tst_spinner
|
||||
MANUALTEST
|
||||
DEPENDS Spinner Qt::Widgets
|
||||
SOURCES
|
||||
main.cpp
|
||||
)
|
||||
167
tests/manual/spinner/main.cpp
Normal file
167
tests/manual/spinner/main.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
|
||||
|
||||
#include <spinner/spinner.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBoxLayout>
|
||||
#include <QCalendarWidget>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QMetaEnum>
|
||||
#include <QTextEdit>
|
||||
#include <QToolButton>
|
||||
|
||||
using namespace SpinnerSolution;
|
||||
|
||||
enum class State {
|
||||
NotRunning,
|
||||
Running
|
||||
};
|
||||
|
||||
static QString colorButtonStyleSheet(const QColor &bgColor)
|
||||
{
|
||||
QString rc("border-width: 2px; border-radius: 2px; border-color: black; ");
|
||||
rc += bgColor.isValid() ? "border-style: solid; background:" + bgColor.name() + ";"
|
||||
: QString("border-style: dotted;");
|
||||
return rc;
|
||||
}
|
||||
|
||||
static QColor stateToColor(State state)
|
||||
{
|
||||
switch (state) {
|
||||
case State::NotRunning: return Qt::gray;
|
||||
case State::Running: return Qt::yellow;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
class StateIndicator : public QLabel
|
||||
{
|
||||
public:
|
||||
StateIndicator(QWidget *parent = nullptr)
|
||||
: QLabel(parent)
|
||||
{
|
||||
setFixedSize(30, 30);
|
||||
m_spinner = new Spinner(SpinnerSize::Small, this);
|
||||
m_spinner->hide();
|
||||
updateState();
|
||||
}
|
||||
|
||||
void setState(State state)
|
||||
{
|
||||
if (m_state == state)
|
||||
return;
|
||||
m_state = state;
|
||||
updateState();
|
||||
}
|
||||
|
||||
private:
|
||||
void updateState()
|
||||
{
|
||||
setStyleSheet(colorButtonStyleSheet(stateToColor(m_state)));
|
||||
if (m_state == State::Running)
|
||||
m_spinner->show();
|
||||
else
|
||||
m_spinner->hide();
|
||||
}
|
||||
State m_state = State::NotRunning;
|
||||
Spinner *m_spinner = nullptr;
|
||||
};
|
||||
|
||||
class StateWidget : public QWidget
|
||||
{
|
||||
public:
|
||||
StateWidget() : m_stateIndicator(new StateIndicator(this)) {
|
||||
QBoxLayout *layout = new QHBoxLayout(this);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(m_stateIndicator);
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
}
|
||||
void setState(State state) { m_stateIndicator->setState(state); }
|
||||
|
||||
protected:
|
||||
StateIndicator *m_stateIndicator = nullptr;
|
||||
};
|
||||
|
||||
QGroupBox *createGroupBox(SpinnerSize size, QWidget *widget)
|
||||
{
|
||||
const QMetaEnum spinnerSize = QMetaEnum::fromType<SpinnerSize>();
|
||||
QGroupBox *groupBox = new QGroupBox(spinnerSize.valueToKey(int(size)));
|
||||
|
||||
StateWidget *stateWidget = new StateWidget;
|
||||
QToolButton *startButton = new QToolButton;
|
||||
startButton->setText("Start");
|
||||
QToolButton *stopButton = new QToolButton;
|
||||
stopButton->setText("Stop");
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(groupBox);
|
||||
QHBoxLayout *topLayout = new QHBoxLayout();
|
||||
topLayout->addWidget(stateWidget);
|
||||
topLayout->addWidget(startButton);
|
||||
topLayout->addWidget(stopButton);
|
||||
topLayout->addStretch();
|
||||
mainLayout->addLayout(topLayout);
|
||||
mainLayout->addWidget(widget);
|
||||
|
||||
Spinner *spinner = new Spinner(size, widget);
|
||||
spinner->hide(); // TODO: make the default hidden?
|
||||
|
||||
QObject::connect(startButton, &QAbstractButton::clicked, groupBox, [=] {
|
||||
stateWidget->setState(State::Running);
|
||||
spinner->show();
|
||||
widget->setEnabled(false);
|
||||
});
|
||||
QObject::connect(stopButton, &QAbstractButton::clicked, groupBox, [=] {
|
||||
stateWidget->setState(State::NotRunning);
|
||||
spinner->hide();
|
||||
widget->setEnabled(true);
|
||||
});
|
||||
return groupBox;
|
||||
}
|
||||
|
||||
static QWidget *hr()
|
||||
{
|
||||
auto frame = new QFrame;
|
||||
frame->setFrameShape(QFrame::HLine);
|
||||
frame->setFrameShadow(QFrame::Sunken);
|
||||
return frame;
|
||||
}
|
||||
|
||||
static QString pangram(int count)
|
||||
{
|
||||
return QStringList(count, "The quick brown fox jumps over the lazy dog.").join(' ');
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QWidget mainWidget;
|
||||
mainWidget.setWindowTitle("Spinner Example");
|
||||
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout(&mainWidget);
|
||||
|
||||
QLabel *smallWidget = new QLabel;
|
||||
smallWidget->setFixedWidth(30);
|
||||
QGroupBox *smallGroupBox = createGroupBox(SpinnerSize::Small, smallWidget);
|
||||
mainLayout->addWidget(smallGroupBox);
|
||||
|
||||
mainLayout->addWidget(hr());
|
||||
|
||||
QCalendarWidget *mediumWidget = new QCalendarWidget;
|
||||
mediumWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
QGroupBox *mediumGroupBox = createGroupBox(SpinnerSize::Medium, mediumWidget);
|
||||
mainLayout->addWidget(mediumGroupBox);
|
||||
|
||||
mainLayout->addWidget(hr());
|
||||
|
||||
QTextEdit *largeWidget = new QTextEdit;
|
||||
largeWidget->setText(pangram(25));
|
||||
QGroupBox *largeGroupBox = createGroupBox(SpinnerSize::Large, largeWidget);
|
||||
mainLayout->addWidget(largeGroupBox);
|
||||
|
||||
mainWidget.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
13
tests/manual/spinner/spinner.qbs
Normal file
13
tests/manual/spinner/spinner.qbs
Normal file
@@ -0,0 +1,13 @@
|
||||
import qbs.FileInfo
|
||||
|
||||
QtcManualtest {
|
||||
name: "Spinner example"
|
||||
type: ["application"]
|
||||
|
||||
Depends { name: "Qt"; submodules: ["widgets"] }
|
||||
Depends { name: "Spinner" }
|
||||
|
||||
files: [
|
||||
"main.cpp",
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user