Merge remote-tracking branch 'origin/14.0'

Change-Id: I4260181d32ed514e1a912188dbf1040857a83f54
This commit is contained in:
Eike Ziller
2024-06-06 09:02:20 +02:00
167 changed files with 2381 additions and 1380 deletions

94
dist/changelog/changes-13.0.2.md vendored Normal file
View File

@@ -0,0 +1,94 @@
Qt Creator 13.0.2
=================
Qt Creator version 13.0.2 contains bug fixes.
The most important changes are listed in this document. For a complete list of
changes, see the Git log for the Qt Creator sources that you can check out from
the public Git repository. For example:
git clone git://code.qt.io/qt-creator/qt-creator.git
git log --cherry-pick --pretty=oneline v13.0.1..v13.0.2
General
-------
* Fixed that the `-client` option could start a new Qt Creator instance instead
of using a running one (which affects for example version control operations)
([QTCREATORBUG-30624](https://bugreports.qt.io/browse/QTCREATORBUG-30624))
Editing
-------
* Fixed that closing files with the tool button didn't add an entry to the
navigation history
### Widget Designer
* Fixed that `Use Qt module name in #include-directive` used Qt 4 module names
([QTCREATORBUG-30751](https://bugreports.qt.io/browse/QTCREATORBUG-30751))
### Copilot
* Adapted to changes in the Copilot neovim plugin
Projects
--------
### CMake
* Fixed the environment macro expansion for Presets
### Meson
* Fixed a crash when selecting kits
([QTCREATORBUG-30698](https://bugreports.qt.io/browse/QTCREATORBUG-30698))
Terminal
--------
* Fixed the handling of environment variables with an equal sign `=` in the
value
([QTCREATORBUG-30844](https://bugreports.qt.io/browse/QTCREATORBUG-30844))
Version Control Systems
-----------------------
### Git
* Fixed a crash in `Instant Blame` when reloading externally modified files
([QTCREATORBUG-30824](https://bugreports.qt.io/browse/QTCREATORBUG-30824))
Platforms
---------
### Windows
* Fixed missing paths with `Add build library search path to PATH` for CMake
projects
([QTCREATORBUG-30556](https://bugreports.qt.io/browse/QTCREATORBUG-30556),
[QTCREATORBUG-30827](https://bugreports.qt.io/browse/QTCREATORBUG-30827),
[QTCREATORBUG-30932](https://bugreports.qt.io/browse/QTCREATORBUG-30932))
### Android
* Fixed a crash when re-connecting devices
([QTCREATORBUG-30645](https://bugreports.qt.io/browse/QTCREATORBUG-30645),
[QTCREATORBUG-30770](https://bugreports.qt.io/browse/QTCREATORBUG-30770))
### Remote Linux
* Fixed passing more than one argument to `rsync`
([QTCREATORBUG-30795](https://bugreports.qt.io/browse/QTCREATORBUG-30795))
Credits for these changes go to:
--------------------------------
Alessandro Portale
Christian Kandeler
Christian Stenger
Cristian Adam
David Schulz
Eike Ziller
Leena Miettinen
Marcus Tillmanns
Robert Löhning

View File

@@ -140,13 +140,19 @@
To connect to an Axivion dashboard server:
\list 1
\li Select \uicontrol Edit to create a connection to the Axivion
\list
\li Select \uicontrol Add to add a new connection to an Axivion
dashboard server.
\li Select \uicontrol Edit to modify an existing connection to
an Axivion dashboard server.
\image qtcreator-edit-dashboard-configuration.webp {Edit Dashboard Configuration dialog}
\list
\li In \uicontrol {Dashboard URL}, enter the URL of the server.
\li In \uicontrol Username, enter the username to access the server.
\endlist
\li Select \uicontrol Remove to remove the current selected connection
to an Axivion dashboard server.
\endlist
The first time you access the server, you must enter the password that
matches the username. It is stored safely in your keychain that is provided
@@ -173,6 +179,8 @@
\li Go to \uicontrol Projects > \uicontrol {Project Settings} >
\uicontrol Axivion.
\image qtcreator-preferences-axivion-project.webp {Axivion settings in Project Settings}
\li From \uicontrol {Dashboard} select one of the configured Axivion
dashboard configurations.
\li Select \uicontrol {Fetch Projects} to list projects from Axivion.
\li Select a project, and then select \uicontrol {Link Project} to link
to it.

View File

@@ -113,7 +113,7 @@
LTTng is a tracing toolkit for Linux that you can apply on embedded Linux
systems to find out how to optimize the startup time of an application.
Since Qt 5.13, Qt has a set of kernel trace points and a tracing
Qt has a set of kernel trace points and a tracing
subsystem for custom user space trace points.
\section2 Configuring the Kernel

View File

@@ -131,22 +131,24 @@
C:\Users\Username\AppData\Local\QtProject\qtcreator\android\sdk_definitions.json
\endcode
For example, the SDK configuration file sets the NDK version 19.2.5345600
for use with Qt 5.12.0 to 5.12.5 and Qt 5.13.0 to 5.13.1:
For example, the SDK configuration file sets the NDK version 22.1.7171670
for use with Qt 6.3, Qt 6.2, and Qt 5.15.9 to 5.15.20:
\badcode
"specific_qt_versions": [
{
"versions": ["5.12.[0-5]", "5.13.[0-1]"],
"sdk_essential_packages": ["build-tools;28.0.2", "ndk;19.2.5345600"],
"ndk_path": "ndk/19.2.5345600"
}
{ "versions": ["6.3", "6.2", "5.15.[9-20]"],
"sdk_essential_packages": ["build-tools;31.0.0", "ndk;22.1.7171670"]
},
]
\endcode
You can view the latest version of the configuration file that is up-to-date
with the Android SDK and NDK changes, \l{sdk_definitions.json}, in Git.
\note For Qt 6.5 or later, \QC reads the NDK version that was used for
building Qt from \c modules/Core.json and uses it instead of the version
in \c sdk_definitions.json.
\sa {Android}{How To: Develop for Android},
{Developing for Android}
*/

View File

@@ -25,10 +25,6 @@
information about the state of the UI, and inspect QML properties and
JavaScript variables, as well as change their values temporarily at runtime.
\if defined(qtcreator)
\note You need Qt 5.0 or later to debug Qt Quick projects.
\endif
For an example of how to debug Qt Quick Projects, see
\l{Debugging a Qt Quick Application}.
@@ -86,7 +82,7 @@
rebuild the project.
\li To debug applications on \l{glossary-device}{devices}, check that
Qt 5.0, or later, libraries are installed on the device and
Qt libraries are installed on the device and
\l{Run on many platforms}{select the corresponding kit for the device}
before you start debugging.
@@ -250,12 +246,6 @@
application to jump to their definitions in the code. The properties of the
selected item are displayed in the \uicontrol {Locals} view.
\if defined(qtcreator)
The \uicontrol Select tool will be enabled either if your application is
using Qt 5.7 or later, or if your application is using an earlier version
of Qt and is based on the \c QQuickView class.
\endif
You can also view the item hierarchy in the running application:
Double-click an item in the running application to cycle through the item

View File

@@ -417,18 +417,18 @@
\image qtcreator-options-texteditor-behavior-file-encodings.png {File encoding preferences}
Qt 5 and Qt 6 require UTF-8 encoded source files, and therefore the default
Qt requires UTF-8 encoded source files, and therefore the default
encoding is set to \uicontrol UTF-8.
Detecting the correct encoding is tricky, so \QC will not try to do so.
Instead, it displays the following error message when you try to edit a file
that is not UTF-8 encoded: \uicontrol {Error: Could not decode "filename" with
If you try to edit a file that is not UTF-8 encoded, you see the following
error message: \uicontrol {Error: Could not decode "filename" with
"UTF-8"-encoding. Editing not possible.}
To resolve the issue, use a file conversion tool to convert the file
encoding to UTF-8 when developing Qt 5 applications. Otherwise, conversion
of string constants to QString might not work as expected.
encoding to UTF-8. Otherwise, conversion of string constants to
QString might not work as expected.
If you develop only Qt 4 applications or other than Qt applications, you
If you do not develop Qt applications, you
can set other encoding options as the default encoding. Select the
\uicontrol System option to use the file encoding used by your system.

View File

@@ -849,8 +849,7 @@
\section1 Blacklisting Tests
Since Qt 5.4, you can add a BLACKLIST file for tests. It is mainly used
internally by the Qt CI system.
A BLACKLIST file for tests is mainly used internally by the Qt CI system.
\table
\header

View File

@@ -121,14 +121,10 @@
\b {The Qt API Reference Documentation is missing and context help does
not find topics. What can I do?}
\QC comes fully integrated with Qt documentation and examples using
the Qt Help plugin. The integrated Qt Reference Documentation is available
for Qt 4.4 and later. \QC and other Qt deliverables have
documentation as .qch files. All the documentation is accessible in the
\uicontrol Help mode.
Install a Qt version and Qt documentation with \QOI.
To view the documentation that is available and to add documentation,
select \preferences > \uicontrol Help >
To view the installed documentation (.qch files) and to add documentation,
go to \preferences > \uicontrol Help >
\uicontrol Documentation. For more information, see
\l{Add external documentation}.

View File

@@ -16,8 +16,8 @@
the necessary \l{Kits}{kits} to build applications for and run them on
configured iOS devices.
You only need Qt libraries that are built for iOS. You can install them as
part of Qt 5.2, or later.
You only need Qt libraries that are built for iOS. You can install Qt for iOS
with \QOI.
\section1 iOS 17 Devices

View File

@@ -45,8 +45,7 @@
\li libxft-dev
\li libxi-dev
\li libxrandr-dev
\li libgl-dev and libglu-dev if you use Qt OpenGL (deprecated
in Qt 5) or Qt GUI OpenGL functions
\li libgl-dev and libglu-dev if you use Qt GUI OpenGL functions
\endlist
\section1 macOS

View File

@@ -141,6 +141,10 @@
Use the following syntax to enter environment variable names and values:
\c {<VARIABLE>=<VALUE>}.
To temporarily disable a variable, add a hash character (#) to the beginning
of the line.
\note Using this approach for a different statement (append, prepend, unset)
may result in unexpected changes of the environment.
To remove a variable value from the environment, enter the variable name.
For example, \c TEST sets the value of the \c TEST variable empty when
@@ -160,8 +164,8 @@
following lines. However, you can remove a value after you have referred to
it on an earlier line.
To temporarily disable a variable, add a hash character (#) to the beginning
of the line.
To add a comment or disable any of the above actions, prefix it with two hash
characters (##).
\sa {Specify the environment for projects}, {Configure projects for building},
{Configure projects for running}, {Use Qt Creator variables}

View File

@@ -149,8 +149,7 @@
imports that are used in the QML files.
You can add imports later to combine Qt Quick basic types with
Qt Quick Controls, Qt Quick Dialogs, and Qt Quick Layouts (available
since Qt 5.1).
Qt Quick Controls, Qt Quick Dialogs, and Qt Quick Layouts.
\li Select the \uicontrol {Use Qt Virtual Keyboard} check box to add
support for \l{Qt Virtual Keyboard} to the application.

View File

@@ -261,33 +261,24 @@
of the code the event is associated with.
The following types of events are displayed in the timeline view on one or
several rows. The availability of event types depends on the Qt version that
you build the application with and the Qt Quick version you use.
several rows.
\table
\header
\li Event Category
\li Description
\li Minimum Qt Version
\li Qt Quick Version
\row
\li \uicontrol {Pixmap Cache}
\li Displays the general amount of pixmap data cached, in pixels. In
addition, displays a separate event for each picture being loaded,
with specifics about its file name and size.
\li Qt 5.1
\li Qt Quick 2
\row
\li \uicontrol {Scene Graph}
\li Displays the time when scene graph frames are rendered and some
additional timing information for the various stages executed to do
so.
\li Qt 5.1
\li Qt Quick 2
\row
\li \uicontrol {Memory Usage}
\li Displays block allocations of the JavaScript memory manager.
@@ -300,82 +291,49 @@
The second row displays the actual usage of the allocated memory.
This is the amount of JavaScript heap the application has actually
requested.
\li Qt 5.4
\li Qt Quick 2
\row
\li \uicontrol {Input Events}
\li Displays mouse and keyboard events.
\li Qt 4.7.4
\li Qt Quick 1 or Qt Quick 2
\row
\li \uicontrol Painting
\li Displays the time spent painting the scene for each frame.
\li Qt 4.7.4
\li Qt Quick 1
\li Not used.
\row
\li \uicontrol Animations
\li Displays the amount of animations that are active and the frame
rate that they are running at.
Information about render thread animations is displayed for
applications that are built with Qt 5.3 or later. Render thread
animations are shown in a separate row then.
\li Qt 5.0 (Qt 5.3)
\li Qt Quick 2
Render thread animations are shown on a separate row.
\row
\li \uicontrol Compiling
\li Displays the time spent compiling the QML files.
\li Qt 4.7.4
\li Qt Quick 1 or Qt Quick 2
\row
\li \uicontrol Creating
\li Displays the time spent creating the elements in the scene. In Qt
Quick 2, creation of elements takes place in two stages. The first
\li Displays the time spent creating the elements in the scene.
The creation of elements takes place in two stages. The first
stage is for the creation of the data structures, including child
elements. The second stage represents the completion callbacks. Not
all elements trigger completion callbacks, though. The stages are
shown as separate events in the timeline.
For Qt Quick 2 applications compiled with versions of Qt before
5.2.1 only the creation of top-level elements is shown, as single
events.
\li Qt 4.7.4 (Qt 5.2.1)
\li Qt Quick 1 or Qt Quick 2
\row
\li \uicontrol Binding
\li Displays the time when a binding is evaluated and how long the
evaluation takes.
\li Qt 4.7.4
\li Qt Quick 1 or Qt Quick 2
\row
\li \uicontrol {Handling Signal}
\li Displays the time when a signal is handled and how long the
handling takes.
\li Qt 4.7.4
\li Qt Quick 1 or Qt Quick 2
\row
\li \uicontrol JavaScript
\li Displays the time spent executing the actual JavaScript behind
bindings and signal handlers. It lists all the JavaScript functions
you may be using to evaluate bindings or handle signals.
\li Qt 5.3
\li Qt Quick 2
\row
\li \uicontrol Quick3D
\li Displays the time spent rendering Qt Quick 3D frames, timing information
for frame preparation and synchronization, particle system update times
and particle update count, as well as texture and mesh memory allocations
and memory consumption.
\li Qt 6.3
\li Qt Quick 3D
This event type is available since Qt 6.3.
\endtable
\section2 Analyzing Scene Graph Events
@@ -399,7 +357,6 @@
\li Render Loop Types
\li Label in output of QSG_RENDER_TIMING
\li Description
\li Caveats
\row
\li \uicontrol {Polish}
\li GUI
@@ -407,8 +364,6 @@
\li polish
\li Final touch-up of items before they are rendered using
QQuickItem::updatePolish().
\li Versions of Qt prior to Qt 5.4 record no polish times for the basic
render loop and incorrect ones for the windows render loop.
\row
\li \uicontrol {GUI Thread Wait}
\li GUI
@@ -419,14 +374,12 @@
the same mutex at \uicontrol {GUI Thread Sync}. If this starts long
before \uicontrol {Render Thread Sync}, there is \e free time in the GUI
thread you could be using for running additional QML or JavaScript.
\li None
\row
\li \uicontrol {GUI Thread Sync}
\li GUI
\li Threaded
\li blockedForSync
\li The time the GUI thread is blocked, waiting for the render thread.
\li None
\row
\li \uicontrol {Animations}
\li GUI
@@ -437,7 +390,6 @@
animation events will be shown when using the basic render loop.
Watch the \uicontrol {Animations} category to see animation timing in
this case.
\li None
\row
\li \uicontrol {Render Thread Sync}
\li Render
@@ -445,7 +397,6 @@
\li Frame rendered ... sync
\li Synchronizing the QML state into the scene graph using
QQuickItem::updatePaintNode().
\li None
\row
\li \uicontrol {Render}
\li Render
@@ -455,20 +406,12 @@
uploading all the necessary data to the GPU. This is the \e gross
render time. Do not confuse it with the \e net \uicontrol{Render Render}
time below.
\li With versions of Qt prior to Qt 5.5, the gross render time and the
below breakup of render times may be misaligned by some
microseconds due to different, unsynchronized timers being used to
measure them. For example \uicontrol {Render Preprocess} might seem to
start before \uicontrol {Render Thread Sync} is finished.
\row
\li \uicontrol {Swap}
\li Render
\li Threaded, Basic, Windows
\li Frame rendered ... swap
\li Swapping frames after rendering.
\li The output of swap times triggered by setting QSG_RENDER_TIMING is
incorrect for the basic render loop and versions of Qt prior to
Qt 5.4. QML Profiler shows the correct swap times.
\row
\li \uicontrol {Render Preprocess}
\li Render
@@ -476,8 +419,6 @@
\li time in renderer ... preprocess
\li Calling QSGNode::preprocess() on all nodes that need to be
preprocessed. This is part of the gross \uicontrol {Render} step.
\li May not be properly aligned with \uicontrol {Render} with versions of Qt
prior to Qt 5.5.
\row
\li \uicontrol {Render Update}
\li Render
@@ -489,8 +430,6 @@
with state from the GUI thread. In \uicontrol {Render Update}, all the
nodes are combined to create the final scene. This is part of the
gross \uicontrol {Render} step.
\li May not be properly aligned with \uicontrol {Render} with versions of Qt
prior to Qt 5.5.
\row
\li \uicontrol {Render Bind}
\li Render
@@ -498,8 +437,6 @@
\li time in renderer ... binding
\li Binding the correct framebuffer for OpenGL rendering. This is part
of the gross \uicontrol {Render} step.
\li May not be properly aligned with \uicontrol {Render} with versions of Qt
prior to Qt 5.5.
\row
\li \uicontrol {Render Render}
\li Render
@@ -507,38 +444,30 @@
\li time in renderer ... rendering
\li The actual process of sending all the data to the GPU via OpenGL.
This is part of the gross \uicontrol {Render} step.
\li May not be properly aligned with \uicontrol {Render} with versions of Qt
prior to Qt 5.5.
\row
\li \uicontrol {Material Compile}
\li Render
\li Threaded, Basic, Windows
\li shader compiled
\li Compiling GLSL shader programs.
\li None
\row
\li \uicontrol {Glyph Render}
\li Render
\li Threaded, Basic, Windows
\li glyphs ... rendering
\li Rendering of font glyphs into textures.
\li Versions of Qt prior to Qt 5.4 report incorrect times for these
events.
\row
\li \uicontrol {Glyph Upload}
\li Render
\li Threaded, Basic, Windows
\li glyphs ... upload
\li Uploading of glyph textures to the GPU.
\li Versions of Qt prior to Qt 5.4 report incorrect times for these
events.
\row
\li \uicontrol {Texture Bind}
\li Render
\li Threaded, Basic, Windows
\li plain texture ... bind
\li Binding a texture in the OpenGL context using glBindTextures.
\li None
\row
\li \uicontrol {Texture Convert}
\li Render
@@ -546,35 +475,30 @@
\li plain texture ... convert
\li Converting the format and downscaling an image to make it suitable
for usage as a texture.
\li None
\row
\li \uicontrol {Texture Swizzle}
\li Render
\li Threaded, Basic, Windows
\li plain texture ... swizzle
\li Swizzling the texture data on the CPU if necessary.
\li None
\row
\li \uicontrol {Texture Upload}
\li Render
\li Threaded, Basic, Windows
\li plain texture ... upload / atlastexture uploaded
\li Uploading the texture data to the GPU.
\li None
\row
\li \uicontrol {Texture Mipmap}
\li Render
\li Threaded, Basic, Windows
\li plain texture ... mipmap
\li Mipmapping a texture on the GPU.
\li None
\row
\li \uicontrol {Texture Delete}
\li Render
\li Threaded, Basic, Windows
\li plain texture deleted
\li Deleting a texture from the GPU that became unnecessary.
\li None
\endtable
\section2 Analyzing Qt Quick 3D Events
@@ -676,9 +600,6 @@
To copy the contents of one view or row to the clipboard, select
\uicontrol {Copy Table} or \uicontrol {Copy Row} in the context menu.
JavaScript events are shown in the \uicontrol Statistics view only for applications
that use Qt Quick 2 and are built with Qt 5.3 or later.
\section2 Visualizing Statistics as Flame Graphs
The \uicontrol {Flame Graph} view shows a more concise statistical overview

View File

@@ -3,6 +3,7 @@ set(resource_directories
cplusplus
glsl
indexer_preincludes
jsonschemas
modeleditor
qmldesigner
qmlicons

View File

@@ -0,0 +1,57 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Qt Creator workspace project definition",
"description": "A Qt Creator workspace project definition",
"type": "object",
"properties": {
"project.name": {
"type": "string",
"description": "The name of the project"
},
"files.exclude": {
"type": "array",
"items": [
{
"type": "string"
}
],
"description": "Files to exclude from the project"
},
"targets": {
"type": "array",
"description": "A list of targets",
"items": [
{
"type": "object",
"properties": {
"arguments": {
"type": "array",
"items": [
{
"type": "string"
}
],
"description": "Arguments to pass to the executable"
},
"executable": {
"type": "string",
"description": "The executable to run"
},
"name": {
"type": "string",
"description": "The name of the target"
},
"workingDirectory": {
"type": "string",
"description": "The working directory to run the executable in"
}
},
"required": [
"executable",
"name"
]
}
]
}
}
}

View File

@@ -36,3 +36,8 @@ Token_Notification_Alert=ffc98014
Token_Notification_Success=ff1f9b5d
Token_Notification_Neutral=ff016876
Token_Notification_Danger=ffb22245
Token_Gradient01_Start=ff016876
Token_Gradient01_End=ff1F9B5D
Token_Gradient02_Start=ff3A3A3A
Token_Gradient02_End=ff838383

View File

@@ -35,3 +35,8 @@ Token_Notification_Alert=ffeb991f
Token_Notification_Success=ff23b26a
Token_Notification_Neutral=ff0e7887
Token_Notification_Danger=ffdc1343
Token_Gradient01_Start=ff23B26A
Token_Gradient01_End=ff0E7887
Token_Gradient02_Start=ff949494
Token_Gradient02_End=ff474747

View File

@@ -16,6 +16,7 @@ Product {
"debugger/**/*",
"designer/**/*",
"glsl/**/*",
"jsonschemas/**/*",
"modeleditor/**/*",
"qml/**/*",
"qmldesigner/**/*",

View File

@@ -307,7 +307,7 @@ recognize:
if (currentExpanded) {
QTC_ASSERT(macroOffset != -1 && macroLength != -1, continue);
_expansionPositions[_tokens->size() - 1] = std::make_pair(macroOffset, macroLength);
_expansionPositions[int(_tokens->size()) - 1] = std::make_pair(macroOffset, macroLength);
}
} while (tk.kind());

View File

@@ -627,10 +627,14 @@ void PluginSpec::setEnabledIndirectly(bool value)
}
void PluginSpec::setForceDisabled(bool value)
{
if (value)
d->forceEnabled = false;
d->forceDisabled = value;
}
void PluginSpec::setForceEnabled(bool value)
{
if (value)
d->forceDisabled = false;
d->forceEnabled = value;
}

View File

@@ -1157,11 +1157,8 @@ QColor StereotypeDefinitionParser::parseColorExpression()
Token token = d->m_scanner->read();
if (token.type() == Token::TokenIdentifier || token.type() == Token::TokenColor) {
QString value = token.text().toLower();
QColor color;
if (QColor::isValidColor(value)) {
color.setNamedColor(value);
return color;
}
if (QColor::isValidColorName(value))
return QColor::fromString(value);
}
throw StereotypeDefinitionParserError("Expected color name.", token.sourcePos());
}
@@ -1191,9 +1188,8 @@ StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseExpression()
return Value(Float, QVariant(value));
} else if (token.type() == Token::TokenColor) {
QString value = token.text().toLower();
QColor color;
if (QColor::isValidColor(value)) {
color.setNamedColor(value);
if (QColor::isValidColorName(value)) {
const QColor color = QColor::fromString(value);
return Value(Color, QVariant(color));
} else {
throw StereotypeDefinitionParserError("Invalid color.", token.sourcePos());

View File

@@ -73,7 +73,7 @@ ModelTreeFilter::ModelTreeFilter(QWidget *parent) :
},
d->relationsCheckBox,
d->diagramElementsCheckBox,
customMargin({margin, 0, margin, 0}),
customMargins(margin, 0, margin, 0),
},
Space(10),
line(),
@@ -88,11 +88,11 @@ ModelTreeFilter::ModelTreeFilter(QWidget *parent) :
Tr::tr("Name:"), d->nameLineEdit, br,
Tr::tr("Direction:"), d->directionComboBox, br,
},
customMargin({margin, 0, margin, 0}),
customMargins(margin, 0, margin, 0),
},
st,
line(),
customMargin({0, margin, 0, 0}),
customMargins(0, margin, 0, 0),
}.attachTo(this);
connect(d->resetViewButton, &QPushButton::clicked, this, &ModelTreeFilter::resetView);

View File

@@ -6,23 +6,20 @@
namespace Tasking {
// That's cut down qtcassert.{c,h} to avoid the dependency.
#define QTC_STRINGIFY_HELPER(x) #x
#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
#define QT_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QT_STRINGIFY(__LINE__))
#define QT_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QT_STRING(#cond); action; } do {} while (0)
void Barrier::setLimit(int value)
{
QTC_ASSERT(!isRunning(), return);
QTC_ASSERT(value > 0, return);
QT_ASSERT(!isRunning(), return);
QT_ASSERT(value > 0, return);
m_limit = value;
}
void Barrier::start()
{
QTC_ASSERT(!isRunning(), return);
QT_ASSERT(!isRunning(), return);
m_current = 0;
m_result = {};
}
@@ -30,7 +27,7 @@ void Barrier::start()
void Barrier::advance()
{
// Calling advance on finished is OK
QTC_ASSERT(isRunning() || m_result, return);
QT_ASSERT(isRunning() || m_result, return);
if (!isRunning()) // no-op
return;
++m_current;
@@ -41,7 +38,7 @@ void Barrier::advance()
void Barrier::stopWithResult(DoneResult result)
{
// Calling stopWithResult on finished is OK when the same success is passed
QTC_ASSERT(isRunning() || (m_result && *m_result == result), return);
QT_ASSERT(isRunning() || (m_result && *m_result == result), return);
if (!isRunning()) // no-op
return;
m_current = -1;

View File

@@ -25,7 +25,7 @@ public:
int current() const { return m_current; }
std::optional<DoneResult> result() const { return m_result; }
signals:
Q_SIGNALS:
void done(DoneResult success);
private:

View File

@@ -5,7 +5,7 @@
#include "tasktree.h"
#include <QtConcurrent>
#include <QtConcurrent/QtConcurrent>
namespace Tasking {
@@ -76,7 +76,7 @@ public:
}
}
void start() {
void start() final {
if (!this->task()->m_startHandler) {
emit this->done(DoneResult::Error); // TODO: Add runtime assert
return;

View File

@@ -3,7 +3,7 @@
#include "networkquery.h"
#include <QNetworkAccessManager>
#include <QtNetwork/QNetworkAccessManager>
namespace Tasking {

View File

@@ -7,8 +7,8 @@
#include "tasktree.h"
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <memory>
@@ -37,7 +37,7 @@ public:
QNetworkReply *reply() const { return m_reply.get(); }
void start();
signals:
Q_SIGNALS:
void started();
void done(DoneResult result);

View File

@@ -3,12 +3,12 @@
#include "qprocesstask.h"
#include <QCoreApplication>
#include <QDebug>
#include <QMutex>
#include <QThread>
#include <QTimer>
#include <QWaitCondition>
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QMutex>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QWaitCondition>
namespace Tasking {
@@ -60,7 +60,7 @@ public:
terminate();
}
signals:
Q_SIGNALS:
void finished();
private:

View File

@@ -7,7 +7,7 @@
#include "tasktree.h"
#include <QProcess>
#include <QtCore/QProcess>
namespace Tasking {
@@ -45,17 +45,17 @@ public:
class TASKING_EXPORT QProcessAdapter : public TaskAdapter<QProcess, QProcessDeleter>
{
private:
void start() override {
void start() final {
connect(task(), &QProcess::finished, this, [this] {
const bool success = task()->exitStatus() == QProcess::NormalExit
&& task()->error() == QProcess::UnknownError
&& task()->exitCode() == 0;
emit done(toDoneResult(success));
Q_EMIT done(toDoneResult(success));
});
connect(task(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
if (error != QProcess::FailedToStart)
return;
emit done(DoneResult::Error);
Q_EMIT done(DoneResult::Error);
});
task()->start();
}

View File

@@ -3,7 +3,7 @@
#pragma once
#include <qglobal.h>
#include <QtCore/qglobal.h>
#if defined(TASKING_LIBRARY)
# define TASKING_EXPORT Q_DECL_EXPORT

View File

@@ -5,17 +5,17 @@
#include "barrier.h"
#include <QDebug>
#include <QEventLoop>
#include <QFutureWatcher>
#include <QHash>
#include <QMetaEnum>
#include <QMutex>
#include <QPromise>
#include <QPointer>
#include <QSet>
#include <QTime>
#include <QTimer>
#include <QtCore/QDebug>
#include <QtCore/QEventLoop>
#include <QtCore/QFutureWatcher>
#include <QtCore/QHash>
#include <QtCore/QMetaEnum>
#include <QtCore/QMutex>
#include <QtCore/QPointer>
#include <QtCore/QPromise>
#include <QtCore/QSet>
#include <QtCore/QTime>
#include <QtCore/QTimer>
using namespace std::chrono;
@@ -1461,7 +1461,7 @@ static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateW
ExecutableItem ExecutableItem::withLog(const QString &logName) const
{
const auto header = [logName] {
return QString("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName);
return QString::fromLatin1("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName);
};
struct LogStorage
{
@@ -1482,8 +1482,8 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const
const int asyncCountDiff = activeTaskTree()->asyncCount() - storage->asyncCount;
QT_CHECK(asyncCountDiff >= 0);
const QMetaEnum doneWithEnum = QMetaEnum::fromType<DoneWith>();
const QString syncType = asyncCountDiff ? QString("asynchronously")
: QString("synchronously");
const QString syncType = asyncCountDiff ? QString::fromLatin1("asynchronously")
: QString::fromLatin1("synchronously");
qDebug().noquote().nospace() << header() << " finished " << syncType << " with "
<< doneWithEnum.valueToKey(int(result)) << " within " << elapsed.count() << "ms.";
})

View File

@@ -5,8 +5,8 @@
#include "tasking_global.h"
#include <QObject>
#include <QList>
#include <QtCore/QList>
#include <QtCore/QObject>
#include <memory>
@@ -84,7 +84,7 @@ class TASKING_EXPORT TaskInterface : public QObject
{
Q_OBJECT
signals:
Q_SIGNALS:
void done(DoneResult result);
private:
@@ -345,7 +345,7 @@ private:
std::invoke(handler);
return SetupResult::Continue;
};
};
}
template <typename Handler>
static GroupDoneHandler wrapGroupDone(Handler &&handler)
{
@@ -368,7 +368,7 @@ private:
std::invoke(handler);
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
};
};
}
};
template <typename Handler>
@@ -433,7 +433,7 @@ private:
std::invoke(handler);
return SetupResult::StopWithSuccess;
};
};
}
};
template <typename Task, typename Deleter = std::default_delete<Task>>
@@ -490,7 +490,7 @@ private:
std::invoke(handler, *adapter.task());
return SetupResult::Continue;
};
};
}
template <typename Handler>
static InterfaceDoneHandler wrapDone(Handler &&handler) {
@@ -529,7 +529,7 @@ private:
std::invoke(handler);
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
};
};
}
};
class TASKING_EXPORT TaskTree final : public QObject
@@ -579,7 +579,7 @@ public:
wrapHandler<const StorageStruct>(std::forward<Handler>(handler)));
}
signals:
Q_SIGNALS:
void started();
void done(DoneWith result);
void asyncCountChanged(int count);

View File

@@ -6,7 +6,7 @@
#include "tasking_global.h"
#include "tasktree.h"
#include <QObject>
#include <QtCore/QObject>
namespace Tasking {
@@ -33,7 +33,7 @@ public:
// No done() signal is emitted.
void reset();
signals:
Q_SIGNALS:
void aboutToStart(TaskTree *taskTree);
void done(DoneWith result);

View File

@@ -1410,6 +1410,7 @@ public:
FilePath m_baseFileName;
StringAspect::ValueAcceptor m_valueAcceptor;
std::optional<FancyLineEdit::ValidationFunction> m_validator;
std::optional<FilePath> m_effectiveBinary;
std::function<void()> m_openTerminal;
CheckableAspectImplementation m_checkerImpl;
@@ -1430,6 +1431,8 @@ FilePathAspect::FilePathAspect(AspectContainer *container)
addDataExtractor(this, &FilePathAspect::value, &Data::value);
addDataExtractor(this, &FilePathAspect::operator(), &Data::filePath);
connect(this, &BaseAspect::changed, this, [this] { d->m_effectiveBinary.reset(); });
}
FilePathAspect::~FilePathAspect() = default;
@@ -1455,6 +1458,29 @@ FilePath FilePathAspect::expandedValue() const
return FilePath::fromUserInput(value);
}
/*!
Returns the full path of the set command. Only makes a difference if
expected kind is \c Command or \c ExistingCommand and the current
file path is an executable provided without its path.
Performs a lookup in PATH if necessary.
*/
FilePath FilePathAspect::effectiveBinary() const
{
if (d->m_effectiveBinary)
return *d->m_effectiveBinary;
const FilePath current = expandedValue();
const PathChooser::Kind kind = d->m_expectedKind;
if (kind != PathChooser::ExistingCommand && kind != PathChooser::Command)
return current;
if (current.needsDevice())
return current;
d->m_effectiveBinary.emplace(current.searchInPath());
return *d->m_effectiveBinary;
}
QString FilePathAspect::value() const
{
return TypedAspect::value();
@@ -1693,9 +1719,12 @@ void FilePathAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
*/
void FilePathAspect::setExpectedKind(const PathChooser::Kind expectedKind)
{
if (d->m_expectedKind != expectedKind) {
d->m_expectedKind = expectedKind;
d->m_effectiveBinary.reset();
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setExpectedKind(expectedKind);
}
}
void FilePathAspect::setEnvironment(const Environment &env)

View File

@@ -663,6 +663,7 @@ public:
};
FilePath operator()() const;
FilePath effectiveBinary() const;
FilePath expandedValue() const;
QString value() const;
void setValue(const FilePath &filePath, Announcement howToAnnounce = DoEmit);

View File

@@ -397,7 +397,8 @@ EnvironmentItems EnvironmentModel::userChanges() const
void EnvironmentModel::setUserChanges(const EnvironmentItems &items)
{
EnvironmentItems filtered = Utils::filtered(items, [](const EnvironmentItem &i) {
return i.name != "export " && !i.name.contains('=');
return i.operation == EnvironmentItem::Comment
|| (i.name != "export " && !i.name.contains('='));
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)

View File

@@ -1230,6 +1230,9 @@ FilePathInfo FilePath::filePathInfo() const
*/
bool FilePath::exists() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1242,6 +1245,9 @@ bool FilePath::exists() const
*/
bool FilePath::isExecutableFile() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1254,6 +1260,9 @@ bool FilePath::isExecutableFile() const
*/
bool FilePath::isWritableDir() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1266,6 +1275,9 @@ bool FilePath::isWritableDir() const
*/
bool FilePath::isWritableFile() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1273,9 +1285,11 @@ bool FilePath::isWritableFile() const
return (*access)->isWritableFile(*this);
}
bool FilePath::isReadableFile() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1285,6 +1299,9 @@ bool FilePath::isReadableFile() const
bool FilePath::isReadableDir() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1294,6 +1311,9 @@ bool FilePath::isReadableDir() const
bool FilePath::isFile() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1303,6 +1323,9 @@ bool FilePath::isFile() const
bool FilePath::isDir() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1312,6 +1335,9 @@ bool FilePath::isDir() const
bool FilePath::isSymLink() const
{
if (isEmpty())
return false;
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;

View File

@@ -54,11 +54,11 @@ public:
optCopy.state |= QStyle::State_HasFocus;
QItemDelegate::paint(painter,option,index);
// add remove button
QWindow *window = view->window()->windowHandle();
const QPixmap iconPixmap = icon.pixmap(window, option.rect.size());
const qreal devicePixelRatio = painter->device()->devicePixelRatio();
const QPixmap iconPixmap = icon.pixmap(option.rect.size(), devicePixelRatio);
QRect pixmapRect = QStyle::alignedRect(option.direction,
Qt::AlignRight | Qt::AlignVCenter,
iconPixmap.size() / window->devicePixelRatio(),
iconPixmap.size() / devicePixelRatio,
option.rect);
if (!clearIconSize.isValid())
clearIconSize = pixmapRect.size();

View File

@@ -221,11 +221,11 @@ QIcon Icon::modeIcon(const Icon &classic, const Icon &flat, const Icon &flatActi
QIcon Icon::combinedIcon(const QList<QIcon> &icons)
{
QIcon result;
QWindow *window = QApplication::allWidgets().constFirst()->windowHandle();
const qreal devicePixelRatio = QApplication::allWidgets().constFirst()->devicePixelRatio();
for (const QIcon &icon: icons)
for (const QIcon::Mode mode: {QIcon::Disabled, QIcon::Normal})
for (const QSize &size: icon.availableSizes(mode))
result.addPixmap(icon.pixmap(window, size, mode), mode);
result.addPixmap(icon.pixmap(size, devicePixelRatio, mode), mode);
return result;
}

View File

@@ -118,10 +118,9 @@ void InfoLabel::paintEvent(QPaintEvent *event)
p.restore();
}
const QIcon &icon = iconForType(m_type);
QWindow *window = this->window()->windowHandle();
const QIcon::Mode mode = !this->isEnabled() ? QIcon::Disabled : QIcon::Normal;
const QPixmap iconPx =
icon.pixmap(window, QSize(iconSize, iconSize) * devicePixelRatio(), mode);
icon.pixmap(QSize(iconSize, iconSize) * devicePixelRatio(), devicePixelRatio(), mode);
p.drawPixmap(iconRect, iconPx);
ElidingLabel::paintEvent(event);
}

View File

@@ -339,19 +339,19 @@ void Layout::span(int cols, int rows)
pendingItems.back().spanRows = rows;
}
void Layout::noMargin()
void Layout::setNoMargins()
{
customMargin({});
setContentsMargins(0, 0, 0, 0);
}
void Layout::normalMargin()
void Layout::setNormalMargins()
{
customMargin({9, 9, 9, 9});
setContentsMargins(9, 9, 9, 9);
}
void Layout::customMargin(const QMargins &margin)
void Layout::setContentsMargins(int left, int top, int right, int bottom)
{
access(this)->setContentsMargins(margin);
access(this)->setContentsMargins(left, top, right, bottom);
}
/*!
@@ -467,11 +467,11 @@ void addToLayout(Layout *layout, const QString &inner)
layout->addLayoutItem(item);
}
void empty(Layout *iface)
void empty(Layout *layout)
{
LayoutItem item;
item.empty = true;
iface->addLayoutItem(item);
layout->addLayoutItem(item);
}
void hr(Layout *layout)
@@ -479,26 +479,26 @@ void hr(Layout *layout)
layout->addLayoutItem(createHr());
}
void br(Layout *iface)
void br(Layout *layout)
{
iface->flush();
layout->flush();
}
void st(Layout *iface)
void st(Layout *layout)
{
LayoutItem item;
item.stretch = 1;
iface->addLayoutItem(item);
layout->addLayoutItem(item);
}
void noMargin(Layout *iface)
void noMargin(Layout *layout)
{
iface->noMargin();
layout->setNoMargins();
}
void normalMargin(Layout *iface)
void normalMargin(Layout *layout)
{
iface->normalMargin();
layout->setNormalMargins();
}
QFormLayout *Layout::asForm()
@@ -612,9 +612,9 @@ void Layout::flush_() const
const_cast<Layout *>(this)->flush();
}
void withFormAlignment(Layout *iface)
void withFormAlignment(Layout *layout)
{
iface->useFormAlignment = true;
layout->useFormAlignment = true;
}
// Flow
@@ -672,7 +672,7 @@ Form::Form(std::initializer_list<I> ps)
flush();
}
void Layout::fieldGrowthPolicy(int policy)
void Layout::setFieldGrowthPolicy(int policy)
{
if (auto lt = asForm())
lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
@@ -699,7 +699,7 @@ Widget::Widget(std::initializer_list<I> ps)
apply(this, ps);
}
void Widget::resize(int w, int h)
void Widget::setSize(int w, int h)
{
access(this)->resize(w, h);
}
@@ -724,19 +724,19 @@ void Widget::show()
access(this)->show();
}
void Widget::noMargin(int)
void Widget::setNoMargins(int)
{
customMargin({});
setContentsMargins(0, 0, 0, 0);
}
void Widget::normalMargin(int)
void Widget::setNormalMargins(int)
{
customMargin({9, 9, 9, 9});
setContentsMargins(9, 9, 9, 9);
}
void Widget::customMargin(const QMargins &margin)
void Widget::setContentsMargins(int left, int top, int right, int bottom)
{
access(this)->setContentsMargins(margin);
access(this)->setContentsMargins(left, top, right, bottom);
}
QWidget *Widget::emerge() const
@@ -979,7 +979,7 @@ void addToLayout(Layout *layout, const Span &inner)
LayoutModifier spacing(int space)
{
return [space](Layout *iface) { iface->setSpacing(space); };
return [space](Layout *layout) { layout->setSpacing(space); };
}
void addToLayout(Layout *layout, const Space &inner)

View File

@@ -3,7 +3,6 @@
#pragma once
#include <QMargins>
#include <QString>
#include <functional>
@@ -26,7 +25,6 @@ class QGroupBox;
class QHBoxLayout;
class QLabel;
class QLayout;
class QMargins;
class QObject;
class QPushButton;
class QSpinBox;
@@ -52,6 +50,45 @@ public:
const T2 arg; // FIXME: Could be const &, but this would currently break bindTo().
};
template<typename T1, typename T2>
struct Arg2
{
Arg2(const T1 &a1, const T2 &a2)
: p1(a1)
, p2(a2)
{}
const T1 p1;
const T2 p2;
};
template<typename T1, typename T2, typename T3>
struct Arg3
{
Arg3(const T1 &a1, const T2 &a2, const T3 &a3)
: p1(a1)
, p2(a2)
, p3(a3)
{}
const T1 p1;
const T2 p2;
const T3 p3;
};
template<typename T1, typename T2, typename T3, typename T4>
struct Arg4
{
Arg4(const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4)
: p1(a1)
, p2(a2)
, p3(a3)
, p4(a4)
{}
const T1 p1;
const T2 p2;
const T3 p3;
const T4 p4;
};
// The main dispatcher
void doit(auto x, auto id, auto p);
@@ -139,13 +176,16 @@ public:
Layout(Implementation *w) { ptr = w; }
void span(int cols, int rows);
void noMargin();
void normalMargin();
void customMargin(const QMargins &margin);
void setNoMargins();
void setNormalMargins();
void setContentsMargins(int left, int top, int right, int bottom);
void setColumnStretch(int cols, int rows);
void setSpacing(int space);
void setFieldGrowthPolicy(int policy);
void attachTo(QWidget *);
void addItem(I item);
void addItems(std::initializer_list<I> items);
void addRow(std::initializer_list<I> items);
@@ -153,7 +193,6 @@ public:
void flush();
void flush_() const;
void fieldGrowthPolicy(int policy);
QWidget *emerge() const;
void show() const;
@@ -258,15 +297,15 @@ public:
Widget(Implementation *w) { ptr = w; }
QWidget *emerge() const;
void show();
void resize(int, int);
void setLayout(const Layout &layout);
void setSize(int, int);
void setWindowTitle(const QString &);
void setToolTip(const QString &);
void noMargin(int = 0);
void normalMargin(int = 0);
void customMargin(const QMargins &margin);
void setNoMargins(int = 0);
void setNormalMargins(int = 0);
void setContentsMargins(int left, int top, int right, int bottom);
};
class QTCREATOR_UTILS_EXPORT Label : public Widget
@@ -439,69 +478,42 @@ void doit(Interface *x, IdId, auto p)
// Setter dispatchers
class SizeId {};
auto size(auto w, auto h) { return IdAndArg{SizeId{}, std::pair{w, h}}; }
void doit(auto x, SizeId, auto p) { x->resize(p->first, p->second); }
#define QTCREATOR_SETTER(name, setter) \
class name##_TAG {}; \
inline auto name(auto p) { return IdAndArg{name##_TAG{}, p}; } \
inline void doit(auto x, name##_TAG, auto p) { x->setter(p); }
class TextId {};
auto text(auto p) { return IdAndArg{TextId{}, p}; }
void doit(auto x, TextId, auto p) { x->setText(p); }
#define QTCREATOR_SETTER2(name, setter) \
class name##_TAG {}; \
inline auto name(auto p1, auto p2) { return IdAndArg{name##_TAG{}, Arg2{p1, p2}}; } \
inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2); }
class TitleId {};
auto title(auto p) { return IdAndArg{TitleId{}, p}; }
void doit(auto x, TitleId, auto p) { x->setTitle(p); }
#define QTCREATOR_SETTER3(name, setter) \
class name##_TAG {}; \
inline auto name(auto p1, auto p2, auto p3) { return IdAndArg{name##_TAG{}, Arg3{p1, p2, p3}}; } \
inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3); }
class TextFormatId {};
auto textFormat(auto p) { return IdAndArg{TextFormatId{}, p}; }
void doit(auto x, TextFormatId, auto p) { x->setTextFormat(p); }
#define QTCREATOR_SETTER4(name, setter) \
class name##_TAG {}; \
inline auto name(auto p1, auto p2, auto p3, auto p4) { return IdAndArg{name##_TAG{}, Arg4{p1, p2, p3, p4}}; } \
inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3, p.p4); }
class WordWrapId {};
auto wordWrap(auto p) { return IdAndArg{WordWrapId{}, p}; }
void doit(auto x, WordWrapId, auto p) { x->setWordWrap(p); }
class TextInteractionFlagId {};
auto textInteractionFlags(auto p) { return IdAndArg{TextInteractionFlagId{}, p}; }
void doit(auto x, TextInteractionFlagId, auto p) { x->setTextInteractionFlags(p); }
class OpenExternalLinksId {};
auto openExternalLinks(auto p) { return IdAndArg{OpenExternalLinksId{}, p}; }
void doit(auto x, OpenExternalLinksId, auto p) { x->setOpenExternalLinks(p); }
class OnLinkHoveredId {};
auto onLinkHovered(auto p, QObject *guard) { return IdAndArg{OnLinkHoveredId{}, std::pair{p, guard}}; }
void doit(auto x, OnLinkHoveredId, auto p) { x->onLinkHovered(p.first, p.second); }
class GroupCheckerId {};
auto groupChecker(auto p) { return IdAndArg{GroupCheckerId{}, p}; }
void doit(auto x, GroupCheckerId, auto p) { x->setGroupChecker(p); }
class ToolTipId {};
auto toolTip(auto p) { return IdAndArg{ToolTipId{}, p}; }
void doit(auto x, ToolTipId, auto p) { x->setToolTip(p); }
class WindowTitleId {};
auto windowTitle(auto p) { return IdAndArg{WindowTitleId{}, p}; }
void doit(auto x, WindowTitleId, auto p) { x->setWindowTitle(p); }
class OnTextChangedId {};
auto onTextChanged(auto p) { return IdAndArg{OnTextChangedId{}, p}; }
void doit(auto x, OnTextChangedId, auto p) { x->onTextChanged(p); }
class OnClickedId {};
auto onClicked(auto p, auto guard) { return IdAndArg{OnClickedId{}, std::pair{p, guard}}; }
void doit(auto x, OnClickedId, auto p) { x->onClicked(p.first, p.second); }
class CustomMarginId {};
inline auto customMargin(const QMargins &p) { return IdAndArg{CustomMarginId{}, p}; }
void doit(auto x, CustomMarginId, auto p) { x->customMargin(p); }
class FieldGrowthPolicyId {};
inline auto fieldGrowthPolicy(auto p) { return IdAndArg{FieldGrowthPolicyId{}, p}; }
void doit(auto x, FieldGrowthPolicyId, auto p) { x->fieldGrowthPolicy(p); }
class ColumnStretchId {};
inline auto columnStretch(int column, int stretch) { return IdAndArg{ColumnStretchId{}, std::pair{column, stretch}}; }
void doit(auto x, ColumnStretchId, auto p) { x->setColumnStretch(p.first, p.second); }
QTCREATOR_SETTER(fieldGrowthPolicy, setFieldGrowthPolicy);
QTCREATOR_SETTER(groupChecker, setGroupChecker);
QTCREATOR_SETTER(openExternalLinks, setOpenExternalLinks);
QTCREATOR_SETTER2(size, setSize)
QTCREATOR_SETTER(text, setText)
QTCREATOR_SETTER(textFormat, setTextFormat);
QTCREATOR_SETTER(textInteractionFlags, setTextInteractionFlags);
QTCREATOR_SETTER(title, setTitle)
QTCREATOR_SETTER(toolTip, setToolTip);
QTCREATOR_SETTER(windowTitle, setWindowTitle);
QTCREATOR_SETTER(wordWrap, setWordWrap);
QTCREATOR_SETTER2(columnStretch, setColumnStretch);
QTCREATOR_SETTER2(onClicked, onClicked);
QTCREATOR_SETTER2(onLinkHovered, onLinkHovered);
QTCREATOR_SETTER2(onTextChanged, onTextChanged);
QTCREATOR_SETTER4(customMargins, setContentsMargins);
// Nesting dispatchers

View File

@@ -19,6 +19,10 @@ EnvironmentItems EnvironmentItem::fromStringList(const QStringList &list)
{
EnvironmentItems result;
for (const QString &string : list) {
if (string.startsWith("##")) {
result.append({string.mid(2), {}, EnvironmentItem::Comment});
continue;
}
int pos = string.indexOf("+=");
if (pos != -1) {
result.append({string.left(pos), string.mid(pos + 2), EnvironmentItem::Append});
@@ -59,6 +63,8 @@ QStringList EnvironmentItem::toStringList(const EnvironmentItems &list)
return QString('#' + item.name + '=' + item.value);
case EnvironmentItem::SetEnabled:
return QString(item.name + '=' + item.value);
case EnvironmentItem::Comment:
return QString("##" + item.name);
}
return QString();
});
@@ -170,6 +176,8 @@ void EnvironmentItem::apply(NameValueDictionary *dictionary, Operation op) const
apply(dictionary, SetEnabled);
}
} break;
case Comment: // ignore comments when applying to environment
break;
}
}
@@ -195,6 +203,9 @@ QDebug operator<<(QDebug debug, const EnvironmentItem &i)
case EnvironmentItem::Append:
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
break;
case EnvironmentItem::Comment:
debug << "comment:" << i.name;
break;
}
debug << ')';
return debug;

View File

@@ -16,7 +16,7 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT EnvironmentItem
{
public:
enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled };
enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled, Comment };
EnvironmentItem() = default;
EnvironmentItem(const QString &key, const QString &value, Operation operation = SetEnabled)
: name(key)

View File

@@ -60,11 +60,12 @@ NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
const QString helpText = Tr::tr(
"Enter one environment variable per line.\n"
"To set or change a variable, use VARIABLE=VALUE.\n"
"To disable a variable, prefix this line with \"#\".\n"
"To append to a variable, use VARIABLE+=VALUE.\n"
"To prepend to a variable, use VARIABLE=+VALUE.\n"
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
"To clear a variable, put its name on a line with nothing else on it.\n"
"To disable a variable, prefix the line with \"#\".");
"Lines starting with \"##\" will be treated as comments.");
m_editor = new Internal::TextEditHelper(this);
auto layout = new QVBoxLayout(this);
@@ -141,7 +142,7 @@ bool NameValueItemsWidget::editVariable(const QString &name, Selection selection
skipWhiteSpace();
if (offset < line.length()) {
QChar nextChar = line.at(offset);
if (nextChar.isLetterOrNumber())
if (nextChar.isLetterOrNumber() || nextChar == '_')
continue;
if (nextChar == '=') {
if (++offset < line.length() && line.at(offset) == '+')

View File

@@ -8,16 +8,34 @@
#include <QEvent>
#include <QPainter>
namespace Utils::Internal {
class OverlayWidgetPrivate
{
public:
OverlayWidget::PaintFunction m_paint;
OverlayWidget::ResizeFunction m_resize;
};
} // namespace Utils::Internal
Utils::OverlayWidget::OverlayWidget(QWidget *parent)
: d(new Internal::OverlayWidgetPrivate)
{
setAttribute(Qt::WA_TransparentForMouseEvents);
if (parent)
attachToWidget(parent);
d->m_resize = [](QWidget *w, const QSize &size) { w->setGeometry(QRect(QPoint(0, 0), size)); };
}
Utils::OverlayWidget::~OverlayWidget() = default;
void Utils::OverlayWidget::setPaintFunction(const Utils::OverlayWidget::PaintFunction &paint)
{
m_paint = paint;
d->m_paint = paint;
}
void Utils::OverlayWidget::setResizeFunction(const ResizeFunction &resize)
{
d->m_resize = resize;
}
bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev)
@@ -29,9 +47,9 @@ bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev)
void Utils::OverlayWidget::paintEvent(QPaintEvent *ev)
{
if (m_paint) {
if (d->m_paint) {
QPainter p(this);
m_paint(this, p, ev);
d->m_paint(this, p, ev);
}
}
@@ -50,5 +68,6 @@ void Utils::OverlayWidget::attachToWidget(QWidget *parent)
void Utils::OverlayWidget::resizeToParent()
{
QTC_ASSERT(parentWidget(), return );
setGeometry(QRect(QPoint(0, 0), parentWidget()->size()));
if (d->m_resize)
d->m_resize(this, parentWidget()->size());
}

View File

@@ -8,18 +8,26 @@
#include <QWidget>
#include <functional>
#include <memory>
namespace Utils {
namespace Internal {
class OverlayWidgetPrivate;
}
class QTCREATOR_UTILS_EXPORT OverlayWidget : public QWidget
{
public:
using PaintFunction = std::function<void(QWidget *, QPainter &, QPaintEvent *)>;
using ResizeFunction = std::function<void(QWidget *, QSize)>;
explicit OverlayWidget(QWidget *parent = nullptr);
~OverlayWidget();
void attachToWidget(QWidget *parent);
void setPaintFunction(const PaintFunction &paint);
void setResizeFunction(const ResizeFunction &resize);
protected:
bool eventFilter(QObject *obj, QEvent *ev) override;
@@ -28,7 +36,7 @@ protected:
private:
void resizeToParent();
PaintFunction m_paint;
std::unique_ptr<Internal::OverlayWidgetPrivate> d;
};
} // namespace Utils

View File

@@ -551,8 +551,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect,
// return a high-dpi pixmap, which will in that case have a devicePixelRatio
// different than 1. The shadow drawing caluculations are done in device
// pixels.
QWindow *window = dynamic_cast<QWidget*>(p->device())->window()->windowHandle();
QPixmap px = icon.pixmap(window, rect.size(), iconMode);
QPixmap px = icon.pixmap(rect.size(), devicePixelRatio, iconMode);
int radius = int(dipRadius * devicePixelRatio);
QPoint offset = dipOffset * devicePixelRatio;
cache = QPixmap(px.size() + QSize(radius * 2, radius * 2));
@@ -563,7 +562,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect,
const bool hasDisabledState =
icon.availableSizes().count() == icon.availableSizes(QIcon::Disabled).count();
if (!hasDisabledState)
px = disabledSideBarIcon(icon.pixmap(window, rect.size()));
px = disabledSideBarIcon(icon.pixmap(rect.size(), devicePixelRatio));
} else if (creatorTheme()->flag(Theme::ToolBarIconShadow)) {
// Draw shadow
QImage tmp(px.size() + QSize(radius * 2, radius * 2 + 1), QImage::Format_ARGB32_Premultiplied);
@@ -950,6 +949,8 @@ static const UiFontMetrics& uiFontMetrics(StyleHelper::UiElement element)
{StyleHelper::UiElementBody2, {12, 20, QFont::Light}},
{StyleHelper::UiElementButtonMedium, {12, 16, QFont::Bold}},
{StyleHelper::UiElementButtonSmall, {10, 12, QFont::Bold}},
{StyleHelper::UiElementLabelMedium, {12, 16, QFont::DemiBold}},
{StyleHelper::UiElementLabelSmall, {10, 12, QFont::DemiBold}},
{StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}},
{StyleHelper::UiElementCaption, {10, 12, QFont::Normal}},
{StyleHelper::UiElementIconStandard, {12, 16, QFont::Medium}},

View File

@@ -79,6 +79,8 @@ enum ToolbarStyle {
};
constexpr ToolbarStyle defaultToolbarStyle = ToolbarStyleCompact;
// Keep in sync with:
// SyleHelper::uiFontMetrics, ICore::uiConfigInformation, tst_manual_widgets_uifonts::main
enum UiElement {
UiElementH1,
UiElementH2,
@@ -91,6 +93,8 @@ enum UiElement {
UiElementBody2,
UiElementButtonMedium,
UiElementButtonSmall,
UiElementLabelMedium,
UiElementLabelSmall,
UiElementCaptionStrong,
UiElementCaption,
UiElementIconStandard,

View File

@@ -334,22 +334,12 @@ void TerminalInterface::start()
Environment finalEnv = m_setup.m_environment;
if (HostOsInfo::isWindowsHost()) {
if (!finalEnv.hasKey("PATH")) {
const QString path = qtcEnvironmentVariable("PATH");
if (!path.isEmpty())
finalEnv.set("PATH", path);
}
if (!finalEnv.hasKey("SystemRoot")) {
const QString systemRoot = qtcEnvironmentVariable("SystemRoot");
if (!systemRoot.isEmpty())
finalEnv.set("SystemRoot", systemRoot);
}
} else if (HostOsInfo::isMacHost()) {
if (HostOsInfo::isMacHost())
finalEnv.set("TERM", "xterm-256color");
}
if (finalEnv.hasChanges()) {
finalEnv = finalEnv.appliedToEnvironment(Environment::systemEnvironment());
d->envListFile = std::make_unique<QTemporaryFile>(this);
if (!d->envListFile->open()) {
cleanupAfterStartFailure(msgCannotCreateTempFile(d->envListFile->errorString()));

View File

@@ -247,6 +247,10 @@ public:
Token_Notification_Success,
Token_Notification_Neutral,
Token_Notification_Danger,
Token_Gradient01_Start,
Token_Gradient01_End,
Token_Gradient02_Start,
Token_Gradient02_End,
/* Timeline Library */
Timeline_TextColor,

View File

@@ -45,7 +45,9 @@ public:
m_indicatorPixmap(indicatorPixmap)
{
m_indicatorLabel = new QLabel(this);
m_indicatorLabel->setFixedSize(m_indicatorPixmap.size());
const QSizeF indicatorSize = m_indicatorPixmap.deviceIndependentSize();
m_indicatorLabel->setFixedSize(
{qCeil(indicatorSize.width()), qCeil(indicatorSize.height())});
m_titleLabel = new QLabel(title, this);
auto l = new QHBoxLayout(this);
l->setContentsMargins(0, 0, 0, 0);

View File

@@ -89,12 +89,18 @@ bool startAvdAsync(const QString &avdName)
QString findAvd(const QString &avdName)
{
const QList<AndroidDeviceInfo> devices = AndroidConfig::connectedDevices();
for (const AndroidDeviceInfo &device : devices) {
if (device.type != ProjectExplorer::IDevice::Emulator)
const QStringList lines = AndroidConfig::devicesCommandOutput();
for (const QString &line : lines) {
// skip the daemon logs
if (line.startsWith("* daemon"))
continue;
if (device.avdName == avdName)
return device.serialNumber;
const QString serialNumber = line.left(line.indexOf('\t')).trimmed();
if (!serialNumber.startsWith("emulator"))
continue;
if (AndroidConfig::getAvdName(serialNumber) == avdName)
return serialNumber;
}
return {};
}

View File

@@ -141,33 +141,6 @@ static QString buildToolsPackageMarker()
return QLatin1String(Constants::buildToolsPackageName) + ";";
}
static QString getAvdName(const QString &serialnumber)
{
const int index = serialnumber.indexOf(QLatin1String("-"));
if (index == -1)
return {};
bool ok;
const int port = serialnumber.mid(index + 1).toInt(&ok);
if (!ok)
return {};
QTcpSocket tcpSocket;
tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port);
if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection
return {};
tcpSocket.write("avd name\nexit\n");
tcpSocket.waitForDisconnected(500);
const QByteArrayList response = tcpSocket.readAll().split('\n');
// The input "avd name" might not be echoed as-is, but contain ASCII control sequences.
for (int i = response.size() - 1; i > 1; --i) {
if (response.at(i).startsWith("OK"))
return QString::fromLatin1(response.at(i - 1)).trimmed();
}
return {};
}
static QString getDeviceProperty(const QString &device, const QString &property)
{
// workaround for '????????????' serial numbers
@@ -311,6 +284,33 @@ static FilePath ndkSubPathFromQtVersion(const QtVersion &version)
// AndroidConfig
//////////////////////////////////
QString getAvdName(const QString &serialnumber)
{
const int index = serialnumber.indexOf(QLatin1String("-"));
if (index == -1)
return {};
bool ok;
const int port = serialnumber.mid(index + 1).toInt(&ok);
if (!ok)
return {};
QTcpSocket tcpSocket;
tcpSocket.connectToHost(QHostAddress(QHostAddress::LocalHost), port);
if (!tcpSocket.waitForConnected(100)) // Don't wait more than 100ms for a local connection
return {};
tcpSocket.write("avd name\nexit\n");
tcpSocket.waitForDisconnected(500);
const QByteArrayList response = tcpSocket.readAll().split('\n');
// The input "avd name" might not be echoed as-is, but contain ASCII control sequences.
for (int i = response.size() - 1; i > 1; --i) {
if (response.at(i).startsWith("OK"))
return QString::fromLatin1(response.at(i - 1)).trimmed();
}
return {};
}
QLatin1String displayName(const Abi &abi)
{
switch (abi.architecture()) {
@@ -674,66 +674,25 @@ FilePath keytoolPath()
return openJDKBinPath().pathAppended(keytoolName).withExecutableSuffix();
}
QList<AndroidDeviceInfo> connectedDevices(QString *error)
QStringList devicesCommandOutput()
{
QList<AndroidDeviceInfo> devices;
Process adbProc;
CommandLine cmd{adbToolPath(), {"devices"}};
adbProc.setCommand(cmd);
using namespace std::chrono_literals;
adbProc.runBlocking(30s);
if (adbProc.result() != ProcessResult::FinishedWithSuccess) {
if (error)
*error = Tr::tr("Could not run: %1").arg(cmd.toUserOutput());
return devices;
}
QStringList adbDevs = adbProc.allOutput().split('\n', Qt::SkipEmptyParts);
if (adbDevs.empty())
return devices;
Process adbProcess;
adbProcess.setCommand({adbToolPath(), {"devices"}});
adbProcess.runBlocking();
if (adbProcess.result() != ProcessResult::FinishedWithSuccess)
return {};
for (const QString &line : adbDevs) // remove the daemon logs
if (line.startsWith("* daemon"))
adbDevs.removeOne(line);
adbDevs.removeFirst(); // remove "List of devices attached" header line
// workaround for '????????????' serial numbers:
// can use "adb -d" when only one usb device attached
for (const QString &device : std::as_const(adbDevs)) {
const QString serialNo = device.left(device.indexOf('\t')).trimmed();
const QString deviceType = device.mid(device.indexOf('\t')).trimmed();
AndroidDeviceInfo dev;
dev.serialNumber = serialNo;
dev.type = serialNo.startsWith(QLatin1String("emulator")) ? IDevice::Emulator
: IDevice::Hardware;
dev.sdk = getSDKVersion(dev.serialNumber);
dev.cpuAbi = getAbis(dev.serialNumber);
if (deviceType == QLatin1String("unauthorized"))
dev.state = IDevice::DeviceConnected;
else if (deviceType == QLatin1String("offline"))
dev.state = IDevice::DeviceDisconnected;
else
dev.state = IDevice::DeviceReadyToUse;
if (dev.type == IDevice::Emulator) {
dev.avdName = getAvdName(dev.serialNumber);
if (dev.avdName.isEmpty())
dev.avdName = serialNo;
}
devices.push_back(dev);
}
Utils::sort(devices);
if (devices.isEmpty() && error)
*error = Tr::tr("No devices found in output of: %1").arg(cmd.toUserOutput());
return devices;
// mid(1) - remove "List of devices attached" header line.
// Example output: "List of devices attached\nemulator-5554\tdevice\n\n".
return adbProcess.allOutput().split('\n', Qt::SkipEmptyParts).mid(1);
}
bool isConnected(const QString &serialNumber)
{
const QList<AndroidDeviceInfo> devices = connectedDevices();
for (const AndroidDeviceInfo &device : devices) {
if (device.serialNumber == serialNumber)
const QStringList lines = devicesCommandOutput();
for (const QString &line : lines) {
// skip the daemon logs
if (!line.startsWith("* daemon") && line.left(line.indexOf('\t')).trimmed() == serialNumber)
return true;
}
return false;
@@ -1588,8 +1547,7 @@ void AndroidConfigurations::updateAndroidDevice()
IDevice::ConstPtr dev = devMgr->find(Constants::ANDROID_DEVICE_ID);
if (dev)
devMgr->removeDevice(dev->id());
AndroidDeviceManager::instance()->setupDevicesWatcher();
AndroidDeviceManager::setupDevicesWatcher();
}
#ifdef WITH_TESTS

View File

@@ -38,6 +38,7 @@ public:
namespace AndroidConfig {
QString getAvdName(const QString &serialnumber);
QStringList apiLevelNamesFor(const SdkPlatformList &platforms);
QString apiLevelNameFor(const SdkPlatform *platform);
@@ -88,7 +89,7 @@ Utils::FilePath makePathFromNdk(const Utils::FilePath &ndkLocation);
Utils::FilePath keytoolPath();
QList<AndroidDeviceInfo> connectedDevices(QString *error = nullptr);
QStringList devicesCommandOutput();
QString bestNdkPlatformMatch(int target, const QtSupport::QtVersion *qtVersion);

View File

@@ -39,6 +39,7 @@
#include <QTimer>
using namespace ProjectExplorer;
using namespace Tasking;
using namespace Utils;
namespace {
@@ -51,16 +52,160 @@ static constexpr char ipRegexStr[] = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}
static const QRegularExpression ipRegex = QRegularExpression(ipRegexStr);
static constexpr char wifiDevicePort[] = "5555";
enum TagModification { CommentOut, Uncomment };
static class AndroidDeviceManagerInstance *s_instance = nullptr;
class AndroidDeviceManagerInstance : public QObject
{
public:
AndroidDeviceManagerInstance(QObject *parent);
~AndroidDeviceManagerInstance()
{
QTC_ASSERT(s_instance == this, return);
s_instance = nullptr;
}
void setupDevicesWatcher();
void eraseAvd(const IDevice::Ptr &device, QWidget *parent);
Group m_avdListRecipe;
TaskTreeRunner m_avdListRunner;
std::unique_ptr<Process> m_removeAvdProcess;
QFileSystemWatcher m_avdFileSystemWatcher;
Guard m_avdPathGuard;
std::unique_ptr<Process> m_adbDeviceWatcherProcess;
};
static QString displayNameFromInfo(const AndroidDeviceInfo &info)
{
return info.type == IDevice::Hardware ? AndroidConfig::getProductModel(info.serialNumber)
: info.avdName;
}
static IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type)
{
const QStringList args = AndroidDeviceInfo::adbSelector(serial) << "shell" << "echo 1";
const SdkToolResult result = AndroidManager::runAdbCommand(args);
if (result.success())
return IDevice::DeviceReadyToUse;
else if (type == IDevice::Emulator || result.stdErr().contains("unauthorized"))
return IDevice::DeviceConnected;
return IDevice::DeviceDisconnected;
}
static void updateDeviceState(const IDevice::ConstPtr &device)
{
const AndroidDevice *dev = static_cast<const AndroidDevice *>(device.get());
const QString serial = dev->serialNumber();
DeviceManager *const devMgr = DeviceManager::instance();
const Id id = dev->id();
if (!serial.isEmpty())
devMgr->setDeviceState(id, getDeviceState(serial, dev->machineType()));
else if (dev->machineType() == IDevice::Emulator)
devMgr->setDeviceState(id, IDevice::DeviceConnected);
}
static void startAvd(const IDevice::Ptr &device, QWidget *parent)
{
Q_UNUSED(parent)
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(device.get());
const QString name = androidDev->avdName();
qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name));
auto future = Utils::asyncRun([name, device] {
const QString serialNumber = AndroidAvdManager::startAvd(name);
// Mark the AVD as ReadyToUse once we know it's started
if (!serialNumber.isEmpty()) {
DeviceManager *const devMgr = DeviceManager::instance();
devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse);
}
});
// TODO: use future!
}
static void setEmulatorArguments(QWidget *parent)
{
const QString helpUrl =
"https://developer.android.com/studio/run/emulator-commandline#startup-options";
QInputDialog dialog(parent ? parent : Core::ICore::dialogParent());
dialog.setWindowTitle(Tr::tr("Emulator Command-line Startup Options"));
dialog.setLabelText(Tr::tr("Emulator command-line startup options "
"(<a href=\"%1\">Help Web Page</a>):")
.arg(helpUrl));
dialog.setTextValue(AndroidConfig::emulatorArgs());
if (auto label = dialog.findChild<QLabel *>()) {
label->setOpenExternalLinks(true);
label->setMinimumWidth(500);
}
if (dialog.exec() == QDialog::Accepted)
AndroidConfig::setEmulatorArgs(dialog.textValue());
}
static QString emulatorName(const QString &serialNumber)
{
const QStringList args = AndroidDeviceInfo::adbSelector(serialNumber) << "emu" << "avd" << "name";
return AndroidManager::runAdbCommand(args).stdOut();
}
static QString getRunningAvdsSerialNumber(const QString &name)
{
const QStringList lines = AndroidConfig::devicesCommandOutput();
for (const QString &line : lines) {
// skip the daemon logs
if (line.startsWith("* daemon"))
continue;
const QString serialNumber = line.left(line.indexOf('\t')).trimmed();
if (!serialNumber.startsWith("emulator"))
continue;
const QString stdOut = emulatorName(serialNumber);
if (stdOut.isEmpty())
continue; // Not an avd
if (stdOut.left(stdOut.indexOf('\n')) == name)
return serialNumber;
}
return {};
}
static FilePath avdFilePath()
{
QString avdEnvVar = qtcEnvironmentVariable("ANDROID_AVD_HOME");
if (avdEnvVar.isEmpty()) {
avdEnvVar = qtcEnvironmentVariable("ANDROID_SDK_HOME");
if (avdEnvVar.isEmpty())
avdEnvVar = qtcEnvironmentVariable("HOME");
avdEnvVar.append("/.android/avd");
}
return FilePath::fromUserInput(avdEnvVar);
}
static IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info)
{
if (info.apiLevel < 0) {
qCWarning(androidDeviceLog) << "System image of the created AVD is nullptr";
return IDevice::Ptr();
}
AndroidDevice *dev = new AndroidDevice;
const Id deviceId = AndroidDevice::idFromAvdInfo(info);
dev->setupId(IDevice::AutoDetected, deviceId);
dev->setMachineType(IDevice::Emulator);
dev->settings()->displayName.setValue(info.name);
dev->setDeviceState(IDevice::DeviceConnected);
dev->setAvdPath(avdFilePath() / (info.name + ".avd"));
dev->setExtraData(Constants::AndroidAvdName, info.name);
dev->setExtraData(Constants::AndroidCpuAbi, {info.abi});
dev->setExtraData(Constants::AndroidSdk, info.apiLevel);
return IDevice::Ptr(dev);
}
class AndroidDeviceWidget : public IDeviceWidget
{
public:
AndroidDeviceWidget(const ProjectExplorer::IDevice::Ptr &device);
AndroidDeviceWidget(const IDevice::Ptr &device);
void updateDeviceFromUi() final {}
static QString dialogTitle();
@@ -70,6 +215,66 @@ public:
static bool questionDialog(const QString &question, QWidget *parent = nullptr);
};
static void setupWifiForDevice(const IDevice::Ptr &device, QWidget *parent)
{
if (device->deviceState() != IDevice::DeviceReadyToUse) {
AndroidDeviceWidget::infoDialog(
Tr::tr("The device has to be connected with ADB debugging "
"enabled to use this feature."), parent);
return;
}
const auto androidDev = static_cast<const AndroidDevice *>(device.get());
const QStringList adbSelector = AndroidDeviceInfo::adbSelector(androidDev->serialNumber());
// prepare port
QStringList args = adbSelector;
args.append({"tcpip", wifiDevicePort});
const SdkToolResult result = AndroidManager::runAdbCommand(args);
if (!result.success()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("Opening connection port %1 failed.").arg(wifiDevicePort),
parent);
return;
}
QTimer::singleShot(2000, parent, [adbSelector, parent] {
// Get device IP address
QStringList args = adbSelector;
args.append({"shell", "ip", "route"});
const SdkToolResult ipRes = AndroidManager::runAdbCommand(args);
if (!ipRes.success()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("Retrieving the device IP address failed."), parent);
return;
}
// Expected output from "ip route" is:
// 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.190
// where the ip of interest is at the end of the line
const QStringList ipParts = ipRes.stdOut().split(" ");
QString ip;
if (!ipParts.isEmpty()) {
ip = ipParts.last();
}
if (!ipRegex.match(ipParts.last()).hasMatch()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("The retrieved IP address is invalid."), parent);
return;
}
// Connect to device
args = adbSelector;
args.append({"connect", QString("%1:%2").arg(ip).arg(wifiDevicePort)});
const SdkToolResult connectRes = AndroidManager::runAdbCommand(args);
if (!connectRes.success()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("Connecting to the device IP \"%1\" failed.").arg(ip),
parent);
return;
}
});
}
AndroidDeviceWidget::AndroidDeviceWidget(const IDevice::Ptr &device)
: IDeviceWidget(device)
{
@@ -168,7 +373,7 @@ AndroidDevice::AndroidDevice()
addDeviceAction({Tr::tr("Refresh"), [](const IDevice::Ptr &device, QWidget *parent) {
Q_UNUSED(parent)
AndroidDeviceManager::instance()->updateDeviceState(device);
updateDeviceState(device);
}});
}
@@ -198,26 +403,26 @@ void AndroidDevice::addActionsIfNotFound()
if (machineType() == Emulator) {
if (!hasStartAction) {
addDeviceAction({startAvdAction, [](const IDevice::Ptr &device, QWidget *parent) {
AndroidDeviceManager::instance()->startAvd(device, parent);
startAvd(device, parent);
}});
}
if (!hasEraseAction) {
addDeviceAction({eraseAvdAction, [](const IDevice::Ptr &device, QWidget *parent) {
AndroidDeviceManager::instance()->eraseAvd(device, parent);
s_instance->eraseAvd(device, parent);
}});
}
if (!hasAvdArgumentsAction) {
addDeviceAction({avdArgumentsAction, [](const IDevice::Ptr &device, QWidget *parent) {
Q_UNUSED(device)
AndroidDeviceManager::instance()->setEmulatorArguments(parent);
setEmulatorArguments(parent);
}});
}
} else if (machineType() == Hardware && !ipRegex.match(id().toString()).hasMatch()) {
if (!hasSetupWifi) {
addDeviceAction({setupWifi, [](const IDevice::Ptr &device, QWidget *parent) {
AndroidDeviceManager::instance()->setupWifiForDevice(device, parent);
setupWifiForDevice(device, parent);
}});
}
}
@@ -247,7 +452,6 @@ AndroidDeviceInfo AndroidDevice::androidDeviceInfoFromIDevice(const IDevice *dev
info.avdPath = FilePath::fromSettings(dev->extraData(Constants::AndroidAvdPath));
info.sdk = dev->extraData(Constants::AndroidSdk).toInt();
info.type = dev->machineType();
return info;
}
@@ -318,8 +522,7 @@ QString AndroidDevice::serialNumber() const
const QString serialNumber = extraData(Constants::AndroidSerialNumber).toString();
if (machineType() == Hardware)
return serialNumber;
return AndroidDeviceManager::instance()->getRunningAvdsSerialNumber(avdName());
return getRunningAvdsSerialNumber(avdName());
}
QString AndroidDevice::avdName() const
@@ -413,395 +616,7 @@ void AndroidDevice::initAvdSettings()
m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat));
}
void AndroidDeviceManager::updateAvdList()
{
if (AndroidConfig::adbToolPath().exists())
m_avdListRunner.start(m_avdListRecipe);
}
IDevice::DeviceState AndroidDeviceManager::getDeviceState(const QString &serial,
IDevice::MachineType type) const
{
const QStringList args = AndroidDeviceInfo::adbSelector(serial) << "shell" << "echo 1";
const SdkToolResult result = AndroidManager::runAdbCommand(args);
if (result.success())
return IDevice::DeviceReadyToUse;
else if (type == IDevice::Emulator || result.stdErr().contains("unauthorized"))
return IDevice::DeviceConnected;
return IDevice::DeviceDisconnected;
}
void AndroidDeviceManager::updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device)
{
const AndroidDevice *dev = static_cast<const AndroidDevice *>(device.get());
const QString serial = dev->serialNumber();
DeviceManager *const devMgr = DeviceManager::instance();
const Id id = dev->id();
if (!serial.isEmpty())
devMgr->setDeviceState(id, getDeviceState(serial, dev->machineType()));
else if (dev->machineType() == IDevice::Emulator)
devMgr->setDeviceState(id, IDevice::DeviceConnected);
}
expected_str<void> AndroidDeviceManager::createAvd(const CreateAvdInfo &info, bool force)
{
CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name});
cmd.addArgs({"-k", info.sdkStylePath});
if (info.sdcardSize > 0)
cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)});
const QString deviceDef = info.deviceDefinition;
if (!deviceDef.isEmpty() && deviceDef != "Custom")
cmd.addArgs({"-d", deviceDef});
if (force)
cmd.addArg("-f");
Process process;
process.setProcessMode(ProcessMode::Writer);
process.setEnvironment(AndroidConfig::toolsEnvironment());
process.setCommand(cmd);
process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile"
QByteArray buffer;
QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] {
// This interaction is needed only if there is no "-d" arg for the avdmanager command.
buffer += process.readAllRawStandardOutput();
if (buffer.endsWith(QByteArray("]:"))) {
// truncate to last line
const int index = buffer.lastIndexOf('\n');
if (index != -1)
buffer = buffer.mid(index);
if (buffer.contains("hw.gpu.enabled"))
process.write("yes\n");
else
process.write("\n");
buffer.clear();
}
});
GuardLocker locker(m_avdPathGuard);
process.runBlocking();
if (process.result() != ProcessResult::FinishedWithSuccess)
return Utils::make_unexpected(process.exitMessage());
return {};
}
void AndroidDeviceManager::startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent)
{
Q_UNUSED(parent)
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(device.get());
const QString name = androidDev->avdName();
qCDebug(androidDeviceLog, "Starting Android AVD id \"%s\".", qPrintable(name));
auto future = Utils::asyncRun([name, device] {
const QString serialNumber = AndroidAvdManager::startAvd(name);
// Mark the AVD as ReadyToUse once we know it's started
if (!serialNumber.isEmpty()) {
DeviceManager *const devMgr = DeviceManager::instance();
devMgr->setDeviceState(device->id(), IDevice::DeviceReadyToUse);
}
});
// TODO: use future!
}
void AndroidDeviceManager::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
{
if (!device)
return;
if (device->machineType() == IDevice::Hardware)
return;
const QString name = static_cast<const AndroidDevice *>(device.get())->avdName();
const QString question
= Tr::tr("Erase the Android AVD \"%1\"?\nThis cannot be undone.").arg(name);
if (!AndroidDeviceWidget::questionDialog(question, parent))
return;
qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name);
m_removeAvdProcess.reset(new Process);
const CommandLine command(AndroidConfig::avdManagerToolPath(), {"delete", "avd", "-n", name});
qCDebug(androidDeviceLog).noquote() << "Running command (removeAvd):" << command.toUserOutput();
m_removeAvdProcess->setEnvironment(AndroidConfig::toolsEnvironment());
m_removeAvdProcess->setCommand(command);
connect(m_removeAvdProcess.get(), &Process::done, this, [this, device] {
const QString name = device->displayName();
if (m_removeAvdProcess->result() == ProcessResult::FinishedWithSuccess) {
qCDebug(androidDeviceLog, "Android AVD id \"%s\" removed from the system.",
qPrintable(name));
// Remove the device from QtC after it's been removed using avdmanager.
DeviceManager::instance()->removeDevice(device->id());
} else {
AndroidDeviceWidget::criticalDialog(Tr::tr("An error occurred while removing the "
"Android AVD \"%1\" using avdmanager tool.").arg(name));
}
m_removeAvdProcess.release()->deleteLater();
});
m_removeAvdProcess->start();
}
void AndroidDeviceManager::setupWifiForDevice(const IDevice::Ptr &device, QWidget *parent)
{
if (device->deviceState() != IDevice::DeviceReadyToUse) {
AndroidDeviceWidget::infoDialog(
Tr::tr("The device has to be connected with ADB debugging "
"enabled to use this feature."), parent);
return;
}
const auto androidDev = static_cast<const AndroidDevice *>(device.get());
const QStringList adbSelector = AndroidDeviceInfo::adbSelector(androidDev->serialNumber());
// prepare port
QStringList args = adbSelector;
args.append({"tcpip", wifiDevicePort});
const SdkToolResult result = AndroidManager::runAdbCommand(args);
if (!result.success()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("Opening connection port %1 failed.").arg(wifiDevicePort),
parent);
return;
}
QTimer::singleShot(2000, parent, [adbSelector, parent] {
// Get device IP address
QStringList args = adbSelector;
args.append({"shell", "ip", "route"});
const SdkToolResult ipRes = AndroidManager::runAdbCommand(args);
if (!ipRes.success()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("Retrieving the device IP address failed."), parent);
return;
}
// Expected output from "ip route" is:
// 192.168.1.0/24 dev wlan0 proto kernel scope link src 192.168.1.190
// where the ip of interest is at the end of the line
const QStringList ipParts = ipRes.stdOut().split(" ");
QString ip;
if (!ipParts.isEmpty()) {
ip = ipParts.last();
}
if (!ipRegex.match(ipParts.last()).hasMatch()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("The retrieved IP address is invalid."), parent);
return;
}
// Connect to device
args = adbSelector;
args.append({"connect", QString("%1:%2").arg(ip).arg(wifiDevicePort)});
const SdkToolResult connectRes = AndroidManager::runAdbCommand(args);
if (!connectRes.success()) {
AndroidDeviceWidget::criticalDialog(
Tr::tr("Connecting to the device IP \"%1\" failed.").arg(ip),
parent);
return;
}
});
}
QString AndroidDeviceManager::emulatorName(const QString &serialNumber) const
{
QStringList args = AndroidDeviceInfo::adbSelector(serialNumber);
args.append({"emu", "avd", "name"});
return AndroidManager::runAdbCommand(args).stdOut();
}
void AndroidDeviceManager::setEmulatorArguments(QWidget *parent)
{
const QString helpUrl =
"https://developer.android.com/studio/run/emulator-commandline#startup-options";
QInputDialog dialog(parent ? parent : Core::ICore::dialogParent());
dialog.setWindowTitle(Tr::tr("Emulator Command-line Startup Options"));
dialog.setLabelText(Tr::tr("Emulator command-line startup options "
"(<a href=\"%1\">Help Web Page</a>):")
.arg(helpUrl));
dialog.setTextValue(AndroidConfig::emulatorArgs());
if (auto label = dialog.findChild<QLabel*>()) {
label->setOpenExternalLinks(true);
label->setMinimumWidth(500);
}
if (dialog.exec() != QDialog::Accepted)
return;
AndroidConfig::setEmulatorArgs(dialog.textValue());
}
QString AndroidDeviceManager::getRunningAvdsSerialNumber(const QString &name) const
{
for (const AndroidDeviceInfo &dev : AndroidConfig::connectedDevices()) {
if (!dev.serialNumber.startsWith("emulator"))
continue;
const QString stdOut = emulatorName(dev.serialNumber);
if (stdOut.isEmpty())
continue; // Not an avd
const QStringList outputLines = stdOut.split('\n');
if (outputLines.size() > 1 && outputLines.first() == name)
return dev.serialNumber;
}
return {};
}
static FilePath avdFilePath()
{
QString avdEnvVar = qtcEnvironmentVariable("ANDROID_AVD_HOME");
if (avdEnvVar.isEmpty()) {
avdEnvVar = qtcEnvironmentVariable("ANDROID_SDK_HOME");
if (avdEnvVar.isEmpty())
avdEnvVar = qtcEnvironmentVariable("HOME");
avdEnvVar.append("/.android/avd");
}
return FilePath::fromUserInput(avdEnvVar);
}
void AndroidDeviceManager::setupDevicesWatcher()
{
if (!AndroidConfig::adbToolPath().exists()) {
qCDebug(androidDeviceLog) << "Cannot start ADB device watcher"
<< "because adb path does not exist.";
return;
}
if (!m_adbDeviceWatcherProcess)
m_adbDeviceWatcherProcess.reset(new Process(this));
if (m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "ADB device watcher is already running.";
return;
}
connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] {
if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) {
qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:"
<< m_adbDeviceWatcherProcess->errorString();
if (!m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now.";
QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start);
}
}
qCDebug(androidDeviceLog) << "ADB device watcher finished.";
});
m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) {
qCDebug(androidDeviceLog) << "ADB device watcher error" << error; });
m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) {
handleDevicesListChange(output);
});
const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}};
m_adbDeviceWatcherProcess->setCommand(command);
m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir());
m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment());
m_adbDeviceWatcherProcess->start();
// Setup AVD filesystem watcher to listen for changes when an avd is created/deleted,
// or started/stopped
m_avdFileSystemWatcher.addPath(avdFilePath().toString());
connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] {
if (!m_avdPathGuard.isLocked())
updateAvdList();
});
// Call initial update
updateAvdList();
}
IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &info)
{
if (info.apiLevel < 0) {
qCWarning(androidDeviceLog) << "System image of the created AVD is nullptr";
return IDevice::Ptr();
}
AndroidDevice *dev = new AndroidDevice;
const Utils::Id deviceId = AndroidDevice::idFromAvdInfo(info);
dev->setupId(IDevice::AutoDetected, deviceId);
dev->setMachineType(IDevice::Emulator);
dev->settings()->displayName.setValue(info.name);
dev->setDeviceState(IDevice::DeviceConnected);
dev->setAvdPath(avdFilePath() / (info.name + ".avd"));
dev->setExtraData(Constants::AndroidAvdName, info.name);
dev->setExtraData(Constants::AndroidCpuAbi, {info.abi});
dev->setExtraData(Constants::AndroidSdk, info.apiLevel);
return IDevice::Ptr(dev);
}
void AndroidDeviceManager::handleAvdListChange(const AndroidDeviceInfoList &avdList)
{
DeviceManager *const devMgr = DeviceManager::instance();
QList<Id> existingAvds;
for (int i = 0; i < devMgr->deviceCount(); ++i) {
const IDevice::ConstPtr dev = devMgr->deviceAt(i);
const bool isEmulator = dev->machineType() == IDevice::Emulator;
if (isEmulator && dev->type() == Constants::ANDROID_DEVICE_TYPE)
existingAvds.append(dev->id());
}
QList<Id> connectedDevs;
for (const AndroidDeviceInfo &item : avdList) {
const Id deviceId = AndroidDevice::idFromDeviceInfo(item);
const QString displayName = displayNameFromInfo(item);
IDevice::ConstPtr dev = devMgr->find(deviceId);
if (dev) {
const auto androidDev = static_cast<const AndroidDevice *>(dev.get());
// DeviceManager doens't seem to have a way to directly update the name, if the name
// of the device has changed, remove it and register it again with the new name.
// Also account for the case of an AVD registered through old QC which might have
// invalid data by checking if the avdPath is not empty.
if (dev->displayName() != displayName || androidDev->avdPath().toString().isEmpty()) {
devMgr->removeDevice(dev->id());
} else {
// Find the state of the AVD retrieved from the AVD watcher
const QString serial = getRunningAvdsSerialNumber(item.avdName);
if (!serial.isEmpty()) {
const IDevice::DeviceState state = getDeviceState(serial, IDevice::Emulator);
if (dev->deviceState() != state) {
devMgr->setDeviceState(dev->id(), state);
qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.",
dev->id().toString().toUtf8().data());
}
} else {
devMgr->setDeviceState(dev->id(), IDevice::DeviceConnected);
}
connectedDevs.append(dev->id());
continue;
}
}
AndroidDevice *newDev = new AndroidDevice();
newDev->setupId(IDevice::AutoDetected, deviceId);
newDev->settings()->displayName.setValue(displayName);
newDev->setMachineType(item.type);
newDev->setDeviceState(item.state);
newDev->setExtraData(Constants::AndroidAvdName, item.avdName);
newDev->setExtraData(Constants::AndroidSerialNumber, item.serialNumber);
newDev->setExtraData(Constants::AndroidCpuAbi, item.cpuAbi);
newDev->setExtraData(Constants::AndroidSdk, item.sdk);
newDev->setAvdPath(item.avdPath);
qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".",
newDev->id().toString().toUtf8().data());
const IDevice::ConstPtr constNewDev = IDevice::ConstPtr(newDev);
devMgr->addDevice(IDevice::ConstPtr(constNewDev));
connectedDevs.append(constNewDev->id());
}
// Set devices no longer connected to disconnected state.
for (const Id &id : existingAvds) {
if (!connectedDevs.contains(id)) {
qCDebug(androidDeviceLog, "Removing AVD id \"%s\" because it no longer exists.",
id.toString().toUtf8().data());
devMgr->removeDevice(id);
}
}
}
void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber)
static void handleDevicesListChange(const QString &serialNumber)
{
DeviceManager *const devMgr = DeviceManager::instance();
const QStringList serialBits = serialNumber.split('\t');
@@ -870,15 +685,6 @@ void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber)
}
}
static AndroidDeviceManager *s_instance = nullptr;
AndroidDeviceManager *AndroidDeviceManager::instance()
{
return s_instance;
}
enum TagModification { CommentOut, Uncomment };
static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification)
{
if (!avdPath.exists())
@@ -905,15 +711,85 @@ static void modifyManufacturerTag(const FilePath &avdPath, TagModification modif
saver.finalize();
}
AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
static void handleAvdListChange(const AndroidDeviceInfoList &avdList)
{
DeviceManager *const devMgr = DeviceManager::instance();
QList<Id> existingAvds;
for (int i = 0; i < devMgr->deviceCount(); ++i) {
const IDevice::ConstPtr dev = devMgr->deviceAt(i);
const bool isEmulator = dev->machineType() == IDevice::Emulator;
if (isEmulator && dev->type() == Constants::ANDROID_DEVICE_TYPE)
existingAvds.append(dev->id());
}
QList<Id> connectedDevs;
for (const AndroidDeviceInfo &item : avdList) {
const Id deviceId = AndroidDevice::idFromDeviceInfo(item);
const QString displayName = displayNameFromInfo(item);
IDevice::ConstPtr dev = devMgr->find(deviceId);
if (dev) {
const auto androidDev = static_cast<const AndroidDevice *>(dev.get());
// DeviceManager doens't seem to have a way to directly update the name, if the name
// of the device has changed, remove it and register it again with the new name.
// Also account for the case of an AVD registered through old QC which might have
// invalid data by checking if the avdPath is not empty.
if (dev->displayName() != displayName || androidDev->avdPath().toString().isEmpty()) {
devMgr->removeDevice(dev->id());
} else {
// Find the state of the AVD retrieved from the AVD watcher
const QString serial = getRunningAvdsSerialNumber(item.avdName);
if (!serial.isEmpty()) {
const IDevice::DeviceState state = getDeviceState(serial, IDevice::Emulator);
if (dev->deviceState() != state) {
devMgr->setDeviceState(dev->id(), state);
qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.",
dev->id().toString().toUtf8().data());
}
} else {
devMgr->setDeviceState(dev->id(), IDevice::DeviceConnected);
}
connectedDevs.append(dev->id());
continue;
}
}
AndroidDevice *newDev = new AndroidDevice;
newDev->setupId(IDevice::AutoDetected, deviceId);
newDev->settings()->displayName.setValue(displayName);
newDev->setMachineType(item.type);
newDev->setDeviceState(item.state);
newDev->setExtraData(Constants::AndroidAvdName, item.avdName);
newDev->setExtraData(Constants::AndroidSerialNumber, item.serialNumber);
newDev->setExtraData(Constants::AndroidCpuAbi, item.cpuAbi);
newDev->setExtraData(Constants::AndroidSdk, item.sdk);
newDev->setAvdPath(item.avdPath);
qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".",
newDev->id().toString().toUtf8().data());
const IDevice::ConstPtr constNewDev = IDevice::ConstPtr(newDev);
devMgr->addDevice(IDevice::ConstPtr(constNewDev));
connectedDevs.append(constNewDev->id());
}
// Set devices no longer connected to disconnected state.
for (const Id &id : existingAvds) {
if (!connectedDevs.contains(id)) {
qCDebug(androidDeviceLog, "Removing AVD id \"%s\" because it no longer exists.",
id.toString().toUtf8().data());
devMgr->removeDevice(id);
}
}
}
AndroidDeviceManagerInstance::AndroidDeviceManagerInstance(QObject *parent)
: QObject(parent)
, m_avdListRecipe{}
{
QTC_ASSERT(!s_instance, return);
s_instance = this;
using namespace Tasking;
const Storage<FilePaths> storage;
const LoopUntil iterator([storage](int iteration) {
@@ -926,7 +802,7 @@ AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
process.setEnvironment(AndroidConfig::toolsEnvironment());
process.setCommand(cmd);
};
const auto onProcessDone = [this, storage](const Process &process, DoneWith result) {
const auto onProcessDone = [storage](const Process &process, DoneWith result) {
const QString output = process.allOutput();
if (result != DoneWith::Success) {
qCDebug(androidDeviceLog)
@@ -962,15 +838,153 @@ AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
};
}
AndroidDeviceManager::~AndroidDeviceManager()
void AndroidDeviceManagerInstance::setupDevicesWatcher()
{
QTC_ASSERT(s_instance == this, return);
s_instance = nullptr;
if (!AndroidConfig::adbToolPath().exists()) {
qCDebug(androidDeviceLog) << "Cannot start ADB device watcher"
<< "because adb path does not exist.";
return;
}
if (!m_adbDeviceWatcherProcess)
m_adbDeviceWatcherProcess.reset(new Process(this));
if (m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "ADB device watcher is already running.";
return;
}
connect(m_adbDeviceWatcherProcess.get(), &Process::done, this, [this] {
if (m_adbDeviceWatcherProcess->error() != QProcess::UnknownError) {
qCDebug(androidDeviceLog) << "ADB device watcher encountered an error:"
<< m_adbDeviceWatcherProcess->errorString();
if (!m_adbDeviceWatcherProcess->isRunning()) {
qCDebug(androidDeviceLog) << "Restarting the ADB device watcher now.";
QTimer::singleShot(0, m_adbDeviceWatcherProcess.get(), &Process::start);
}
}
qCDebug(androidDeviceLog) << "ADB device watcher finished.";
});
m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) {
qCDebug(androidDeviceLog) << "ADB device watcher error" << error; });
m_adbDeviceWatcherProcess->setStdOutLineCallback([](const QString &output) {
handleDevicesListChange(output);
});
const CommandLine command{AndroidConfig::adbToolPath(), {"track-devices"}};
m_adbDeviceWatcherProcess->setCommand(command);
m_adbDeviceWatcherProcess->setWorkingDirectory(command.executable().parentDir());
m_adbDeviceWatcherProcess->setEnvironment(AndroidConfig::toolsEnvironment());
m_adbDeviceWatcherProcess->start();
// Setup AVD filesystem watcher to listen for changes when an avd is created/deleted,
// or started/stopped
m_avdFileSystemWatcher.addPath(avdFilePath().toString());
connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] {
if (!m_avdPathGuard.isLocked())
AndroidDeviceManager::updateAvdList();
});
// Call initial update
AndroidDeviceManager::updateAvdList();
}
void AndroidDeviceManagerInstance::eraseAvd(const IDevice::Ptr &device, QWidget *parent)
{
if (!device)
return;
if (device->machineType() == IDevice::Hardware)
return;
const QString name = static_cast<const AndroidDevice *>(device.get())->avdName();
const QString question
= Tr::tr("Erase the Android AVD \"%1\"?\nThis cannot be undone.").arg(name);
if (!AndroidDeviceWidget::questionDialog(question, parent))
return;
qCDebug(androidDeviceLog) << QString("Erasing Android AVD \"%1\" from the system.").arg(name);
m_removeAvdProcess.reset(new Process);
const CommandLine command(AndroidConfig::avdManagerToolPath(), {"delete", "avd", "-n", name});
qCDebug(androidDeviceLog).noquote() << "Running command (removeAvd):" << command.toUserOutput();
m_removeAvdProcess->setEnvironment(AndroidConfig::toolsEnvironment());
m_removeAvdProcess->setCommand(command);
connect(m_removeAvdProcess.get(), &Process::done, this, [this, device] {
const QString name = device->displayName();
if (m_removeAvdProcess->result() == ProcessResult::FinishedWithSuccess) {
qCDebug(androidDeviceLog, "Android AVD id \"%s\" removed from the system.",
qPrintable(name));
// Remove the device from QtC after it's been removed using avdmanager.
DeviceManager::instance()->removeDevice(device->id());
} else {
AndroidDeviceWidget::criticalDialog(Tr::tr("An error occurred while removing the "
"Android AVD \"%1\" using avdmanager tool.").arg(name));
}
m_removeAvdProcess.release()->deleteLater();
});
m_removeAvdProcess->start();
}
namespace AndroidDeviceManager {
void setupDevicesWatcher() { s_instance->setupDevicesWatcher(); }
void updateAvdList()
{
if (AndroidConfig::adbToolPath().exists())
s_instance->m_avdListRunner.start(s_instance->m_avdListRecipe);
}
expected_str<void> createAvd(const CreateAvdInfo &info, bool force)
{
CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name});
cmd.addArgs({"-k", info.sdkStylePath});
if (info.sdcardSize > 0)
cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)});
const QString deviceDef = info.deviceDefinition;
if (!deviceDef.isEmpty() && deviceDef != "Custom")
cmd.addArgs({"-d", deviceDef});
if (force)
cmd.addArg("-f");
Process process;
process.setProcessMode(ProcessMode::Writer);
process.setEnvironment(AndroidConfig::toolsEnvironment());
process.setCommand(cmd);
process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile"
QByteArray buffer;
QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] {
// This interaction is needed only if there is no "-d" arg for the avdmanager command.
buffer += process.readAllRawStandardOutput();
if (buffer.endsWith(QByteArray("]:"))) {
// truncate to last line
const int index = buffer.lastIndexOf('\n');
if (index != -1)
buffer = buffer.mid(index);
if (buffer.contains("hw.gpu.enabled"))
process.write("yes\n");
else
process.write("\n");
buffer.clear();
}
});
GuardLocker locker(s_instance->m_avdPathGuard);
process.runBlocking();
if (process.result() != ProcessResult::FinishedWithSuccess)
return Utils::make_unexpected(process.exitMessage());
return {};
}
} // namespace AndroidDeviceManager
// Factory
class AndroidDeviceFactory final : public ProjectExplorer::IDeviceFactory
class AndroidDeviceFactory final : public IDeviceFactory
{
public:
AndroidDeviceFactory()
@@ -990,7 +1004,7 @@ public:
if (dialog.exec() != QDialog::Accepted)
return IDevice::Ptr();
const IDevice::Ptr dev = AndroidDeviceManager::createDeviceFromInfo(dialog.avdInfo());
const IDevice::Ptr dev = createDeviceFromInfo(dialog.avdInfo());
if (const auto androidDev = static_cast<AndroidDevice *>(dev.get())) {
qCDebug(androidDeviceLog, "Created new Android AVD id \"%s\".",
qPrintable(androidDev->avdName()));
@@ -1010,7 +1024,7 @@ void setupAndroidDevice()
void setupAndroidDeviceManager(QObject *guard)
{
(void) new AndroidDeviceManager(guard);
(void) new AndroidDeviceManagerInstance(guard);
}
} // Android::Internal

View File

@@ -70,44 +70,13 @@ private:
std::unique_ptr<QSettings> m_avdSettings;
};
class AndroidDeviceManager : public QObject
{
public:
static AndroidDeviceManager *instance();
void setupDevicesWatcher();
void updateAvdList();
IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) const;
void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device);
namespace AndroidDeviceManager {
Utils::expected_str<void> createAvd(const CreateAvdInfo &info, bool force);
void startAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr);
void eraseAvd(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr);
void setupWifiForDevice(const ProjectExplorer::IDevice::Ptr &device, QWidget *parent = nullptr);
void setupDevicesWatcher();
void updateAvdList();
Utils::expected_str<void> createAvd(const CreateAvdInfo &info, bool force);
void setEmulatorArguments(QWidget *parent = nullptr);
QString getRunningAvdsSerialNumber(const QString &name) const;
static ProjectExplorer::IDevice::Ptr createDeviceFromInfo(const CreateAvdInfo &info);
private:
explicit AndroidDeviceManager(QObject *parent);
~AndroidDeviceManager();
void handleDevicesListChange(const QString &serialNumber);
void handleAvdListChange(const AndroidDeviceInfoList &avdList);
QString emulatorName(const QString &serialNumber) const;
Tasking::Group m_avdListRecipe;
Tasking::TaskTreeRunner m_avdListRunner;
std::unique_ptr<Utils::Process> m_removeAvdProcess;
QFileSystemWatcher m_avdFileSystemWatcher;
Utils::Guard m_avdPathGuard;
std::unique_ptr<Utils::Process> m_adbDeviceWatcherProcess;
friend void setupAndroidDeviceManager(QObject *guard);
};
} // namespace AndroidDeviceManager
void setupAndroidDevice();
void setupAndroidDeviceManager(QObject *guard);

