forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/14.0'
Change-Id: I4260181d32ed514e1a912188dbf1040857a83f54
This commit is contained in:
94
dist/changelog/changes-13.0.2.md
vendored
Normal file
94
dist/changelog/changes-13.0.2.md
vendored
Normal 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
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -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}
|
||||
*/
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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}.
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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}
|
||||
|
@@ -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.
|
||||
|
@@ -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
|
||||
|
@@ -3,6 +3,7 @@ set(resource_directories
|
||||
cplusplus
|
||||
glsl
|
||||
indexer_preincludes
|
||||
jsonschemas
|
||||
modeleditor
|
||||
qmldesigner
|
||||
qmlicons
|
||||
|
57
share/qtcreator/jsonschemas/project.json
Normal file
57
share/qtcreator/jsonschemas/project.json
Normal 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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -16,6 +16,7 @@ Product {
|
||||
"debugger/**/*",
|
||||
"designer/**/*",
|
||||
"glsl/**/*",
|
||||
"jsonschemas/**/*",
|
||||
"modeleditor/**/*",
|
||||
"qml/**/*",
|
||||
"qmldesigner/**/*",
|
||||
|
@@ -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());
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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());
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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:
|
||||
|
@@ -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;
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "networkquery.h"
|
||||
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
|
||||
namespace Tasking {
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qglobal.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#if defined(TASKING_LIBRARY)
|
||||
# define TASKING_EXPORT Q_DECL_EXPORT
|
||||
|
@@ -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.";
|
||||
})
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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);
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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)
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
@@ -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) == '+')
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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}},
|
||||
|
@@ -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,
|
||||
|
@@ -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()));
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
|
@@ -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 {};
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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); })
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -3,6 +3,9 @@ QtcPlugin {
|
||||
|
||||
Depends { name: "Core" }
|
||||
Depends { name: "ProjectExplorer" }
|
||||
Depends { name: "Qt.charts"; required: false }
|
||||
|
||||
condition: Qt.charts.present
|
||||
|
||||
files: [
|
||||
"appstatisticsmonitorplugin.cpp",
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
|
@@ -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()});
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -210,6 +210,21 @@ static bool comesDirectlyAfterIf(const QTextDocument *doc, int pos)
|
||||
return pos > 0 && doc->characterAt(pos) == 'f' && doc->characterAt(pos - 1) == 'i';
|
||||
}
|
||||
|
||||
static bool startsWithKeyWord(const QString &keyWord, const QString &text)
|
||||
{
|
||||
if (text.size() <= keyWord.size())
|
||||
return false;
|
||||
|
||||
const QChar chAfter = text.at(keyWord.size());
|
||||
return text.startsWith(keyWord) && !chAfter.isDigit() && !chAfter.isLetter() && chAfter != '_';
|
||||
}
|
||||
|
||||
static bool startsWithKeyWords(const QString &text)
|
||||
{
|
||||
return startsWithKeyWord("if", text) || startsWithKeyWord("while", text)
|
||||
|| startsWithKeyWord("for", text);
|
||||
}
|
||||
|
||||
static CharacterContext characterContext(const QTextBlock ¤tBlock)
|
||||
{
|
||||
QTextBlock previousNonEmptyBlock = reverseFindLastEmptyBlock(currentBlock);
|
||||
@@ -220,8 +235,9 @@ static CharacterContext characterContext(const QTextBlock ¤tBlock)
|
||||
if (prevLineText.isEmpty())
|
||||
return CharacterContext::NewStatementOrContinuation;
|
||||
|
||||
if ((currentBlock.text().trimmed().isEmpty() || currentBlock.text().trimmed().endsWith(")"))
|
||||
&& prevLineText.endsWith("{"))
|
||||
const QString currentBlockText = currentBlock.text().trimmed();
|
||||
if ((currentBlockText.isEmpty() || currentBlockText.endsWith(")"))
|
||||
&& prevLineText.endsWith("{") && !startsWithKeyWords(currentBlockText))
|
||||
return CharacterContext::BracketAfterFunctionCall;
|
||||
|
||||
const QChar firstNonWhitespaceChar = findFirstNonWhitespaceCharacter(currentBlock);
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -605,7 +605,7 @@ QWidget *CMakeBuildStep::createConfigWidget()
|
||||
if (!isCleanStep() && !m_buildPreset.isEmpty())
|
||||
createAndAddEnvironmentWidgets(builder);
|
||||
|
||||
builder.noMargin();
|
||||
builder.setNoMargins();
|
||||
auto widget = builder.emerge();
|
||||
|
||||
updateDetails();
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -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()
|
||||
{
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,7 @@ public:
|
||||
MimeBinaryCache &mimeBinaryCache,
|
||||
ProjectExplorer::BuildSystem::ParseGuard &&guard,
|
||||
QObject *parent = nullptr);
|
||||
~CompilationDbParser();
|
||||
|
||||
|
||||
void setPreviousProjectFileHash(const QByteArray &fileHash) { m_projectFileHash = fileHash; }
|
||||
|
@@ -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 {
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -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] {
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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();
|
||||
|
@@ -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();
|
||||
|
||||
|
@@ -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.
|
||||
*/
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -1203,7 +1203,7 @@ ListModel *SectionedGridView::addSection(const Section §ion, 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 §ion)
|
||||
st,
|
||||
backLink,
|
||||
Space(ExVPaddingGapXl),
|
||||
customMargin({0, ExPaddingGapL, 0, VPaddingL}),
|
||||
customMargins(0, ExPaddingGapL, 0, VPaddingL),
|
||||
}.emerge();
|
||||
|
||||
auto gridView = new GridView(zoomedInWidget);
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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
Reference in New Issue
Block a user