diff --git a/dist/changelog/changes-13.0.2.md b/dist/changelog/changes-13.0.2.md new file mode 100644 index 00000000000..629efc3d1b0 --- /dev/null +++ b/dist/changelog/changes-13.0.2.md @@ -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 diff --git a/doc/qtcreator/src/analyze/creator-axivion.qdoc b/doc/qtcreator/src/analyze/creator-axivion.qdoc index 9a827f7bb4d..53735a9c31a 100644 --- a/doc/qtcreator/src/analyze/creator-axivion.qdoc +++ b/doc/qtcreator/src/analyze/creator-axivion.qdoc @@ -140,12 +140,18 @@ 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} - \li In \uicontrol {Dashboard URL}, enter the URL of the server. - \li In \uicontrol Username, enter the username to access the server. + \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 @@ -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. diff --git a/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc b/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc index 16031b8f6ae..8f02ee0607e 100644 --- a/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc +++ b/doc/qtcreator/src/analyze/creator-ctf-visualizer.qdoc @@ -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 diff --git a/doc/qtcreator/src/android/androiddev.qdoc b/doc/qtcreator/src/android/androiddev.qdoc index 5d1e49de6c4..ab6ae2d9092 100644 --- a/doc/qtcreator/src/android/androiddev.qdoc +++ b/doc/qtcreator/src/android/androiddev.qdoc @@ -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} */ diff --git a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc index f027ec0f960..1d4f41f789e 100644 --- a/doc/qtcreator/src/debugger/qtquick-debugging.qdoc +++ b/doc/qtcreator/src/debugger/qtquick-debugging.qdoc @@ -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 diff --git a/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc b/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc index c90a21f80e7..9bbfe729d93 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-coding-edit-mode.qdoc @@ -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. diff --git a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc index a678e094636..f6006e8eded 100644 --- a/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc +++ b/doc/qtcreator/src/howto/creator-only/creator-autotest.qdoc @@ -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 diff --git a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc index 4302cd8e438..967031f69be 100644 --- a/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc +++ b/doc/qtcreator/src/howto/creator-only/qtcreator-faq.qdoc @@ -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}. diff --git a/doc/qtcreator/src/ios/creator-ios-dev.qdoc b/doc/qtcreator/src/ios/creator-ios-dev.qdoc index 5dccfd1dccc..f02397f149f 100644 --- a/doc/qtcreator/src/ios/creator-ios-dev.qdoc +++ b/doc/qtcreator/src/ios/creator-ios-dev.qdoc @@ -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 diff --git a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc index 67605429f86..6a2602b34d0 100644 --- a/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc +++ b/doc/qtcreator/src/overview/creator-only/creator-desktop-platforms.qdoc @@ -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 diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc index ce809fc536d..db6aefeb64c 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-environment.qdoc @@ -141,6 +141,10 @@ Use the following syntax to enter environment variable names and values: \c {=}. + 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} diff --git a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc index 0ff290f0d0d..0fbb4fdea07 100644 --- a/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc +++ b/doc/qtcreator/src/qtquick/creator-only/qtquick-creating.qdoc @@ -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. diff --git a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc index bd0985e3121..b22bd5be5e7 100644 --- a/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-profiler.qdoc @@ -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 diff --git a/share/qtcreator/CMakeLists.txt b/share/qtcreator/CMakeLists.txt index f8be01a7a7b..73a0837ede2 100644 --- a/share/qtcreator/CMakeLists.txt +++ b/share/qtcreator/CMakeLists.txt @@ -3,6 +3,7 @@ set(resource_directories cplusplus glsl indexer_preincludes + jsonschemas modeleditor qmldesigner qmlicons diff --git a/share/qtcreator/jsonschemas/project.json b/share/qtcreator/jsonschemas/project.json new file mode 100644 index 00000000000..df55dbd693a --- /dev/null +++ b/share/qtcreator/jsonschemas/project.json @@ -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" + ] + } + ] + } + } +} diff --git a/share/qtcreator/themes/dark.figmatokens b/share/qtcreator/themes/dark.figmatokens index 194ce7fab25..d65ae21fa3f 100644 --- a/share/qtcreator/themes/dark.figmatokens +++ b/share/qtcreator/themes/dark.figmatokens @@ -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 diff --git a/share/qtcreator/themes/light.figmatokens b/share/qtcreator/themes/light.figmatokens index 138ad74afdf..8bbe56319d3 100644 --- a/share/qtcreator/themes/light.figmatokens +++ b/share/qtcreator/themes/light.figmatokens @@ -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 diff --git a/share/share.qbs b/share/share.qbs index a2114dad2d2..2a1c8d8fce4 100644 --- a/share/share.qbs +++ b/share/share.qbs @@ -16,6 +16,7 @@ Product { "debugger/**/*", "designer/**/*", "glsl/**/*", + "jsonschemas/**/*", "modeleditor/**/*", "qml/**/*", "qmldesigner/**/*", diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp index 8505deb0bdc..3e51352b147 100644 --- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp +++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp @@ -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()); diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 2158b99ac27..18c8d11e78f 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -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; } diff --git a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp index 981641e0ad0..d81f71c5000 100644 --- a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp +++ b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp @@ -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()); diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp index 3fa671283af..f02b4668300 100644 --- a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp +++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp @@ -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); diff --git a/src/libs/solutions/tasking/barrier.cpp b/src/libs/solutions/tasking/barrier.cpp index e160dbac769..74c11f50de5 100644 --- a/src/libs/solutions/tasking/barrier.cpp +++ b/src/libs/solutions/tasking/barrier.cpp @@ -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; diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h index a9ed9949f06..88ef5f9a96e 100644 --- a/src/libs/solutions/tasking/barrier.h +++ b/src/libs/solutions/tasking/barrier.h @@ -25,7 +25,7 @@ public: int current() const { return m_current; } std::optional result() const { return m_result; } -signals: +Q_SIGNALS: void done(DoneResult success); private: diff --git a/src/libs/solutions/tasking/concurrentcall.h b/src/libs/solutions/tasking/concurrentcall.h index 6bd44601318..a3721fca0cf 100644 --- a/src/libs/solutions/tasking/concurrentcall.h +++ b/src/libs/solutions/tasking/concurrentcall.h @@ -5,7 +5,7 @@ #include "tasktree.h" -#include +#include 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; diff --git a/src/libs/solutions/tasking/networkquery.cpp b/src/libs/solutions/tasking/networkquery.cpp index 335f79afd94..c3de4455eb6 100644 --- a/src/libs/solutions/tasking/networkquery.cpp +++ b/src/libs/solutions/tasking/networkquery.cpp @@ -3,7 +3,7 @@ #include "networkquery.h" -#include +#include namespace Tasking { diff --git a/src/libs/solutions/tasking/networkquery.h b/src/libs/solutions/tasking/networkquery.h index dd099dc7a88..b8ad13bdfae 100644 --- a/src/libs/solutions/tasking/networkquery.h +++ b/src/libs/solutions/tasking/networkquery.h @@ -7,8 +7,8 @@ #include "tasktree.h" -#include -#include +#include +#include #include @@ -37,7 +37,7 @@ public: QNetworkReply *reply() const { return m_reply.get(); } void start(); -signals: +Q_SIGNALS: void started(); void done(DoneResult result); diff --git a/src/libs/solutions/tasking/qprocesstask.cpp b/src/libs/solutions/tasking/qprocesstask.cpp index 7d63e658478..70f9cc2ad8e 100644 --- a/src/libs/solutions/tasking/qprocesstask.cpp +++ b/src/libs/solutions/tasking/qprocesstask.cpp @@ -3,12 +3,12 @@ #include "qprocesstask.h" -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include namespace Tasking { @@ -60,7 +60,7 @@ public: terminate(); } -signals: +Q_SIGNALS: void finished(); private: diff --git a/src/libs/solutions/tasking/qprocesstask.h b/src/libs/solutions/tasking/qprocesstask.h index cc0b83698b8..fb71d159aab 100644 --- a/src/libs/solutions/tasking/qprocesstask.h +++ b/src/libs/solutions/tasking/qprocesstask.h @@ -7,7 +7,7 @@ #include "tasktree.h" -#include +#include namespace Tasking { @@ -45,17 +45,17 @@ public: class TASKING_EXPORT QProcessAdapter : public TaskAdapter { 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(); } diff --git a/src/libs/solutions/tasking/tasking_global.h b/src/libs/solutions/tasking/tasking_global.h index d7e76fa9e6f..9d9e183b9a3 100644 --- a/src/libs/solutions/tasking/tasking_global.h +++ b/src/libs/solutions/tasking/tasking_global.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #if defined(TASKING_LIBRARY) # define TASKING_EXPORT Q_DECL_EXPORT diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp index 107559d6a53..c5a6ed6a70e 100644 --- a/src/libs/solutions/tasking/tasktree.cpp +++ b/src/libs/solutions/tasking/tasktree.cpp @@ -5,17 +5,17 @@ #include "barrier.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include 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(); - 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."; }) diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h index 9b1e4b9eafd..06a3d4ea79c 100644 --- a/src/libs/solutions/tasking/tasktree.h +++ b/src/libs/solutions/tasking/tasktree.h @@ -5,8 +5,8 @@ #include "tasking_global.h" -#include -#include +#include +#include #include @@ -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 static GroupDoneHandler wrapGroupDone(Handler &&handler) { @@ -368,7 +368,7 @@ private: std::invoke(handler); return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error; }; - }; + } }; template @@ -433,7 +433,7 @@ private: std::invoke(handler); return SetupResult::StopWithSuccess; }; - }; + } }; template > @@ -490,7 +490,7 @@ private: std::invoke(handler, *adapter.task()); return SetupResult::Continue; }; - }; + } template 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(std::forward(handler))); } -signals: +Q_SIGNALS: void started(); void done(DoneWith result); void asyncCountChanged(int count); diff --git a/src/libs/solutions/tasking/tasktreerunner.h b/src/libs/solutions/tasking/tasktreerunner.h index 92d9f9c0b45..766ea074f50 100644 --- a/src/libs/solutions/tasking/tasktreerunner.h +++ b/src/libs/solutions/tasking/tasktreerunner.h @@ -6,7 +6,7 @@ #include "tasking_global.h" #include "tasktree.h" -#include +#include 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); diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp index bc233ed188a..13db6a1ecef 100644 --- a/src/libs/utils/aspects.cpp +++ b/src/libs/utils/aspects.cpp @@ -1410,6 +1410,7 @@ public: FilePath m_baseFileName; StringAspect::ValueAcceptor m_valueAcceptor; std::optional m_validator; + std::optional m_effectiveBinary; std::function 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) { - d->m_expectedKind = expectedKind; - if (d->m_pathChooserDisplay) - d->m_pathChooserDisplay->setExpectedKind(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) diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h index 47aeb35458b..8faeaf7a9e3 100644 --- a/src/libs/utils/aspects.h +++ b/src/libs/utils/aspects.h @@ -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); diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp index 6ac9b356406..e81ee520d81 100644 --- a/src/libs/utils/environmentmodel.cpp +++ b/src/libs/utils/environmentmodel.cpp @@ -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) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index 0965a52b1f3..c631034eba2 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -1230,6 +1230,9 @@ FilePathInfo FilePath::filePathInfo() const */ bool FilePath::exists() const { + if (isEmpty()) + return false; + const expected_str 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 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 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 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 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 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 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 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 access = getFileAccess(*this); if (!access) return false; diff --git a/src/libs/utils/historycompleter.cpp b/src/libs/utils/historycompleter.cpp index 8eff3fdad82..8dc9ec3887e 100644 --- a/src/libs/utils/historycompleter.cpp +++ b/src/libs/utils/historycompleter.cpp @@ -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(); diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp index 132494e8252..df880ac67ec 100644 --- a/src/libs/utils/icon.cpp +++ b/src/libs/utils/icon.cpp @@ -221,11 +221,11 @@ QIcon Icon::modeIcon(const Icon &classic, const Icon &flat, const Icon &flatActi QIcon Icon::combinedIcon(const QList &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; } diff --git a/src/libs/utils/infolabel.cpp b/src/libs/utils/infolabel.cpp index 9eb9f801060..e05c093ca57 100644 --- a/src/libs/utils/infolabel.cpp +++ b/src/libs/utils/infolabel.cpp @@ -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); } diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp index 09e3a83520e..e285ed98f6c 100644 --- a/src/libs/utils/layoutbuilder.cpp +++ b/src/libs/utils/layoutbuilder.cpp @@ -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(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 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 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) diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h index 07a6b9478ef..eb516cecc88 100644 --- a/src/libs/utils/layoutbuilder.h +++ b/src/libs/utils/layoutbuilder.h @@ -3,7 +3,6 @@ #pragma once -#include #include #include @@ -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 +struct Arg2 +{ + Arg2(const T1 &a1, const T2 &a2) + : p1(a1) + , p2(a2) + {} + const T1 p1; + const T2 p2; +}; + +template +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 +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 items); void addRow(std::initializer_list 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 diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp index 3f4cdbd4b2c..cb34f8a5505 100644 --- a/src/libs/utils/namevalueitem.cpp +++ b/src/libs/utils/namevalueitem.cpp @@ -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; diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h index 6ad5ed03791..c518b17e667 100644 --- a/src/libs/utils/namevalueitem.h +++ b/src/libs/utils/namevalueitem.h @@ -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) diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp index 0b009c7568e..5a45c54a75e 100644 --- a/src/libs/utils/namevaluesdialog.cpp +++ b/src/libs/utils/namevaluesdialog.cpp @@ -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) == '+') diff --git a/src/libs/utils/overlaywidget.cpp b/src/libs/utils/overlaywidget.cpp index 720c5a90e18..950dc4b5dc3 100644 --- a/src/libs/utils/overlaywidget.cpp +++ b/src/libs/utils/overlaywidget.cpp @@ -8,16 +8,34 @@ #include #include +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()); } diff --git a/src/libs/utils/overlaywidget.h b/src/libs/utils/overlaywidget.h index 8bc5b7d1eb5..c5686ad171c 100644 --- a/src/libs/utils/overlaywidget.h +++ b/src/libs/utils/overlaywidget.h @@ -8,18 +8,26 @@ #include #include +#include namespace Utils { +namespace Internal { +class OverlayWidgetPrivate; +} + class QTCREATOR_UTILS_EXPORT OverlayWidget : public QWidget { public: using PaintFunction = std::function; + using ResizeFunction = std::function; 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 d; }; } // namespace Utils diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index 617dac09a8e..080bb8a3820 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -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(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}}, diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 7d434925e66..ecc2eea2b96 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -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, diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp index 0cd7ceb1df8..6f7bb11f81e 100644 --- a/src/libs/utils/terminalinterface.cpp +++ b/src/libs/utils/terminalinterface.cpp @@ -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(this); if (!d->envListFile->open()) { cleanupAfterStartFailure(msgCannotCreateTempFile(d->envListFile->errorString())); diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 59475aff820..8d7e922e79d 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -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, diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp index 71069e60f66..29bfb787a6a 100644 --- a/src/libs/utils/wizard.cpp +++ b/src/libs/utils/wizard.cpp @@ -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); diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 38cfa2cdbf8..3b5c6ccf916 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -89,12 +89,18 @@ bool startAvdAsync(const QString &avdName) QString findAvd(const QString &avdName) { - const QList 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 {}; } diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 8a4a0465c3b..feadec6111e 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -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 connectedDevices(QString *error) +QStringList devicesCommandOutput() { - QList 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 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 diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index ca7825cf03e..81477b408a8 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -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 connectedDevices(QString *error = nullptr); +QStringList devicesCommandOutput(); QString bestNdkPlatformMatch(int target, const QtSupport::QtVersion *qtVersion); diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index c5b7b78a80f..7e6e715dc86 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -39,6 +39,7 @@ #include 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 m_removeAvdProcess; + QFileSystemWatcher m_avdFileSystemWatcher; + Guard m_avdPathGuard; + std::unique_ptr 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(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(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 " + "(Help Web Page):") + .arg(helpUrl)); + dialog.setTextValue(AndroidConfig::emulatorArgs()); + + if (auto label = dialog.findChild()) { + 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(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; } @@ -259,7 +463,7 @@ Id AndroidDevice::idFromDeviceInfo(const AndroidDeviceInfo &info) Id AndroidDevice::idFromAvdInfo(const CreateAvdInfo &info) { - return Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name); + return Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name); } QStringList AndroidDevice::supportedAbis() const @@ -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(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 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(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(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(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 " - "(Help Web Page):") - .arg(helpUrl)); - dialog.setTextValue(AndroidConfig::emulatorArgs()); - - if (auto label = dialog.findChild()) { - 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 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 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(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 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 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(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 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(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 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(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 diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 789fa75ff12..fbb7e0bccb6 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -70,44 +70,13 @@ private: std::unique_ptr 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 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 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 m_removeAvdProcess; - QFileSystemWatcher m_avdFileSystemWatcher; - Utils::Guard m_avdPathGuard; - std::unique_ptr m_adbDeviceWatcherProcess; - - friend void setupAndroidDeviceManager(QObject *guard); -}; +} // namespace AndroidDeviceManager void setupAndroidDevice(); void setupAndroidDeviceManager(QObject *guard); diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index 7202c93f4cf..b5041917b09 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -628,17 +628,15 @@ void AndroidRunnerWorker::asyncStart() using namespace Tasking; const Storage pidStorage; - const LoopUntil iterator([pidStorage](int) { return pidStorage->first <= 0; }); const FilePath adbPath = AndroidConfig::adbToolPath(); const QStringList args = selector(); + const QString pidScript = m_isPreNougat + ? QString("for p in /proc/[0-9]*; do cat <$p/cmdline && echo :${p##*/}; done") + : QString("pidof -s '%1'").arg(m_packageName); - const auto onPidSetup = [adbPath, args, packageName = m_packageName, - isPreNougat = m_isPreNougat](Process &process) { - const QString pidScript = 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}}); + 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); }) diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index a6c08ffb054..4ae894a1920 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -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; } diff --git a/src/plugins/appstatisticsmonitor/CMakeLists.txt b/src/plugins/appstatisticsmonitor/CMakeLists.txt index 891da1f812a..292eb2a3eb2 100644 --- a/src/plugins/appstatisticsmonitor/CMakeLists.txt +++ b/src/plugins/appstatisticsmonitor/CMakeLists.txt @@ -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 diff --git a/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs b/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs index 3905a41a0aa..177dd9d0a28 100644 --- a/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs +++ b/src/plugins/appstatisticsmonitor/appstatisticsmonitor.qbs @@ -3,6 +3,9 @@ QtcPlugin { Depends { name: "Core" } Depends { name: "ProjectExplorer" } + Depends { name: "Qt.charts"; required: false } + + condition: Qt.charts.present files: [ "appstatisticsmonitorplugin.cpp", diff --git a/src/plugins/appstatisticsmonitor/chart.cpp b/src/plugins/appstatisticsmonitor/chart.cpp index 2abf6d1ceb6..260282b39a4 100644 --- a/src/plugins/appstatisticsmonitor/chart.cpp +++ b/src/plugins/appstatisticsmonitor/chart.cpp @@ -5,11 +5,19 @@ #include +#include +#include +#include #include #include #include #include +#include +#include #include +#include + +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 &data) +{ + clear(); + QList 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 data) +void Chart::loadNewProcessData(const QList &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); diff --git a/src/plugins/appstatisticsmonitor/chart.h b/src/plugins/appstatisticsmonitor/chart.h index 0a9ed927512..ed478061ce7 100644 --- a/src/plugins/appstatisticsmonitor/chart.h +++ b/src/plugins/appstatisticsmonitor/chart.h @@ -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 #include #include #include #include #include +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 &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 data); + void loadNewProcessData(const QList &data); double lastPointX() const; void clear(); diff --git a/src/plugins/appstatisticsmonitor/manager.cpp b/src/plugins/appstatisticsmonitor/manager.cpp index ad17b80dd78..a0a08fd36a0 100644 --- a/src/plugins/appstatisticsmonitor/manager.cpp +++ b/src/plugins/appstatisticsmonitor/manager.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -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 m_chartMem; + std::unique_ptr m_chartCpu; AppStatisticsMonitorManager *m_manager; }; @@ -114,25 +115,21 @@ QHash 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(tr("Memory consumption")); + m_chartCpu = std::make_unique(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 &memConsumptionHistory + = m_manager->currentDataProvider()->memoryConsumptionHistory(); + const QList &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()}); } diff --git a/src/plugins/autotest/testresultdelegate.cpp b/src/plugins/autotest/testresultdelegate.cpp index 14fe1f646f9..8c9f11b2c79 100644 --- a/src/plugins/autotest/testresultdelegate.cpp +++ b/src/plugins/autotest/testresultdelegate.cpp @@ -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(painter->device()); - QWindow *window = widget ? widget->window()->windowHandle() : nullptr; - QIcon icon = index.data(Qt::DecorationRole).value(); - 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); diff --git a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp index 885e20fac42..773a2ac577f 100644 --- a/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/gdb/gdbserverprovider.cpp @@ -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(); - 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()) - inferior.command.setArguments(argAspect->arguments); + inferior.command.setArguments(runnable.command.arguments()); runTool->setInferior(inferior); runTool->setSymbolFile(bin); runTool->setStartMode(AttachToRemoteServer); diff --git a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp index 3709736d842..4dac9b654c4 100644 --- a/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp +++ b/src/plugins/baremetal/debugservers/uvsc/uvscserverprovider.cpp @@ -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(); - 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; diff --git a/src/plugins/bazaar/bazaarplugin.cpp b/src/plugins/bazaar/bazaarplugin.cpp index 47c4bd8f8c5..31d9c243906 100644 --- a/src/plugins/bazaar/bazaarplugin.cpp +++ b/src/plugins/bazaar/bazaarplugin.cpp @@ -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(); } diff --git a/src/plugins/clangcodemodel/clangdclient.cpp b/src/plugins/clangcodemodel/clangdclient.cpp index f3600352642..19cd4bd5a91 100644 --- a/src/plugins/clangcodemodel/clangdclient.cpp +++ b/src/plugins/clangcodemodel/clangdclient.cpp @@ -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; } diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 4d89ea8aa2e..295c86fcb6f 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -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); diff --git a/src/plugins/clangformat/tests/clangformat-test.cpp b/src/plugins/clangformat/tests/clangformat-test.cpp index c52767baa27..6e7cdd367be 100644 --- a/src/plugins/clangformat/tests/clangformat-test.cpp +++ b/src/plugins/clangformat/tests/clangformat-test.cpp @@ -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 &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{"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{"int main()", + "{", + " if_somthing()", + " return 0;", + "}"})); +} + QObject *createClangFormatTest() { return new ClangFormatTest; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index ee2658076e2..274ebd8f83f 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -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 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); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp index e992dc8e9ea..aacd273f63e 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildstep.cpp @@ -605,7 +605,7 @@ QWidget *CMakeBuildStep::createConfigWidget() if (!isCleanStep() && !m_buildPreset.isEmpty()) createAndAddEnvironmentWidgets(builder); - builder.noMargin(); + builder.setNoMargins(); auto widget = builder.emerge(); updateDetails(); diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 7b2520cc903..730cdaed4cf 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -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>( + [mimeType](const QHash &cache) -> std::optional { + 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); } } diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h index dd85a52e228..32966c64bbb 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.h +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.h @@ -10,6 +10,7 @@ #include #include +#include #include namespace ProjectExplorer { @@ -221,7 +222,7 @@ private: ProjectExplorer::TreeScanner m_treeScanner; std::shared_ptr m_allFiles; - QHash m_mimeBinaryCache; + Utils::SynchronizedValue> m_mimeBinaryCache; bool m_waitingForParse = false; bool m_combinedScanAndParseResult = false; diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp index 5189c7934fd..998ccbf7ca8 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectimporter.cpp @@ -1124,6 +1124,11 @@ const QList 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 diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index 59e893569e6..6f4050a3aa4 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -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 &sharedLibraryArtifacts) + bool relativeLibs) { const FilePath currentBuildDir = buildDirectory.resolvePath(t.buildDir); - const QSet 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"})) + 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)) - 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 generateBuildTargets(const QFuture &cancelF const FilePath &buildDirectory, bool relativeLibs) { - QSet 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 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; } diff --git a/src/plugins/cmakeprojectmanager/presetsmacros.cpp b/src/plugins/cmakeprojectmanager/presetsmacros.cpp index b7496d0c412..cfc161b417e 100644 --- a/src/plugins/cmakeprojectmanager/presetsmacros.cpp +++ b/src/plugins/cmakeprojectmanager/presetsmacros.cpp @@ -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; + } }); } diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp index ebe7ff44753..af22b135203 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseproject.cpp @@ -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() { diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h index df0d4c9c9fe..dd202efbf39 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdatabaseutils.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include @@ -29,7 +30,7 @@ public: QStringList extras; }; -using MimeBinaryCache = QHash; +using MimeBinaryCache = Utils::SynchronizedValue>; QStringList filterFromFileName(const QStringList &flags, const QString &fileName); diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp index 1ba5a9a1f1b..d613c81faf5 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.cpp @@ -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>( + [mimeType](const QHash &cache) -> std::optional { + 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); } } diff --git a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h index a06ba82581e..065dfbe129a 100644 --- a/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h +++ b/src/plugins/compilationdatabaseprojectmanager/compilationdbparser.h @@ -32,6 +32,7 @@ public: MimeBinaryCache &mimeBinaryCache, ProjectExplorer::BuildSystem::ParseGuard &&guard, QObject *parent = nullptr); + ~CompilationDbParser(); void setPreviousProjectFileHash(const QByteArray &fileHash) { m_projectFileHash = fileHash; } diff --git a/src/plugins/compilerexplorer/compilerexplorereditor.cpp b/src/plugins/compilerexplorer/compilerexplorereditor.cpp index 340cc7ba2ce..1d00f8cb95c 100644 --- a/src/plugins/compilerexplorer/compilerexplorereditor.cpp +++ b/src/plugins/compilerexplorer/compilerexplorereditor.cpp @@ -258,7 +258,7 @@ SourceEditorWidget::SourceEditorWidget(const std::shared_ptr &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 &sourceSett m_compilerSettings->compiler, advButton, removeCompilerBtn, - customMargin({6, 0, 0, 0}), spacing(0), + customMargins(6, 0, 0, 0), spacing(0), }.attachTo(toolBar); Column { diff --git a/src/plugins/copilot/copilotsettings.cpp b/src/plugins/copilot/copilotsettings.cpp index aeac499c631..af16b56db57 100644 --- a/src/plugins/copilot/copilotsettings.cpp +++ b/src/plugins/copilot/copilotsettings.cpp @@ -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 { diff --git a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp index 8649adc1679..4c7de88fd9b 100644 --- a/src/plugins/coreplugin/dialogs/shortcutsettings.cpp +++ b/src/plugins/coreplugin/dialogs/shortcutsettings.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -420,6 +421,7 @@ private: QGridLayout *m_shortcutLayout; std::vector> m_shortcutInputs; QPointer 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); diff --git a/src/plugins/coreplugin/editormanager/editorview.cpp b/src/plugins/coreplugin/editormanager/editorview.cpp index fd9d375d81d..42ad07d2498 100644 --- a/src/plugins/coreplugin/editormanager/editorview.cpp +++ b/src/plugins/coreplugin/editormanager/editorview.cpp @@ -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] { diff --git a/src/plugins/coreplugin/find/findplugin.cpp b/src/plugins/coreplugin/find/findplugin.cpp index 0e5ef38be8b..56042cb478c 100644 --- a/src/plugins/coreplugin/find/findplugin.cpp +++ b/src/plugins/coreplugin/find/findplugin.cpp @@ -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 @@ -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 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,26 +433,70 @@ 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() { - QtcSettings *settings = ICore::settings(); - settings->beginGroup("Find"); - { - QSignalBlocker blocker(m_instance); - Find::setBackward(settings->value("Backward", false).toBool()); - Find::setCaseSensitive(settings->value("CaseSensitively", false).toBool()); - Find::setWholeWord(settings->value("WholeWords", false).toBool()); - Find::setRegularExpression(settings->value("RegularExpression", false).toBool()); - Find::setPreserveCase(settings->value("PreserveCase", false).toBool()); + 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"); + { + QSignalBlocker blocker(m_instance); + Find::setBackward(settings->value("Backward", false).toBool()); + Find::setCaseSensitive(settings->value("CaseSensitively", false).toBool()); + Find::setWholeWord(settings->value("WholeWords", false).toBool()); + Find::setRegularExpression(settings->value("RegularExpression", false).toBool()); + Find::setPreserveCase(settings->value("PreserveCase", false).toBool()); + } + m_findCompletionModel.readSettings(settings); + m_replaceCompletions = settings->value("ReplaceStrings").toStringList(); + m_replaceCompletionModel.setStringList(m_replaceCompletions); + 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"))); } - m_findCompletionModel.readSettings(settings); - m_replaceCompletions = settings->value("ReplaceStrings").toStringList(); - m_replaceCompletionModel.setStringList(m_replaceCompletions); - settings->endGroup(); - m_findToolBar->readSettings(); - m_findDialog->readSettings(); emit m_instance->findFlagsChanged(); // would have been done in the setXXX methods above } diff --git a/src/plugins/coreplugin/find/findtoolbar.cpp b/src/plugins/coreplugin/find/findtoolbar.cpp index 4460367896a..681fa407063 100644 --- a/src/plugins/coreplugin/find/findtoolbar.cpp +++ b/src/plugins/coreplugin/find/findtoolbar.cpp @@ -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(); diff --git a/src/plugins/coreplugin/find/findtoolbar.h b/src/plugins/coreplugin/find/findtoolbar.h index baca57597ca..1080407dae9 100644 --- a/src/plugins/coreplugin/find/findtoolbar.h +++ b/src/plugins/coreplugin/find/findtoolbar.h @@ -6,6 +6,7 @@ #include "currentdocumentfind.h" #include +#include #include #include @@ -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(); diff --git a/src/plugins/coreplugin/find/findtoolwindow.cpp b/src/plugins/coreplugin/find/findtoolwindow.cpp index a239d310be6..bc8e241dd40 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.cpp +++ b/src/plugins/coreplugin/find/findtoolwindow.cpp @@ -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(); diff --git a/src/plugins/coreplugin/find/findtoolwindow.h b/src/plugins/coreplugin/find/findtoolwindow.h index 91b045295a3..77226a6b435 100644 --- a/src/plugins/coreplugin/find/findtoolwindow.h +++ b/src/plugins/coreplugin/find/findtoolwindow.h @@ -3,6 +3,8 @@ #pragma once +#include + #include #include @@ -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(); diff --git a/src/plugins/coreplugin/find/ifindfilter.cpp b/src/plugins/coreplugin/find/ifindfilter.cpp index 49a891e659f..eca6c4f2a91 100644 --- a/src/plugins/coreplugin/find/ifindfilter.cpp +++ b/src/plugins/coreplugin/find/ifindfilter.cpp @@ -6,6 +6,8 @@ #include "../coreicons.h" #include "../coreplugintr.h" +#include + #include #include #include @@ -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. */ diff --git a/src/plugins/coreplugin/find/ifindfilter.h b/src/plugins/coreplugin/find/ifindfilter.h index 2d6325eda01..a76dd2808f9 100644 --- a/src/plugins/coreplugin/find/ifindfilter.h +++ b/src/plugins/coreplugin/find/ifindfilter.h @@ -6,6 +6,7 @@ #include "../core_global.h" #include +#include 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); diff --git a/src/plugins/coreplugin/icore.cpp b/src/plugins/coreplugin/icore.cpp index da47d44b6e0..a4098963a60 100644 --- a/src/plugins/coreplugin/icore.cpp +++ b/src/plugins/coreplugin/icore.cpp @@ -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); diff --git a/src/plugins/coreplugin/manhattanstyle.cpp b/src/plugins/coreplugin/manhattanstyle.cpp index 390970eb84e..7a51c633782 100644 --- a/src/plugins/coreplugin/manhattanstyle.cpp +++ b/src/plugins/coreplugin/manhattanstyle.cpp @@ -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; } diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index a5495612d5c..a36b55a9021 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -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); diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index 63ec6745b5e..3fc432f7d01 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -1203,7 +1203,7 @@ ListModel *SectionedGridView::addSection(const Section §ion, const QList(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); diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp index 84479a78be5..85a17d92df4 100644 --- a/src/plugins/cppcheck/cppchecktool.cpp +++ b/src/plugins/cppcheck/cppchecktool.cpp @@ -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 diff --git a/src/plugins/cppeditor/symbolsfindfilter.cpp b/src/plugins/cppeditor/symbolsfindfilter.cpp index f53fd34c4d1..2888cf3604a 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.cpp +++ b/src/plugins/cppeditor/symbolsfindfilter.cpp @@ -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( - settings->value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt()); + s.value(SETTINGS_SYMBOLTYPES, int(SearchSymbols::AllTypes)).toInt()); m_scope = static_cast( - 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) { diff --git a/src/plugins/cppeditor/symbolsfindfilter.h b/src/plugins/cppeditor/symbolsfindfilter.h index e9c997d219b..2513111cdc9 100644 --- a/src/plugins/cppeditor/symbolsfindfilter.h +++ b/src/plugins/cppeditor/symbolsfindfilter.h @@ -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(); diff --git a/src/plugins/cvs/cvsplugin.cpp b/src/plugins/cvs/cvsplugin.cpp index 01c47b1646f..6a25b29fa04 100644 --- a/src/plugins/cvs/cvsplugin.cpp +++ b/src/plugins/cvs/cvsplugin.cpp @@ -339,7 +339,7 @@ bool CvsPluginPrivate::isVcsFileOrDirectory(const Utils::FilePath &filePath) con bool CvsPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); if (binary.isEmpty()) return false; QFileInfo fi = binary.toFileInfo(); diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 432205ff027..840f78dfc93 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -2800,7 +2800,7 @@ void CdbEngine::setupScripting(const DebuggerResponse &response) } const FilePath path = settings().extraDumperFile(); - if (!path.isEmpty() && path.isReadableFile()) { + if (path.isReadableFile()) { DebuggerCommand cmd("theDumper.addDumperModule", ScriptCommand); cmd.arg("path", path.path()); runCommand(cmd); diff --git a/src/plugins/debugger/console/consoleview.cpp b/src/plugins/debugger/console/consoleview.cpp index d42f4ae9185..de68944e8f4 100644 --- a/src/plugins/debugger/console/consoleview.cpp +++ b/src/plugins/debugger/console/consoleview.cpp @@ -162,9 +162,8 @@ void ConsoleView::onRowActivated(const QModelIndex &index) const Utils::FilePath fp = m_finder.findFile(model()->data(index, ConsoleItem::FileRole).toString()).constFirst(); - if (fp.exists() && fp.isFile() && fp.isReadableFile()) { + if (fp.isReadableFile()) Core::EditorManager::openEditorAt({fp, model()->data(index, ConsoleItem::LineRole).toInt()}); - } } void ConsoleView::copyToClipboard(const QModelIndex &index) diff --git a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp index dc1919ce8b0..4bd1dec9675 100644 --- a/src/plugins/debugger/debuggerrunconfigurationaspect.cpp +++ b/src/plugins/debugger/debuggerrunconfigurationaspect.cpp @@ -72,7 +72,7 @@ DebuggerRunConfigurationAspect::DebuggerRunConfigurationAspect(Target *target) details->setState(DetailsWidget::Expanded); auto innerPane = new QWidget; details->setWidget(innerPane); - builder.noMargin(); + builder.setNoMargins(); builder.attachTo(innerPane); const auto setSummaryText = [this, details] { diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index 3abaea75a46..1079fb24eaf 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -4044,7 +4044,7 @@ void GdbEngine::handleGdbStarted() } const FilePath path = settings().extraDumperFile(); - if (!path.isEmpty() && path.isReadableFile()) { + if (path.isReadableFile()) { DebuggerCommand cmd("addDumperModule"); cmd.arg("path", path.path()); runCommand(cmd); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index d7994be0070..e234aefc216 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -238,7 +238,7 @@ void LldbEngine::handleLldbStarted() executeCommand(commands); const FilePath path = settings().extraDumperFile(); - if (!path.isEmpty() && path.isReadableFile()) { + if (path.isReadableFile()) { DebuggerCommand cmd("addDumperModule"); cmd.arg("path", path.path()); runCommand(cmd); diff --git a/src/plugins/extensionmanager/extensionmanager.qrc b/src/plugins/extensionmanager/extensionmanager.qrc index cff16c156ff..b6a3554cb1f 100644 --- a/src/plugins/extensionmanager/extensionmanager.qrc +++ b/src/plugins/extensionmanager/extensionmanager.qrc @@ -1,5 +1,7 @@ + images/download.png + images/download@2x.png images/extensionsmall.png images/extensionsmall@2x.png images/mode_extensionmanager_mask.png diff --git a/src/plugins/extensionmanager/extensionsbrowser.cpp b/src/plugins/extensionmanager/extensionsbrowser.cpp index b5d42ec4f95..ccf814cd7c8 100644 --- a/src/plugins/extensionmanager/extensionsbrowser.cpp +++ b/src/plugins/extensionmanager/extensionsbrowser.cpp @@ -40,27 +40,37 @@ #include #include -using namespace ExtensionSystem; using namespace Core; +using namespace ExtensionSystem; using namespace Utils; +using namespace StyleHelper; +using namespace SpacingTokens; +using namespace WelcomePageHelpers; namespace ExtensionManager::Internal { Q_LOGGING_CATEGORY(browserLog, "qtc.extensionmanager.browser", QtWarningMsg) -constexpr QSize itemSize = {330, 86}; -constexpr int gapSize = StyleHelper::SpacingTokens::ExVPaddingGapXl; -constexpr QSize cellSize = {itemSize.width() + gapSize, itemSize.height() + gapSize}; - -static QColor colorForExtensionName(const QString &name) -{ - const size_t hash = qHash(name); - return QColor::fromHsv(hash % 360, 180, 110); -} +constexpr int gapSize = ExVPaddingGapXl; +constexpr int itemWidth = 330; +constexpr int cellWidth = itemWidth + HPaddingL; class ExtensionItemDelegate : public QItemDelegate { public: + constexpr static QSize dividerS{1, 16}; + constexpr static QSize iconBgS{50, 50}; + constexpr static TextFormat itemNameTF + {Theme::Token_Text_Default, UiElement::UiElementH6}; + constexpr static TextFormat countTF + {Theme::Token_Text_Default, UiElement::UiElementLabelSmall, + Qt::AlignCenter | Qt::TextDontClip}; + constexpr static TextFormat vendorTF + {Theme::Token_Text_Muted, UiElement::UiElementLabelSmall, + Qt::AlignVCenter | Qt::TextDontClip}; + constexpr static TextFormat tagsTF + {Theme::Token_Text_Default, UiElement::UiElementCaption}; + explicit ExtensionItemDelegate(QObject *parent = nullptr) : QItemDelegate(parent) { @@ -69,12 +79,54 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { + // +---------------+-------+---------------+----------------------------------------------------------------------+---------------+-----------+ + // | | | | (ExPaddingGapL) | | | + // | | | +-------------------------------------------------------------+--------+ | | + // | | | | || | | + // | | | +-------------------------------------------------------------+--------+ | | + // | | | | (VGapXxs) | | | + // | | | +--------+--------+--------------+--------+--------+---------+---------+ | | + // |(ExPaddingGapL)| |(ExPaddingGapL)||(HGapXs)|(h16)|(HGapXs)||(HGapXxs)||(ExPaddingGapL)|(HPaddingL)| + // | |(50x50)| +--------+--------+--------------+--------+--------+---------+---------+ | | + // | | | | (VGapXxs) | | | + // | | | +----------------------------------------------------------------------+ | | + // | | | | | | | + // | | | +----------------------------------------------------------------------+ | | + // | | | | (ExPaddingGapL) | | | + // +---------------+-------+---------------+----------------------------------------------------------------------+---------------+-----------+ + // | (ExVPaddingGapXl) | + // +------------------------------------------------------------------------------------------------------------------------------------------+ + + const QRect bgRGlobal = option.rect.adjusted(0, 0, -HPaddingL, -gapSize); + const QRect bgR = bgRGlobal.translated(-option.rect.topLeft()); + + const int middleColumnW = bgR.width() - ExPaddingGapL - iconBgS.width() - ExPaddingGapL + - ExPaddingGapL; + + int x = bgR.x(); + int y = bgR.y(); + x += ExPaddingGapL; + const QRect iconBgR(x, y + (bgR.height() - iconBgS.height()) / 2, + iconBgS.width(), iconBgS.height()); + x += iconBgS.width() + ExPaddingGapL; + y += ExPaddingGapL; + const QRect itemNameR(x, y, middleColumnW, itemNameTF.lineHeight()); + const QString itemName = index.data().toString(); + + y += itemNameR.height() + VGapXxs; + const QRect vendorRowR(x, y, middleColumnW, vendorRowHeight()); + QRect vendorR = vendorRowR; + + y += vendorRowR.height() + VGapXxs; + const QRect tagsR(x, y, middleColumnW, tagsTF.lineHeight()); + + QTC_CHECK(option.rect.height() - 1 == tagsR.bottom() + ExPaddingGapL + gapSize); + painter->save(); painter->setRenderHint(QPainter::Antialiasing); + painter->translate(bgRGlobal.topLeft()); - const QString itemName = index.data().toString(); const bool isPack = index.data(RoleItemType) == ItemTypePack; - const QRectF itemRect(option.rect.topLeft(), itemSize); { const bool selected = option.state & QStyle::State_Selected; const bool hovered = option.state & QStyle::State_MouseOver; @@ -85,94 +137,114 @@ public: creatorColor(selected ? Theme::Token_Stroke_Strong : hovered ? WelcomePageHelpers::cardHoverStroke : WelcomePageHelpers::cardDefaultStroke); - WelcomePageHelpers::drawCardBackground(painter, itemRect, fillColor, strokeColor); + WelcomePageHelpers::drawCardBackground(painter, bgR, fillColor, strokeColor); } { - constexpr QRectF bigCircle(16, 16, 48, 48); - constexpr double gradientMargin = 0.14645; - const QRectF bigCircleLocal = bigCircle.translated(itemRect.topLeft()); - QPainterPath bigCirclePath; - bigCirclePath.addEllipse(bigCircleLocal); - QLinearGradient gradient(bigCircleLocal.topLeft(), bigCircleLocal.bottomRight()); - const QColor startColor = isPack ? qRgb(0x1e, 0x99, 0x6e) - : colorForExtensionName(itemName); - const QColor endColor = isPack ? qRgb(0x07, 0x6b, 0x6d) : startColor.lighter(150); - gradient.setColorAt(gradientMargin, startColor); - gradient.setColorAt(1 - gradientMargin, endColor); - painter->fillPath(bigCirclePath, gradient); + QLinearGradient gradient(iconBgR.topRight(), iconBgR.bottomLeft()); + const QColor startColor = creatorColor(Utils::Theme::Token_Gradient01_Start); + const QColor endColor = creatorColor(Utils::Theme::Token_Gradient01_End); + gradient.setColorAt(0, startColor); + gradient.setColorAt(1, endColor); + constexpr int iconRectRounding = 4; + drawCardBackground(painter, iconBgR, gradient, Qt::NoPen, iconRectRounding); - static const QIcon packIcon = - Icon({{":/extensionmanager/images/packsmall.png", - Theme::Token_Text_Default}}, Icon::Tint).icon(); - static const QIcon extensionIcon = - Icon({{":/extensionmanager/images/extensionsmall.png", - Theme::Token_Text_Default}}, Icon::Tint).icon(); - QRectF iconRect(0, 0, 32, 32); - iconRect.moveCenter(bigCircleLocal.center()); - (isPack ? packIcon : extensionIcon).paint(painter, iconRect.toRect()); + // Icon + constexpr Theme::Color color = Theme::Token_Basic_White; + static const QIcon pack = Icon({{":/extensionmanager/images/packsmall.png", color}}, + Icon::Tint).icon(); + static const QIcon extension = Icon({{":/extensionmanager/images/extensionsmall.png", + color}}, Icon::Tint).icon(); + (isPack ? pack : extension).paint(painter, iconBgR); } if (isPack) { - constexpr QRectF smallCircle(47, 50, 18, 18); - constexpr qreal strokeWidth = 1; - constexpr qreal shrink = strokeWidth / 2; - constexpr QRectF smallCircleAdjusted = smallCircle.adjusted(shrink, shrink, - -shrink, -shrink); - const QRectF smallCircleLocal = smallCircleAdjusted.translated(itemRect.topLeft()); + constexpr int circleSize = 18; + constexpr int circleOverlap = 3; // Protrusion from lower right corner of iconRect + const QRect smallCircle(iconBgR.right() + 1 + circleOverlap - circleSize, + iconBgR.bottom() + 1 + circleOverlap - circleSize, + circleSize, circleSize); const QColor fillColor = creatorColor(Theme::Token_Foreground_Muted); const QColor strokeColor = creatorColor(Theme::Token_Stroke_Subtle); - painter->setBrush(fillColor); - painter->setPen(strokeColor); - painter->drawEllipse(smallCircleLocal); + drawCardBackground(painter, smallCircle, fillColor, strokeColor, circleSize / 2); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong)); - const QColor textColor = creatorColor(Theme::Token_Text_Default); - painter->setPen(textColor); + painter->setFont(countTF.font()); + painter->setPen(countTF.color()); const PluginsData plugins = index.data(RolePlugins).value(); - painter->drawText( - smallCircleLocal, - QString::number(plugins.count()), - QTextOption(Qt::AlignCenter)); + painter->drawText(smallCircle, countTF.drawTextFlags, QString::number(plugins.count())); } { - constexpr int textX = 80; - constexpr int rightMargin = StyleHelper::SpacingTokens::ExVPaddingGapXl; - constexpr int maxTextWidth = itemSize.width() - textX - rightMargin; - constexpr Qt::TextElideMode elideMode = Qt::ElideRight; - - constexpr int titleY = 30; - const QPointF titleOrigin(itemRect.topLeft() + QPointF(textX, titleY)); - painter->setPen(creatorColor(Theme::Token_Text_Default)); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementH6)); + painter->setPen(itemNameTF.color()); + painter->setFont(itemNameTF.font()); const QString titleElided - = painter->fontMetrics().elidedText(itemName, elideMode, maxTextWidth); - painter->drawText(titleOrigin, titleElided); + = painter->fontMetrics().elidedText(itemName, Qt::ElideRight, itemNameR.width()); + painter->drawText(itemNameR, itemNameTF.drawTextFlags, titleElided); + } + { + const QString vendor = index.data(RoleVendor).toString(); + const QFontMetrics fm(vendorTF.font()); + painter->setPen(vendorTF.color()); + painter->setFont(vendorTF.font()); - constexpr int copyrightY = 52; - const QPointF copyrightOrigin(itemRect.topLeft() + QPointF(textX, copyrightY)); - painter->setPen(creatorColor(Theme::Token_Text_Muted)); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaptionStrong)); - const QString copyright = index.data(RoleCopyright).toString(); - const QString copyrightElided - = painter->fontMetrics().elidedText(copyright, elideMode, maxTextWidth); - painter->drawText(copyrightOrigin, copyrightElided); + if (const int dlCount = index.data(RoleDownloadCount).toInt(); dlCount > 0) { + constexpr QSize dlIconS(16, 16); + const QString dlCountString = QString::number(dlCount); + const int dlCountW = fm.horizontalAdvance(dlCountString); + const int dlItemsW = HGapXs + dividerS.width() + HGapXs + dlIconS.width() + + HGapXxs + dlCountW; + const int vendorW = fm.horizontalAdvance(vendor); + vendorR.setWidth(qMin(middleColumnW - dlItemsW, vendorW)); - constexpr int tagsY = 70; - const QPointF tagsOrigin(itemRect.topLeft() + QPointF(textX, tagsY)); - const QString tags = index.data(RoleTags).toStringList().join(", "); - painter->setPen(creatorColor(Theme::Token_Text_Default)); - painter->setFont(StyleHelper::uiFont(StyleHelper::UiElementCaption)); - const QString tagsElided = painter->fontMetrics().elidedText( - tags, elideMode, maxTextWidth); - painter->drawText(tagsOrigin, tagsElided); + QRect dividerR = vendorRowR; + dividerR.setLeft(vendorR.right() + HGapXs); + dividerR.setWidth(dividerS.width()); + painter->fillRect(dividerR, vendorTF.color()); + + QRect dlIconR = vendorRowR; + dlIconR.setLeft(dividerR.right() + HGapXs); + dlIconR.setWidth(dlIconS.width()); + static const QIcon dlIcon = Icon({{":/extensionmanager/images/download.png", + vendorTF.themeColor}}, Icon::Tint).icon(); + dlIcon.paint(painter, dlIconR); + + QRect dlCountR = vendorRowR; + dlCountR.setLeft(dlIconR.right() + HGapXxs); + painter->drawText(dlCountR, vendorTF.drawTextFlags, dlCountString); + } + + const QString vendorElided = fm.elidedText(vendor, Qt::ElideRight, vendorR.width()); + painter->drawText(vendorR, vendorTF.drawTextFlags, vendorElided); + } + { + const QStringList tagList = index.data(RoleTags).toStringList(); + const QString tags = tagList.join(", "); + painter->setPen(tagsTF.color()); + painter->setFont(tagsTF.font()); + const QString tagsElided + = painter->fontMetrics().elidedText(tags, Qt::ElideRight, tagsR.width()); + painter->drawText(tagsR, tagsTF.drawTextFlags, tagsElided); } painter->restore(); } + static int vendorRowHeight() + { + return qMax(vendorTF.lineHeight(), dividerS.height()); + } + QSize sizeHint([[maybe_unused]] const QStyleOptionViewItem &option, [[maybe_unused]] const QModelIndex &index) const override { - return cellSize; + const int middleColumnH = + itemNameTF.lineHeight() + + VGapXxs + + vendorRowHeight() + + VGapXxs + + tagsTF.lineHeight(); + const int height = + ExPaddingGapL + + qMax(iconBgS.height(), middleColumnH) + + ExPaddingGapL; + return {cellWidth, height + gapSize}; } }; @@ -196,11 +268,10 @@ ExtensionsBrowser::ExtensionsBrowser(QWidget *parent) setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred); auto manageLabel = new QLabel(Tr::tr("Manage Extensions")); - manageLabel->setFont(StyleHelper::uiFont(StyleHelper::UiElementH1)); + manageLabel->setFont(uiFont(UiElementH1)); d->searchBox = new SearchBox; - d->searchBox->setFixedWidth(itemSize.width()); - + d->searchBox->setFixedWidth(itemWidth); d->updateButton = new Button(Tr::tr("Install..."), Button::MediumPrimary); d->model = new ExtensionsModel(this); @@ -267,14 +338,14 @@ ExtensionsBrowser::~ExtensionsBrowser() void ExtensionsBrowser::adjustToWidth(const int width) { const int widthForItems = width - extraListViewWidth(); - d->columnsCount = qMax(1, qFloor(widthForItems / cellSize.width())); + d->columnsCount = qMax(1, qFloor(widthForItems / cellWidth)); d->updateButton->setVisible(d->columnsCount > 1); updateGeometry(); } QSize ExtensionsBrowser::sizeHint() const { - const int columsWidth = d->columnsCount * cellSize.width(); + const int columsWidth = d->columnsCount * cellWidth; return { columsWidth + extraListViewWidth(), 0}; } diff --git a/src/plugins/extensionmanager/extensionsmodel.cpp b/src/plugins/extensionmanager/extensionsmodel.cpp index 881125555df..56fcc54ad8b 100644 --- a/src/plugins/extensionmanager/extensionsmodel.cpp +++ b/src/plugins/extensionmanager/extensionsmodel.cpp @@ -38,6 +38,7 @@ struct Plugin { Dependencies dependencies; QString copyright; + bool isInternal = false; QString name; QString packageUrl; QString vendor; @@ -54,6 +55,8 @@ struct Description { struct Extension { QString copyright; Description description; + int downloadCount = -1; + QString id; QString license; QString name; QStringList platforms; @@ -83,6 +86,7 @@ static Plugin pluginFromJson(const QJsonObject &obj) return { .dependencies = dependencies, .copyright = metaDataObj.value("Copyright").toString(), + .isInternal = obj.value("is_internal").toBool(false), .name = metaDataObj.value("Name").toString(), .packageUrl = obj.value("url").toString(), .vendor = metaDataObj.value("Vendor").toString(), @@ -158,6 +162,8 @@ static Extension extensionFromJson(const QJsonObject &obj) const Extension extension = { .copyright = obj.value("copyright").toString(), .description = description, + .downloadCount = obj.value("download_count").toInt(-1), + .id = obj.value("id").toString(), .license = obj.value("license").toString(), .name = obj.value("name").toString(), .platforms = platforms, @@ -318,6 +324,10 @@ static QVariant dataFromExtension(const Extension &extension, int role) return QVariant::fromValue(extension.description.links); case RoleDescriptionText: return QVariant::fromValue(extension.description.text); + case RoleDownloadCount: + return extension.downloadCount; + case RoleId: + return extension.id; case RoleItemType: return extension.type; case RoleLicense: diff --git a/src/plugins/extensionmanager/extensionsmodel.h b/src/plugins/extensionmanager/extensionsmodel.h index d426f443533..1fc86d3afde 100644 --- a/src/plugins/extensionmanager/extensionsmodel.h +++ b/src/plugins/extensionmanager/extensionsmodel.h @@ -30,6 +30,8 @@ enum Role { RoleDescriptionImages, RoleDescriptionLinks, RoleDescriptionText, + RoleDownloadCount, + RoleId, RoleItemType, RoleLicense, RoleLocation, diff --git a/src/plugins/extensionmanager/images/download.png b/src/plugins/extensionmanager/images/download.png new file mode 100644 index 00000000000..d328e062fcc Binary files /dev/null and b/src/plugins/extensionmanager/images/download.png differ diff --git a/src/plugins/extensionmanager/images/download@2x.png b/src/plugins/extensionmanager/images/download@2x.png new file mode 100644 index 00000000000..404df01da06 Binary files /dev/null and b/src/plugins/extensionmanager/images/download@2x.png differ diff --git a/src/plugins/extensionmanager/images/extensionsmall.png b/src/plugins/extensionmanager/images/extensionsmall.png index 6cdb8df12c9..b3d270c5146 100644 Binary files a/src/plugins/extensionmanager/images/extensionsmall.png and b/src/plugins/extensionmanager/images/extensionsmall.png differ diff --git a/src/plugins/extensionmanager/images/extensionsmall@2x.png b/src/plugins/extensionmanager/images/extensionsmall@2x.png index f788b5565a8..414f7197ae8 100644 Binary files a/src/plugins/extensionmanager/images/extensionsmall@2x.png and b/src/plugins/extensionmanager/images/extensionsmall@2x.png differ diff --git a/src/plugins/extensionmanager/images/packsmall.png b/src/plugins/extensionmanager/images/packsmall.png index 7a1c5e09bc7..92c8ab99d6d 100644 Binary files a/src/plugins/extensionmanager/images/packsmall.png and b/src/plugins/extensionmanager/images/packsmall.png differ diff --git a/src/plugins/extensionmanager/images/packsmall@2x.png b/src/plugins/extensionmanager/images/packsmall@2x.png index a58632c756c..281ccd8207f 100644 Binary files a/src/plugins/extensionmanager/images/packsmall@2x.png and b/src/plugins/extensionmanager/images/packsmall@2x.png differ diff --git a/src/plugins/fossil/fossilclient.cpp b/src/plugins/fossil/fossilclient.cpp index 662785930d4..2c077a3b591 100644 --- a/src/plugins/fossil/fossilclient.cpp +++ b/src/plugins/fossil/fossilclient.cpp @@ -766,7 +766,7 @@ unsigned int FossilClient::binaryVersion() const static unsigned int cachedBinaryVersion = 0; static FilePath cachedBinaryPath; - const FilePath currentBinaryPath = settings().binaryPath(); + const FilePath currentBinaryPath = settings().binaryPath.effectiveBinary(); if (currentBinaryPath.isEmpty()) return 0; diff --git a/src/plugins/git/gitgrep.cpp b/src/plugins/git/gitgrep.cpp index 73feffd68f8..84bf26238f6 100644 --- a/src/plugins/git/gitgrep.cpp +++ b/src/plugins/git/gitgrep.cpp @@ -245,14 +245,15 @@ GitGrepParameters GitGrep::gitParameters() const return {m_treeLineEdit->text(), m_recurseSubmodules && m_recurseSubmodules->isChecked()}; } -void GitGrep::readSettings(QtcSettings *settings) +void GitGrep::readSettings(const Store &s) { - m_treeLineEdit->setText(settings->value(GitGrepRef).toString()); + m_treeLineEdit->setText(s.value(GitGrepRef).toString()); } -void GitGrep::writeSettings(QtcSettings *settings) const +void GitGrep::writeSettings(Store &s) const { - settings->setValue(GitGrepRef, m_treeLineEdit->text()); + if (!m_treeLineEdit->text().isEmpty()) + s.insert(GitGrepRef, m_treeLineEdit->text()); } SearchExecutor GitGrep::searchExecutor() const diff --git a/src/plugins/git/gitgrep.h b/src/plugins/git/gitgrep.h index bb6967eb675..4e5c75d97ab 100644 --- a/src/plugins/git/gitgrep.h +++ b/src/plugins/git/gitgrep.h @@ -24,8 +24,8 @@ public: QString title() const override; QString toolTip() const override; QWidget *widget() const override; - void readSettings(Utils::QtcSettings *settings) override; - void writeSettings(Utils::QtcSettings *settings) const override; + void readSettings(const Utils::Store &settings) override; + void writeSettings(Utils::Store &settings) const override; TextEditor::SearchExecutor searchExecutor() const override; TextEditor::EditorOpener editorOpener() const override; diff --git a/src/plugins/gitlab/gitlaboptionspage.cpp b/src/plugins/gitlab/gitlaboptionspage.cpp index 03d078ba14e..181d5fba829 100644 --- a/src/plugins/gitlab/gitlaboptionspage.cpp +++ b/src/plugins/gitlab/gitlaboptionspage.cpp @@ -102,7 +102,7 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent) m_token, br, m_port, br, m_secure, - m == Edit ? &Layout::normalMargin : &Layout::noMargin + m == Edit ? &Layout::setNormalMargins : &Layout::setNoMargins }, }.attachTo(this); } diff --git a/src/plugins/insight/Insight.json.in b/src/plugins/insight/Insight.json.in index 12b9e51f2b9..1f60965d80a 100644 --- a/src/plugins/insight/Insight.json.in +++ b/src/plugins/insight/Insight.json.in @@ -18,7 +18,7 @@ "LongDescription" : [ "Use Qt Insight with Qt Design Studio.", "You also need:", - "- Qt Insight", + "- Qt Insight" ], "DisabledByDefault" : true, "Url" : "https://www.qt.io", diff --git a/src/plugins/lua/Lua.json.in b/src/plugins/lua/Lua.json.in index c41ce7bc88c..68b54fd1add 100644 --- a/src/plugins/lua/Lua.json.in +++ b/src/plugins/lua/Lua.json.in @@ -17,5 +17,13 @@ "Category" : "Scripting", "Description" : "Support for Lua based plugins.", "Url" : "https://www.qt.io", + "JsonWizardPaths" : [":/lua/wizards"], + "Arguments" : [ + { + "Name" : "-loadluaplugin", + "Parameter" : "path", + "Description" : "Load an additional lua plugin from the specified path" + } + ], ${IDE_PLUGIN_DEPENDENCIES} } diff --git a/src/plugins/lua/bindings/gui.cpp b/src/plugins/lua/bindings/gui.cpp index 7e236972330..aaba86f461c 100644 --- a/src/plugins/lua/bindings/gui.cpp +++ b/src/plugins/lua/bindings/gui.cpp @@ -306,8 +306,8 @@ void addGuiModule() }), "show", &Widget::show, - "resize", - &Widget::resize, + "setSize", + &Widget::setSize, sol::base_classes, sol::bases()); diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index 93d48ff6cf8..60505ade850 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -49,6 +49,24 @@ public: } }; +struct Arguments +{ + std::optional loadPlugin; +}; + +Arguments parseArguments(const QStringList &arguments) +{ + Arguments args; + for (int i = 0; i < arguments.size() - 1; ++i) { + if (arguments.at(i) == QLatin1String("-loadluaplugin")) { + const QString path(arguments.at(i + 1)); + args.loadPlugin = FilePath::fromUserInput(path); + i++; // skip the argument + } + } + return args; +} + class LuaPlugin : public IPlugin { Q_OBJECT @@ -56,15 +74,18 @@ class LuaPlugin : public IPlugin private: std::unique_ptr m_luaEngine; + Arguments m_arguments; public: LuaPlugin() {} ~LuaPlugin() override = default; - void initialize() final + bool initialize(const QStringList &arguments, QString *) final { m_luaEngine.reset(new LuaEngine()); + m_arguments = parseArguments(arguments); + addAsyncModule(); addFetchModule(); addActionModule(); @@ -79,6 +100,8 @@ public: addInstallModule(); Core::JsExpander::registerGlobalObject("Lua", [] { return new LuaJsExtension(); }); + + return true; } bool delayedInitialize() final @@ -94,8 +117,7 @@ public: { QSet plugins; for (const FilePath &path : paths) { - const FilePaths folders = path.dirEntries( - FileFilter({}, QDir::Dirs | QDir::NoDotAndDotDot)); + FilePaths folders = path.dirEntries(FileFilter({}, QDir::Dirs | QDir::NoDotAndDotDot)); for (const FilePath &folder : folders) { const FilePath script = folder / (folder.baseName() + ".lua"); @@ -113,6 +135,23 @@ public: } } + if (m_arguments.loadPlugin) { + const FilePath folder = *m_arguments.loadPlugin; + const FilePath script = folder / (folder.baseName() + ".lua"); + const expected_str result = m_luaEngine->loadPlugin(script); + + if (!result) { + qWarning() << "Failed to load plugin" << script << ":" << result.error(); + MessageManager::writeFlashing(tr("Failed to load plugin %1: %2") + .arg(script.toUserOutput()) + .arg(result.error())); + + } else { + (*result)->setEnabledBySettings(true); + plugins.insert(*result); + } + } + PluginManager::addPlugins({plugins.begin(), plugins.end()}); PluginManager::loadPluginsAtRuntime(plugins); } diff --git a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp index e315ce7efb9..f26a618a10a 100644 --- a/src/plugins/marketplace/qtmarketplacewelcomepage.cpp +++ b/src/plugins/marketplace/qtmarketplacewelcomepage.cpp @@ -62,11 +62,11 @@ public: Column { Row { m_searcher, - customMargin({0, 0, ExVPaddingGapXl, 0}), + customMargins(0, 0, ExVPaddingGapXl, 0), }, m_sectionedProducts, spacing(VPaddingL), - customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0), }.attachTo(this); connect(m_sectionedProducts, &SectionedProducts::toggleProgressIndicator, diff --git a/src/plugins/mcusupport/mcupackage.cpp b/src/plugins/mcusupport/mcupackage.cpp index 2ff913c680e..2ccc4bcc1a0 100644 --- a/src/plugins/mcusupport/mcupackage.cpp +++ b/src/plugins/mcusupport/mcupackage.cpp @@ -43,11 +43,11 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, const McuPackageVersionDetector *versionDetector, const bool addToSystemPath, const Utils::PathChooser::Kind &valueType, - const bool useNewestVersionKey) + const bool allowNewerVersionKey) : settingsHandler(settingsHandler) , m_label(label) , m_detectionPaths(detectionPaths) - , m_settingsKey(settingsHandler->getVersionedKey(settingsKey, QSettings::SystemScope, versions, useNewestVersionKey)) + , m_settingsKey(settingsKey) , m_versionDetector(versionDetector) , m_versions(versions) , m_cmakeVariableName(cmakeVarName) @@ -56,8 +56,18 @@ McuPackage::McuPackage(const SettingsHandler::Ptr &settingsHandler, , m_addToSystemPath(addToSystemPath) , m_valueType(valueType) { - m_defaultPath = settingsHandler->getPath(m_settingsKey, QSettings::SystemScope, defaultPath); - m_path = settingsHandler->getPath(m_settingsKey, QSettings::UserScope, m_defaultPath); + // The installer writes versioned keys as well as the plain key as found in the kits. + // Use the versioned key in case the plain key was removed by an uninstall operation + const Utils::Key versionedKey = settingsHandler->getVersionedKey(settingsKey, + QSettings::SystemScope, + versions, + allowNewerVersionKey); + m_defaultPath = settingsHandler->getPath(versionedKey, QSettings::SystemScope, defaultPath); + m_path = settingsHandler->getPath(m_settingsKey, QSettings::UserScope, ""); + // The user settings may have been written with a versioned key in older versions of QtCreator + if (m_path.isEmpty()) { + m_path = settingsHandler->getPath(versionedKey, QSettings::UserScope, m_defaultPath); + } if (m_path.isEmpty()) { m_path = FilePath::fromUserInput(qtcEnvironmentVariable(m_environmentVariableName)); } @@ -559,6 +569,10 @@ QVariant McuToolchainPackage::debuggerId() const switch (m_type) { case ToolchainType::ArmGcc: { sub = QString::fromLatin1("bin/arm-none-eabi-gdb-py"); + const FilePath command = (path() / sub).withExecutableSuffix(); + if (!command.exists()) { + sub = QString::fromLatin1("bin/arm-none-eabi-gdb"); + } displayName = Tr::tr("Arm GDB at %1"); engineType = Debugger::GdbEngineType; break; diff --git a/src/plugins/mcusupport/mcupackage.h b/src/plugins/mcusupport/mcupackage.h index a74c08bcf2d..7b9ef646506 100644 --- a/src/plugins/mcusupport/mcupackage.h +++ b/src/plugins/mcusupport/mcupackage.h @@ -41,7 +41,7 @@ public: const bool addToPath = false, const Utils::PathChooser::Kind &valueType = Utils::PathChooser::Kind::ExistingDirectory, - const bool useNewestVersionKey = false); + const bool allowNewerVersionKey = false); ~McuPackage() override = default; diff --git a/src/plugins/mcusupport/settingshandler.cpp b/src/plugins/mcusupport/settingshandler.cpp index 1ecec030b45..939ba125d1c 100644 --- a/src/plugins/mcusupport/settingshandler.cpp +++ b/src/plugins/mcusupport/settingshandler.cpp @@ -105,12 +105,24 @@ FilePath SettingsHandler::getPath(const Key &settingsKey, bool SettingsHandler::write(const Key &settingsKey, const FilePath &path, - const FilePath &defaultPath) const + const FilePath &maybeDefaultPath) const { const FilePath savedPath = packagePathFromSettings(settingsKey, *Core::ICore::settings(QSettings::UserScope), - defaultPath); + maybeDefaultPath); const Key key = Key(Constants::SETTINGS_GROUP) + '/' + Constants::SETTINGS_KEY_PACKAGE_PREFIX + settingsKey; + + FilePath defaultPath = maybeDefaultPath; + if (path == maybeDefaultPath) { + // If the installer has overwritten the non-versioned key with an older version than the + // newest versioned key, and the user wants to manually return to the newest installed + // version, the defaultPath will match the desired path, and the settings object will + // assume it can simply remove the key instead of writing a new value to it. + // To work around this, pretend like the default value is the value found from the global scope + defaultPath = packagePathFromSettings(settingsKey, + *Core::ICore::settings(QSettings::SystemScope), + maybeDefaultPath);; + } Core::ICore::settings()->setValueWithDefault(key, path.toUserOutput(), defaultPath.toUserOutput()); diff --git a/src/plugins/mercurial/mercurialplugin.cpp b/src/plugins/mercurial/mercurialplugin.cpp index 39b3fe44320..6d7a1ef7302 100644 --- a/src/plugins/mercurial/mercurialplugin.cpp +++ b/src/plugins/mercurial/mercurialplugin.cpp @@ -669,7 +669,7 @@ bool MercurialPluginPrivate::managesFile(const FilePath &workingDirectory, const bool MercurialPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); if (binary.isEmpty()) return false; QFileInfo fi = binary.toFileInfo(); diff --git a/src/plugins/nim/project/nimbuildsystem.cpp b/src/plugins/nim/project/nimbuildsystem.cpp index ef7d6c47320..373a263ba27 100644 --- a/src/plugins/nim/project/nimbuildsystem.cpp +++ b/src/plugins/nim/project/nimbuildsystem.cpp @@ -25,14 +25,6 @@ const char EXCLUDED_FILES_KEY[] = "ExcludedFiles"; NimProjectScanner::NimProjectScanner(Project *project) : m_project(project) { - m_scanner.setFilter([this](const Utils::MimeType &, const FilePath &fp) { - const QString path = fp.toString(); - return excludedFiles().contains(path) - || path.endsWith(".nimproject") - || path.contains(".nimproject.user") - || path.contains(".nimble.user"); - }); - connect(&m_directoryWatcher, &FileSystemWatcher::directoryChanged, this, &NimProjectScanner::directoryChanged); connect(&m_directoryWatcher, &FileSystemWatcher::fileChanged, @@ -91,6 +83,13 @@ void NimProjectScanner::saveSettings() void NimProjectScanner::startScan() { + m_scanner.setFilter( + [excludedFiles = excludedFiles()](const Utils::MimeType &, const FilePath &fp) { + const QString path = fp.toString(); + return excludedFiles.contains(path) || path.endsWith(".nimproject") + || path.contains(".nimproject.user") || path.contains(".nimble.user"); + }); + m_scanner.asyncScanForFiles(m_project->projectDirectory()); } diff --git a/src/plugins/projectexplorer/CMakeLists.txt b/src/plugins/projectexplorer/CMakeLists.txt index 0c62d2177a1..ea7a079d908 100644 --- a/src/plugins/projectexplorer/CMakeLists.txt +++ b/src/plugins/projectexplorer/CMakeLists.txt @@ -140,6 +140,7 @@ add_qtc_plugin(ProjectExplorer projectmanager.cpp projectmanager.h projectmodels.cpp projectmodels.h projectnodes.cpp projectnodes.h + projectnodeshelper.h projectpanelfactory.cpp projectpanelfactory.h projectsettingswidget.cpp projectsettingswidget.h projecttree.cpp projecttree.h diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index c2cee55e309..5d8f285b001 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -113,16 +113,22 @@ QWidget *AllProjectsFind::createConfigWidget() return m_configWidget; } -void AllProjectsFind::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + +Store AllProjectsFind::save() const { - settings->beginGroup("AllProjectsFind"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void AllProjectsFind::readSettings(QtcSettings *settings) +void AllProjectsFind::restore(const Utils::Store &s) { - settings->beginGroup("AllProjectsFind"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); +} + +QByteArray AllProjectsFind::settingsKey() const +{ + return "AllProjectsFind"; } diff --git a/src/plugins/projectexplorer/allprojectsfind.h b/src/plugins/projectexplorer/allprojectsfind.h index 02dfc2a27ae..4b43f1ed4be 100644 --- a/src/plugins/projectexplorer/allprojectsfind.h +++ b/src/plugins/projectexplorer/allprojectsfind.h @@ -26,8 +26,12 @@ public: bool isEnabled() const 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; + + // deprecated + QByteArray settingsKey() const override; protected: static Utils::FileContainer filesForProjects(const QStringList &nameFilters, diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 6b60b659215..a69133b2190 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -326,7 +326,7 @@ NamedWidget *BuildConfiguration::createConfigWidget() } Layouting::Form form; - form.noMargin(); + form.setNoMargins(); for (BaseAspect *aspect : aspects()) { if (aspect->isVisible()) { form.addItem(aspect); diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp index 9b28cd08b23..a290fc0c69a 100644 --- a/src/plugins/projectexplorer/buildstep.cpp +++ b/src/plugins/projectexplorer/buildstep.cpp @@ -113,7 +113,7 @@ QWidget *BuildStep::doCreateConfigWidget() QWidget *BuildStep::createConfigWidget() { Layouting::Form form; - form.noMargin(); + form.setNoMargins(); for (BaseAspect *aspect : std::as_const(*this)) { if (aspect->isVisible()) { form.addItem(aspect); diff --git a/src/plugins/projectexplorer/currentprojectfind.cpp b/src/plugins/projectexplorer/currentprojectfind.cpp index 76439d93b74..5ec53f3dd2c 100644 --- a/src/plugins/projectexplorer/currentprojectfind.cpp +++ b/src/plugins/projectexplorer/currentprojectfind.cpp @@ -29,8 +29,11 @@ private: bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; + + // deprecated + QByteArray settingsKey() const final; QString label() const final; @@ -115,18 +118,24 @@ void CurrentProjectFind::setupSearch(Core::SearchResult *search) }); } -void CurrentProjectFind::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + +Store CurrentProjectFind::save() const { - settings->beginGroup("CurrentProjectFind"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void CurrentProjectFind::readSettings(QtcSettings *settings) +void CurrentProjectFind::restore(const Store &s) { - settings->beginGroup("CurrentProjectFind"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); +} + +QByteArray CurrentProjectFind::settingsKey() const +{ + return "CurrentProjectFind"; } void setupCurrentProjectFind() diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp index 8639bc00cee..12bc39a7c4d 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.cpp @@ -49,21 +49,6 @@ bool CustomExecutableRunConfiguration::isEnabled(Id) const return true; } -ProcessRunData CustomExecutableRunConfiguration::runnable() const -{ - ProcessRunData r; - r.command = commandLine(); - r.environment = environment.environment(); - r.workingDirectory = workingDir(); - - if (!r.command.isEmpty()) { - const FilePath expanded = macroExpander()->expand(r.command.executable()); - r.command.setExecutable(expanded); - } - - return r; -} - QString CustomExecutableRunConfiguration::defaultDisplayName() const { if (executable().isEmpty()) diff --git a/src/plugins/projectexplorer/customexecutablerunconfiguration.h b/src/plugins/projectexplorer/customexecutablerunconfiguration.h index ced9892c016..3b9e8996b8a 100644 --- a/src/plugins/projectexplorer/customexecutablerunconfiguration.h +++ b/src/plugins/projectexplorer/customexecutablerunconfiguration.h @@ -20,7 +20,6 @@ public: QString defaultDisplayName() const; private: - Utils::ProcessRunData runnable() const override; bool isEnabled(Utils::Id) const override; Tasks checkForIssues() const override; diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index a34f8bc256e..c438b7b1f2c 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -197,10 +197,7 @@ EnvironmentWidget::EnvironmentWidget(QWidget *parent, Type type, QWidget *additi auto horizontalLayout = new QHBoxLayout(); horizontalLayout->setContentsMargins(0, 0, 0, 0); - auto tree = new Utils::TreeView(this); - connect(tree, &QAbstractItemView::activated, - tree, [tree](const QModelIndex &idx) { tree->edit(idx); }); - d->m_environmentView = tree; + d->m_environmentView = new Utils::TreeView(this); d->m_environmentView->setModel(d->m_model); d->m_environmentView->setMinimumHeight(400); d->m_environmentView->setRootIsDecorated(false); @@ -363,7 +360,10 @@ void EnvironmentWidget::updateSummaryText() return; } - Utils::EnvironmentItems list = d->m_model->userChanges(); + Utils::EnvironmentItems list + = Utils::filtered(d->m_model->userChanges(), [](const EnvironmentItem &it) { + return it.operation != Utils::EnvironmentItem::Comment; + }); Utils::EnvironmentItem::sort(&list); QString text; @@ -387,6 +387,8 @@ void EnvironmentWidget::updateSummaryText() case Utils::EnvironmentItem::SetDisabled: text.append(Tr::tr("Set %1 to %2 [disabled]").arg(item.name.toHtmlEscaped(), item.value.toHtmlEscaped())); break; + case Utils::EnvironmentItem::Comment: + break; } } } diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.cpp b/src/plugins/projectexplorer/filesinallprojectsfind.cpp index 92c2fc80596..62ac4e48392 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.cpp +++ b/src/plugins/projectexplorer/filesinallprojectsfind.cpp @@ -29,22 +29,25 @@ QString FilesInAllProjectsFind::displayName() const } const char kSettingsKey[] = "FilesInAllProjectDirectories"; +const char kDefaultInclusion[] + = "CMakeLists.txt,*.cmake,*.pro,*.pri,*.qbs,*.cpp,*.h,*.mm,*.qml,*.md,*.txt,*.qdoc"; +const char kDefaultExclusion[] = "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"; -void FilesInAllProjectsFind::writeSettings(QtcSettings *settings) +Store FilesInAllProjectsFind::save() const { - settings->beginGroup(kSettingsKey); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void FilesInAllProjectsFind::readSettings(QtcSettings *settings) +void FilesInAllProjectsFind::restore(const Utils::Store &s) { - settings->beginGroup(kSettingsKey); - readCommonSettings( - settings, - "CMakeLists.txt,*.cmake,*.pro,*.pri,*.qbs,*.cpp,*.h,*.mm,*.qml,*.md,*.txt,*.qdoc", - "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); +} + +QByteArray FilesInAllProjectsFind::settingsKey() const +{ + return kSettingsKey; } FileContainerProvider FilesInAllProjectsFind::fileContainerProvider() const diff --git a/src/plugins/projectexplorer/filesinallprojectsfind.h b/src/plugins/projectexplorer/filesinallprojectsfind.h index f96d2a6df9a..39d8e2c0ed6 100644 --- a/src/plugins/projectexplorer/filesinallprojectsfind.h +++ b/src/plugins/projectexplorer/filesinallprojectsfind.h @@ -16,8 +16,11 @@ public: QString id() const override; QString displayName() const 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; + + // deprecated + QByteArray settingsKey() const override; protected: TextEditor::FileContainerProvider fileContainerProvider() const override; diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp index 16e6f2c4e72..7bf7a1f6df4 100644 --- a/src/plugins/projectexplorer/makestep.cpp +++ b/src/plugins/projectexplorer/makestep.cpp @@ -313,7 +313,7 @@ QWidget *MakeStep::createConfigWidget() if (m_disablingForSubDirsSupported) builder.addRow({m_disabledForSubdirsAspect}); builder.addRow({m_buildTargetsAspect}); - builder.noMargin(); + builder.setNoMargins(); auto widget = builder.emerge(); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index c3d827eb28c..b9da6f33a87 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -796,6 +796,7 @@ static void restoreRecentProjects(QtcSettings *s) {FilePath::fromUserInput(filePaths.at(i)), displayNames.at(i), exists}); } } + dd->updateRecentProjectMenu(); dd->checkRecentProjectsAsync(); } @@ -2184,7 +2185,7 @@ void ProjectExplorerPluginPrivate::checkRecentProjectsAsync() m_recentProjectsFuture = QtConcurrent::mapped(&m_recentProjectsPool, m_recentProjects, [](RecentProjectsEntry p) { // check if project is available, but avoid querying devices - p.exists = p.filePath.needsDevice() || p.filePath.isFile(); + p.exists = p.filePath.needsDevice() || p.filePath.exists(); return p; }); Utils::futureSynchronizer()->addFuture(m_recentProjectsFuture); diff --git a/src/plugins/projectexplorer/projectexplorer.qbs b/src/plugins/projectexplorer/projectexplorer.qbs index bce24dc1361..19b9221de2b 100644 --- a/src/plugins/projectexplorer/projectexplorer.qbs +++ b/src/plugins/projectexplorer/projectexplorer.qbs @@ -115,6 +115,7 @@ QtcPlugin { "projectmanager.cpp", "projectmanager.h", "projectmodels.cpp", "projectmodels.h", "projectnodes.cpp", "projectnodes.h", + "projectnodeshelper.h", "projectpanelfactory.cpp", "projectpanelfactory.h", "projectsettingswidget.cpp", "projectsettingswidget.h", "projecttree.cpp", diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp index 7597f88f06e..34ffab0b18a 100644 --- a/src/plugins/projectexplorer/projectwelcomepage.cpp +++ b/src/plugins/projectexplorer/projectwelcomepage.cpp @@ -799,11 +799,11 @@ public: sessionsLabel, st, manageSessionsButton, - customMargin({HPaddingS, 0, sessionScrollBarGap, 0}), + customMargins(HPaddingS, 0, sessionScrollBarGap, 0), }, sessionsList, spacing(ExPaddingGapL), - customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0), }.attachTo(sessions); connect(manageSessionsButton, &Button::clicked, this, &SessionManager::showSessionManager); @@ -823,11 +823,11 @@ public: Column { Row { projectsLabel, - customMargin({HPaddingS, 0, 0, 0}), + customMargins(HPaddingS, 0, 0, 0), }, projectsList, spacing(ExPaddingGapL), - customMargin({ExVPaddingGapXl - sessionScrollBarGap, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl - sessionScrollBarGap, ExVPaddingGapXl, 0, 0), }.attachTo(projects); } diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 3aa2457ef95..97d467fbc75 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -201,7 +201,7 @@ bool RunConfiguration::isEnabled(Utils::Id) const QWidget *RunConfiguration::createConfigurationWidget() { Layouting::Form form; - form.noMargin(); + form.setNoMargins(); for (BaseAspect *aspect : std::as_const(*this)) { if (aspect->isVisible()) { form.addItem(aspect); @@ -425,6 +425,13 @@ ProcessRunData RunConfiguration::runnable() const r.environment = environmentAspect->environment(); if (m_runnableModifier) m_runnableModifier(r); + + // TODO: Do expansion in commandLine()? + if (!r.command.isEmpty()) { + const FilePath expanded = macroExpander()->expand(r.command.executable()); + r.command.setExecutable(expanded); + } + return r; } diff --git a/src/plugins/projectexplorer/runconfigurationaspects.cpp b/src/plugins/projectexplorer/runconfigurationaspects.cpp index 80e82b9f826..a383438c8b5 100644 --- a/src/plugins/projectexplorer/runconfigurationaspects.cpp +++ b/src/plugins/projectexplorer/runconfigurationaspects.cpp @@ -203,6 +203,9 @@ void WorkingDirectoryAspect::addToLayout(Layout &builder) m_chooser->setEnvironment(m_envAspect->environment()); } + m_chooser->setReadOnly(isReadOnly()); + m_resetButton->setEnabled(!isReadOnly()); + builder.addItems({Tr::tr("Working directory:"), m_chooser.data(), m_resetButton.data()}); } @@ -437,6 +440,7 @@ QWidget *ArgumentsAspect::setupChooser() this, [this] { setArguments(m_multiLineChooser->toPlainText()); }); } m_multiLineChooser->setPlainText(m_arguments); + m_multiLineChooser->setReadOnly(isReadOnly()); return m_multiLineChooser.data(); } if (!m_chooser) { @@ -445,6 +449,8 @@ QWidget *ArgumentsAspect::setupChooser() connect(m_chooser.data(), &QLineEdit::textChanged, this, &ArgumentsAspect::setArguments); } m_chooser->setText(m_arguments); + m_chooser->setReadOnly(isReadOnly()); + return m_chooser.data(); } @@ -597,6 +603,7 @@ void ExecutableAspect::setEnvironment(const Environment &env) void ExecutableAspect::setReadOnly(bool readOnly) { + BaseAspect::setReadOnly(readOnly); m_executable.setReadOnly(readOnly); } diff --git a/src/plugins/projectexplorer/treescanner.cpp b/src/plugins/projectexplorer/treescanner.cpp index dac83598fb7..49680621ec2 100644 --- a/src/plugins/projectexplorer/treescanner.cpp +++ b/src/plugins/projectexplorer/treescanner.cpp @@ -155,7 +155,7 @@ void TreeScanner::scanForFiles( { QList nodes = ProjectExplorer::scanForFiles( promise, directory, dirFilter, [&filter, &factory](const Utils::FilePath &fn) -> FileNode * { - const Utils::MimeType mimeType = Utils::mimeTypeForFile(fn); + const Utils::MimeType mimeType = Utils::mimeTypesForFileName(fn.path()).value(0); // Skip some files during scan. if (filter && filter(mimeType, fn)) diff --git a/src/plugins/projectexplorer/workspaceproject.cpp b/src/plugins/projectexplorer/workspaceproject.cpp index d380f7cea88..2a22c65c4be 100644 --- a/src/plugins/projectexplorer/workspaceproject.cpp +++ b/src/plugins/projectexplorer/workspaceproject.cpp @@ -9,6 +9,9 @@ #include "projectexplorertr.h" #include "projectmanager.h" #include "projecttree.h" +#include "runconfiguration.h" +#include "runconfigurationaspects.h" +#include "runcontrol.h" #include "target.h" #include "treescanner.h" @@ -24,6 +27,7 @@ const QLatin1StringView FOLDER_MIMETYPE{"inode/directory"}; const QLatin1StringView WORKSPACE_MIMETYPE{"text/x-workspace-project"}; const QLatin1StringView WORKSPACE_PROJECT_ID{"ProjectExplorer.WorkspaceProject"}; +const QLatin1StringView WORKSPACE_PROJECT_RUNCONFIG_ID{"WorkspaceProject.RunConfiguration:"}; const QLatin1StringView PROJECT_NAME_KEY{"project.name"}; const QLatin1StringView FILES_EXCLUDE_KEY{"files.exclude"}; @@ -100,7 +104,7 @@ public: if (projectNameValue.isString()) project()->setDisplayName(projectNameValue.toString()); const QJsonArray excludesJson = json.value(FILES_EXCLUDE_KEY).toArray(); - for (QJsonValue excludeJson : excludesJson) { + for (const QJsonValue &excludeJson : excludesJson) { if (excludeJson.isString()) { FilePath absolute = projectPath.pathAppended(excludeJson.toString()); if (absolute.isDir()) @@ -111,6 +115,46 @@ public: } } + QList targetInfos; + + const QJsonArray targets = json.value("targets").toArray(); + int i = 0; + for (const QJsonValue &target : targets) { + i++; + QTC_ASSERT(target.isObject(), continue); + const QJsonObject targetObject = target.toObject(); + + QJsonArray args = targetObject["arguments"].toArray(); + QStringList arguments = Utils::transform(args, [](const QJsonValue &arg) { + return arg.toString(); + }); + FilePath workingDirectory = FilePath::fromUserInput( + targetObject["workingDirectory"].toString()); + + if (!workingDirectory.isDir()) + workingDirectory = FilePath::currentWorkingPath(); + + const QString name = targetObject["name"].toString(); + const FilePath executable = FilePath::fromUserInput( + targetObject["executable"].toString()); + + if (name.isEmpty() || executable.isEmpty()) + continue; + + BuildTargetInfo bti; + bti.buildKey = name + QString::number(i); + bti.displayName = name; + bti.displayNameUniquifier = QString(" (%1)").arg(i); + bti.targetFilePath = executable; + bti.projectFilePath = projectPath; + bti.workingDirectory = workingDirectory; + bti.additionalData = QVariantMap{{"arguments", arguments}}; + + targetInfos << bti; + } + + setApplicationTargets(targetInfos); + m_parseGuard = guardParsingRun(); m_scanner.asyncScanForFiles(target()->project()->projectDirectory()); } @@ -123,6 +167,86 @@ private: TreeScanner m_scanner; }; +class WorkspaceRunConfiguration : public RunConfiguration +{ +public: + WorkspaceRunConfiguration(Target *target, Id id) + : RunConfiguration(target, id) + { + hint.setText( + Tr::tr("You can edit this configuration inside the .qtcreator/project.json file.")); + + const BuildTargetInfo bti = buildTargetInfo(); + executable.setLabelText(Tr::tr("Executable:")); + executable.setReadOnly(true); + executable.setValue(bti.targetFilePath); + executable.setMacroExpanderProvider( + [this]() -> MacroExpander * { return const_cast(macroExpander()); }); + + auto argumentsAsString = [this]() { + return CommandLine{ + "", buildTargetInfo().additionalData.toMap()["arguments"].toStringList()} + .arguments(); + }; + + arguments.setLabelText(Tr::tr("Arguments:")); + arguments.setReadOnly(true); + arguments.setMacroExpander(macroExpander()); + arguments.setArguments(argumentsAsString()); + + workingDirectory.setLabelText(Tr::tr("Working directory:")); + workingDirectory.setReadOnly(true); + workingDirectory.setDefaultWorkingDirectory(bti.workingDirectory); + + setCommandLineGetter([this] { + const BuildTargetInfo bti = buildTargetInfo(); + CommandLine cmdLine{ + macroExpander()->expand(bti.targetFilePath), + Utils::transform( + bti.additionalData.toMap()["arguments"].toStringList(), + [this](const QString &arg) { return macroExpander()->expand(arg); })}; + + return cmdLine; + }); + + setUpdater([this, argumentsAsString] { + const BuildTargetInfo bti = buildTargetInfo(); + executable.setValue(bti.targetFilePath); + arguments.setArguments(argumentsAsString()); + workingDirectory.setDefaultWorkingDirectory(bti.workingDirectory); + }); + + connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); + } + + TextDisplay hint{this}; + FilePathAspect executable{this}; + ArgumentsAspect arguments{this}; + WorkingDirectoryAspect workingDirectory{this}; +}; + +class WorkspaceProjectRunConfigurationFactory : public RunConfigurationFactory +{ +public: + WorkspaceProjectRunConfigurationFactory() + { + registerRunConfiguration( + Id::fromString(WORKSPACE_PROJECT_RUNCONFIG_ID)); + addSupportedProjectType(Id::fromString(WORKSPACE_PROJECT_ID)); + } +}; + +class WorkspaceProjectRunWorkerFactory : public RunWorkerFactory +{ +public: + WorkspaceProjectRunWorkerFactory() + { + setProduct(); + addSupportedRunMode(Constants::NORMAL_RUN_MODE); + addSupportedRunConfig(Id::fromString(WORKSPACE_PROJECT_RUNCONFIG_ID)); + } +}; + class WorkspaceProject : public Project { Q_OBJECT @@ -203,6 +327,9 @@ void setupWorkspaceProject(QObject *guard) QTC_ASSERT(project, return); project->excludeNode(node); }); + + static WorkspaceProjectRunConfigurationFactory theRunConfigurationFactory; + static WorkspaceProjectRunWorkerFactory theRunWorkerFactory; } } // namespace ProjectExplorer diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index d1de44d1f57..65b79a7238a 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -428,7 +428,7 @@ QWidget *QMakeStep::createConfigWidget() builder.addRow({userArguments}); builder.addRow({effectiveCall}); builder.addRow({abisLabel, abisListWidget}); - builder.noMargin(); + builder.setNoMargins(); auto widget = builder.emerge(); qmakeBuildConfigChanged(); diff --git a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp index 5b36bee7f97..09cf5945e82 100644 --- a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp +++ b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp @@ -79,15 +79,12 @@ void IconCheckboxItemDelegate::paint(QPainter *painter, if (rowIsPropertyRole(modelIndex.model(), modelIndex) || getModelNode(modelIndex).isRootNode()) return; // Do not paint icons for property rows or root node - QWindow *window = dynamic_cast(painter->device())->window()->windowHandle(); - QTC_ASSERT(window, return); - const QSize iconSize(16, 16); QPoint iconPosition(styleOption.rect.left() + (styleOption.rect.width() - iconSize.width()) / 2, styleOption.rect.top() + 2 + delegateMargin); const QIcon::State state = isChecked(modelIndex) ? QIcon::State::On : QIcon::State::Off; - const QPixmap iconPixmap = m_icon.pixmap(window, iconSize, mode, state); + const QPixmap iconPixmap = m_icon.pixmap(iconSize, painter->device()->devicePixelRatio(), mode, state); // Shift the lock icon (last column) slightly to the left due to vertical scrollbar width if (modelIndex.column() == NavigatorTreeModel::ColumnType::Lock) diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp index f02a433fbe7..1240c9bbd33 100644 --- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp +++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp @@ -279,7 +279,7 @@ public: using namespace Layouting; Row titleRow { - customMargin({0, 0, ExVPaddingGapXl, 0}), + customMargins(0, 0, ExVPaddingGapXl, 0), spacing(ExVPaddingGapXl), }; @@ -317,7 +317,7 @@ public: titleRow, gridView, spacing(ExVPaddingGapXl), - customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}), + customMargins(ExVPaddingGapXl, ExVPaddingGapXl, 0, 0), }.attachTo(this); connect(&m_exampleDelegate, &ExampleDelegate::tagClicked, diff --git a/src/plugins/remotelinux/linuxdevice.cpp b/src/plugins/remotelinux/linuxdevice.cpp index d2779e2ebb8..c971c8caa2e 100644 --- a/src/plugins/remotelinux/linuxdevice.cpp +++ b/src/plugins/remotelinux/linuxdevice.cpp @@ -366,7 +366,8 @@ Environment LinuxDevicePrivate::getEnvironment() Process getEnvProc; getEnvProc.setCommand(CommandLine{q->filePath("env")}); - getEnvProc.runBlocking(); + using namespace std::chrono; + getEnvProc.runBlocking(5s); const QString remoteOutput = getEnvProc.cleanedStdOut(); m_environmentCache = Environment(remoteOutput.split('\n', Qt::SkipEmptyParts), q->osType()); @@ -1231,25 +1232,20 @@ RunResult LinuxDevicePrivate::runInShell(const CommandLine &cmd, const QByteArra void LinuxDevicePrivate::announceConnectionAttempt() { - const auto announce = [id = announceId(), name = q->displayName()] { - Core::ICore::infoBar()->addInfo( - InfoBarEntry(id, - Tr::tr("Establishing initial connection to device \"%1\". " - "This might take a moment.") - .arg(name))); + const QString message = Tr::tr("Establishing initial connection to device \"%1\". " + "This might take a moment.").arg(q->displayName()); + qCDebug(linuxDeviceLog) << message; + if (isMainThread()) { + Core::ICore::infoBar()->addInfo(InfoBarEntry(announceId(), message)); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); // Yes, twice. - }; - if (QThread::currentThread() == qApp->thread()) - announce(); - else - QMetaObject::invokeMethod(Core::ICore::infoBar(), announce, Qt::BlockingQueuedConnection); + } } void LinuxDevicePrivate::unannounceConnectionAttempt() { - QMetaObject::invokeMethod(Core::ICore::infoBar(), - [id = announceId()] { Core::ICore::infoBar()->removeInfo(id); }); + if (isMainThread()) + Core::ICore::infoBar()->removeInfo(announceId()); } bool LinuxDevicePrivate::checkDisconnectedWithWarning() diff --git a/src/plugins/silversearcher/findinfilessilversearcher.cpp b/src/plugins/silversearcher/findinfilessilversearcher.cpp index cb290e046d8..57a2683428c 100644 --- a/src/plugins/silversearcher/findinfilessilversearcher.cpp +++ b/src/plugins/silversearcher/findinfilessilversearcher.cpp @@ -128,14 +128,15 @@ public: QString toolTip() const final { return {}; } QWidget *widget() const final { return m_widget; } - void readSettings(QtcSettings *settings) final + void readSettings(const Store &s) final { - m_searchOptionsLineEdit->setText(settings->value(s_searchOptionsString).toString()); + m_searchOptionsLineEdit->setText(s.value(s_searchOptionsString).toString()); } - void writeSettings(QtcSettings *settings) const final + void writeSettings(Store &s) const final { - settings->setValue(s_searchOptionsString, m_searchOptionsLineEdit->text()); + if (!m_searchOptionsLineEdit->text().isEmpty()) + s.insert(s_searchOptionsString, m_searchOptionsLineEdit->text()); } SearchExecutor searchExecutor() const final diff --git a/src/plugins/subversion/subversionplugin.cpp b/src/plugins/subversion/subversionplugin.cpp index b28224a8667..36a5455a629 100644 --- a/src/plugins/subversion/subversionplugin.cpp +++ b/src/plugins/subversion/subversionplugin.cpp @@ -1067,7 +1067,7 @@ bool SubversionPluginPrivate::isVcsFileOrDirectory(const FilePath &filePath) con bool SubversionPluginPrivate::isConfigured() const { - const FilePath binary = settings().binaryPath(); + const FilePath binary = settings().binaryPath.effectiveBinary(); if (binary.isEmpty()) return false; QFileInfo fi = binary.toFileInfo(); diff --git a/src/plugins/texteditor/basefilefind.cpp b/src/plugins/texteditor/basefilefind.cpp index 0faa871b5ce..d18a75531b3 100644 --- a/src/plugins/texteditor/basefilefind.cpp +++ b/src/plugins/texteditor/basefilefind.cpp @@ -139,8 +139,8 @@ public: QString title() const override { return Tr::tr("Internal"); } QString toolTip() const override { return {}; } QWidget *widget() const override { return m_widget; } - void readSettings(QtcSettings * /*settings*/) override {} - void writeSettings(QtcSettings * /*settings*/) const override {} + void readSettings(const Store &) override {} + void writeSettings(Store &) const override {} SearchExecutor searchExecutor() const override { return [](const FileFindParameters ¶meters) { @@ -442,47 +442,57 @@ FilePath BaseFileFind::searchDir() const return d->m_searchDir; } -void BaseFileFind::writeCommonSettings(QtcSettings *settings) +void BaseFileFind::writeCommonSettings( + Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) const { const auto fromNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::fromNativeSeparators); }; - settings->setValue("filters", fromNativeSeparators(d->m_filterStrings.stringList())); - if (d->m_filterCombo) - settings->setValue("currentFilter", - QDir::fromNativeSeparators(d->m_filterCombo->currentText())); - settings->setValue("exclusionFilters", fromNativeSeparators(d->m_exclusionStrings.stringList())); - if (d->m_exclusionCombo) - settings->setValue("currentExclusionFilter", - QDir::fromNativeSeparators(d->m_exclusionCombo->currentText())); + const QStringList filterStrings = fromNativeSeparators(d->m_filterStrings.stringList()); + if (filterStrings.size() != 1 || filterStrings.first() != defaultFilter) + s.insert("filters", filterStrings); + const QString currentFilter = d->m_filterCombo + ? QDir::fromNativeSeparators(d->m_filterCombo->currentText()) + : d->m_filterSetting; + if (currentFilter != defaultFilter) + s.insert("currentFilter", currentFilter); + const QStringList exclusionFilters = fromNativeSeparators(d->m_exclusionStrings.stringList()); + if (exclusionFilters.size() != 1 || exclusionFilters.first() != defaultExclusionFilter) + s.insert("exclusionFilters", exclusionFilters); + const QString currentExclusionFilter = d->m_exclusionCombo ? QDir::fromNativeSeparators( + d->m_exclusionCombo->currentText()) + : d->m_exclusionSetting; + if (currentExclusionFilter != defaultExclusionFilter) + s.insert("currentExclusionFilter", currentExclusionFilter); for (const SearchEngine *searchEngine : std::as_const(d->m_searchEngines)) - searchEngine->writeSettings(settings); - settings->setValue("currentSearchEngineIndex", d->m_currentSearchEngineIndex); + searchEngine->writeSettings(s); + if (d->m_currentSearchEngineIndex != 0) + s.insert("currentSearchEngineIndex", d->m_currentSearchEngineIndex); } -void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defaultFilter, - const QString &defaultExclusionFilter) +void BaseFileFind::readCommonSettings( + const Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) { const auto toNativeSeparators = [](const QStringList &files) -> QStringList { return Utils::transform(files, &QDir::toNativeSeparators); }; - const QStringList filterSetting = settings->value("filters").toStringList(); + const QStringList filterSetting = s.value("filters").toStringList(); const QStringList filters = filterSetting.isEmpty() ? QStringList(defaultFilter) : filterSetting; - const QVariant currentFilter = settings->value("currentFilter"); + const QVariant currentFilter = s.value("currentFilter"); d->m_filterSetting = currentFilter.isValid() ? currentFilter.toString() : filters.first(); d->m_filterStrings.setStringList(toNativeSeparators(filters)); if (d->m_filterCombo) syncComboWithSettings(d->m_filterCombo, d->m_filterSetting); - QStringList exclusionFilters = settings->value("exclusionFilters").toStringList(); + QStringList exclusionFilters = s.value("exclusionFilters").toStringList(); if (!exclusionFilters.contains(defaultExclusionFilter)) exclusionFilters << defaultExclusionFilter; - const QVariant currentExclusionFilter = settings->value("currentExclusionFilter"); + const QVariant currentExclusionFilter = s.value("currentExclusionFilter"); d->m_exclusionSetting = currentExclusionFilter.isValid() ? currentExclusionFilter.toString() : exclusionFilters.first(); d->m_exclusionStrings.setStringList(toNativeSeparators(exclusionFilters)); @@ -490,8 +500,8 @@ void BaseFileFind::readCommonSettings(QtcSettings *settings, const QString &defa syncComboWithSettings(d->m_exclusionCombo, d->m_exclusionSetting); for (SearchEngine* searchEngine : std::as_const(d->m_searchEngines)) - searchEngine->readSettings(settings); - const int currentSearchEngineIndex = settings->value("currentSearchEngineIndex", 0).toInt(); + searchEngine->readSettings(s); + const int currentSearchEngineIndex = s.value("currentSearchEngineIndex", 0).toInt(); syncSearchEngineCombo(currentSearchEngineIndex); } diff --git a/src/plugins/texteditor/basefilefind.h b/src/plugins/texteditor/basefilefind.h index 47adae4b866..de4286c31b6 100644 --- a/src/plugins/texteditor/basefilefind.h +++ b/src/plugins/texteditor/basefilefind.h @@ -69,8 +69,8 @@ public: virtual QString title() const = 0; virtual QString toolTip() const = 0; // add %1 placeholder where the find flags should be put virtual QWidget *widget() const = 0; - virtual void readSettings(Utils::QtcSettings *settings) = 0; - virtual void writeSettings(Utils::QtcSettings *settings) const = 0; + virtual void readSettings(const Utils::Store &s) = 0; + virtual void writeSettings(Utils::Store &settings) const = 0; virtual SearchExecutor searchExecutor() const = 0; virtual EditorOpener editorOpener() const { return {}; } bool isEnabled() const; @@ -108,8 +108,10 @@ protected: virtual QString toolTip() const = 0; // see Core::SearchResultWindow::startNewSearch, // add %1 placeholder where the find flags should be put - void writeCommonSettings(Utils::QtcSettings *settings); - void readCommonSettings(Utils::QtcSettings *settings, const QString &defaultFilter, const QString &defaultExclusionFilter); + void writeCommonSettings( + Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter) const; + void readCommonSettings( + const Utils::Store &s, const QString &defaultFilter, const QString &defaultExclusionFilter); QList> createPatternWidgets(); QStringList fileNameFilters() const; QStringList fileExclusionFilters() const; diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index 7d3ad849972..45121f72d84 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -27,8 +27,8 @@ private: QString id() const final; QString displayName() const final; bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; QString label() const final; QString toolTip() const final; @@ -37,6 +37,9 @@ private: void handleFileChange(Core::IEditor *editor); QPointer m_currentDocument; + + // deprecated + QByteArray settingsKey() const final; }; FindInCurrentFile::FindInCurrentFile() @@ -97,18 +100,24 @@ void FindInCurrentFile::handleFileChange(Core::IEditor *editor) } } -void FindInCurrentFile::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + +Store FindInCurrentFile::save() const { - settings->beginGroup("FindInCurrentFile"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void FindInCurrentFile::readSettings(QtcSettings *settings) +void FindInCurrentFile::restore(const Store &s) { - settings->beginGroup("FindInCurrentFile"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); +} + +QByteArray FindInCurrentFile::settingsKey() const +{ + return "FindInCurrentFile"; } void setupFindInCurrentFile() diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 7348c600e21..9dc68cd6532 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -188,18 +188,24 @@ QWidget *FindInFiles::createConfigWidget() return m_configWidget; } -void FindInFiles::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*.cpp,*.h"; +const char kDefaultExclusion[] = "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"; + +Store FindInFiles::save() const { - settings->beginGroup("FindInFiles"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void FindInFiles::readSettings(QtcSettings *settings) +void FindInFiles::restore(const Utils::Store &s) { - settings->beginGroup("FindInFiles"); - readCommonSettings(settings, "*.cpp,*.h", "*/.git/*,*/.cvs/*,*/.svn/*,*.autosave,*/build/*"); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); +} + +QByteArray FindInFiles::settingsKey() const +{ + return "FindInFiles"; } void FindInFiles::setBaseDirectory(const FilePath &directory) diff --git a/src/plugins/texteditor/findinfiles.h b/src/plugins/texteditor/findinfiles.h index c7803a16ed4..b3bd7b7095b 100644 --- a/src/plugins/texteditor/findinfiles.h +++ b/src/plugins/texteditor/findinfiles.h @@ -30,8 +30,8 @@ public: QString id() const override; QString displayName() const 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; bool isValid() const override; void setDirectory(const Utils::FilePath &directory); @@ -39,6 +39,9 @@ public: static void findOnFileSystem(const QString &path); static FindInFiles *instance(); + // deprecated + QByteArray settingsKey() const override; + protected: QString label() const override; QString toolTip() const override; diff --git a/src/plugins/texteditor/findinopenfiles.cpp b/src/plugins/texteditor/findinopenfiles.cpp index dcd505cbe1f..deeb66e9f5f 100644 --- a/src/plugins/texteditor/findinopenfiles.cpp +++ b/src/plugins/texteditor/findinopenfiles.cpp @@ -25,14 +25,17 @@ private: QString id() const final; QString displayName() const final; bool isEnabled() const final; - void writeSettings(Utils::QtcSettings *settings) final; - void readSettings(Utils::QtcSettings *settings) final; + Utils::Store save() const final; + void restore(const Utils::Store &s) final; QString label() const final; QString toolTip() const final; FileContainerProvider fileContainerProvider() const final; void updateEnabledState() { emit enabledChanged(isEnabled()); } + + // deprecated + QByteArray settingsKey() const final; }; FindInOpenFiles::FindInOpenFiles() @@ -90,21 +93,25 @@ bool FindInOpenFiles::isEnabled() const return Core::DocumentModel::entryCount() > 0; } -void FindInOpenFiles::writeSettings(QtcSettings *settings) +const char kDefaultInclusion[] = "*"; +const char kDefaultExclusion[] = ""; + +Store FindInOpenFiles::save() const { - settings->beginGroup("FindInOpenFiles"); - writeCommonSettings(settings); - settings->endGroup(); + Store s; + writeCommonSettings(s, kDefaultInclusion, kDefaultExclusion); + return s; } -void FindInOpenFiles::readSettings(QtcSettings *settings) +void FindInOpenFiles::restore(const Store &s) { - settings->beginGroup("FindInOpenFiles"); - readCommonSettings(settings, "*", ""); - settings->endGroup(); + readCommonSettings(s, kDefaultInclusion, kDefaultExclusion); } - +QByteArray FindInOpenFiles::settingsKey() const +{ + return "FindInOpenFiles"; +} void setupFindInOpenFiles() { diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp index 68e6d549f81..432ce59cb96 100644 --- a/src/plugins/welcome/welcomeplugin.cpp +++ b/src/plugins/welcome/welcomeplugin.cpp @@ -162,7 +162,7 @@ public: welcomeLabel, st, spacing(ExVPaddingGapXl), - customMargin({HPaddingM, VPaddingM, HPaddingM, VPaddingM}), + customMargins(HPaddingM, VPaddingM, HPaddingM, VPaddingM), }, createRule(Qt::Horizontal), noMargin, spacing(0), @@ -188,7 +188,7 @@ public: Column mainColumn { spacing(0), - customMargin({ExVPaddingGapXl, 0, ExVPaddingGapXl, 0}), + customMargins(ExVPaddingGapXl, 0, ExVPaddingGapXl, 0), }; m_essentials = new QWidget; @@ -205,7 +205,7 @@ public: newButton, openButton, spacing(ExPaddingGapL), - customMargin({0, ExVPaddingGapXl, 0, ExVPaddingGapXl}), + customMargins(0, ExVPaddingGapXl, 0, ExVPaddingGapXl), }; essentials.addItem(projectButtons); @@ -238,7 +238,7 @@ public: Column linksLayout { label, spacing(VGapS), - customMargin({0, VGapL, 0, ExVPaddingGapXl}), + customMargins(0, VGapL, 0, ExVPaddingGapXl), }; const struct { diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg index 7d78434c75a..9c62d2a0ef3 100644 --- a/src/tools/icons/qtcreatoricons.svg +++ b/src/tools/icons/qtcreatoricons.svg @@ -3772,7 +3772,7 @@ height="100%" /> @@ -3787,15 +3787,29 @@ width="100%" height="100%" /> + id="path6795-3" + style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-linecap:round" + d="m 48,422.5 v 4 m 0,4 v 4 M 59,418 c 0,0 -1.6471,0.674 -3.6667,1.5 M 51.6667,421 C 49.6472,421.826 48,422.5 48,422.5 c 0,0 -1.6471,-0.674 -3.6667,-1.5 m -3.6666,-1.5 C 38.6472,418.674 37,418 37,418 m 7.3333,15.5 C 46.3528,434.326 48,435 48,435 m 11,-17.5 c 0,0 -1.6471,-0.674 -3.6667,-1.5 m -3.6666,-1.5 C 49.6472,413.674 48,413 48,413 c 0,0 -1.6471,0.674 -3.6667,1.5 M 40.6667,416 C 38.6472,416.826 37,417.5 37,417.5 v 4.333 m 0,4.334 v 4.333 c 0,0 1.6471,0.674 3.6667,1.5" /> + style="fill:#000000;stroke:#000000;stroke-width:2;stroke-linejoin:round" + d="M 59,421.75 V 430.5 l -8,3.25 V 425 Z" + id="path32048" + sodipodi:nodetypes="ccccc" /> + + + + diff --git a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp index a7ab2bf8737..6ba2fa7fcaa 100644 --- a/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp +++ b/src/tools/qml2puppet/instances/nodeinstanceclientproxy.cpp @@ -541,34 +541,34 @@ void NodeInstanceClientProxy::dispatchCommand(const QVariant &command) { NANOTRACE_SCOPE_ARGS("Update", "dispatchCommand", {"name", command.typeName()}); - static const int createInstancesCommandType = QMetaType::type("CreateInstancesCommand"); - static const int update3dViewStateCommand = QMetaType::type("Update3dViewStateCommand"); - static const int changeFileUrlCommandType = QMetaType::type("ChangeFileUrlCommand"); - static const int createSceneCommandType = QMetaType::type("CreateSceneCommand"); - static const int clearSceneCommandType = QMetaType::type("ClearSceneCommand"); - static const int removeInstancesCommandType = QMetaType::type("RemoveInstancesCommand"); - static const int removePropertiesCommandType = QMetaType::type("RemovePropertiesCommand"); - static const int changeBindingsCommandType = QMetaType::type("ChangeBindingsCommand"); - static const int changeValuesCommandType = QMetaType::type("ChangeValuesCommand"); - static const int changeAuxiliaryCommandType = QMetaType::type("ChangeAuxiliaryCommand"); - static const int reparentInstancesCommandType = QMetaType::type("ReparentInstancesCommand"); - static const int changeIdsCommandType = QMetaType::type("ChangeIdsCommand"); - static const int changeStateCommandType = QMetaType::type("ChangeStateCommand"); - static const int completeComponentCommandType = QMetaType::type("CompleteComponentCommand"); - static const int synchronizeCommandType = QMetaType::type("SynchronizeCommand"); - static const int changeNodeSourceCommandType = QMetaType::type("ChangeNodeSourceCommand"); - static const int removeSharedMemoryCommandType = QMetaType::type("RemoveSharedMemoryCommand"); - static const int tokenCommandType = QMetaType::type("TokenCommand"); - static const int endPuppetCommandType = QMetaType::type("EndPuppetCommand"); - static const int changeSelectionCommandType = QMetaType::type("ChangeSelectionCommand"); - static const int inputEventCommandType = QMetaType::type("InputEventCommand"); - static const int view3DActionCommandType = QMetaType::type("View3DActionCommand"); - static const int requestModelNodePreviewImageCommandType = QMetaType::type("RequestModelNodePreviewImageCommand"); - static const int changeLanguageCommand = QMetaType::type("ChangeLanguageCommand"); - static const int changePreviewImageSizeCommand = QMetaType::type( - "ChangePreviewImageSizeCommand"); - static const int startNanotraceCommandType = QMetaType::type("StartNanotraceCommand"); - static const int endNanotraceCommandType = QMetaType::type("EndNanotraceCommand"); + static const int createInstancesCommandType = QMetaType::fromName("CreateInstancesCommand").id(); + static const int update3dViewStateCommand = QMetaType::fromName("Update3dViewStateCommand").id(); + static const int changeFileUrlCommandType = QMetaType::fromName("ChangeFileUrlCommand").id(); + static const int createSceneCommandType = QMetaType::fromName("CreateSceneCommand").id(); + static const int clearSceneCommandType = QMetaType::fromName("ClearSceneCommand").id(); + static const int removeInstancesCommandType = QMetaType::fromName("RemoveInstancesCommand").id(); + static const int removePropertiesCommandType = QMetaType::fromName("RemovePropertiesCommand").id(); + static const int changeBindingsCommandType = QMetaType::fromName("ChangeBindingsCommand").id(); + static const int changeValuesCommandType = QMetaType::fromName("ChangeValuesCommand").id(); + static const int changeAuxiliaryCommandType = QMetaType::fromName("ChangeAuxiliaryCommand").id(); + static const int reparentInstancesCommandType = QMetaType::fromName("ReparentInstancesCommand").id(); + static const int changeIdsCommandType = QMetaType::fromName("ChangeIdsCommand").id(); + static const int changeStateCommandType = QMetaType::fromName("ChangeStateCommand").id(); + static const int completeComponentCommandType = QMetaType::fromName("CompleteComponentCommand").id(); + static const int synchronizeCommandType = QMetaType::fromName("SynchronizeCommand").id(); + static const int changeNodeSourceCommandType = QMetaType::fromName("ChangeNodeSourceCommand").id(); + static const int removeSharedMemoryCommandType = QMetaType::fromName("RemoveSharedMemoryCommand").id(); + static const int tokenCommandType = QMetaType::fromName("TokenCommand").id(); + static const int endPuppetCommandType = QMetaType::fromName("EndPuppetCommand").id(); + static const int changeSelectionCommandType = QMetaType::fromName("ChangeSelectionCommand").id(); + static const int inputEventCommandType = QMetaType::fromName("InputEventCommand").id(); + static const int view3DActionCommandType = QMetaType::fromName("View3DActionCommand").id(); + static const int requestModelNodePreviewImageCommandType = QMetaType::fromName("RequestModelNodePreviewImageCommand").id(); + static const int changeLanguageCommand = QMetaType::fromName("ChangeLanguageCommand").id(); + static const int changePreviewImageSizeCommand = QMetaType::fromName( + "ChangePreviewImageSizeCommand").id(); + static const int startNanotraceCommandType = QMetaType::fromName("StartNanotraceCommand").id(); + static const int endNanotraceCommandType = QMetaType::fromName("EndNanotraceCommand").id(); const int commandType = command.typeId(); diff --git a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp index e2529f42af7..2a673bb2e9e 100644 --- a/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/src/tools/qml2puppet/qml2puppet/instances/objectnodeinstance.cpp @@ -471,7 +471,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar QVariant oldValue = property.read(); - if (oldValue.typeId() == QVariant::Url) { + if (oldValue.typeId() == QMetaType::QUrl) { QUrl url = oldValue.toUrl(); QString path = url.toLocalFile(); if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty()) @@ -488,7 +488,7 @@ void ObjectNodeInstance::setPropertyVariant(const PropertyName &name, const QVar qDebug() << "ObjectNodeInstance.setPropertyVariant: Cannot be written: " << object() << name << adjustedValue; QVariant newValue = property.read(); - if (newValue.typeId() == QVariant::Url) { + if (newValue.typeId() == QMetaType::QUrl) { QUrl url = newValue.toUrl(); QString path = url.toLocalFile(); if (QFileInfo::exists(path) && nodeInstanceServer() && !path.isEmpty()) @@ -578,7 +578,7 @@ void ObjectNodeInstance::refreshProperty(const PropertyName &name) else property.write(resetValue(name)); - if (oldValue.typeId() == QVariant::Url) { + if (oldValue.typeId() == QMetaType::QUrl) { QByteArray key = oldValue.toUrl().toEncoded(QUrl::UrlFormattingOption(0x100)); QString pixmapKey = QString::fromUtf8(key.constData(), key.size()); QPixmapCache::remove(pixmapKey); @@ -623,7 +623,7 @@ QVariant ObjectNodeInstance::property(const PropertyName &name) const return QVariant::fromValue(Enumeration(me.scope(), me.valueToKey(value.toInt()))); } - if (property.propertyType() == QVariant::Url) { + if (property.propertyType() == QMetaType::QUrl) { QUrl url = property.read().toUrl(); if (url.isEmpty()) return QVariant(); diff --git a/tests/auto/environment/tst_environment.cpp b/tests/auto/environment/tst_environment.cpp index 3cf9c127a22..58ee3a72a51 100644 --- a/tests/auto/environment/tst_environment.cpp +++ b/tests/auto/environment/tst_environment.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include #include #include @@ -263,10 +264,18 @@ void tst_Environment::incrementalChanges() { const Environment origEnv({{"VAR1", "VALUE1"}, {"VAR2", "VALUE2"}, {"PATH", "/usr/bin"}}); const EnvironmentItems changes({ + {"VAR1", QString(), EnvironmentItem::Comment}, {"VAR1", QString(), EnvironmentItem::Unset}, {"VAR2", "VALUE2", EnvironmentItem::SetDisabled}, + {"PATH+=/bin", QString(), EnvironmentItem::Comment}, {"PATH", "/usr/local/bin", EnvironmentItem::Append}, - {"PATH", "/tmp", EnvironmentItem::Prepend}}); + {"PATH", "/tmp", EnvironmentItem::Prepend}, + {"PATH=/opt/bin", QString(), EnvironmentItem::Comment}}); + + const QStringList changesStringList = EnvironmentItem::toStringList(changes); + const int comments = Utils::count(changesStringList, + [](const QString &line) { return line.startsWith("##"); }); + QCOMPARE(comments, 3); // Check values after change application. Environment newEnv = origEnv; @@ -290,7 +299,7 @@ void tst_Environment::incrementalChanges() QCOMPARE(newEnv2, origEnv); // Check conversion round-trips. - QCOMPARE(EnvironmentItem::fromStringList(EnvironmentItem::toStringList(changes)), changes); + QCOMPARE(EnvironmentItem::fromStringList(changesStringList), changes); QCOMPARE(EnvironmentItem::fromStringList(EnvironmentItem::toStringList(diff)), diff); QCOMPARE(EnvironmentItem::fromStringList(EnvironmentItem::toStringList(reverseDiff)), reverseDiff); QCOMPARE(EnvironmentItem::itemsFromVariantList(EnvironmentItem::toVariantList(changes)), changes); @@ -368,9 +377,12 @@ void tst_Environment::pathChanges() else environment.appendOrSet(variable, value); - qDebug() << "Actual :" << environment.toStringList(); - qDebug() << "Expected:" << expected.toStringList(); - QCOMPARE(environment, expected); + const bool envEqualsExpected = environment == expected; + if (!envEqualsExpected) { + qDebug() << "Actual :" << environment.toStringList(); + qDebug() << "Expected:" << expected.toStringList(); + } + QVERIFY(envEqualsExpected); } void tst_Environment::find_data() diff --git a/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp b/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp index 247bba67728..ecbf4b274b8 100644 --- a/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp +++ b/tests/manual/widgets/uifonts/tst_manual_widgets_uifonts.cpp @@ -29,6 +29,8 @@ int main(int argc, char *argv[]) { StyleHelper::UiElementBody2, "Body-02" }, { StyleHelper::UiElementButtonMedium, "Button Medium" }, { StyleHelper::UiElementButtonSmall, "Button Small" }, + { StyleHelper::UiElementLabelMedium, "Label Medium" }, + { StyleHelper::UiElementLabelSmall, "Label Small" }, { StyleHelper::UiElementCaptionStrong, "Caption strong" }, { StyleHelper::UiElementCaption, "Caption" }, { StyleHelper::UiElementIconStandard, "Icon Standard" },