View File

@@ -628,17 +628,15 @@ void AndroidRunnerWorker::asyncStart()
using namespace Tasking;
const Storage<PidUserPair> pidStorage;
const LoopUntil iterator([pidStorage](int) { return pidStorage->first <= 0; });
const FilePath adbPath = AndroidConfig::adbToolPath();
const QStringList args = selector();
const auto onPidSetup = [adbPath, args, packageName = m_packageName,
isPreNougat = m_isPreNougat](Process &process) {
const QString pidScript = isPreNougat
const QString pidScript = m_isPreNougat
? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done")
: QString("pidof -s '%1'").arg(packageName);
process.setCommand({adbPath, args + QStringList{"shell", pidScript}});
: QString("pidof -s '%1'").arg(m_packageName);
const auto onPidSetup = [adbPath, args, pidScript](Process &process) {
process.setCommand({adbPath, {args, "shell", pidScript}});
};
const auto onPidDone = [pidStorage, packageName = m_packageName,
isPreNougat = m_isPreNougat](const Process &process) {
@@ -650,8 +648,8 @@ void AndroidRunnerWorker::asyncStart()
};
const auto onUserSetup = [pidStorage, adbPath, args](Process &process) {
process.setCommand({adbPath, args
+ QStringList{"shell", "ps", "-o", "user", "-p", QString::number(pidStorage->first)}});
process.setCommand({adbPath, {args, "shell", "ps", "-o", "user", "-p",
QString::number(pidStorage->first)}});
};
const auto onUserDone = [pidStorage](const Process &process) {
const QString out = process.allOutput();
@@ -674,10 +672,11 @@ void AndroidRunnerWorker::asyncStart()
const Group root {
pidStorage,
onGroupSetup([pidStorage] { *pidStorage = {-1, 0}; }),
Group {
iterator,
Forever {
stopOnSuccess,
ProcessTask(onPidSetup, onPidDone, CallDoneIf::Success),
TimeoutTask([](std::chrono::milliseconds &timeout) { timeout = 200ms; })
TimeoutTask([](std::chrono::milliseconds &timeout) { timeout = 200ms; },
[] { return DoneResult::Error; })
}.withTimeout(45s),
ProcessTask(onUserSetup, onUserDone, CallDoneIf::Success),
onGroupDone([pidStorage, this] { onProcessIdChanged(*pidStorage); })

View File

@@ -131,15 +131,15 @@ int AvdDialog::exec()
const CreateAvdInfo avdInfo{si->sdkStylePath(), si->apiLevel(), name(), abi(),
deviceDefinition(), sdcardSize()};
const auto result = AndroidDeviceManager::instance()->createAvd(
avdInfo, m_overwriteCheckBox->isChecked());
const auto result = AndroidDeviceManager::createAvd(avdInfo,
m_overwriteCheckBox->isChecked());
if (!result) {
QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Create new AVD"),
result.error());
return QDialog::Rejected;
}
m_createdAvdInfo = avdInfo;
AndroidDeviceManager::instance()->updateAvdList();
AndroidDeviceManager::updateAvdList();
}
return execResult;
}

View File

@@ -1,4 +1,8 @@
find_package(Qt6 COMPONENTS Charts QUIET)
add_qtc_plugin(AppStatisticsMonitor
CONDITION TARGET Qt6::Charts
DEPENDS Qt6::Charts
SKIP_TRANSLATION
PLUGIN_DEPENDS Core ProjectExplorer
SOURCES

View File

@@ -3,6 +3,9 @@ QtcPlugin {
Depends { name: "Core" }
Depends { name: "ProjectExplorer" }
Depends { name: "Qt.charts"; required: false }
condition: Qt.charts.present
files: [
"appstatisticsmonitorplugin.cpp",

View File

@@ -5,11 +5,19 @@
#include <utils/theme/theme.h>
#include <QAbstractAxis>
#include <QChartView>
#include <QDebug>
#include <QPaintEvent>
#include <QPainter>
#include <QPen>
#include <QPointF>
#include <QRandomGenerator>
#include <QSplineSeries>
#include <QString>
#include <QValueAxis>
using namespace Utils;
namespace AppStatisticsMonitor::Internal {
@@ -17,6 +25,100 @@ static const int padding = 40;
static const int numPadding = 10;
static const QRectF dataRangeDefault = QRectF(0, 0, 5, 1);
AppStatisticsMonitorChart::AppStatisticsMonitorChart(
const QString &name, QGraphicsItem *parent, Qt::WindowFlags wFlags)
: QChart(QChart::ChartTypeCartesian, parent, wFlags)
, m_series(new QLineSeries(this))
, m_axisX(new QValueAxis())
, m_axisY(new QValueAxis())
, m_point(0, 0)
, m_chartView(new QChartView(this))
, m_name(name)
{
m_chartView->setMinimumHeight(200);
m_chartView->setMinimumWidth(400);
const QBrush brushTitle(creatorColor(Theme::Token_Text_Muted));
const QBrush brush(creatorColor(Theme::Token_Background_Default));
const QPen penBack(creatorColor(Theme::Token_Text_Muted));
const QPen penAxis(creatorColor(Theme::Token_Text_Muted));
setTitleBrush(brushTitle);
setBackgroundBrush(brush);
setBackgroundPen(penBack);
m_axisX->setLinePen(penAxis);
m_axisY->setLinePen(penAxis);
m_axisX->setLabelsColor(creatorColor(Theme::Token_Text_Muted));
m_axisY->setLabelsColor(creatorColor(Theme::Token_Text_Muted));
QPen pen(creatorColor(Theme::Token_Accent_Default));
pen.setWidth(2);
m_series->setPen(pen);
setTitle(m_name + " " + QString::number(m_point.y(), 'g', 4) + "%");
m_chartView->setRenderHint(QPainter::Antialiasing);
m_chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
addSeries(m_series);
addAxis(m_axisX, Qt::AlignBottom);
addAxis(m_axisY, Qt::AlignLeft);
m_series->attachAxis(m_axisX);
m_series->attachAxis(m_axisY);
m_axisX->applyNiceNumbers();
m_axisY->applyNiceNumbers();
legend()->hide();
clear();
}
QChartView *AppStatisticsMonitorChart::chartView()
{
return m_chartView;
}
void AppStatisticsMonitorChart::addNewPoint(const QPointF &point)
{
m_point = point;
if (m_axisY->max() < m_point.y())
m_axisY->setRange(0, qRound(m_point.y()) + 1);
m_axisX->setRange(0, qRound(m_point.x()) + 1);
setTitle(m_name + " " + QString::number(m_point.y(), 'g', 4) + "%");
m_series->append(m_point);
}
void AppStatisticsMonitorChart::loadNewProcessData(const QList<double> &data)
{
clear();
QList<QPointF> points{{0, 0}};
int i = 0;
double max_y = 0;
for (double e : qAsConst(data)) {
points.push_back({double(++i), e});
max_y = qMax(max_y, e);
}
m_axisY->setRange(0, qRound(max_y) + 1);
m_axisX->setRange(0, data.size() + 1);
m_series->clear();
m_series->append(points);
}
void AppStatisticsMonitorChart::clear()
{
m_axisX->setRange(0, 5);
m_axisY->setRange(0, 1);
m_series->clear();
m_series->append(0, 0);
}
double AppStatisticsMonitorChart::lastPointX() const
{
return m_point.x();
}
//---------------------- Chart -----------------------
Chart::Chart(const QString &name, QWidget *parent)
: QWidget(parent)
, m_name(name)
@@ -31,7 +133,7 @@ void Chart::addNewPoint(const QPointF &point)
update();
}
void Chart::loadNewProcessData(QList<double> data)
void Chart::loadNewProcessData(const QList<double> &data)
{
clear();
for (long i = 0; i < data.size(); ++i) {
@@ -59,7 +161,7 @@ void Chart::paintEvent(QPaintEvent *event)
Q_UNUSED(event);
QPainter painter(this);
painter.fillRect(rect(), Utils::creatorColor(Utils::Theme::Token_Background_Default));
painter.fillRect(rect(), creatorColor(Theme::Token_Background_Default));
// add the name of the chart in the middle of the widget width and on the top
painter.drawText(
@@ -74,10 +176,10 @@ void Chart::paintEvent(QPaintEvent *event)
double xPos = padding + (x - dataRange.left()) * m_xScale;
if (xPos < padding || xPos > width() - padding)
continue;
painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default));
painter.setPen(creatorColor(Theme::Token_Foreground_Default));
painter.drawLine(xPos, padding, xPos, height() - padding);
painter.setPen(Utils::creatorColor(Utils::Theme::Token_Text_Muted));
painter.setPen(creatorColor(Theme::Token_Text_Muted));
painter.drawText(xPos, height() - numPadding, QString::number(x));
}
@@ -86,18 +188,18 @@ void Chart::paintEvent(QPaintEvent *event)
if (yPos < padding || yPos > height() - padding)
continue;
painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default));
painter.setPen(creatorColor(Theme::Token_Foreground_Default));
painter.drawLine(padding, yPos, width() - padding, yPos);
painter.setPen(Utils::creatorColor(Utils::Theme::Token_Text_Muted));
painter.setPen(creatorColor(Theme::Token_Text_Muted));
painter.drawText(numPadding, yPos, QString::number(y));
}
painter.setPen(Utils::creatorColor(Utils::Theme::Token_Foreground_Default));
painter.setPen(creatorColor(Theme::Token_Foreground_Default));
painter.drawLine(padding, height() - padding, width() - padding, height() - padding); // X axis
painter.drawLine(padding, height() - padding, padding, padding); // Y axis
QPen pen(Utils::creatorColor(Utils::Theme::Token_Accent_Default));
QPen pen(creatorColor(Theme::Token_Accent_Default));
pen.setWidth(2);
painter.setPen(pen);
painter.setRenderHint(QPainter::Antialiasing);

View File

@@ -2,21 +2,51 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#pragma once
#include <QChart>
#include <QList>
#include <QPaintEvent>
#include <QPointF>
#include <QRectF>
#include <QWidget>
QT_BEGIN_NAMESPACE
class QChartView;
class QLineSeries;
class QValueAxis;
QT_END_NAMESPACE
namespace AppStatisticsMonitor::Internal {
class AppStatisticsMonitorChart : public QChart
{
public:
AppStatisticsMonitorChart(
const QString &name, QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {});
void addNewPoint(const QPointF &point);
void loadNewProcessData(const QList<double> &data);
double lastPointX() const;
void clear();
QChartView *chartView();
private:
QLineSeries *m_series;
QStringList m_titles;
QValueAxis *m_axisX;
QValueAxis *m_axisY;
QPointF m_point;
QChartView *m_chartView;
QString m_name;
};
class Chart : public QWidget
{
public:
Chart(const QString &name, QWidget *parent = nullptr);
void addNewPoint(const QPointF &point);
void loadNewProcessData(QList<double> data);
void loadNewProcessData(const QList<double> &data);
double lastPointX() const;
void clear();

View File

@@ -11,6 +11,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/runcontrol.h>
#include <QChartView>
#include <QFormLayout>
#include <QGraphicsItem>
#include <QHash>
@@ -24,15 +25,15 @@ class AppStatisticsMonitorView : public QWidget
{
public:
explicit AppStatisticsMonitorView(
AppStatisticsMonitorManager *appStatisticManager, QWidget *parent = nullptr);
AppStatisticsMonitorManager *appStatisticManager);
~AppStatisticsMonitorView() override;
private:
QComboBox *m_comboBox;
Chart *m_memChart;
Chart *m_cpuChart;
std::unique_ptr<AppStatisticsMonitorChart> m_chartMem;
std::unique_ptr<AppStatisticsMonitorChart> m_chartCpu;
AppStatisticsMonitorManager *m_manager;
};
@@ -114,25 +115,21 @@ QHash<qint64, QString> AppStatisticsMonitorManager::pidNameMap() const
}
// AppStatisticsMonitorView
AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *dapManager, QWidget *)
: m_manager(dapManager)
AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *appManager)
: m_manager(appManager)
{
auto layout = new QVBoxLayout;
auto form = new QFormLayout;
setLayout(layout);
m_comboBox = new QComboBox();
m_comboBox = new QComboBox(this);
form->addRow(m_comboBox);
m_memChart = new Chart("Memory consumption ");
m_memChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_memChart->clear();
form->addRow(m_memChart);
m_chartMem = std::make_unique<AppStatisticsMonitorChart>(tr("Memory consumption"));
m_chartCpu = std::make_unique<AppStatisticsMonitorChart>(tr("CPU consumption"));
m_cpuChart = new Chart("CPU consumption ");
m_cpuChart->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_cpuChart->clear();
form->addRow(m_cpuChart);
form->addRow(m_chartMem->chartView());
form->addRow(m_chartCpu->chartView());
layout->addLayout(form);
@@ -142,16 +139,19 @@ AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *
}
m_comboBox->setCurrentIndex(m_comboBox->count() - 1);
m_memChart->clear();
m_cpuChart->clear();
m_chartCpu->clear();
m_chartMem->clear();
auto updateCharts = [this](int index) {
m_manager->setCurrentDataProvider(m_comboBox->itemData(index).toLongLong());
if (m_manager->currentDataProvider() != nullptr) {
m_memChart->loadNewProcessData(
m_manager->currentDataProvider()->memoryConsumptionHistory());
m_cpuChart->loadNewProcessData(
m_manager->currentDataProvider()->cpuConsumptionHistory());
const QList<double> &memConsumptionHistory
= m_manager->currentDataProvider()->memoryConsumptionHistory();
const QList<double> &cpuConsumptionHistory
= m_manager->currentDataProvider()->cpuConsumptionHistory();
m_chartMem->loadNewProcessData(memConsumptionHistory);
m_chartCpu->loadNewProcessData(cpuConsumptionHistory);
}
};
@@ -170,16 +170,17 @@ AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *
if (pid != m_comboBox->currentData()) {
m_comboBox->addItem(name + " : " + QString::number(pid), pid);
m_memChart->clear();
m_cpuChart->clear();
m_chartMem->clear();
m_chartCpu->clear();
m_comboBox->setCurrentIndex(m_comboBox->count() - 1);
}
});
connect(m_manager, &AppStatisticsMonitorManager::appStoped, this, [this](qint64 pid) {
m_memChart->addNewPoint({m_memChart->lastPointX() + 1, 0});
m_cpuChart->addNewPoint({m_cpuChart->lastPointX() + 1, 0});
m_chartMem->addNewPoint({m_chartMem->lastPointX() + 1, 0});
m_chartCpu->addNewPoint({m_chartCpu->lastPointX() + 1, 0});
const int indx = m_comboBox->findData(pid);
if (indx != -1)
m_comboBox->removeItem(indx);
@@ -188,10 +189,10 @@ AppStatisticsMonitorView::AppStatisticsMonitorView(AppStatisticsMonitorManager *
connect(m_manager, &AppStatisticsMonitorManager::newDataAvailable, this, [this] {
const IDataProvider *currentDataProvider = m_manager->currentDataProvider();
if (currentDataProvider != nullptr) {
m_memChart->addNewPoint(
m_chartMem->addNewPoint(
{(double) currentDataProvider->memoryConsumptionHistory().size(),
currentDataProvider->memoryConsumptionLast()});
m_cpuChart->addNewPoint(
m_chartCpu->addNewPoint(
{(double) currentDataProvider->cpuConsumptionHistory().size(),
currentDataProvider->cpuConsumptionLast()});
}

View File

@@ -53,13 +53,12 @@ void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &op
const TestResult testResult = resultFilterModel->testResult(index);
QTC_ASSERT(testResult.isValid(), painter->restore(); return);
const QWidget *widget = dynamic_cast<const QWidget*>(painter->device());
QWindow *window = widget ? widget->window()->windowHandle() : nullptr;
QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
if (!icon.isNull())
if (!icon.isNull()) {
painter->drawPixmap(positions.left(), positions.top(),
icon.pixmap(window, QSize(positions.iconSize(), positions.iconSize())));
icon.pixmap(QSize(positions.iconSize(), positions.iconSize()),
painter->device()->devicePixelRatio()));
}
TestResultItem *item = resultFilterModel->itemForIndex(index);
QTC_ASSERT(item, painter->restore(); return);

View File

@@ -138,11 +138,8 @@ bool GdbServerProvider::isValid() const
bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessage) const
{
QTC_ASSERT(runTool, return false);
const RunControl *runControl = runTool->runControl();
const auto exeAspect = runControl->aspectData<ExecutableAspect>();
QTC_ASSERT(exeAspect, return false);
const FilePath bin = FilePath::fromString(exeAspect->executable.path());
const ProcessRunData runnable = runTool->runControl()->runnable();
const FilePath bin = FilePath::fromString(runnable.command.executable().path());
if (bin.isEmpty()) {
errorMessage = Tr::tr("Cannot debug: Local executable is not set.");
return false;
@@ -155,8 +152,7 @@ bool GdbServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessa
ProcessRunData inferior;
inferior.command.setExecutable(bin);
if (const auto argAspect = runControl->aspectData<ArgumentsAspect>())
inferior.command.setArguments(argAspect->arguments);
inferior.command.setArguments(runnable.command.arguments());
runTool->setInferior(inferior);
runTool->setSymbolFile(bin);
runTool->setStartMode(AttachToRemoteServer);

View File

@@ -168,11 +168,7 @@ QString UvscServerProvider::channelString() const
bool UvscServerProvider::aboutToRun(DebuggerRunTool *runTool, QString &errorMessage) const
{
QTC_ASSERT(runTool, return false);
const RunControl *runControl = runTool->runControl();
const auto exeAspect = runControl->aspectData<ExecutableAspect>();
QTC_ASSERT(exeAspect, return false);
const FilePath bin = exeAspect->executable;
const FilePath bin = runTool->runControl()->runnable().command.executable();
if (bin.isEmpty()) {
errorMessage = Tr::tr("Cannot debug: Local executable is not set.");
return false;

View File

@@ -884,7 +884,7 @@ bool BazaarPluginPrivate::managesFile(const FilePath &workingDirectory, const QS
bool BazaarPluginPrivate::isConfigured() const
{
const FilePath binary = settings().binaryPath();
const FilePath binary = settings().binaryPath.effectiveBinary();
return !binary.isEmpty() && binary.isExecutableFile();
}

View File

@@ -1011,9 +1011,10 @@ void ClangdClient::followSymbol(TextDocument *document,
: ClangdFollowSymbol::Origin::Code;
if (origin == ClangdFollowSymbol::Origin::User) {
for (auto it = d->followSymbolOps.begin(); it != d->followSymbolOps.end(); ) {
if ((*it)->isInteractive()) {
(*it)->cancel();
ClangdFollowSymbol * const followSymbol = *it;
if (followSymbol->isInteractive()) {
it = d->followSymbolOps.erase(it);
followSymbol->cancel();
} else {
++it;
}

View File

@@ -210,6 +210,21 @@ static bool comesDirectlyAfterIf(const QTextDocument *doc, int pos)
return pos > 0 && doc->characterAt(pos) == 'f' && doc->characterAt(pos - 1) == 'i';
}
static bool startsWithKeyWord(const QString &keyWord, const QString &text)
{
if (text.size() <= keyWord.size())
return false;
const QChar chAfter = text.at(keyWord.size());
return text.startsWith(keyWord) && !chAfter.isDigit() && !chAfter.isLetter() && chAfter != '_';
}
static bool startsWithKeyWords(const QString &text)
{
return startsWithKeyWord("if", text) || startsWithKeyWord("while", text)
|| startsWithKeyWord("for", text);
}
static CharacterContext characterContext(const QTextBlock &currentBlock)
{
QTextBlock previousNonEmptyBlock = reverseFindLastEmptyBlock(currentBlock);
@@ -220,8 +235,9 @@ static CharacterContext characterContext(const QTextBlock &currentBlock)
if (prevLineText.isEmpty())
return CharacterContext::NewStatementOrContinuation;
if ((currentBlock.text().trimmed().isEmpty() || currentBlock.text().trimmed().endsWith(")"))
&& prevLineText.endsWith("{"))
const QString currentBlockText = currentBlock.text().trimmed();
if ((currentBlockText.isEmpty() || currentBlockText.endsWith(")"))
&& prevLineText.endsWith("{") && !startsWithKeyWords(currentBlockText))
return CharacterContext::BracketAfterFunctionCall;
const QChar firstNonWhitespaceChar = findFirstNonWhitespaceCharacter(currentBlock);

View File

@@ -114,8 +114,10 @@ private slots:
void testFunctionCallClosingParenthesis();
void testFunctionCallClosingParenthesisEmptyLine();
void testNoIndentationInMiddleOfLine();
void testIndentationInTheBegginingOfLine();
void testIndentationInMiddleOfLine();
void testIndentationInTheBegginingOfLine();
void testIndentationReturnAfterIf();
void testIndentationReturnAfterIfSomthingFunction();
private:
void insertLines(const std::vector<QString> &lines);
@@ -965,6 +967,38 @@ void ClangFormatTest::testIndentationInTheBegginingOfLine()
"}"}));
}
void ClangFormatTest::testIndentationReturnAfterIf()
{
insertLines({"int main()",
"{",
" if (true)",
" return 0;",
"}"});
m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettings());
QCOMPARE(documentLines(),
(std::vector<QString>{"int main()",
"{",
" if (true)",
" return 0;",
"}"}));
}
void ClangFormatTest::testIndentationReturnAfterIfSomthingFunction()
{
insertLines({"int main()",
"{",
" if_somthing()",
" return 0;",
"}"});
m_indenter->indent(*m_cursor, QChar::Null, TextEditor::TabSettings());
QCOMPARE(documentLines(),
(std::vector<QString>{"int main()",
"{",
" if_somthing()",
" return 0;",
"}"}));
}
QObject *createClangFormatTest()
{
return new ClangFormatTest;

View File

@@ -34,6 +34,7 @@
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/environmentaspectwidget.h>
#include <projectexplorer/environmentwidget.h>
#include <projectexplorer/gcctoolchain.h>
#include <projectexplorer/kitaspects.h>
#include <projectexplorer/namedwidget.h>
#include <projectexplorer/processparameters.h>
@@ -96,6 +97,13 @@ const char USER_ENVIRONMENT_CHANGES_KEY[] = "CMake.Configure.UserEnvironmentChan
const char BASE_ENVIRONMENT_KEY[] = "CMake.Configure.BaseEnvironment";
const char GENERATE_QMLLS_INI_SETTING[] = "J.QtQuick/QmlJSEditor.GenerateQmllsIniFiles";
const char CMAKE_TOOLCHAIN_FILE[] = "CMAKE_TOOLCHAIN_FILE";
const char CMAKE_C_FLAGS_INIT[] = "CMAKE_C_FLAGS_INIT";
const char CMAKE_CXX_FLAGS_INIT[] = "CMAKE_CXX_FLAGS_INIT";
const char CMAKE_CXX_FLAGS[] = "CMAKE_CXX_FLAGS";
const char CMAKE_CXX_FLAGS_DEBUG[] = "CMAKE_CXX_FLAGS_DEBUG";
const char CMAKE_CXX_FLAGS_RELWITHDEBINFO[] = "CMAKE_CXX_FLAGS_RELWITHDEBINFO";
namespace Internal {
class CMakeBuildSettingsWidget : public NamedWidget
@@ -888,11 +896,11 @@ CMakeConfig CMakeBuildSettingsWidget::getQmlDebugCxxFlags()
const bool enable = m_buildConfig->qmlDebugging() == TriState::Enabled;
const CMakeConfig configList = m_buildConfig->cmakeBuildSystem()->configurationFromCMake();
const QByteArrayList cxxFlagsPrev{"CMAKE_CXX_FLAGS",
"CMAKE_CXX_FLAGS_DEBUG",
"CMAKE_CXX_FLAGS_RELWITHDEBINFO",
"CMAKE_CXX_FLAGS_INIT"};
const QByteArrayList cxxFlags{"CMAKE_CXX_FLAGS_INIT", "CMAKE_CXX_FLAGS"};
const QByteArrayList cxxFlagsPrev{CMAKE_CXX_FLAGS,
CMAKE_CXX_FLAGS_DEBUG,
CMAKE_CXX_FLAGS_RELWITHDEBINFO,
CMAKE_CXX_FLAGS_INIT};
const QByteArrayList cxxFlags{CMAKE_CXX_FLAGS_INIT, CMAKE_CXX_FLAGS};
const QByteArray qmlDebug(QT_QML_DEBUG_PARAM);
CMakeConfig changedConfig;
@@ -1176,6 +1184,30 @@ static CommandLine defaultInitialCMakeCommand(
}
}
// GCC compiler and linker specific flags
for (Toolchain *tc : ToolchainKitAspect::toolChains(k)) {
if (auto *gccTc = tc->asGccToolchain()) {
const QStringList compilerFlags = gccTc->platformCodeGenFlags();
QLatin1String languageFlagsInit;
if (gccTc->language() == ProjectExplorer::Constants::C_LANGUAGE_ID)
languageFlagsInit = QLatin1String(CMAKE_C_FLAGS_INIT);
else if (gccTc->language() == ProjectExplorer::Constants::CXX_LANGUAGE_ID)
languageFlagsInit = QLatin1String(CMAKE_CXX_FLAGS_INIT);
if (!languageFlagsInit.isEmpty() && !compilerFlags.isEmpty())
cmd.addArg("-D" + languageFlagsInit + ":STRING=" + compilerFlags.join(" "));
const QStringList linkerFlags = gccTc->platformLinkerFlags();
if (!linkerFlags.isEmpty()) {
const QString joinedLinkerFlags = linkerFlags.join(" ");
cmd.addArg("-DCMAKE_EXE_LINKER_FLAGS_INIT:STRING=" + joinedLinkerFlags);
cmd.addArg("-DCMAKE_MODULE_LINKER_FLAGS_INIT:STRING=" + joinedLinkerFlags);
cmd.addArg("-DCMAKE_SHARED_LINKER_FLAGS_INIT:STRING=" + joinedLinkerFlags);
}
}
}
cmd.addArgs(CMakeConfigurationKitAspect::toArgumentsList(k));
cmd.addArgs(CMakeConfigurationKitAspect::additionalConfiguration(k), CommandLine::Raw);
@@ -1317,13 +1349,21 @@ static void addCMakeConfigurePresetToInitialArguments(QStringList &initialArgume
}
arg = argItem.toArgument();
} else if (argItem.key == "CMAKE_TOOLCHAIN_FILE") {
} else if (argItem.key == CMAKE_TOOLCHAIN_FILE) {
const FilePath argFilePath = FilePath::fromString(argItem.expandedValue(k));
const FilePath presetFilePath = FilePath::fromUserInput(
QString::fromUtf8(presetItem.value));
if (argFilePath != presetFilePath)
arg = presetItem.toArgument();
} else if (argItem.key == CMAKE_CXX_FLAGS_INIT) {
// Append the preset value with at the initial parameters value (e.g. QML Debugging)
if (argItem.expandedValue(k) != QString::fromUtf8(presetItem.value)) {
argItem.value.append(" ");
argItem.value.append(presetItem.value);
arg = argItem.toArgument();
}
} else if (argItem.expandedValue(k) != QString::fromUtf8(presetItem.value)) {
arg = presetItem.toArgument();
}
@@ -1585,7 +1625,8 @@ CMakeBuildConfiguration::CMakeBuildConfiguration(Target *target, Id id)
: TriState::Default);
if (qt && qt->isQmlDebuggingSupported())
cmd.addArg("-DCMAKE_CXX_FLAGS_INIT:STRING=%{" + QLatin1String(QT_QML_DEBUG_FLAG) + "}");
cmd.addArg(
QLatin1String("-D") + CMAKE_CXX_FLAGS_INIT + ":STRING=%{" + QT_QML_DEBUG_FLAG + "}");
// QT_QML_GENERATE_QMLLS_INI, if enabled via the settings checkbox:
if (Core::ICore::settings()->value(GENERATE_QMLLS_INI_SETTING).toBool()) {
@@ -1646,8 +1687,8 @@ bool CMakeBuildConfiguration::hasQmlDebugging(const CMakeConfig &config)
// Determine QML debugging flags. This must match what we do in
// CMakeBuildSettingsWidget::getQmlDebugCxxFlags()
// such that in doubt we leave the QML Debugging setting at "Leave at default"
const QString cxxFlagsInit = config.stringValueOf("CMAKE_CXX_FLAGS_INIT");
const QString cxxFlags = config.stringValueOf("CMAKE_CXX_FLAGS");
const QString cxxFlagsInit = config.stringValueOf(CMAKE_CXX_FLAGS_INIT);
const QString cxxFlags = config.stringValueOf(CMAKE_CXX_FLAGS);
return cxxFlagsInit.contains(QT_QML_DEBUG_PARAM) && cxxFlags.contains(QT_QML_DEBUG_PARAM);
}
@@ -2163,9 +2204,20 @@ void InitialCMakeArgumentsAspect::setAllValues(const QString &values, QStringLis
if (!cmakeGenerator.isEmpty())
arguments.append(cmakeGenerator);
m_cmakeConfiguration = CMakeConfig::fromArguments(arguments, additionalOptions);
for (CMakeConfigItem &ci : m_cmakeConfiguration)
CMakeConfig config = CMakeConfig::fromArguments(arguments, additionalOptions);
// Join CMAKE_CXX_FLAGS_INIT values if more entries are present, or skip the same
// values like CMAKE_EXE_LINKER_FLAGS_INIT coming from both C and CXX compilers
QHash<QByteArray, CMakeConfigItem> uniqueConfig;
for (CMakeConfigItem &ci : config) {
ci.isInitial = true;
if (uniqueConfig.contains(ci.key)) {
if (uniqueConfig[ci.key].value != ci.value)
uniqueConfig[ci.key].value = uniqueConfig[ci.key].value + " " + ci.value;
} else {
uniqueConfig.insert(ci.key, ci);
}
}
m_cmakeConfiguration = uniqueConfig.values();
// Display the unknown arguments in "Additional CMake Options"
const QString additionalOptionsValue = ProcessArgs::joinArgs(additionalOptions);

View File

@@ -605,7 +605,7 @@ QWidget *CMakeBuildStep::createConfigWidget()
if (!isCleanStep() && !m_buildPreset.isEmpty())
createAndAddEnvironmentWidgets(builder);
builder.noMargin();
builder.setNoMargins();
auto widget = builder.emerge();
updateDetails();

View File

@@ -83,12 +83,17 @@ CMakeBuildSystem::CMakeBuildSystem(CMakeBuildConfiguration *bc)
// Cache mime check result for speed up
if (!isIgnored) {
auto it = m_mimeBinaryCache.find(mimeType.name());
if (it != m_mimeBinaryCache.end()) {
if (auto it = m_mimeBinaryCache.get<std::optional<bool>>(
[mimeType](const QHash<QString, bool> &cache) -> std::optional<bool> {
auto it = cache.find(mimeType.name());
if (it != cache.end())
return *it;
return {};
})) {
isIgnored = *it;
} else {
isIgnored = TreeScanner::isMimeBinary(mimeType, fn);
m_mimeBinaryCache[mimeType.name()] = isIgnored;
m_mimeBinaryCache.writeLocked()->insert(mimeType.name(), isIgnored);
}
}

View File

@@ -10,6 +10,7 @@
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildsystem.h>
#include <utils/synchronizedvalue.h>
#include <utils/temporarydirectory.h>
namespace ProjectExplorer {
@@ -221,7 +222,7 @@ private:
ProjectExplorer::TreeScanner m_treeScanner;
std::shared_ptr<ProjectExplorer::FolderNode> m_allFiles;
QHash<QString, bool> m_mimeBinaryCache;
Utils::SynchronizedValue<QHash<QString, bool>> m_mimeBinaryCache;
bool m_waitingForParse = false;
bool m_combinedScanAndParseResult = false;

View File

@@ -1124,6 +1124,11 @@ const QList<BuildInfo> CMakeProjectImporter::buildInfoList(void *directoryData)
&& data->hasQmlDebugging)
buildType = CMakeBuildConfigurationFactory::BuildTypeProfile;
BuildInfo info = CMakeBuildConfigurationFactory::createBuildInfo(buildType);
// For CMake Presets use the provided build type if is not mapped to a known type
if (!data->cmakePreset.isEmpty() && info.buildType == BuildConfiguration::Unknown)
info.typeName = info.displayName = QString::fromUtf8(data->cmakeBuildType);
info.buildDirectory = data->buildDirectory;
QVariantMap config = info.extraInfo.toMap(); // new empty, or existing one from createBuildInfo

View File

@@ -209,12 +209,9 @@ static bool isChildOf(const FilePath &path, const FilePaths &prefixes)
static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
const FilePath &sourceDirectory,
const FilePath &buildDirectory,
bool relativeLibs,
const QSet<FilePath> &sharedLibraryArtifacts)
bool relativeLibs)
{
const FilePath currentBuildDir = buildDirectory.resolvePath(t.buildDir);
const QSet<FilePath> sharedLibraryArtifactsPaths
= transform(sharedLibraryArtifacts, &FilePath::parentDir);
CMakeBuildTarget ct;
ct.title = t.name;
@@ -323,18 +320,18 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
// "/usr/local/lib" since these are usually in the standard search
// paths. There probably are more, but the naming schemes are arbitrary
// so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR").
if (buildDir.osType() != OsTypeWindows
&& !isChildOf(tmp,
{"/lib", "/lib64", "/usr/lib", "/usr/lib64", "/usr/local/lib"}))
librarySeachPaths.append(tmp);
if (buildDir.osType() == OsTypeWindows) {
if (sharedLibraryArtifactsPaths.contains(tmp))
if (buildDir.osType() == OsTypeWindows
|| !isChildOf(tmp,
{"/lib",
"/lib64",
"/usr/lib",
"/usr/lib64",
"/usr/local/lib"})) {
librarySeachPaths.append(tmp);
// Libraries often have their import libs in ../lib and the
// actual dll files in ../bin on windows. Qt is one example of that.
if (tmp.fileName() == "lib") {
if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) {
const FilePath path = tmp.parentDir().pathAppended("bin");
if (path.isDir())
librarySeachPaths.append(path);
@@ -355,19 +352,12 @@ static QList<CMakeBuildTarget> generateBuildTargets(const QFuture<void> &cancelF
const FilePath &buildDirectory,
bool relativeLibs)
{
QSet<FilePath> sharedLibraryArtifacts;
for (const TargetDetails &t : input.targetDetails)
if (t.type == "MODULE_LIBRARY" || t.type == "SHARED_LIBRARY")
for (const FilePath &p : t.artifacts)
sharedLibraryArtifacts.insert(buildDirectory.resolvePath(p));
QList<CMakeBuildTarget> result;
result.reserve(input.targetDetails.size());
for (const TargetDetails &t : input.targetDetails) {
if (cancelFuture.isCanceled())
return {};
result.append(
toBuildTarget(t, sourceDirectory, buildDirectory, relativeLibs, sharedLibraryArtifacts));
result.append(toBuildTarget(t, sourceDirectory, buildDirectory, relativeLibs));
}
return result;
}

View File

@@ -139,11 +139,13 @@ void expand(const PresetType &preset, Environment &env, const FilePath &sourceDi
return presetEnv.value(macroName);
});
bool append = true;
enum Operation { set, appendOrSet, prependOrSet };
Operation op = set;
if (key.compare("PATH", Qt::CaseInsensitive) == 0) {
op = appendOrSet;
const int index = value.indexOf("$penv{PATH}", 0, Qt::CaseInsensitive);
if (index != 0)
append = false;
op = prependOrSet;
value.replace("$penv{PATH}", "", Qt::CaseInsensitive);
}
@@ -154,10 +156,17 @@ void expand(const PresetType &preset, Environment &env, const FilePath &sourceDi
// Make sure to expand the CMake macros also for environment variables
expandAllButEnv(preset, sourceDirectory, value);
if (append)
switch (op) {
case set:
env.set(key, value);
break;
case appendOrSet:
env.appendOrSet(key, value);
else
break;
case prependOrSet:
env.prependOrSet(key, value);
break;
}
});
}

View File

@@ -332,7 +332,11 @@ CompilationDatabaseBuildSystem::CompilationDatabaseBuildSystem(Target *target)
this, &CompilationDatabaseBuildSystem::updateDeploymentData);
}
CompilationDatabaseBuildSystem::~CompilationDatabaseBuildSystem() = default;
CompilationDatabaseBuildSystem::~CompilationDatabaseBuildSystem()
{
if (m_parser)
delete m_parser;
}
void CompilationDatabaseBuildSystem::triggerParsing()
{

View File

@@ -4,6 +4,7 @@
#pragma once
#include <cppeditor/cppprojectfile.h>
#include <utils/synchronizedvalue.h>
#include <QHash>
@@ -29,7 +30,7 @@ public:
QStringList extras;
};
using MimeBinaryCache = QHash<QString, bool>;
using MimeBinaryCache = Utils::SynchronizedValue<QHash<QString, bool>>;
QStringList filterFromFileName(const QStringList &flags, const QString &fileName);

View File

@@ -130,6 +130,15 @@ CompilationDbParser::CompilationDbParser(const QString &projectName,
});
}
CompilationDbParser::~CompilationDbParser()
{
if (m_treeScanner && !m_treeScanner->isFinished()) {
auto future = m_treeScanner->future();
future.cancel();
future.waitForFinished();
}
}
void CompilationDbParser::start()
{
// Check hash first.
@@ -158,12 +167,17 @@ void CompilationDbParser::start()
// Cache mime check result for speed up
if (!isIgnored) {
auto it = m_mimeBinaryCache.find(mimeType.name());
if (it != m_mimeBinaryCache.end()) {
if (auto it = m_mimeBinaryCache.get<std::optional<bool>>(
[mimeType](const QHash<QString, bool> &cache) -> std::optional<bool> {
auto it = cache.find(mimeType.name());
if (it != cache.end())
return *it;
return {};
})) {
isIgnored = *it;
} else {
isIgnored = TreeScanner::isMimeBinary(mimeType, fn);
m_mimeBinaryCache[mimeType.name()] = isIgnored;
m_mimeBinaryCache.writeLocked()->insert(mimeType.name(), isIgnored);
}
}

View File

@@ -32,6 +32,7 @@ public:
MimeBinaryCache &mimeBinaryCache,
ProjectExplorer::BuildSystem::ParseGuard &&guard,
QObject *parent = nullptr);
~CompilationDbParser();
void setPreviousProjectFileHash(const QByteArray &fileHash) { m_projectFileHash = fileHash; }

View File

@@ -258,7 +258,7 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr<SourceSettings> &se
settings->languageId,
addCompilerButton,
removeSourceButton,
customMargin({6, 0, 0, 0}), spacing(0),
customMargins(6, 0, 0, 0), spacing(0),
}.attachTo(toolBar);
Column {
@@ -403,7 +403,7 @@ CompilerWidget::CompilerWidget(const std::shared_ptr<SourceSettings> &sourceSett
m_compilerSettings->compiler,
advButton,
removeCompilerBtn,
customMargin({6, 0, 0, 0}), spacing(0),
customMargins(6, 0, 0, 0), spacing(0),
}.attachTo(toolBar);
Column {

View File

@@ -50,18 +50,22 @@ CopilotSettings::CopilotSettings()
// Vim, Linux/macOS:
FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"),
FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/language-server.js"),
// Neovim, Linux/macOS:
FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/dist/agent.js"),
FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
FilePath::fromUserInput("~/.config/nvim/pack/github/start/copilot.vim/dist/language-server.js"),
// Vim, Windows (PowerShell command):
FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/dist/agent.js"),
FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/copilot/dist/agent.js"),
FilePath::fromUserInput("~/vimfiles/pack/github/start/copilot.vim/dist/language-server.js"),
// Neovim, Windows (PowerShell command):
FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/dist/agent.js"),
FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js")
FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
FilePath::fromUserInput("~/AppData/Local/nvim/pack/github/start/copilot.vim/dist/language-server.js")
};
// clang-format on
@@ -198,7 +202,7 @@ CopilotSettings::CopilotSettings()
"file from the Copilot neovim plugin.",
"Markdown text for the copilot instruction label")
.arg("[README.md](https://github.com/github/copilot.vim)")
.arg("[agent.js](https://github.com/github/copilot.vim/tree/release/dist)"))
.arg("[language-server.js](https://github.com/github/copilot.vim/tree/release/dist)"))
};
return Column {

View File

@@ -30,6 +30,7 @@
#include <QLineEdit>
#include <QPointer>
#include <QPushButton>
#include <QTimer>
#include <QTreeWidgetItem>
#include <array>
@@ -420,6 +421,7 @@ private:
QGridLayout *m_shortcutLayout;
std::vector<std::unique_ptr<ShortcutInput>> m_shortcutInputs;
QPointer<QPushButton> m_addButton = nullptr;
QTimer m_updateTimer;
};
ShortcutSettingsWidget::ShortcutSettingsWidget()
@@ -428,7 +430,12 @@ ShortcutSettingsWidget::ShortcutSettingsWidget()
setTargetHeader(Tr::tr("Shortcut"));
setResetVisible(true);
m_updateTimer.setSingleShot(true);
m_updateTimer.setInterval(100);
connect(ActionManager::instance(), &ActionManager::commandListChanged,
&m_updateTimer, qOverload<>(&QTimer::start));
connect(&m_updateTimer, &QTimer::timeout,
this, &ShortcutSettingsWidget::initialize);
connect(this, &ShortcutSettingsWidget::currentCommandChanged,
this, &ShortcutSettingsWidget::handleCurrentCommandChanged);

View File

@@ -133,13 +133,13 @@ EditorView::EditorView(SplitterOrView *parentSplitterOrView, QWidget *parent)
auto currentViewOverlay = new OverlayWidget;
currentViewOverlay->attachToWidget(this);
currentViewOverlay->setPaintFunction([this](QWidget *w, QPainter &p, QPaintEvent *) {
const int width = 2;
const QPoint margin{0, width};
p.setPen({w->palette().color(QPalette::Highlight), width});
p.drawLine(
m_toolBar->geometry().bottomLeft() + margin,
m_toolBar->geometry().bottomRight() + margin);
currentViewOverlay->setAttribute(Qt::WA_OpaquePaintEvent);
currentViewOverlay->setResizeFunction([this](QWidget *w, const QSize &) {
const QRect toolbarRect = m_toolBar->geometry();
w->setGeometry(toolbarRect.x(), toolbarRect.bottom() - 1, toolbarRect.width(), 2);
});
currentViewOverlay->setPaintFunction([](QWidget *w, QPainter &p, QPaintEvent *) {
p.fillRect(w->rect(), w->palette().color(QPalette::Highlight));
});
currentViewOverlay->setVisible(false);
const auto updateCurrentViewOverlay = [this, currentViewOverlay] {

View File

@@ -3,12 +3,6 @@
#include "findplugin.h"
#include "currentdocumentfind.h"
#include "findtoolbar.h"
#include "findtoolwindow.h"
#include "ifindfilter.h"
#include "searchresultwindow.h"
#include "textfindconstants.h"
#include "../actionmanager/actioncontainer.h"
#include "../actionmanager/actionmanager.h"
#include "../actionmanager/command.h"
@@ -16,6 +10,13 @@
#include "../coreplugintr.h"
#include "../icontext.h"
#include "../icore.h"
#include "../session.h"
#include "currentdocumentfind.h"
#include "findtoolbar.h"
#include "findtoolwindow.h"
#include "ifindfilter.h"
#include "searchresultwindow.h"
#include "textfindconstants.h"
#include <extensionsystem/pluginmanager.h>
@@ -75,6 +76,10 @@ public:
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
void restore(const Store &s);
Store save() const;
// TODO deprecated since QtC 14.0
void writeSettings(QtcSettings *settings) const;
void readSettings(QtcSettings *settings);
@@ -105,6 +110,41 @@ static Utils::Key completionSettingsArrayPrefix() { return "FindCompletions"; }
static Utils::Key completionSettingsTextKey() { return "Text"; }
static Utils::Key completionSettingsFlagsKey() { return "Flags"; }
void CompletionModel::restore(const Store &s)
{
beginResetModel();
const QStringList texts = s.value(completionSettingsTextKey()).toStringList();
const QList<FindFlags> flags
= transform(s.value(completionSettingsFlagsKey()).toList(), [](const QVariant &v) {
return FindFlags(v.toInt());
});
const int size = texts.size();
m_entries.clear();
m_entries.reserve(size);
for (int i = 0; i < size; ++i) {
CompletionEntry entry;
entry.text = texts.at(i);
entry.findFlags = i < flags.size() ? flags.at(i) : FindFlags();
if (!entry.text.isEmpty())
m_entries.append(entry);
}
endResetModel();
}
Store CompletionModel::save() const
{
if (m_entries.isEmpty())
return {};
const QStringList texts = transform(m_entries, [](const CompletionEntry &e) { return e.text; });
const QVariantList flags = transform(m_entries, [](const CompletionEntry &e) {
return QVariant::fromValue(int(e.findFlags));
});
Store s;
s.insert(completionSettingsTextKey(), texts);
s.insert(completionSettingsFlagsKey(), flags);
return s;
}
void CompletionModel::writeSettings(QtcSettings *settings) const
{
if (m_entries.isEmpty()) {
@@ -215,13 +255,20 @@ void Find::initialize()
d->m_findDialog = new Internal::FindToolWindow;
d->m_searchResultWindow = new SearchResultWindow(d->m_findDialog);
ExtensionSystem::PluginManager::addObject(d->m_searchResultWindow);
QObject::connect(ICore::instance(), &ICore::saveSettingsRequested, d, &FindPrivate::writeSettings);
QObject::connect(
SessionManager::instance(),
&SessionManager::aboutToSaveSession,
d,
&FindPrivate::writeSettings);
QObject::connect(
SessionManager::instance(), &SessionManager::sessionLoaded, d, &FindPrivate::readSettings);
}
void Find::extensionsInitialized()
{
d->setupFilterMenuItems();
d->readSettings();
}
void Find::aboutToShutdown()
@@ -369,6 +416,8 @@ bool Find::hasFindFlag(FindFlag flag)
void FindPrivate::writeSettings()
{
// TODO for backwards compatibility
// deprecated since QtC 14.0
QtcSettings *settings = ICore::settings();
settings->beginGroup("Find");
settings->setValueWithDefault("Backward", bool(m_findFlags & FindBackward), false);
@@ -384,10 +433,39 @@ void FindPrivate::writeSettings()
m_findToolBar->writeSettings();
m_findDialog->writeSettings();
m_searchResultWindow->writeSettings();
// save in session
Store s;
if (m_findFlags & FindBackward)
s.insert("Backward", true);
if (m_findFlags & FindCaseSensitively)
s.insert("CaseSensitively", true);
if (m_findFlags & FindWholeWords)
s.insert("WholeWords", true);
if (m_findFlags & FindRegularExpression)
s.insert("RegularExpression", true);
if (m_findFlags & FindPreserveCase)
s.insert("PreserveCase", true);
const Store completion = m_findCompletionModel.save();
if (!completion.isEmpty())
s.insert("FindCompletions", variantFromStore(completion));
if (!m_replaceCompletions.isEmpty())
s.insert("ReplaceStrings", m_replaceCompletions);
const Store toolbar = m_findToolBar->save();
if (!toolbar.isEmpty())
s.insert("ToolBar", variantFromStore(toolbar));
const Store advanced = m_findDialog->save();
if (!advanced.isEmpty())
s.insert("AdvancedSearch", variantFromStore(advanced));
SessionManager::setValue("Find", variantFromStore(s));
}
void FindPrivate::readSettings()
{
const Store s = storeFromVariant(SessionManager::value("Find"));
if (s.isEmpty() && SessionManager::isDefaultVirgin()) {
// TODO compatibility path when opening Qt Creator
// TODO deprecated since QtC 14.0
QtcSettings *settings = ICore::settings();
settings->beginGroup("Find");
{
@@ -404,6 +482,21 @@ void FindPrivate::readSettings()
settings->endGroup();
m_findToolBar->readSettings();
m_findDialog->readSettings();
} else if (!s.empty()) {
{
QSignalBlocker blocker(m_instance);
Find::setBackward(s.value("Backward", false).toBool());
Find::setCaseSensitive(s.value("CaseSensitively", false).toBool());
Find::setWholeWord(s.value("WholeWords", false).toBool());
Find::setRegularExpression(s.value("RegularExpression", false).toBool());
Find::setPreserveCase(s.value("PreserveCase", false).toBool());
}
m_findCompletionModel.restore(storeFromVariant(s.value("FindCompletions")));
m_replaceCompletions = s.value("ReplaceStrings").toStringList();
m_replaceCompletionModel.setStringList(m_replaceCompletions);
m_findToolBar->restore(storeFromVariant(s.value("ToolBar")));
m_findDialog->restore(storeFromVariant(s.value("AdvancedSearch")));
}
emit m_instance->findFlagsChanged(); // would have been done in the setXXX methods above
}

View File

@@ -1047,6 +1047,39 @@ void FindToolBar::resizeEvent(QResizeEvent *event)
QMetaObject::invokeMethod(this, &FindToolBar::updateToolBar, Qt::QueuedConnection);
}
void FindToolBar::restore(const Store &s)
{
FindFlags flags;
if (s.value("Backward", false).toBool())
flags |= FindBackward;
if (s.value("CaseSensitively", false).toBool())
flags |= FindCaseSensitively;
if (s.value("WholeWords", false).toBool())
flags |= FindWholeWords;
if (s.value("RegularExpression", false).toBool())
flags |= FindRegularExpression;
if (s.value("PreserveCase", false).toBool())
flags |= FindPreserveCase;
m_findFlags = flags;
findFlagsChanged();
}
Store FindToolBar::save() const
{
Store s;
if (m_findFlags & FindBackward)
s.insert("Backward", true);
if (m_findFlags & FindCaseSensitively)
s.insert("CaseSensitively", true);
if (m_findFlags & FindWholeWords)
s.insert("WholeWords", true);
if (m_findFlags & FindRegularExpression)
s.insert("RegularExpression", true);
if (m_findFlags & FindPreserveCase)
s.insert("PreserveCase", true);
return s;
}
void FindToolBar::writeSettings()
{
Utils::QtcSettings *settings = ICore::settings();

View File

@@ -6,6 +6,7 @@
#include "currentdocumentfind.h"
#include <utils/id.h>
#include <utils/store.h>
#include <utils/styledbar.h>
#include <QTimer>
@@ -18,7 +19,9 @@ class QSpacerItem;
class QToolButton;
QT_END_NAMESPACE
namespace Utils { class FancyLineEdit; }
namespace Utils {
class FancyLineEdit;
} // namespace Utils
namespace Core {
@@ -43,6 +46,10 @@ public:
explicit FindToolBar(CurrentDocumentFind *currentDocumentFind);
~FindToolBar() override;
void restore(const Utils::Store &s);
Utils::Store save() const;
// TODO deprecated since QtC 14.0
void readSettings();
void writeSettings();

View File

@@ -346,6 +346,30 @@ void FindToolWindow::replace()
filter->replaceAll(term, Find::findFlags());
}
void FindToolWindow::restore(const Utils::Store &s)
{
const QString currentFilter = s.value("CurrentFilter").toString();
for (int i = 0; i < m_filters.size(); ++i) {
IFindFilter *filter = m_filters.at(i);
filter->restore(storeFromVariant(s.value(filter->id().toUtf8())));
if (filter->id() == currentFilter)
setCurrentFilterIndex(i);
}
}
Store FindToolWindow::save() const
{
Store s;
if (m_currentFilter && (m_filters.isEmpty() || m_filters.first() != m_currentFilter))
s.insert("CurrentFilter", m_currentFilter->id());
for (IFindFilter *filter : std::as_const(m_filters)) {
const Store store = filter->save();
if (!store.isEmpty())
s.insert(filter->id().toUtf8(), variantFromStore(store));
}
return s;
}
void FindToolWindow::writeSettings()
{
Utils::QtcSettings *settings = ICore::settings();

View File

@@ -3,6 +3,8 @@
#pragma once
#include <utils/store.h>
#include <QList>
#include <QWidget>
@@ -33,6 +35,11 @@ public:
void setFindText(const QString &text);
void setCurrentFilter(IFindFilter *filter);
void restore(const Utils::Store &s);
Utils::Store save() const;
// TODO deprecated since QtC 14.0
void readSettings();
void writeSettings();

View File

@@ -6,6 +6,8 @@
#include "../coreicons.h"
#include "../coreplugintr.h"
#include <utils/qtcsettings.h>
#include <QApplication>
#include <QKeySequence>
#include <QPainter>
@@ -177,18 +179,6 @@ using namespace Utils;
dialog. It will be reparented and deleted by the find plugin.
*/
/*!
\fn void Core::IFindFilter::writeSettings(Utils::QtcSettings *settings)
Called at shutdown to write the state of the additional options
for this find filter to the \a settings.
*/
/*!
\fn void Core::IFindFilter::readSettings(Utils::QtcSettings *settings)
Called at startup to read the state of the additional options
for this find filter from the \a settings.
*/
/*!
\fn void Core::IFindFilter::enabledChanged(bool enabled)
@@ -264,6 +254,60 @@ FindFlags IFindFilter::supportedFindFlags() const
| FindWholeWords;
}
/*!
Returns a Store with the find filter's settings to store
in the session. Default values should not be saved.
The default implementation returns an empty store.
\sa restore()
*/
Store IFindFilter::save() const
{
return {};
}
/*!
Restores the find filter's settings from the Store \a s.
Settings that are not present in the store should be reset to
the default.
The default implementation does nothing.
\sa save()
*/
void IFindFilter::restore([[maybe_unused]] const Utils::Store &s) {}
/*!
Called at shutdown to write the state of the additional options
for this find filter to the \a settings.
\deprecated [14.0] Implement save() instead.
*/
void IFindFilter::writeSettings(Utils::QtcSettings *settings)
{
settings->remove(settingsKey()); // make sure defaults are removed
storeToSettings(settingsKey(), settings, save());
}
/*!
Called at startup to read the state of the additional options
for this find filter from the \a settings.
\deprecated [14.0] Implement restore() instead.
*/
void IFindFilter::readSettings(Utils::QtcSettings *settings)
{
restore(storeFromSettings(settingsKey(), settings));
}
/*!
\internal
\deprecated [14.0]
*/
QByteArray IFindFilter::settingsKey() const
{
return id().toUtf8();
}
/*!
Returns icons for the find flags \a flags.
*/

View File

@@ -6,6 +6,7 @@
#include "../core_global.h"
#include <utils/filesearch.h>
#include <utils/store.h>
QT_BEGIN_NAMESPACE
class QWidget;
@@ -42,8 +43,13 @@ public:
{ Q_UNUSED(txt) Q_UNUSED(findFlags) }
virtual QWidget *createConfigWidget() { return nullptr; }
virtual void writeSettings(Utils::QtcSettings *settings) { Q_UNUSED(settings) }
virtual void readSettings(Utils::QtcSettings *settings) { Q_UNUSED(settings) }
virtual Utils::Store save() const;
virtual void restore(const Utils::Store &s);
// deprecated in 14.0
virtual void writeSettings(Utils::QtcSettings *settings);
virtual void readSettings(Utils::QtcSettings *settings);
virtual QByteArray settingsKey() const;
static QPixmap pixmapForFindFlags(Utils::FindFlags flags);
static QString descriptionForFindFlags(Utils::FindFlags flags);

View File

@@ -1068,6 +1068,8 @@ QString uiConfigInformation()
QTC_ADD_UIELEMENT_FONT(Body2);
QTC_ADD_UIELEMENT_FONT(ButtonMedium);
QTC_ADD_UIELEMENT_FONT(ButtonSmall);
QTC_ADD_UIELEMENT_FONT(LabelMedium);
QTC_ADD_UIELEMENT_FONT(LabelSmall);
QTC_ADD_UIELEMENT_FONT(CaptionStrong);
QTC_ADD_UIELEMENT_FONT(Caption);
QTC_ADD_UIELEMENT_FONT(IconStandard);

View File

@@ -563,16 +563,15 @@ static void drawPrimitiveTweakedForDarkTheme(QStyle::PrimitiveElement element,
break;
}
case QStyle::PE_IndicatorTabClose: {
QWindow *window = widget ? widget->window()->windowHandle() : nullptr;
const qreal devicePixelRatio = painter->device()->devicePixelRatio();
QRect iconRect = QRect(0, 0, 16, 16);
iconRect.moveCenter(option->rect.center());
const QIcon::Mode mode = !isEnabled ? QIcon::Disabled : QIcon::Normal;
const static QIcon closeIcon = Utils::Icons::CLOSE_FOREGROUND.icon();
if (option->state & QStyle::State_MouseOver && widget)
widget->style()->drawPrimitive(QStyle::PE_PanelButtonCommand, option, painter, widget);
const int devicePixelRatio = widget ? widget->devicePixelRatio() : 1;
const QPixmap iconPx =
closeIcon.pixmap(window, iconRect.size() * devicePixelRatio, mode);
closeIcon.pixmap(iconRect.size() * devicePixelRatio, devicePixelRatio, mode);
painter->drawPixmap(iconRect, iconPx);
break;
}

View File

@@ -421,7 +421,7 @@ OutputPaneManager::OutputPaneManager(QWidget *parent) :
}.attachTo(this);
Row {
spacing(creatorTheme()->flag(Theme::FlatToolBars) ? 9 : 4), customMargin({5, 0, 0, 0}),
spacing(creatorTheme()->flag(Theme::FlatToolBars) ? 9 : 4), customMargins(5, 0, 0, 0),
}.attachTo(m_buttonsWidget);
StatusBarManager::addStatusBarWidget(m_buttonsWidget, StatusBarManager::Second);

View File

@@ -1203,7 +1203,7 @@ ListModel *SectionedGridView::addSection(const Section &section, const QList<Lis
st,
seeAllLink,
Space(ExVPaddingGapXl),
customMargin({0, ExPaddingGapL, 0, VPaddingL}),
customMargins(0, ExPaddingGapL, 0, VPaddingL),
}.emerge();
m_sectionLabels.append(sectionLabel);
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
@@ -1274,7 +1274,7 @@ void SectionedGridView::zoomInSection(const Section &section)
st,
backLink,
Space(ExVPaddingGapXl),
customMargin({0, ExPaddingGapL, 0, VPaddingL}),
customMargins(0, ExPaddingGapL, 0, VPaddingL),
}.emerge();
auto gridView = new GridView(zoomedInWidget);

View File

@@ -101,7 +101,7 @@ void CppcheckTool::updateArguments()
arguments.push_back("--template=\"{file},{line},{severity},{id},{message}\"");
m_runner->reconfigure(s.binary(), arguments.join(' '));
m_runner->reconfigure(s.binary.effectiveBinary(), arguments.join(' '));
}
QStringList CppcheckTool::additionalArguments(const CppEditor::ProjectPart &part) const

View File

@@ -166,26 +166,30 @@ QWidget *SymbolsFindFilter::createConfigWidget()
return new SymbolsFindFilterConfigWidget(this);
}
void SymbolsFindFilter::writeSettings(QtcSettings *settings)
Store SymbolsFindFilter::save() const
{
settings->beginGroup(SETTINGS_GROUP);
settings->setValue(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch));
settings->setValue(SETTINGS_SEARCHSCOPE, int(m_scope));
settings->endGroup();
Store s;
if (m_symbolsToSearch != SearchSymbols::AllTypes)
s.insert(SETTINGS_SYMBOLTYPES, int(m_symbolsToSearch));
if (m_scope != SymbolSearcher::SearchProjectsOnly)
s.insert(SETTINGS_SEARCHSCOPE, int(m_scope));
return s;
}
void SymbolsFindFilter::readSettings(QtcSettings *settings)
void SymbolsFindFilter::restore(const Utils::Store &s)
{
settings->beginGroup(SETTINGS_GROUP);
m_symbolsToSearch = static_cast<SearchSymbols::SymbolTypes>(
settings->value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt());
s.value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt());
m_scope = static_cast<SearchScope>(
settings->value(SETTINGS_SEARCHSCOPE,
int(SymbolSearcher::SearchProjectsOnly)).toInt());
settings->endGroup();
s.value(SETTINGS_SEARCHSCOPE, int(SymbolSearcher::SearchProjectsOnly)).toInt());
emit symbolsToSearchChanged();
}
QByteArray SymbolsFindFilter::settingsKey() const
{
return SETTINGS_GROUP;
}
void SymbolsFindFilter::onTaskStarted(Id type)
{
if (type == Constants::TASK_INDEX) {

View File

@@ -37,8 +37,8 @@ public:
void findAll(const QString &txt, Utils::FindFlags findFlags) override;
QWidget *createConfigWidget() override;
void writeSettings(Utils::QtcSettings *settings) override;
void readSettings(Utils::QtcSettings *settings) override;
Utils::Store save() const override;
void restore(const Utils::Store &s) override;
void setSymbolsToSearch(const SearchSymbols::SymbolTypes &types) { m_symbolsToSearch = types; }
SearchSymbols::SymbolTypes symbolsToSearch() const { return m_symbolsToSearch; }
@@ -46,6 +46,9 @@ public:
void setSearchScope(SearchScope scope) { m_scope = scope; }
SearchScope searchScope() const { return m_scope; }
// deprecated
QByteArray settingsKey() const override;
signals:
void symbolsToSearchChanged();

Some files were not shown because too many files have changed in this diff Show More