diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 794ce6b3d72..ab0cbacd8b1 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -8,7 +8,7 @@ on: env: QT_VERSION: 6.2.3 - CLANG_VERSION: 14.0.0 + CLANG_VERSION: 14.0.3 ELFUTILS_VERSION: 0.175 CMAKE_VERSION: 3.21.1 NINJA_VERSION: 1.10.2 diff --git a/CMakeLists.txt b/CMakeLists.txt index aba83469bed..0b9672c0a81 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,7 +107,7 @@ set(CRASHPAD_BACKEND_URL "" CACHE STRING "Crashpad backend URL") set(BUILD_WITH_CRASHPAD OFF) # Linux is not supported for now # x86_64;arm64 is not supported for now -if(CRASHPAD_BACKEND_URL AND (WIN32 OR (APPLE AND NOT "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "x86_64;arm64"))) +if(CRASHPAD_BACKEND_URL AND (WIN32 OR APPLE)) # Linux is not supported for now find_package(Crashpad QUIET) if(TARGET Crashpad::Crashpad) set(BUILD_WITH_CRASHPAD ON) diff --git a/cmake/FindCrashpad.cmake b/cmake/FindCrashpad.cmake index 8b16fd83be2..181c4b0518d 100644 --- a/cmake/FindCrashpad.cmake +++ b/cmake/FindCrashpad.cmake @@ -57,25 +57,9 @@ find_path(CRASHPAD_GEN_DIR "${CMAKE_PREFIX_PATH}" ) -if(APPLE) - find_path(CRASHPAD_OBJ_DIR - NAMES mig_output.child_portServer.o - PATH_SUFFIXES gen/util/mach - HINTS - "${CRASHPAD_OBJECT_DIR}" - "${CRASHPAD_LIB_DIR}/out/Default" - "${CMAKE_PREFIX_PATH}" - ) - set(CRASHPAD_APPLE_VARS CRASHPAD_OBJ_DIR CRASHPAD_GEN_DIR) - find_library(FWbsm bsm) - find_library(FWAppKit AppKit) - find_library(FWIOKit IOKit) - find_library(FWSecurity Security) -endif() - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Crashpad DEFAULT_MSG - CRASHPAD_BIN_DIR CRASHPAD_INCLUDE_DIR CRASHPAD_LIB_DIR ${CRASHPAD_APPLE_VARS} + CRASHPAD_BIN_DIR CRASHPAD_INCLUDE_DIR CRASHPAD_LIB_DIR ) if(Crashpad_FOUND) @@ -93,18 +77,16 @@ if(Crashpad_FOUND) set_target_properties(Crashpad::Crashpad PROPERTIES IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/client.lib") elseif(APPLE) + find_library(FWbsm bsm) + find_library(FWAppKit AppKit) + find_library(FWIOKit IOKit) + find_library(FWSecurity Security) target_link_libraries(Crashpad::Crashpad INTERFACE "${CRASHPAD_LIB_DIR}/third_party/mini_chromium/mini_chromium/base/libbase.a" "${CRASHPAD_LIB_DIR}/util/libutil.a" + "${CRASHPAD_LIB_DIR}/util/libmig_output.a" "${CRASHPAD_LIB_DIR}/client/libclient.a" - "${CRASHPAD_OBJ_DIR}/mig_output.child_portServer.o" - "${CRASHPAD_OBJ_DIR}/mig_output.child_portUser.o" - "${CRASHPAD_OBJ_DIR}/mig_output.excServer.o" - "${CRASHPAD_OBJ_DIR}/mig_output.excUser.o" - "${CRASHPAD_OBJ_DIR}/mig_output.mach_excServer.o" - "${CRASHPAD_OBJ_DIR}/mig_output.mach_excUser.o" - "${CRASHPAD_OBJ_DIR}/mig_output.notifyServer.o" - "${CRASHPAD_OBJ_DIR}/mig_output.notifyUser.o" + "${CRASHPAD_LIB_DIR}/client/libcommon.a" ${FWbsm} ${FWAppKit} ${FWIOKit} ${FWSecurity}) set_target_properties(Crashpad::Crashpad PROPERTIES IMPORTED_LOCATION "${CRASHPAD_LIB_DIR}/client/libclient.a") diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index 5b422ff09cf..4db08d6ac4a 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -335,6 +335,10 @@ function(find_dependent_plugins varName) set(_RESULT ${ARGN}) foreach(i ${ARGN}) + if(NOT TARGET ${i}) + continue() + endif() + set(_dep) get_property(_dep TARGET "${i}" PROPERTY _arg_DEPENDS) if (_dep) find_dependent_plugins(_REC ${_dep}) diff --git a/coin/instructions/common_environment.yaml b/coin/instructions/common_environment.yaml index 19f8d3a61d1..bca60728d6e 100644 --- a/coin/instructions/common_environment.yaml +++ b/coin/instructions/common_environment.yaml @@ -7,7 +7,7 @@ instructions: variableValue: "RelWithDebInfo" - type: EnvironmentVariable variableName: LLVM_BASE_URL - variableValue: http://master.qt.io/development_releases/prebuilt/libclang/libclang-release_14.0.0-based + variableValue: http://master.qt.io/development_releases/prebuilt/libclang/libclang-release_14.0.3-based - type: Group instructions: diff --git a/dist/changes-7.0.2.md b/dist/changes-7.0.2.md new file mode 100644 index 00000000000..4741a483152 --- /dev/null +++ b/dist/changes-7.0.2.md @@ -0,0 +1,83 @@ +Qt Creator 7.0.2 +================ + +Qt Creator version 7.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 origin/v7.0.1..v7.0.2 + +General +------- + +### Locator + +* Fixed saving of command history of `Execute` filter + +Editing +------- + +* Fixed that actions could be applied to wrong editor after switching split + (QTCREATORBUG-27479) + +### C++ + +* Fixed wrong `__cplusplus` value for older GCC versions +* ClangFormat + * Fixed disappearing settings drop down (QTCREATORBUG-26948) + +### Language Client + +* Fixed crash with function argument and quick fix hints (QTCREATORBUG-27404) +* Fixed selection in `Outline` view +* Fixed `Sort Alphabetically` for outline dropdown + +Projects +-------- + +* Fixed crash with `Recent Projects` (QTCREATORBUG-27399) +* Fixed that `-include` flags were ignored by code model (QTCREATORBUG-27450) + +### CMake + +* Fixed crash when cancelling progress indicator (QTCREATORBUG-27499) +* Fixed application of build directory after `Browse` (QTCREATORBUG-27407) + +Debugging +--------- + +* Fixed pretty printer for `QFile` in Qt 6.3 + +Platforms +--------- + +### macOS + +* Fixed compilier identification of `cc` and `c++` (QTCREATORBUG-27523) + +Credits for these changes go to: +-------------------------------- +Alessandro Portale +Artem Sokolovskii +Brook Cronin +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Eike Ziller +Henning Gruendl +Jaroslaw Kobus +Kai Uwe Broulik +Knud Dollereder +Leena Miettinen +Mahmoud Badri +Mats Honkamaa +Miikka Heikkinen +Orgad Shaneh +Robert Löhning +Thomas Hartmann +Tim Jenssen +Vikas Pachdha diff --git a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc index a7518096ba1..e49b17fad1f 100644 --- a/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/qtcreator/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -41,16 +41,14 @@ \title Using QML Modules with Plugins - QML modules may use plugins to expose components defined in C++ to QML - applications. \QC cannot load the plugins to determine the details of - the contained components, and therefore, the modules must provide extra type - information for code completion and the semantic checks to work correctly. + \l{Defining a QML Module}{QML modules} may use \l{Creating C++ Plugins for QML} + {C++ plugins} to expose components defined in C++ to QML applications. - To create a QML module + To create a QML \if defined(qtdesignstudio) - and make it appear in the \l Components view: + module and make it appear in the \l Components view: \else - : + module: \endif \list 1 @@ -97,26 +95,13 @@ \c .metainfo file is in place. \endif - \if defined(qtcreator) - \section1 Registering QML Types - - When you write a QML module or use QML from a C++ application, and the C++ - is a part of your qmake project, you typically register new types with the - \c qmlRegisterType() function or expose some class instances with - \l{QQmlContext::setContextProperty()}. The \QC C++ code model now scans for - these calls and tells the QML code model about them. This means that properties - are displayed during code completion and the JavaScript code checker does not - complain about unknown types. However, this works only when the source code - is available, and therefore, you must explicitly generate type information - for QML modules with plugins before distributing them. - \endif - \section1 Generating Type Description Files - Ideally, QML modules have a \c{plugins.qmltypes} file in the same directory - as the \c qmldir file. The \c qmltypes file contains a description of the - components exported by the module's plugins and is loaded by \QC when the - module is imported. + When \l{Defining QML Types from C++}{registering QML types}, make sure that + the QML module has a \c{plugins.qmltypes} file. Ideally, it should be located + in the same directory as the \c qmldir file. The \c qmltypes file contains a + description of the components exported by the module's plugins and is loaded + by \QC when the module is imported. For more information, see \l{Type Description Files}. @@ -127,7 +112,6 @@ However, this automatic dumping is a fallback mechanism with many points of failure and you cannot rely upon it. - \if defined(qtcreator) \section1 Importing QML Modules By default, \QC will look in the QML import path of Qt for QML modules. @@ -145,7 +129,7 @@ The import path affects all the targets built by the CMake project. - \else + \if defined(qtdesignstudio) \section1 Running QML Modules in Design Mode A QML emulation layer (also called QML Puppet) is used in the @@ -169,7 +153,7 @@ by an application or edited in the \uicontrol Design mode. If you want to use a different module in the \uicontrol Design mode - than in your actual application for example to mockup C++ items, + than in your actual application for example to mockup C++ items, you can use \c{QML_DESIGNER_IMPORT_PATH} in the \c{.pro} file (for qmake projects), or declare and set the property \c qmlDesignerImportPaths in your product (for Qbs projects). diff --git a/doc/qtdesignstudio/images/studio-asset-import.png b/doc/qtdesignstudio/images/studio-asset-import.png new file mode 100644 index 00000000000..9b047caa1fc Binary files /dev/null and b/doc/qtdesignstudio/images/studio-asset-import.png differ diff --git a/doc/qtdesignstudio/images/studio-import-metadata.png b/doc/qtdesignstudio/images/studio-import-metadata.png deleted file mode 100644 index e7317402f82..00000000000 Binary files a/doc/qtdesignstudio/images/studio-import-metadata.png and /dev/null differ diff --git a/doc/qtdesignstudio/images/studio-imported-assets.png b/doc/qtdesignstudio/images/studio-imported-assets.png index 7915769ffab..92042383078 100644 Binary files a/doc/qtdesignstudio/images/studio-imported-assets.png and b/doc/qtdesignstudio/images/studio-imported-assets.png differ diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc index 3c71da8eb9e..dcfce292147 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-overview.qdoc @@ -32,8 +32,8 @@ \title Exporting Designs from Figma - You can use \QBF to export designs from Figma to \e {.metadata} - format that you can \l{Importing 2D Assets}{import} to projects in \QDS. + You can use \QBF to export designs from Figma to a \e {.qtbridge} + archive that you can \l{Importing 2D Assets}{import} to projects in \QDS. \image studio-figma-export.png diff --git a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc index 1d38ca9b5ee..03b354797b6 100644 --- a/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc +++ b/doc/qtdesignstudio/src/qtbridge/qtbridge-figma-using.qdoc @@ -128,9 +128,8 @@ \li When the exporting is done, select \uicontrol OK. \endlist - \QBF exports everything into a single archive. Before importing the design - into \QDS, you have to manually extract the archive. Then you can import the - \e .metainfo into a project in \QDS, as described in \l{Importing 2D Assets}. + \QBF exports everything into a .qtbridge archive. You can import the archive + into a project in \QDS, as described in \l{Importing 2D Assets}. \section1 Export Settings diff --git a/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc index 0fd20c0467a..c1d14667813 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-importing-2d.qdoc @@ -55,48 +55,47 @@ \image studio-imported-assets.png "UI imported into Qt Design Studio" \QB enables you to export assets and then import them to a \QDS project - as image and QML files for editing in \l {Form Editor}. If you make changes + as images and QML files for editing in \l {Form Editor}. If you make changes to your design in the design tool that you originally used to create it, you can merge the changes into existing QML files without overwriting the changes you have made in \QDS. For more information, see \l {Exporting from Design Tools}. - \note Attempting to import assets exported on another system might fail. + \QB exports the designs either as an archive(.qtbridge) or as images with + a .metadata file. \QDS support both formats. The following instructions use an empty project as an example. For more information about the options you have, see \l {Creating Projects}. - To import assets exported in \QB to \QDS projects: + To import the exported assets to \QDS projects: \list 1 \li Select \uicontrol File > \uicontrol {New Project} > - \uicontrol General > \uicontrol {Qt Quick Application - Empty} > - \uicontrol Choose, and follow the instructions of the wizard to - create an empty project. + \uicontrol General > \uicontrol {Empty}. Add \uicontrol {Details} about + the project and select \uicontrol Create. \li In \uicontrol Projects, double-click \e Screen01.ui.qml to move to the Design mode. \li Select \uicontrol Assets > \inlineimage icons/plus.png . \li Select the folder where you exported the assets. - \li Select \uicontrol {Exported Assets (*.metadata)} in the dropdown - menu to filter \e .metadata files. - \li Select a \e .metadata file to import, and then select - \uicontrol Open. + \li Select \uicontrol {Compressed Metadata (*.qtbridge)} or + \uicontrol {Exported Metadata (*.metadata)} in the dropdown menu to + filter the exported files. + \li Select a the file to import and then select \uicontrol Open. \li Select \uicontrol Details next to the - \uicontrol {Metadata Import Paths} field to display the path where - the metadata is imported from. - \image studio-import-metadata.png "Asset Import dialog" + \uicontrol {Import Paths} field to display the path where the exported + assets are imported from. + \image studio-asset-import.png "Asset Import dialog" \li Select \uicontrol Details next to the - \uicontrol {QML/Asset Export Paths} field to display the paths to + \uicontrol {Export Paths} field to display the paths to copy the assets to. \li In the \uicontrol QML field, you can change the folder to copy the QML files to. \li In the \uicontrol Assets field, you can change the folder to copy the image files to. - \li Select the \uicontrol {Create sub directory for each metadata} - check box to copy the directory structure from the metadata file - to \QDS. + \li Select the \uicontrol {Create sub directory} check box to import the + assets in a sub directory inside \uicontrol {Export Paths}. \li Deselect the \uicontrol {Import assets} check box if you only want to create QML files. \li Deselect the \uicontrol {Generate QML} check box if you only @@ -104,26 +103,21 @@ \li Select the \uicontrol {Merge QML} check box if you have imported the assets before and want to merge the changes into existing QML files instead of overwriting the existing files. See \l {Merging QML Files}. + \li Select the \uicontrol {Round off coordinates} check box to round off + the position and dimension values to integers in the generated QML files. + \li Select the \uicontrol {Save Logs} check box to write the export logs + to a text file inside the directory selected in \uicontrol QML export path. \li Select \uicontrol Import to import the QML files and assets. This might take a little while for complex projects. \endlist - The imported assets are displayed in \uicontrol Assets - as PNG images. The components that you specified in the design tool are - displayed in \uicontrol Components > \uicontrol {My Components} as well as - in the \uicontrol Projects view as separate QML files. To start using them, + The imported assets are displayed in \uicontrol Assets as images. + The components that you specified in the design tool are displayed in + \uicontrol Components > \uicontrol {My Components} as well as in the + \uicontrol Projects view as separate QML files. To use them, drag-and-drop them from \uicontrol Components to \uicontrol {Form Editor} or \l Navigator. - \note The layer that was the bottom layer in the design tool becames the top - layer in \uicontrol Navigator to reflect the QML code model. You - can view the QML code in \l{Text Editor}. - - After importing the metadata files, wait a few moments to allow all - imported assets to appear in your project files before selecting your - metadata filename from \uicontrol Assets > \inlineimage icons/plus.png - . - If asset importer conflicts, warnings, and errors are displayed in the \uicontrol {Asset Import} dialog while importing, fix the issues in design tool and export the assets again. diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py index 5d6b5e04141..1a75590e4e4 100644 --- a/share/qtcreator/debugger/creatortypes.py +++ b/share/qtcreator/debugger/creatortypes.py @@ -263,32 +263,44 @@ def qdump__Utils__Environment(d, value): qdump__Utils__NameValueDictionary(d, value) +def qdump__Utils__DictKey(d, value): + d.putStringValue(value["name"]) + + def qdump__Utils__NameValueDictionary(d, value): - dptr = d.extractPointer(value["m_values"]) - (ref, n) = d.split('ii', dptr) - d.check(0 <= n and n <= 100 * 1000 * 1000) - d.check(-1 <= ref and ref < 100000) + dptr = d.extractPointer(value) + if d.qtVersion() >= 0x60000: + if dptr == 0: + d.putItemCount(0) + return + m = value['d']['d']['m'] + d.putItem(m) + d.putBetterType('Utils::NameValueDictionary') + else: # Qt5 + (ref, n) = d.split('ii', dptr) + d.check(0 <= n and n <= 100 * 1000 * 1000) + d.check(-1 <= ref and ref < 100000) - d.putItemCount(n) - if d.isExpanded(): - if n > 10000: - n = 10000 + d.putItemCount(n) + if d.isExpanded(): + if n > 10000: + n = 10000 - typeCode = 'ppp@{%s}@{%s}' % ("Utils::DictKey", "QString") + typeCode = 'ppp@{%s}@{%s}' % ("Utils::DictKey", "@QPair<@QString,bool>") - def helper(node): - (p, left, right, padding1, key, padding2, value) = d.split(typeCode, node) - if left: - for res in helper(left): - yield res - yield (key["name"], value) - if right: - for res in helper(right): - yield res + def helper(node): + (p, left, right, padding1, key, padding2, value) = d.split(typeCode, node) + if left: + for res in helper(left): + yield res + yield (key["name"], value) + if right: + for res in helper(right): + yield res - with Children(d, n): - for (pair, i) in zip(helper(dptr + 8), range(n)): - d.putPairItem(i, pair, 'key', 'value') + with Children(d, n): + for (pair, i) in zip(helper(dptr + 8), range(n)): + d.putPairItem(i, pair, 'key', 'value') def qdump__Utf8String(d, value): diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/HelperGrid.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/HelperGrid.qml index dbd7f38523c..e450a5a796c 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/HelperGrid.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/HelperGrid.qml @@ -39,6 +39,8 @@ Node { // Note: Only one instance of HelperGrid is supported, as the geometry names are fixed Model { // Main grid lines + castsShadows: false + receivesShadows: false geometry: GridGeometry { id: gridGeometry name: "3D Edit View Helper Grid" @@ -55,6 +57,8 @@ Node { } Model { // Subdivision lines + castsShadows: false + receivesShadows: false geometry: GridGeometry { lines: gridGeometry.lines step: gridGeometry.step @@ -73,6 +77,8 @@ Node { } Model { // Z Axis + castsShadows: false + receivesShadows: false geometry: GridGeometry { lines: gridGeometry.lines step: gridGeometry.step @@ -89,6 +95,8 @@ Node { ] } Model { // X Axis + castsShadows: false + receivesShadows: false eulerRotation.z: 90 geometry: GridGeometry { lines: gridGeometry.lines diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/SelectionBox.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/SelectionBox.qml index 201d1a63775..5ab4a599c44 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/SelectionBox.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt5/SelectionBox.qml @@ -54,6 +54,9 @@ Node { visible: selectionBox.targetNode && !selectionBoxGeometry.isEmpty + castsShadows: false + receivesShadows: false + materials: [ DefaultMaterial { diffuseColor: "#fff600" diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/HelperGrid.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/HelperGrid.qml index 10c7afd65b8..66f383518eb 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/HelperGrid.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/HelperGrid.qml @@ -40,6 +40,8 @@ Node { Model { // Main grid lines readonly property bool _edit3dLocked: true // Make this non-pickable + castsShadows: false + receivesShadows: false geometry: GridGeometry { id: gridGeometry name: "3D Edit View Helper Grid" @@ -57,6 +59,8 @@ Node { Model { // Subdivision lines readonly property bool _edit3dLocked: true // Make this non-pickable + castsShadows: false + receivesShadows: false geometry: GridGeometry { lines: gridGeometry.lines step: gridGeometry.step @@ -76,6 +80,8 @@ Node { Model { // Z Axis readonly property bool _edit3dLocked: true // Make this non-pickable + castsShadows: false + receivesShadows: false geometry: GridGeometry { lines: gridGeometry.lines step: gridGeometry.step @@ -93,6 +99,8 @@ Node { } Model { // X Axis readonly property bool _edit3dLocked: true // Make this non-pickable + castsShadows: false + receivesShadows: false eulerRotation.z: 90 geometry: GridGeometry { lines: gridGeometry.lines diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/SelectionBox.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/SelectionBox.qml index dd3300fd941..76fc14e3213 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/SelectionBox.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/qt6/SelectionBox.qml @@ -55,6 +55,9 @@ Node { visible: selectionBox.targetNode && !selectionBoxGeometry.isEmpty + castsShadows: false + receivesShadows: false + materials: [ DefaultMaterial { diffuseColor: "#fff600" diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp index a6e7e259e28..8400c3d4474 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp @@ -1610,7 +1610,7 @@ void NodeInstanceServer::addAnimation(QQuickAbstractAnimation *animation) m_animations.push_back(animation); QQuickPropertyAnimation *panim = qobject_cast(animation); - if (panim) { + if (panim && panim->target()) { QObject *target = panim->target(); QString property = panim->property(); QVariant value = target->property(qPrintable(baseProperty(property))); diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp index acc7894a963..a57df6bd9f0 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp @@ -534,7 +534,7 @@ void Qt5InformationNodeInstanceServer::handleParticleSystemDeselected() for (auto a : anim) { a->stop(); QQuickPropertyAnimation *panim = qobject_cast(a); - if (panim) + if (panim && panim->target()) panim->target()->setProperty(qPrintable(baseProperty(panim->property())), animationDefaultValue(i)); i++; } diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp index ae333343726..553b69e4815 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp @@ -135,7 +135,7 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands() if (rootNodeInstance().isSubclassOf("QQuick3DNode") && rootNodeInstance().contentItem() && DesignerSupport::isDirty(rootNodeInstance().contentItem(), - DesignerSupport::ContentUpdateMask) + DesignerSupport::AllMask) && nodeInstanceClient()->bytesToWrite() < 10000) { Internal::QuickItemNodeInstance::updateDirtyNode(rootNodeInstance().contentItem()); nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()})); diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml index 66760b1a24d..a90dfa50f19 100644 --- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml +++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/Assets.qml @@ -59,9 +59,6 @@ Item { var complexSuffixes = rootView.supportedAssetSuffixes(true); for (const u of drag.urls) { var url = u.toString(); - if (url.startsWith("file:///")) // remove file scheme (happens on Windows) - url = url.substr(8) - var ext = '*.' + url.slice(url.lastIndexOf('.') + 1).toLowerCase() if (simpleSuffixes.includes(ext)) root.dropSimpleExtFiles.push(url) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml index acca2a0dd19..47fc7e4f025 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CharacterSection.qml @@ -143,7 +143,8 @@ Section { PropertyLabel { text: qsTr("Style name") tooltip: qsTr("Font's style.") - blockedByTemplate: !styleNameComboBox.enabled + enabled: styleNameComboBox.model.length + blockedByTemplate: !backendValue.isAvailable } SecondColumnLayout { @@ -156,7 +157,7 @@ Section { backendValue: getBackendValue("styleName") model: styleNamesForFamily(fontComboBox.familyName) valueType: ComboBox.String - enabled: backendValue.isAvailable + enabled: backendValue.isAvailable && styleNameComboBox.model.length } ExpandingSpacer {} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml index 76ed79fc4da..1b2df5756b5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ComboBox.qml @@ -92,7 +92,6 @@ StudioControls.ComboBox { onValueFromBackendChanged: colorLogic.invalidate() function invalidate() { - if (comboBox.block) return @@ -140,6 +139,21 @@ StudioControls.ComboBox { } } + onAccepted: { + if (!comboBox.__isCompleted) + return + + let inputValue = comboBox.editText + + let index = comboBox.find(inputValue) + if (index !== -1) + inputValue = comboBox.textAt(index) + + comboBox.backendValue.value = inputValue + + comboBox.dirty = false + } + onCompressedActivated: { if (!comboBox.__isCompleted) return diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml index bdefc13a977..601f9087f0d 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionLogic.qml @@ -131,7 +131,7 @@ Item { checkable: true onTriggered: { if (checked) - backendValue.exportPopertyAsAlias() + backendValue.exportPropertyAsAlias() else backendValue.removeAliasExport() } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml index 61ffd0ce407..4b5f5caf4ac 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml @@ -29,7 +29,7 @@ import HelperWidgets 2.0 import StudioControls 1.0 as StudioControls StudioControls.ComboBox { - id: comboBox + id: root property variant backendValue property color textColor: colorLogic.textColor @@ -39,17 +39,17 @@ StudioControls.ComboBox { labelColor: colorLogic.textColor editable: true - onTextColorChanged: setColor() + onTextColorChanged: root.setColor() FileResourcesModel { id: fileModel modelNodeBackendProperty: modelNodeBackend - filter: comboBox.fontFilter + filter: root.fontFilter } function createFontLoader(fontUrl) { return Qt.createQmlObject('import QtQuick 2.0; FontLoader { source: "' + fontUrl + '"; }', - comboBox, "dynamicFontLoader") + root, "dynamicFontLoader") } function setupModel() { @@ -63,80 +63,83 @@ StudioControls.ComboBox { // Remove duplicate family names familyNames = [...new Set(familyNames)] familyNames.sort() - comboBox.model = familyNames + root.model = familyNames + root.currentIndex = root.find(root.backendValue.value) } - onModelChanged: editText = comboBox.backendValue.valueToString + function setColor() { + // Hack to style the text input + for (var i = 0; i < root.children.length; i++) { + if (root.children[i].text !== undefined) { + root.children[i].color = root.textColor + } + } + } + + onModelChanged: root.editText = root.backendValue.valueToString ExtendedFunctionLogic { id: extFuncLogic - backendValue: comboBox.backendValue + backendValue: root.backendValue } actionIndicator.icon.color: extFuncLogic.color actionIndicator.icon.text: extFuncLogic.glyph actionIndicator.onClicked: extFuncLogic.show() actionIndicator.forceVisible: extFuncLogic.menuVisible - actionIndicator.visible: comboBox.showExtendedFunctionButton + actionIndicator.visible: root.showExtendedFunctionButton ColorLogic { id: colorLogic - property string textValue: comboBox.backendValue.valueToString - backendValue: comboBox.backendValue - onTextValueChanged: comboBox.editText = colorLogic.textValue + property string textValue: root.backendValue.valueToString + backendValue: root.backendValue + onTextValueChanged: root.editText = colorLogic.textValue } onAccepted: { - if (backendValue === undefined) + if (root.backendValue === undefined) return - if (editText === "") + if (root.editText === "") return - if (backendValue.value !== editText) - backendValue.value = editText; + if (root.backendValue.value !== root.editText) + root.backendValue.value = root.editText } - onActivated: { - if (backendValue === undefined) + onCompressedActivated: function(index, reason) { root.handleActivate(index) } + + function handleActivate(index) + { + if (root.backendValue === undefined) return - if (editText === "") + if (root.editText === "") return - var indexText = comboBox.textAt(index) + var indexText = root.textAt(index) - if (backendValue.value !== indexText) - backendValue.value = indexText + if (root.backendValue.value !== indexText) + root.backendValue.value = indexText } Connections { target: modelNodeBackend function onSelectionChanged() { - comboBox.editText = backendValue.value - setupModel() + root.editText = root.backendValue.value + root.setupModel() } } Component.onCompleted: { - setupModel() + root.setupModel() // Hack to style the text input - for (var i = 0; i < comboBox.children.length; i++) { - if (comboBox.children[i].text !== undefined) { - comboBox.children[i].color = comboBox.textColor - comboBox.children[i].anchors.rightMargin = 34 - comboBox.children[i].anchors.leftMargin = 18 + for (var i = 0; i < root.children.length; i++) { + if (root.children[i].text !== undefined) { + root.children[i].color = root.textColor + root.children[i].anchors.rightMargin = 34 + root.children[i].anchors.leftMargin = 18 } } } - - function setColor() { - // Hack to style the text input - for (var i = 0; i < comboBox.children.length; i++) { - if (comboBox.children[i].text !== undefined) { - comboBox.children[i].color = comboBox.textColor - } - } - } - } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml index 93a0878d377..d52c2ca6389 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/LineEdit.qml @@ -32,36 +32,43 @@ StudioControls.TextField { id: lineEdit property variant backendValue - property color borderColor: "#222" - property color highlightColor: "orange" - color: lineEdit.edit ? StudioTheme.Values.themeTextColor : colorLogic.textColor - - property bool showTranslateCheckBox: true - translationIndicatorVisible: showTranslateCheckBox property bool writeValueManually: false property bool writeAsExpression: false - property bool __dirty: false - + property bool showTranslateCheckBox: true property bool showExtendedFunctionButton: true + property string context - actionIndicator.visible: showExtendedFunctionButton + property bool __dirty: false signal commitData - property string context + color: lineEdit.edit ? StudioTheme.Values.themeTextColor : colorLogic.textColor + actionIndicator.visible: lineEdit.showExtendedFunctionButton + translationIndicatorVisible: lineEdit.showTranslateCheckBox function setTranslateExpression() { if (translateFunction() === "qsTranslate") { - backendValue.expression = translateFunction() - + "(\"" + backendValue.getTranslationContext() - + "\", " + "\"" + escapeString(text) + "\")" + lineEdit.backendValue.expression = translateFunction() + + "(\"" + lineEdit.backendValue.getTranslationContext() + + "\", " + "\"" + lineEdit.escapeString(lineEdit.text) + "\")" } else { - backendValue.expression = translateFunction() + "(\"" + escapeString(text) + "\")" + lineEdit.backendValue.expression = translateFunction() + + "(\"" + lineEdit.escapeString(lineEdit.text) + "\")" } } + function escapeString(string) { + var str = string + str = str.replace(/\\/g, "\\\\") + str.replace(/\"/g, "\\\"") + str = str.replace(/\t/g, "\\t") + str = str.replace(/\r/g, "\\r") + str = str.replace(/\n/g, '\\n') + return str + } + ExtendedFunctionLogic { id: extFuncLogic backendValue: lineEdit.backendValue @@ -79,60 +86,58 @@ StudioControls.TextField { if (colorLogic.valueFromBackend === undefined) { lineEdit.text = "" } else { - if (writeValueManually) + if (lineEdit.writeValueManually) lineEdit.text = convertColorToString(colorLogic.valueFromBackend) else lineEdit.text = colorLogic.valueFromBackend } - __dirty = false + lineEdit.__dirty = false } } - onTextChanged: { - __dirty = true - } + onTextChanged: lineEdit.__dirty = true Connections { target: modelNodeBackend function onSelectionToBeChanged() { - if (__dirty && !writeValueManually) { - if (writeAsExpression) - lineEdit.backendValue.expression = text + if (lineEdit.__dirty && !lineEdit.writeValueManually) { + if (lineEdit.writeAsExpression) + lineEdit.backendValue.expression = lineEdit.text else - lineEdit.backendValue.value = text - } else if (__dirty) { + lineEdit.backendValue.value = lineEdit.text + } else if (lineEdit.__dirty) { commitData() } - __dirty = false + lineEdit.__dirty = false } } onEditingFinished: { - if (writeValueManually) + if (lineEdit.writeValueManually) return - if (!__dirty) + if (!lineEdit.__dirty) return - if (backendValue.isTranslated) { - setTranslateExpression() + if (lineEdit.backendValue.isTranslated) { + lineEdit.setTranslateExpression() } else { - if (writeAsExpression) { - if (lineEdit.backendValue.expression !== text) - lineEdit.backendValue.expression = text - } else if (lineEdit.backendValue.value !== text) { - lineEdit.backendValue.value = text + if (lineEdit.writeAsExpression) { + if (lineEdit.backendValue.expression !== lineEdit.text) + lineEdit.backendValue.expression = lineEdit.text + } else if (lineEdit.backendValue.value !== lineEdit.text) { + lineEdit.backendValue.value = lineEdit.text } } - __dirty = false + lineEdit.__dirty = false } property bool isTranslated: colorLogic.backendValue === undefined ? false : colorLogic.backendValue.isTranslated translationIndicator.onClicked: { - if (translationIndicator.checked) { + if (lineEdit.translationIndicator.checked) { setTranslateExpression() } else { var textValue = lineEdit.text @@ -141,7 +146,8 @@ StudioControls.TextField { colorLogic.evaluate() } - property variant backendValueValueInternal: backendValue === undefined ? 0 : backendValue.value + property variant backendValueValueInternal: lineEdit.backendValue === undefined ? 0 + : lineEdit.backendValue.value onBackendValueValueInternalChanged: { if (lineEdit.backendValue === undefined) lineEdit.translationIndicator.checked = false @@ -155,15 +161,4 @@ StudioControls.TextField { else lineEdit.translationIndicator.checked = lineEdit.backendValue.isTranslated } - - function escapeString(string) { - var str = string - str = str.replace(/\\/g, "\\\\") - str.replace(/\"/g, "\\\"") - str = str.replace(/\t/g, "\\t") - str = str.replace(/\r/g, "\\r") - str = str.replace(/\n/g, '\\n') - return str - } - } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml index dd4a11d9cff..ce2584c4be5 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml @@ -32,7 +32,7 @@ import StudioTheme 1.0 as StudioTheme import QtQuickDesignerTheme 1.0 Row { - id: urlChooser + id: root property variant backendValue property color textColor: colorLogic.highlight ? colorLogic.textColor @@ -47,22 +47,24 @@ Row { FileResourcesModel { id: fileModel modelNodeBackendProperty: modelNodeBackend - filter: urlChooser.filter + filter: root.filter } ColorLogic { id: colorLogic - backendValue: urlChooser.backendValue + backendValue: root.backendValue } - StudioControls.ComboBox { + StudioControls.FilterComboBox { id: comboBox - property ListModel items: ListModel {} + property ListModel listModel: ListModel {} implicitWidth: StudioTheme.Values.singleControlColumnWidth + StudioTheme.Values.actionIndicatorWidth width: implicitWidth + allowUserInput: true + // Note: highlightedIndex property isn't used because it has no setter and it doesn't reset // when the combobox is closed by focusing on some other control. property int hoverIndex: -1 @@ -70,7 +72,7 @@ Row { ToolTip { id: toolTip visible: comboBox.hover && toolTip.text !== "" - text: urlChooser.backendValue.valueToString + text: root.backendValue.valueToString delay: StudioTheme.Values.toolTipDelay height: StudioTheme.Values.toolTipHeight background: Rectangle { @@ -88,27 +90,39 @@ Row { delegate: ItemDelegate { required property string fullPath required property string name + required property int group required property int index - id: delegateItem - width: parent.width + id: delegateRoot + width: comboBox.popup.width - comboBox.popup.leftPadding - comboBox.popup.rightPadding + - (comboBox.popupScrollBar.visible ? comboBox.popupScrollBar.contentItem.implicitWidth + 2 + : 0) // TODO Magic number height: StudioTheme.Values.height - 2 * StudioTheme.Values.border padding: 0 - highlighted: comboBox.highlightedIndex === index + hoverEnabled: true + highlighted: comboBox.highlightedIndex === delegateRoot.DelegateModel.itemsIndex + + onHoveredChanged: { + if (delegateRoot.hovered && !comboBox.popupMouseArea.active) + comboBox.setHighlightedIndexItems(delegateRoot.DelegateModel.itemsIndex) + } + + onClicked: comboBox.selectItem(delegateRoot.DelegateModel.itemsIndex) indicator: Item { id: itemDelegateIconArea - width: delegateItem.height - height: delegateItem.height + width: delegateRoot.height + height: delegateRoot.height Label { id: itemDelegateIcon text: StudioTheme.Constants.tickIcon - color: delegateItem.highlighted ? StudioTheme.Values.themeTextSelectedTextColor + color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor : StudioTheme.Values.themeTextColor font.family: StudioTheme.Constants.iconFont.family font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti - visible: comboBox.currentIndex === index ? true : false + visible: comboBox.currentIndex === delegateRoot.DelegateModel.itemsIndex ? true + : false anchors.fill: parent renderType: Text.NativeRendering horizontalAlignment: Text.AlignHCenter @@ -119,7 +133,7 @@ Row { contentItem: Text { leftPadding: itemDelegateIconArea.width text: name - color: delegateItem.highlighted ? StudioTheme.Values.themeTextSelectedTextColor + color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor : StudioTheme.Values.themeTextColor font: comboBox.font elide: Text.ElideRight @@ -127,17 +141,17 @@ Row { } background: Rectangle { - id: itemDelegateBackground x: 0 y: 0 - width: delegateItem.width - height: delegateItem.height - color: delegateItem.highlighted ? StudioTheme.Values.themeInteraction : "transparent" + width: delegateRoot.width + height: delegateRoot.height + color: delegateRoot.highlighted ? StudioTheme.Values.themeInteraction + : "transparent" } ToolTip { id: itemToolTip - visible: delegateItem.hovered && comboBox.highlightedIndex === index + visible: delegateRoot.hovered && comboBox.highlightedIndex === index text: fullPath delay: StudioTheme.Values.toolTipDelay height: StudioTheme.Values.toolTipHeight @@ -161,7 +175,7 @@ Row { ExtendedFunctionLogic { id: extFuncLogic - backendValue: urlChooser.backendValue + backendValue: root.backendValue onReseted: comboBox.editText = "" } @@ -181,20 +195,15 @@ Row { // Takes into account applied bindings property string textValue: { - if (urlChooser.backendValue.isBound) - return urlChooser.backendValue.expression + if (root.backendValue.isBound) + return root.backendValue.expression - var fullPath = urlChooser.backendValue.valueToString + var fullPath = root.backendValue.valueToString return fullPath.substr(fullPath.lastIndexOf('/') + 1) } onTextValueChanged: comboBox.setCurrentText(comboBox.textValue) - editable: true - textRole: "name" - valueRole: "fullPath" - model: comboBox.items - onModelChanged: { if (!comboBox.isComplete) return @@ -206,20 +215,14 @@ Row { if (!comboBox.isComplete) return - var inputValue = comboBox.editText + let inputValue = comboBox.editText // Check if value set by user matches with a name in the model then pick the full path - var index = comboBox.find(inputValue) + let index = comboBox.find(inputValue) if (index !== -1) - inputValue = comboBox.items.get(index).fullPath + inputValue = comboBox.items.get(index).model.fullPath - // Get the currently assigned backend value, extract its file name and compare it to the - // input value. If they differ the new value needs to be set. - var currentValue = urlChooser.backendValue.value - var fileName = currentValue.substr(currentValue.lastIndexOf('/') + 1); - - if (fileName !== inputValue) - urlChooser.backendValue.value = inputValue + root.backendValue.value = inputValue comboBox.dirty = false } @@ -234,14 +237,16 @@ Row { } function handleActivate(index) { - if (urlChooser.backendValue === undefined || !comboBox.isComplete) + if (root.backendValue === undefined || !comboBox.isComplete) return - if (index === -1) // select first item if index is invalid - index = 0 + let inputValue = comboBox.editText - if (urlChooser.backendValue.value !== comboBox.items.get(index).fullPath) - urlChooser.backendValue.value = comboBox.items.get(index).fullPath + if (index >= 0) + inputValue = comboBox.items.get(index).model.fullPath + + if (root.backendValue.value !== inputValue) + root.backendValue.value = inputValue comboBox.dirty = false } @@ -250,7 +255,7 @@ Row { // Hack to style the text input for (var i = 0; i < comboBox.children.length; i++) { if (comboBox.children[i].text !== undefined) { - comboBox.children[i].color = urlChooser.textColor + comboBox.children[i].color = root.textColor comboBox.children[i].anchors.rightMargin = 34 } } @@ -261,36 +266,44 @@ Row { function createModel() { // Build the combobox model - comboBox.items.clear() + comboBox.listModel.clear() + // While adding items to the model this binding needs to be interrupted, otherwise the + // update function of the SortFilterModel is triggered every time on append() which makes + // QtDS very slow. This will happen when selecting different items in the scene. + comboBox.model = {} - if (urlChooser.defaultItems !== undefined) { - for (var i = 0; i < urlChooser.defaultItems.length; ++i) { - comboBox.items.append({ - fullPath: urlChooser.defaultItems[i], - name: urlChooser.defaultItems[i] + if (root.defaultItems !== undefined) { + for (var i = 0; i < root.defaultItems.length; ++i) { + comboBox.listModel.append({ + fullPath: root.defaultItems[i], + name: root.defaultItems[i], + group: 0 }) } } for (var j = 0; j < fileModel.fullPathModel.length; ++j) { - comboBox.items.append({ + comboBox.listModel.append({ fullPath: fileModel.fullPathModel[j], - name: fileModel.fileNameModel[j] + name: fileModel.fileNameModel[j], + group: 1 }) } + + comboBox.model = Qt.binding(function() { return comboBox.listModel }) } Connections { target: fileModel function onFullPathModelChanged() { - urlChooser.createModel() + root.createModel() comboBox.setCurrentText(comboBox.textValue) } } - onDefaultItemsChanged: urlChooser.createModel() + onDefaultItemsChanged: root.createModel() - Component.onCompleted: urlChooser.createModel() + Component.onCompleted: root.createModel() function indexOf(model, criteria) { for (var i = 0; i < model.count; ++i) { @@ -305,16 +318,16 @@ Row { function onStateChanged(state) { // update currentIndex when the popup opens to override the default behavior in super classes // that selects currentIndex based on values in the combo box. - if (comboBox.popup.opened && !urlChooser.backendValue.isBound) { - var index = urlChooser.indexOf(comboBox.items, + if (comboBox.popup.opened && !root.backendValue.isBound) { + var index = root.indexOf(comboBox.items, function(item) { - return item.fullPath === urlChooser.backendValue.value + return item.fullPath === root.backendValue.value }) if (index !== -1) { comboBox.currentIndex = index comboBox.hoverIndex = index - comboBox.editText = comboBox.items.get(index).name + comboBox.editText = comboBox.items.get(index).model.name } } } @@ -324,11 +337,11 @@ Row { IconIndicator { icon: StudioTheme.Constants.addFile - iconColor: urlChooser.textColor + iconColor: root.textColor onClicked: { fileModel.openFileDialog() if (fileModel.fileName !== "") - urlChooser.backendValue.value = fileModel.fileName + root.backendValue.value = fileModel.fileName } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml index 1997419bab4..2d3bb74c3f7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/CheckIndicator.qml @@ -50,16 +50,16 @@ Rectangle { id: checkIndicatorMouseArea anchors.fill: parent hoverEnabled: true - onPressed: { + onClicked: { + if (myControl.activeFocus) + myControl.focus = false + if (myPopup.opened) { myPopup.close() } else { myPopup.open() myPopup.forceActiveFocus() } - - if (myControl.activeFocus) - myControl.focus = false } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml index b6fd6fc3c64..eb48ab6dc09 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBox.qml @@ -65,6 +65,11 @@ T.ComboBox { comboBoxPopup.close() } + onActiveFocusChanged: { + if (myComboBox.activeFocus) + comboBoxInput.preFocusText = myComboBox.editText + } + ActionIndicator { id: actionIndicator myControl: myComboBox @@ -76,19 +81,22 @@ T.ComboBox { contentItem: ComboBoxInput { id: comboBoxInput + + property string preFocusText: "" + myControl: myComboBox text: myComboBox.editText onEditingFinished: { comboBoxInput.deselect() comboBoxInput.focus = false + myComboBox.focus = false // Only trigger the signal, if the value was modified if (myComboBox.dirty) { myTimer.stop() myComboBox.dirty = false - myComboBox.compressedActivated(myComboBox.find(myComboBox.editText), - ComboBox.ActivatedReason.EditingFinished) + myComboBox.accepted() } } onTextEdited: myComboBox.dirty = true @@ -276,7 +284,7 @@ T.ComboBox { PropertyChanges { target: comboBoxBackground color: StudioTheme.Values.themeControlBackgroundInteraction - border.color: StudioTheme.Values.themeControlOutline + border.color: StudioTheme.Values.themeControlOutlineInteraction } StateChangeScript { script: comboBoxPopup.close() @@ -312,7 +320,10 @@ T.ComboBox { ] Keys.onPressed: function(event) { - if (event.key === Qt.Key_Escape) + if (event.key === Qt.Key_Escape) { + myComboBox.editText = comboBoxInput.preFocusText + myComboBox.dirty = true myComboBox.focus = false + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml index 6d926b034cb..de7dcc1f8c3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/ComboBoxInput.qml @@ -73,13 +73,13 @@ TextInput { acceptedButtons: Qt.LeftButton cursorShape: Qt.PointingHandCursor onPressed: function(mouse) { - if (textInput.readOnly) { + if (!textInput.myControl.editable) { if (myControl.popup.opened) { myControl.popup.close() myControl.focus = false } else { - myControl.forceActiveFocus() myControl.popup.open() + myControl.forceActiveFocus() } } else { textInput.forceActiveFocus() diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml new file mode 100644 index 00000000000..30142652ab3 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/FilterComboBox.qml @@ -0,0 +1,752 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick 3D. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Templates as T +import StudioTheme 1.0 as StudioTheme + +Item { + id: root + + enum Interaction { None, TextEdit, Key } + + property int currentInteraction: FilterComboBox.Interaction.None + + property alias model: sortFilterModel.model + property alias items: sortFilterModel.items + property alias delegate: sortFilterModel.delegate + + property alias font: textInput.font + + // This indicates if the value was committed or the user is still editing + property bool editing: false + + // This is the actual filter that is applied on the model + property string filter: "" + property bool filterActive: root.filter !== "" + + // Accept arbitrary input or only items from the model + property bool allowUserInput: false + + property alias editText: textInput.text + property int highlightedIndex: -1 // items index + property int currentIndex: -1 // items index + + property string autocompleteString: "" + + property bool __isCompleted: false + + property alias actionIndicator: actionIndicator + + // This property is used to indicate the global hover state + property bool hover: actionIndicator.hover || textInput.hover || checkIndicator.hover + property alias edit: textInput.edit + property alias open: popup.visible + + property alias actionIndicatorVisible: actionIndicator.visible + property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth + property real __actionIndicatorHeight: StudioTheme.Values.actionIndicatorHeight + + property bool dirty: false // user modification flag + + property bool escapePressed: false + + signal accepted() + signal activated(int index) + signal compressedActivated(int index, int reason) + + enum ActivatedReason { EditingFinished, Other } + + property alias popup: popup + property alias popupScrollBar: popupScrollBar + property alias popupMouseArea: popupMouseArea + + width: StudioTheme.Values.defaultControlWidth + height: StudioTheme.Values.defaultControlHeight + implicitHeight: StudioTheme.Values.defaultControlHeight + + function selectItem(itemsIndex) { + textInput.text = sortFilterModel.items.get(itemsIndex).model.name + root.currentIndex = itemsIndex + root.finishEditing() + root.activated(itemsIndex) + } + + function submitValue() { + if (!root.allowUserInput) { + // If input isn't according to any item in the model, don't finish editing + if (root.highlightedIndex === -1) + return + + root.selectItem(root.highlightedIndex) + } else { + root.currentIndex = -1 + + // Only trigger the signal, if the value was modified + if (root.dirty) { + myTimer.stop() + root.dirty = false + root.editText = root.editText.trim() + } + + root.finishEditing() + root.accepted() + } + } + + function finishEditing() { + root.editing = false + root.filter = "" + root.autocompleteString = "" + textInput.focus = false // Remove focus from text field + popup.close() + } + + function increaseVisibleIndex() { + let numItems = sortFilterModel.visibleGroup.count + if (!numItems) + return + + if (root.highlightedIndex === -1) // Nothing is selected + root.setHighlightedIndexVisible(0) + else { + let currentVisibleIndex = sortFilterModel.items.get(root.highlightedIndex).visibleIndex + ++currentVisibleIndex + + if (currentVisibleIndex > numItems - 1) + currentVisibleIndex = 0 + + root.setHighlightedIndexVisible(currentVisibleIndex) + } + } + + function decreaseVisibleIndex() { + let numItems = sortFilterModel.visibleGroup.count + if (!numItems) + return + + if (root.highlightedIndex === -1) // Nothing is selected + root.setHighlightedIndexVisible(numItems - 1) + else { + let currentVisibleIndex = sortFilterModel.items.get(root.highlightedIndex).visibleIndex + --currentVisibleIndex + + if (currentVisibleIndex < 0) + currentVisibleIndex = numItems - 1 + + root.setHighlightedIndexVisible(currentVisibleIndex) + } + } + + function updateHighlightedIndex() { + // Check if current index is still part of the filtered list, if not set it to 0 + if (root.highlightedIndex !== -1 && !sortFilterModel.items.get(root.highlightedIndex).inVisible) { + root.setHighlightedIndexVisible(0) + } else { + // Needs to be set in order for ListView to keep its currenIndex up to date, so + // scroll position gets updated according to the higlighted item + root.setHighlightedIndexItems(root.highlightedIndex) + } + } + + function setHighlightedIndexItems(itemsIndex) { // items group index + root.highlightedIndex = itemsIndex + + if (itemsIndex === -1) + listView.currentIndex = -1 + else + listView.currentIndex = sortFilterModel.items.get(itemsIndex).visibleIndex + } + + function setHighlightedIndexVisible(visibleIndex) { // visible group index + if (visibleIndex === -1) + root.highlightedIndex = -1 + else + root.highlightedIndex = sortFilterModel.visibleGroup.get(visibleIndex).itemsIndex + + listView.currentIndex = visibleIndex + } + + function updateAutocomplete() { + if (root.highlightedIndex === -1) + root.autocompleteString = "" + else { + let suggestion = sortFilterModel.items.get(root.highlightedIndex).model.name + root.autocompleteString = suggestion.substring(textInput.text.length) + } + } + + // TODO is this already case insensitiv?! + function find(text) { + for (let i = 0; i < sortFilterModel.items.count; ++i) + if (sortFilterModel.items.get(i).model.name === text) + return i + + return -1 + } + + Timer { + id: myTimer + property int activatedIndex + repeat: false + running: false + interval: 100 + onTriggered: root.compressedActivated(myTimer.activatedIndex, + ComboBox.ActivatedReason.Other) + } + + onActivated: function(index) { + myTimer.activatedIndex = index + myTimer.restart() + } + + onHighlightedIndexChanged: { + if (root.editing || (root.editText === "" && root.allowUserInput)) + root.updateAutocomplete() + } + + DelegateModel { + id: noMatchesModel + + model: ListModel { + ListElement { name: "No matches" } + } + + delegate: ItemDelegate { + id: noMatchesDelegate + width: popup.width - popup.leftPadding - popup.rightPadding + - (popupScrollBar.visible ? popupScrollBar.contentItem.implicitWidth + 2 + : 0) // TODO Magic number + height: StudioTheme.Values.height - 2 * StudioTheme.Values.border + padding: 0 + + contentItem: Text { + leftPadding: StudioTheme.Values.inputHorizontalPadding + text: name + font.italic: true + color: StudioTheme.Values.themeTextColor + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + x: 0 + y: 0 + width: noMatchesDelegate.width + height: noMatchesDelegate.height + color: "transparent" + } + } + } + + SortFilterModel { + id: sortFilterModel + + filterAcceptsItem: function(item) { + return item.name.toLowerCase().startsWith(root.filter.toLowerCase()) + } + + lessThan: function(left, right) { + if (left.group === right.group) { + return left.name.toLowerCase().localeCompare(right.name.toLowerCase()) + } + + return left.group - right.group + } + + delegate: ItemDelegate { + id: delegateRoot + width: popup.width - popup.leftPadding - popup.rightPadding + - (popupScrollBar.visible ? popupScrollBar.contentItem.implicitWidth + 2 + : 0) // TODO Magic number + height: StudioTheme.Values.height - 2 * StudioTheme.Values.border + padding: 0 + hoverEnabled: true + highlighted: root.highlightedIndex === delegateRoot.DelegateModel.itemsIndex + + onHoveredChanged: { + if (delegateRoot.hovered && !popupMouseArea.active) + root.setHighlightedIndexItems(delegateRoot.DelegateModel.itemsIndex) + } + + onClicked: root.selectItem(delegateRoot.DelegateModel.itemsIndex) + + indicator: Item { + id: itemDelegateIconArea + width: delegateRoot.height + height: delegateRoot.height + + T.Label { + id: itemDelegateIcon + text: StudioTheme.Constants.tickIcon + color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor + font.family: StudioTheme.Constants.iconFont.family + font.pixelSize: StudioTheme.Values.spinControlIconSizeMulti + visible: root.currentIndex === delegateRoot.DelegateModel.itemsIndex ? true + : false + anchors.fill: parent + renderType: Text.NativeRendering + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + + contentItem: Text { + leftPadding: itemDelegateIconArea.width + text: name + color: delegateRoot.highlighted ? StudioTheme.Values.themeTextSelectedTextColor + : StudioTheme.Values.themeTextColor + font: textInput.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + + background: Rectangle { + x: 0 + y: 0 + width: delegateRoot.width + height: delegateRoot.height + color: delegateRoot.highlighted ? StudioTheme.Values.themeInteraction + : "transparent" + } + } + + onUpdated: { + if (!root.__isCompleted) + return + + if (sortFilterModel.count === 0) + root.setHighlightedIndexVisible(-1) + else { + if (root.highlightedIndex === -1 && !root.allowUserInput) + root.setHighlightedIndexVisible(0) + } + } + } + + Row { + ActionIndicator { + id: actionIndicator + myControl: root + x: 0 + y: 0 + width: actionIndicator.visible ? root.__actionIndicatorWidth : 0 + height: actionIndicator.visible ? root.__actionIndicatorHeight : 0 + } + + TextInput { + id: textInput + + property bool hover: textInputMouseArea.containsMouse && textInput.enabled + property bool edit: textInput.activeFocus + property string preFocusText: "" + + x: 0 + y: 0 + z: 2 + width: root.width - actionIndicator.width + height: root.height + leftPadding: StudioTheme.Values.inputHorizontalPadding + rightPadding: StudioTheme.Values.inputHorizontalPadding + checkIndicator.width + + StudioTheme.Values.border + horizontalAlignment: Qt.AlignLeft + verticalAlignment: Qt.AlignVCenter + color: StudioTheme.Values.themeTextColor + selectionColor: StudioTheme.Values.themeTextSelectionColor + selectedTextColor: StudioTheme.Values.themeTextSelectedTextColor + selectByMouse: true + clip: true + + Rectangle { + id: textInputBackground + z: -1 + width: textInput.width + height: textInput.height + color: StudioTheme.Values.themeControlBackground + border.color: StudioTheme.Values.themeControlOutline + border.width: StudioTheme.Values.border + } + + MouseArea { + id: textInputMouseArea + anchors.fill: parent + enabled: true + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.LeftButton + cursorShape: Qt.PointingHandCursor + onPressed: function(mouse) { + textInput.forceActiveFocus() + mouse.accepted = false + } + + // Stop scrollable views from scrolling while ComboBox is in edit mode and the mouse + // pointer is on top of it. We might add wheel selection in the future. + onWheel: function(wheel) { + wheel.accepted = root.edit + } + } + + onEditingFinished: { + if (root.escapePressed) { + root.escapePressed = false + root.editText = textInput.preFocusText + } else { + if (root.currentInteraction === FilterComboBox.Interaction.TextEdit) { + if (root.dirty) + root.submitValue() + } else if (root.currentInteraction === FilterComboBox.Interaction.Key) { + root.selectItem(root.highlightedIndex) + } + } + + sortFilterModel.update() + } + + onTextEdited: { + root.currentInteraction = FilterComboBox.Interaction.TextEdit + root.editing = true + popupMouseArea.active = true + root.dirty = true + + if (textInput.text !== "") + root.filter = textInput.text + else { + root.filter = "" + root.autocompleteString = "" + } + + if (!popup.visible) + popup.open() + + sortFilterModel.update() + + if (!root.allowUserInput) + root.updateHighlightedIndex() + else + root.setHighlightedIndexVisible(-1) + + root.updateAutocomplete() + } + + onActiveFocusChanged: { + if (textInput.activeFocus) { + popup.open() + textInput.preFocusText = textInput.text + } else + popup.close() + } + + states: [ + State { + name: "default" + when: root.enabled && !textInput.edit && !root.hover && !root.open + PropertyChanges { + target: textInputBackground + color: StudioTheme.Values.themeControlBackground + } + PropertyChanges { + target: textInputMouseArea + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton + } + }, + State { + name: "globalHover" + when: root.hover && !textInput.hover && !textInput.edit && !root.open + PropertyChanges { + target: textInputBackground + color: StudioTheme.Values.themeControlBackgroundGlobalHover + } + }, + State { + name: "hover" + when: textInput.hover && root.hover && !textInput.edit + PropertyChanges { + target: textInputBackground + color: StudioTheme.Values.themeControlBackgroundHover + } + }, + State { + name: "edit" + when: root.edit + PropertyChanges { + target: textInputBackground + color: StudioTheme.Values.themeControlBackgroundInteraction + border.color: StudioTheme.Values.themeControlOutlineInteraction + } + PropertyChanges { + target: textInputMouseArea + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } + }, + State { + name: "disable" + when: !root.enabled + PropertyChanges { + target: textInputBackground + color: StudioTheme.Values.themeControlBackgroundDisabled + } + PropertyChanges { + target: textInput + color: StudioTheme.Values.themeTextColorDisabled + } + } + ] + + Text { + visible: root.autocompleteString !== "" + text: root.autocompleteString + x: textInput.leftPadding + textMetrics.advanceWidth + y: (textInput.height - Math.ceil(textMetrics.height)) / 2 + color: "gray" // TODO proper color value + font: textInput.font + renderType: textInput.renderType + } + + TextMetrics { + id: textMetrics + font: textInput.font + text: textInput.text + } + + Rectangle { + id: checkIndicator + + property bool hover: checkIndicatorMouseArea.containsMouse && checkIndicator.enabled + property bool pressed: checkIndicatorMouseArea.containsPress + property bool checked: popup.visible + + x: textInput.width - checkIndicator.width - StudioTheme.Values.border + y: StudioTheme.Values.border + width: StudioTheme.Values.height - StudioTheme.Values.border + height: textInput.height - (StudioTheme.Values.border * 2) + color: StudioTheme.Values.themeControlBackground + border.width: 0 + + MouseArea { + id: checkIndicatorMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (popup.visible) + popup.close() + else + popup.open() + + if (!textInput.activeFocus) { + textInput.forceActiveFocus() + textInput.selectAll() + } + } + } + + T.Label { + id: checkIndicatorIcon + anchors.fill: parent + color: StudioTheme.Values.themeTextColor + text: StudioTheme.Constants.upDownSquare2 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + font.pixelSize: StudioTheme.Values.sliderControlSizeMulti + font.family: StudioTheme.Constants.iconFont.family + } + + states: [ + State { + name: "default" + when: root.enabled && checkIndicator.enabled && !root.edit + && !checkIndicator.hover && !root.hover + && !checkIndicator.checked + PropertyChanges { + target: checkIndicator + color: StudioTheme.Values.themeControlBackground + } + }, + State { + name: "globalHover" + when: root.enabled && checkIndicator.enabled + && !checkIndicator.hover && root.hover && !root.edit + && !checkIndicator.checked + PropertyChanges { + target: checkIndicator + color: StudioTheme.Values.themeControlBackgroundGlobalHover + } + }, + State { + name: "hover" + when: root.enabled && checkIndicator.enabled + && checkIndicator.hover && root.hover && !checkIndicator.pressed + && !checkIndicator.checked + PropertyChanges { + target: checkIndicator + color: StudioTheme.Values.themeControlBackgroundHover + } + }, + State { + name: "check" + when: checkIndicator.checked + PropertyChanges { + target: checkIndicatorIcon + color: StudioTheme.Values.themeIconColor + } + PropertyChanges { + target: checkIndicator + color: StudioTheme.Values.themeInteraction + } + }, + State { + name: "press" + when: root.enabled && checkIndicator.enabled + && checkIndicator.pressed + PropertyChanges { + target: checkIndicatorIcon + color: StudioTheme.Values.themeIconColor + } + PropertyChanges { + target: checkIndicator + color: StudioTheme.Values.themeInteraction + } + }, + State { + name: "disable" + when: !root.enabled + PropertyChanges { + target: checkIndicator + color: StudioTheme.Values.themeControlBackgroundDisabled + } + PropertyChanges { + target: checkIndicatorIcon + color: StudioTheme.Values.themeTextColorDisabled + } + } + ] + } + } + } + + T.Popup { + id: popup + x: textInput.x + StudioTheme.Values.border + y: textInput.height + width: textInput.width - (StudioTheme.Values.border * 2) + height: Math.min(popup.contentItem.implicitHeight + popup.topPadding + popup.bottomPadding, + root.Window.height - popup.topMargin - popup.bottomMargin, + StudioTheme.Values.maxComboBoxPopupHeight) + padding: StudioTheme.Values.border + margins: 0 // If not defined margin will be -1 + closePolicy: T.Popup.NoAutoClose + + contentItem: ListView { + id: listView + clip: true + implicitHeight: listView.contentHeight + highlightMoveVelocity: -1 + boundsBehavior: Flickable.StopAtBounds + flickDeceleration: 10000 + + model: { + if (popup.visible) + return sortFilterModel.count ? sortFilterModel : noMatchesModel + + return null + } + + ScrollBar.vertical: ScrollBar { + id: popupScrollBar + visible: listView.height < listView.contentHeight + } + } + + background: Rectangle { + color: StudioTheme.Values.themePopupBackground + border.width: 0 + } + + onOpened: { + // Reset the highlightedIndex of ListView as binding with condition didn't work + if (root.highlightedIndex !== -1) + root.setHighlightedIndexItems(root.highlightedIndex) + } + + onAboutToShow: { + // Select first item in list + if (root.highlightedIndex === -1 && sortFilterModel.count && !root.allowUserInput) + root.setHighlightedIndexVisible(0) + } + + MouseArea { + // This is MouseArea is intended to block the hovered property of an ItemDelegate + // when the ListView changes due to Key interaction. + + id: popupMouseArea + property bool active: true + + anchors.fill: parent + enabled: popup.visible && popupMouseArea.active + hoverEnabled: true + onPositionChanged: { popupMouseArea.active = false } + } + } + + Keys.onDownPressed: { + if (!sortFilterModel.visibleGroup.count) + return + + root.currentInteraction = FilterComboBox.Interaction.Key + root.increaseVisibleIndex() + + popupMouseArea.active = true + } + + Keys.onUpPressed: { + if (!sortFilterModel.visibleGroup.count) + return + + root.currentInteraction = FilterComboBox.Interaction.Key + root.decreaseVisibleIndex() + + popupMouseArea.active = true + } + + Keys.onEscapePressed: { + root.escapePressed = true + root.finishEditing() + } + + Component.onCompleted: { + let index = root.find(root.editText) + root.currentIndex = index + root.highlightedIndex = index // TODO might not be intended + + root.__isCompleted = true + } +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml index 6cf763f3ab5..3020a3817a3 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/RealSpinBox.qml @@ -79,6 +79,8 @@ T.SpinBox { property alias compressedValueTimer: myTimer + property string preFocusText: "" + signal realValueModified signal compressedRealValueModified signal dragStarted @@ -162,6 +164,8 @@ T.SpinBox { validator: doubleValidator function handleEditingFinished() { + mySpinBox.focus = false + // Keep the dirty state before calling setValueFromInput(), // it will be set to false (cleared) internally var valueModified = mySpinBox.dirty @@ -174,7 +178,7 @@ T.SpinBox { mySpinBox.compressedRealValueModified() } - onEditingFinished: handleEditingFinished() + onEditingFinished: spinBoxInput.handleEditingFinished() onTextEdited: mySpinBox.dirty = true } @@ -281,7 +285,7 @@ T.SpinBox { id: myTimer repeat: false running: false - interval: 200 + interval: 400 onTriggered: mySpinBox.compressedRealValueModified() } @@ -306,8 +310,10 @@ T.SpinBox { } onDisplayTextChanged: spinBoxInput.text = mySpinBox.displayText onActiveFocusChanged: { - if (mySpinBox.activeFocus) // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason) + if (mySpinBox.activeFocus) { // QTBUG-75862 && mySpinBox.focusReason === Qt.TabFocusReason) + mySpinBox.preFocusText = spinBoxInput.text spinBoxInput.selectAll() + } } Keys.onPressed: function(event) { @@ -333,8 +339,11 @@ T.SpinBox { mySpinBox.realStepSize = currStepSize } - if (event.key === Qt.Key_Escape) - mySpinBox.focus = false + if (event.key === Qt.Key_Escape) { + spinBoxInput.text = mySpinBox.preFocusText + mySpinBox.dirty = true + spinBoxInput.handleEditingFinished() + } } function clamp(v, lo, hi) { diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml new file mode 100644 index 00000000000..e70b0093e39 --- /dev/null +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/SortFilterModel.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick 3D. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQml.Models + +DelegateModel { + id: delegateModel + + property var visibleGroup: visibleItems + + property var lessThan: function(left, right) { return true } + property var filterAcceptsItem: function(item) { return true } + + signal updated() + + function update() { + if (delegateModel.items.count > 0) { + delegateModel.items.setGroups(0, delegateModel.items.count, "items") + } + + // Filter items + var visible = [] + for (var i = 0; i < delegateModel.items.count; ++i) { + var item = delegateModel.items.get(i) + if (delegateModel.filterAcceptsItem(item.model)) { + visible.push(item) + } + } + + // Sort the list of visible items + visible.sort(function(a, b) { + return delegateModel.lessThan(a.model, b.model); + }); + + // Add all items to the visible group + for (i = 0; i < visible.length; ++i) { + item = visible[i] + item.inVisible = true + if (item.visibleIndex !== i) { + visibleItems.move(item.visibleIndex, i, 1) + } + } + + delegateModel.updated() + } + + items.onChanged: delegateModel.update() + onLessThanChanged: delegateModel.update() + onFilterAcceptsItemChanged: delegateModel.update() + + groups: DelegateModelGroup { + id: visibleItems + + name: "visible" + includeByDefault: false + } + + filterOnGroup: "visible" +} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml index cd2fbf7f722..11a6106f523 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/TextField.qml @@ -28,15 +28,15 @@ import QtQuick.Templates 2.15 as T import StudioTheme 1.0 as StudioTheme T.TextField { - id: myTextField + id: root property alias actionIndicator: actionIndicator property alias translationIndicator: translationIndicator // This property is used to indicate the global hover state property bool hover: (actionIndicator.hover || mouseArea.containsMouse - || translationIndicator.hover) && myTextField.enabled - property bool edit: myTextField.activeFocus + || translationIndicator.hover) && root.enabled + property bool edit: root.activeFocus property alias actionIndicatorVisible: actionIndicator.visible property real __actionIndicatorWidth: StudioTheme.Values.actionIndicatorWidth @@ -46,6 +46,8 @@ T.TextField { property real __translationIndicatorWidth: StudioTheme.Values.translationIndicatorWidth property real __translationIndicatorHeight: StudioTheme.Values.translationIndicatorHeight + property string preFocusText: "" + horizontalAlignment: Qt.AlignLeft verticalAlignment: Qt.AlignVCenter @@ -78,7 +80,7 @@ T.TextField { cursorShape: Qt.PointingHandCursor onPressed: function(mouse) { if (mouse.button === Qt.RightButton) - contextMenu.popup(myTextField) + contextMenu.popup(root) mouse.accepted = false } @@ -86,43 +88,50 @@ T.TextField { onPersistentSelectionChanged: { if (!persistentSelection) - myTextField.deselect() + root.deselect() } ContextMenu { id: contextMenu - myTextEdit: myTextField + myTextEdit: root + } + + onActiveFocusChanged: { + if (root.activeFocus) + root.preFocusText = root.text } onEditChanged: { - if (myTextField.edit) + if (root.edit) contextMenu.close() } + onEditingFinished: root.focus = false + ActionIndicator { id: actionIndicator - myControl: myTextField + myControl: root x: 0 y: 0 - width: actionIndicator.visible ? myTextField.__actionIndicatorWidth : 0 - height: actionIndicator.visible ? myTextField.__actionIndicatorHeight : 0 + width: actionIndicator.visible ? root.__actionIndicatorWidth : 0 + height: actionIndicator.visible ? root.__actionIndicatorHeight : 0 } Text { id: placeholder - x: myTextField.leftPadding - y: myTextField.topPadding - width: myTextField.width - (myTextField.leftPadding + myTextField.rightPadding) - height: myTextField.height - (myTextField.topPadding + myTextField.bottomPadding) + x: root.leftPadding + y: root.topPadding + width: root.width - (root.leftPadding + root.rightPadding) + height: root.height - (root.topPadding + root.bottomPadding) - text: myTextField.placeholderText - font: myTextField.font - color: myTextField.placeholderTextColor - verticalAlignment: myTextField.verticalAlignment - visible: !myTextField.length && !myTextField.preeditText - && (!myTextField.activeFocus || myTextField.horizontalAlignment !== Qt.AlignHCenter) + text: root.placeholderText + font: root.font + color: root.placeholderTextColor + verticalAlignment: root.verticalAlignment + visible: !root.length && !root.preeditText + && (!root.activeFocus || root.horizontalAlignment !== Qt.AlignHCenter) elide: Text.ElideRight - renderType: myTextField.renderType + renderType: root.renderType } background: Rectangle { @@ -131,14 +140,14 @@ T.TextField { border.color: StudioTheme.Values.themeControlOutline border.width: StudioTheme.Values.border x: actionIndicator.width - width: myTextField.width - actionIndicator.width - height: myTextField.height + width: root.width - actionIndicator.width + height: root.height } TranslationIndicator { id: translationIndicator - myControl: myTextField - x: myTextField.width - translationIndicator.width + myControl: root + x: root.width - translationIndicator.width width: translationIndicator.visible ? __translationIndicatorWidth : 0 height: translationIndicator.visible ? __translationIndicatorHeight : 0 } @@ -146,15 +155,14 @@ T.TextField { states: [ State { name: "default" - when: myTextField.enabled && !myTextField.hover - && !myTextField.edit + when: root.enabled && !root.hover && !root.edit PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackground border.color: StudioTheme.Values.themeControlOutline } PropertyChanges { - target: myTextField + target: root color: StudioTheme.Values.themeTextColor placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor } @@ -165,15 +173,15 @@ T.TextField { }, State { name: "globalHover" - when: (actionIndicator.hover || translationIndicator.hover) && !myTextField.edit - && myTextField.enabled + when: (actionIndicator.hover || translationIndicator.hover) && !root.edit + && root.enabled PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundGlobalHover border.color: StudioTheme.Values.themeControlOutline } PropertyChanges { - target: myTextField + target: root color: StudioTheme.Values.themeTextColor placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor } @@ -181,28 +189,28 @@ T.TextField { State { name: "hover" when: mouseArea.containsMouse && !actionIndicator.hover && !translationIndicator.hover - && !myTextField.edit && myTextField.enabled + && !root.edit && root.enabled PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundHover border.color: StudioTheme.Values.themeControlOutline } PropertyChanges { - target: myTextField + target: root color: StudioTheme.Values.themeTextColor placeholderTextColor: StudioTheme.Values.themePlaceholderTextColor } }, State { name: "edit" - when: myTextField.edit + when: root.edit PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundInteraction border.color: StudioTheme.Values.themeControlOutlineInteraction } PropertyChanges { - target: myTextField + target: root color: StudioTheme.Values.themeTextColor placeholderTextColor: StudioTheme.Values.themePlaceholderTextColorInteraction } @@ -213,14 +221,14 @@ T.TextField { }, State { name: "disable" - when: !myTextField.enabled + when: !root.enabled PropertyChanges { target: textFieldBackground color: StudioTheme.Values.themeControlBackgroundDisabled border.color: StudioTheme.Values.themeControlOutlineDisabled } PropertyChanges { - target: myTextField + target: root color: StudioTheme.Values.themeTextColorDisabled placeholderTextColor: StudioTheme.Values.themeTextColorDisabled } @@ -228,7 +236,9 @@ T.TextField { ] Keys.onPressed: function(event) { - if (event.key === Qt.Key_Escape) - myTextField.focus = false + if (event.key === Qt.Key_Escape) { + root.text = root.preFocusText + root.focus = false + } } } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir index b5f8c7a4e30..25be4d0acf2 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioControls/qmldir @@ -8,6 +8,7 @@ CheckIndicator 1.0 CheckIndicator.qml ComboBox 1.0 ComboBox.qml ComboBoxInput 1.0 ComboBoxInput.qml ContextMenu 1.0 ContextMenu.qml +FilterComboBox 1.0 FilterComboBox.qml InfinityLoopIndicator 1.0 InfinityLoopIndicator.qml ItemDelegate 1.0 ItemDelegate.qml LinkIndicator2D 1.0 LinkIndicator2D.qml @@ -30,6 +31,7 @@ SectionLabel 1.0 SectionLabel.qml SectionLayout 1.0 SectionLayout.qml Slider 1.0 Slider.qml SliderPopup 1.0 SliderPopup.qml +SortFilterModel 1.0 SortFilterModel.qml SpinBox 1.0 SpinBox.qml SpinBoxIndicator 1.0 SpinBoxIndicator.qml SpinBoxInput 1.0 SpinBoxInput.qml diff --git a/src/app/main.cpp b/src/app/main.cpp index c2a64e6d9ce..ee7ddbd2035 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -406,11 +406,6 @@ QStringList lastSessionArgument() #ifdef ENABLE_CRASHPAD bool startCrashpad(const QString &libexecPath, bool crashReportingEnabled) { - if (QSysInfo::currentCpuArchitecture() == "arm64") { - qDebug() << "The crashpad_handler binary does not work on arm64 properly. So it is disabled for now."; - return false; - } - using namespace crashpad; // Cache directory that will store crashpad information and minidumps diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 7331c912b7d..a8ae84077ec 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -702,7 +702,8 @@ void CMakeBuildSystem::updateFileSystemNodes() addCMakeLists(newRoot.get(), std::move(fileNodes)); } - addFileSystemNodes(newRoot.get(), m_allFiles); + if (m_allFiles) + addFileSystemNodes(newRoot.get(), m_allFiles); setRootProjectNode(std::move(newRoot)); m_reader.resetData(); diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index c41e9c8e8e4..5e8415af982 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -436,6 +436,13 @@ static bool hideAnalyzeMenu() .toBool(); } +static bool hideDebugMenu() +{ + return Core::ICore::settings() + ->value(ProjectExplorer::Constants::SETTINGS_MENU_HIDE_DEBUG, false) + .toBool(); +} + QAction *addAction(const QObject *parent, QMenu *menu, const QString &display, bool on, const std::function &onTriggered) { @@ -922,7 +929,9 @@ DebuggerPluginPrivate::DebuggerPluginPrivate(const QStringList &arguments) m_visibleStartAction.setAction(&m_startAction); m_visibleStartAction.setObjectName("Debug"); // used for UI introduction - ModeManager::addAction(&m_visibleStartAction, /*priority*/ 90); + + if (!hideDebugMenu()) + ModeManager::addAction(&m_visibleStartAction, /*priority*/ 90); m_undisturbableAction.setIcon(interruptIcon(false)); m_undisturbableAction.setEnabled(false); diff --git a/src/plugins/languageclient/languageclientquickfix.cpp b/src/plugins/languageclient/languageclientquickfix.cpp index 9ea8ac3f90e..e65ba296b18 100644 --- a/src/plugins/languageclient/languageclientquickfix.cpp +++ b/src/plugins/languageclient/languageclientquickfix.cpp @@ -95,6 +95,7 @@ IAssistProposal *LanguageClientQuickFixAssistProcessor::perform(const AssistInte handleCodeActionResponse(response); }); + m_client->addAssistProcessor(this); m_client->requestCodeActions(request); m_currentRequest = request.id(); return nullptr; diff --git a/src/plugins/macros/macromanager.cpp b/src/plugins/macros/macromanager.cpp index 1db0b30d275..0b757c41546 100644 --- a/src/plugins/macros/macromanager.cpp +++ b/src/plugins/macros/macromanager.cpp @@ -280,10 +280,15 @@ void MacroManager::startMacro() foreach (IMacroHandler *handler, d->handlers) handler->startRecording(d->currentMacro); - QString endShortcut = Core::ActionManager::command(Constants::END_MACRO)->keySequence().toString(); - QString executeShortcut = Core::ActionManager::command(Constants::EXECUTE_LAST_MACRO)->keySequence().toString(); - QString help = tr("Macro mode. Type \"%1\" to stop recording and \"%2\" to play the macro.") - .arg(endShortcut).arg(executeShortcut); + const QString endShortcut = Core::ActionManager::command(Constants::END_MACRO) + ->keySequence() + .toString(QKeySequence::NativeText); + const QString executeShortcut = Core::ActionManager::command(Constants::EXECUTE_LAST_MACRO) + ->keySequence() + .toString(QKeySequence::NativeText); + const QString help + = tr("Macro mode. Type \"%1\" to stop recording and \"%2\" to play the macro.") + .arg(endShortcut, executeShortcut); Core::EditorManager::showEditorStatusBar(Constants::M_STATUS_BUFFER, help, tr("Stop Recording Macro"), this, [this] { endMacro(); }); diff --git a/src/plugins/macros/macrooptionswidget.cpp b/src/plugins/macros/macrooptionswidget.cpp index 9bade1fea56..0408dd9377b 100644 --- a/src/plugins/macros/macrooptionswidget.cpp +++ b/src/plugins/macros/macrooptionswidget.cpp @@ -96,8 +96,10 @@ void MacroOptionsWidget::createTable() Core::Command *command = Core::ActionManager::command(base.withSuffix(macro->displayName())); - if (command && command->action()) - macroItem->setText(2, command->action()->shortcut().toString()); + if (command && command->action()) { + macroItem->setText(2, + command->action()->shortcut().toString(QKeySequence::NativeText)); + } } } } diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 6e2ec98263c..168062d154f 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1079,12 +1079,19 @@ Toolchains GccToolChainFactory::autoDetect(const ToolchainDetector &detector) co Toolchains GccToolChainFactory::detectForImport(const ToolChainDescription &tcd) const { const QString fileName = tcd.compilerPath.completeBaseName(); - if ((tcd.language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc") - || fileName.endsWith("gcc") - || fileName == "cc")) - || (tcd.language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++") - || fileName.endsWith("g++") - || fileName == "c++"))) { + const QString resolvedSymlinksFileName = tcd.compilerPath.resolveSymlinks().completeBaseName(); + + const bool isCCompiler = tcd.language == Constants::C_LANGUAGE_ID + && (fileName.startsWith("gcc") + || fileName.endsWith("gcc") + || (fileName == "cc" && !resolvedSymlinksFileName.contains("clang"))); + + const bool isCxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID + && (fileName.startsWith("g++") + || fileName.endsWith("g++") + || (fileName == "c++" && !resolvedSymlinksFileName.contains("clang"))); + + if (isCCompiler || isCxxCompiler) { return autoDetectToolChain(tcd, [](const ToolChain *tc) { return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor; }); @@ -1765,9 +1772,18 @@ Toolchains ClangToolChainFactory::autoDetect(const ToolchainDetector &detector) Toolchains ClangToolChainFactory::detectForImport(const ToolChainDescription &tcd) const { - const QString fileName = tcd.compilerPath.toString(); - if ((tcd.language == Constants::C_LANGUAGE_ID && fileName.startsWith("clang") && !fileName.startsWith("clang++")) - || (tcd.language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("clang++"))) { + const QString fileName = tcd.compilerPath.completeBaseName(); + const QString resolvedSymlinksFileName = tcd.compilerPath.resolveSymlinks().completeBaseName(); + + const bool isCCompiler = tcd.language == Constants::C_LANGUAGE_ID + && ((fileName.startsWith("clang") && !fileName.startsWith("clang++")) + || (fileName == "cc" && resolvedSymlinksFileName.contains("clang"))); + + const bool isCxxCompiler = tcd.language == Constants::CXX_LANGUAGE_ID + && (fileName.startsWith("clang++") + || (fileName == "c++" && resolvedSymlinksFileName.contains("clang"))); + + if (isCCompiler || isCxxCompiler) { return autoDetectToolChain(tcd); } return {}; @@ -2023,7 +2039,7 @@ Toolchains LinuxIccToolChainFactory::autoDetect(const ToolchainDetector &detecto Toolchains LinuxIccToolChainFactory::detectForImport(const ToolChainDescription &tcd) const { - const QString fileName = tcd.compilerPath.toString(); + const QString fileName = tcd.compilerPath.completeBaseName(); if ((tcd.language == Constants::CXX_LANGUAGE_ID && fileName.startsWith("icpc")) || (tcd.language == Constants::C_LANGUAGE_ID && fileName.startsWith("icc"))) { return autoDetectToolChain(tcd); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 90a7a6874b5..b141d95a5d1 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1340,7 +1340,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er dd->m_modeBarBuildAction->initialize(cmd->action()); dd->m_modeBarBuildAction->setAttribute(ProxyAction::UpdateText); dd->m_modeBarBuildAction->setAction(cmd->action()); - ModeManager::addAction(dd->m_modeBarBuildAction, Constants::P_ACTION_BUILDPROJECT); + if (!hideBuildMenu()) + ModeManager::addAction(dd->m_modeBarBuildAction, Constants::P_ACTION_BUILDPROJECT); // build for run config dd->m_buildForRunConfigAction = new ParameterAction( diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp index 6dd1504f95d..719406ea1ad 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarymodel.cpp @@ -36,19 +36,24 @@ #include +#include +#include + +#include #include #include #include +#include #include #include +#include +#include #include #include #include #include -#include -#include -#include -#include + +static Q_LOGGING_CATEGORY(assetsLibraryBenchmark, "qtc.assetsLibrary.setRoot", QtWarningMsg) namespace QmlDesigner { @@ -298,6 +303,12 @@ void AssetsLibraryModel::refresh() void AssetsLibraryModel::setRootPath(const QString &path) { + QElapsedTimer time; + if (assetsLibraryBenchmark().isInfoEnabled()) + time.start(); + + qCInfo(assetsLibraryBenchmark) << "start:" << time.elapsed(); + static const QStringList ignoredTopLevelDirs {"imports", "asset_imports"}; m_fileSystemWatcher->clear(); @@ -345,6 +356,8 @@ void AssetsLibraryModel::setRootPath(const QString &path) return isEmpty; }; + qCInfo(assetsLibraryBenchmark) << "directories parsed:" << time.elapsed(); + if (m_assetsDir) delete m_assetsDir; @@ -360,6 +373,8 @@ void AssetsLibraryModel::setRootPath(const QString &path) m_assetsDir->setDirVisible(!noAssets); // if there are no assets, hide all empty asset folders endResetModel(); + + qCInfo(assetsLibraryBenchmark) << "model reset:" << time.elapsed(); } void AssetsLibraryModel::setSearchText(const QString &searchText) diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp index 162fadcf78c..85f7cfc8542 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.cpp @@ -87,7 +87,7 @@ bool AssetsLibraryWidget::eventFilter(QObject *obj, QEvent *event) auto drag = new QDrag(this); drag->setPixmap(m_assetsIconProvider->requestPixmap(m_assetsToDrag[0], nullptr, {128, 128})); QMimeData *mimeData = new QMimeData; - mimeData->setData("application/vnd.bauhaus.libraryresource", m_assetsToDrag.join(',').toUtf8()); + mimeData->setData(Constants::MIME_TYPE_ASSETS, m_assetsToDrag.join(',').toUtf8()); drag->setMimeData(mimeData); drag->exec(); drag->deleteLater(); @@ -213,26 +213,32 @@ void AssetsLibraryWidget::handleAddAsset() addResources({}); } -void AssetsLibraryWidget::handleExtFilesDrop(const QStringList &simpleFilesPaths, - const QStringList &complexFilesPaths, +void AssetsLibraryWidget::handleExtFilesDrop(const QList &simpleFilePaths, + const QList &complexFilePaths, const QString &targetDirPath) { - if (!simpleFilesPaths.isEmpty()) { + auto toLocalFile = [](const QUrl &url) { return url.toLocalFile(); }; + + QStringList simpleFilePathStrings = Utils::transform(simpleFilePaths, toLocalFile); + QStringList complexFilePathStrings = Utils::transform(complexFilePaths, + toLocalFile); + + if (!simpleFilePathStrings.isEmpty()) { if (targetDirPath.isEmpty()) { - addResources(simpleFilesPaths); + addResources(simpleFilePathStrings); } else { - AddFilesResult result = ModelNodeOperations::addFilesToProject(simpleFilesPaths, + AddFilesResult result = ModelNodeOperations::addFilesToProject(simpleFilePathStrings, targetDirPath); if (result == AddFilesResult::Failed) { Core::AsynchronousMessageBox::warning(tr("Failed to Add Files"), tr("Could not add %1 to project.") - .arg(simpleFilesPaths.join(' '))); + .arg(simpleFilePathStrings.join(' '))); } } } - if (!complexFilesPaths.empty()) - addResources(complexFilesPaths); + if (!complexFilePathStrings.empty()) + addResources(complexFilePathStrings); } QSet AssetsLibraryWidget::supportedAssetSuffixes(bool complex) @@ -301,25 +307,25 @@ QPair AssetsLibraryWidget::getAssetTypeAndData(const QStrin if (!suffix.isEmpty()) { if (AssetsLibraryModel::supportedImageSuffixes().contains(suffix)) { // Data: Image format (suffix) - return {"application/vnd.bauhaus.libraryresource.image", suffix.toUtf8()}; + return {Constants::MIME_TYPE_ASSET_IMAGE, suffix.toUtf8()}; } else if (AssetsLibraryModel::supportedFontSuffixes().contains(suffix)) { // Data: Font family name QRawFont font(assetPath, 10); QString fontFamily = font.isValid() ? font.familyName() : ""; - return {"application/vnd.bauhaus.libraryresource.font", fontFamily.toUtf8()}; + return {Constants::MIME_TYPE_ASSET_FONT, fontFamily.toUtf8()}; } else if (AssetsLibraryModel::supportedShaderSuffixes().contains(suffix)) { // Data: shader type, frament (f) or vertex (v) - return {"application/vnd.bauhaus.libraryresource.shader", + return {Constants::MIME_TYPE_ASSET_SHADER, AssetsLibraryModel::supportedFragmentShaderSuffixes().contains(suffix) ? "f" : "v"}; } else if (AssetsLibraryModel::supportedAudioSuffixes().contains(suffix)) { // No extra data for sounds - return {"application/vnd.bauhaus.libraryresource.sound", {}}; + return {Constants::MIME_TYPE_ASSET_SOUND, {}}; } else if (AssetsLibraryModel::supportedVideoSuffixes().contains(suffix)) { // No extra data for videos - return {"application/vnd.bauhaus.libraryresource.video", {}}; + return {Constants::MIME_TYPE_ASSET_VIDEO, {}}; } else if (AssetsLibraryModel::supportedTexture3DSuffixes().contains(suffix)) { // Data: Image format (suffix) - return {"application/vnd.bauhaus.libraryresource.texture3d", suffix.toUtf8()}; + return {Constants::MIME_TYPE_ASSET_TEXTURE3D, suffix.toUtf8()}; } } return {}; diff --git a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h index 3128b823b3c..125755409e9 100644 --- a/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h +++ b/src/plugins/qmldesigner/components/assetslibrary/assetslibrarywidget.h @@ -79,8 +79,8 @@ public: Q_INVOKABLE void startDragAsset(const QStringList &assetPaths, const QPointF &mousePos); Q_INVOKABLE void handleAddAsset(); Q_INVOKABLE void handleSearchfilterChanged(const QString &filterText); - Q_INVOKABLE void handleExtFilesDrop(const QStringList &simpleFilesPaths, - const QStringList &complexFilesPaths, + Q_INVOKABLE void handleExtFilesDrop(const QList &simpleFilePaths, + const QList &complexFilePaths, const QString &targetDirPath = {}); Q_INVOKABLE QSet supportedAssetSuffixes(bool complex); diff --git a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp index cceecbb0b8e..c4040def71a 100644 --- a/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp +++ b/src/plugins/qmldesigner/components/curveeditor/curveeditorview.cpp @@ -214,10 +214,9 @@ QmlTimeline CurveEditorView::activeTimeline() const if (node.hasVariantProperty("enabled") && node.variantProperty("enabled").value().toBool()) return QmlTimeline(node); - - return {}; } } + return {}; } for (const ModelNode &node : allModelNodesOfType("QtQuick.Timeline.Timeline")) { @@ -226,8 +225,9 @@ QmlTimeline CurveEditorView::activeTimeline() const if (!propertyChanges.isValid()) continue; - if (node.hasVariantProperty("enabled") && node.variantProperty("enabled").value().toBool()) - return QmlTimeline(node); + if (propertyChanges.modelNode().hasProperty("enabled") && + propertyChanges.modelNode().variantProperty("enabled").value().toBool()) + return QmlTimeline(node); } } return {}; diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp index 65e1b763e59..422ceaab7bb 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dcanvas.cpp @@ -169,7 +169,7 @@ void Edit3DCanvas::dragEnterEvent(QDragEnterEvent *e) // Allow drop when there is no valid active scene, as the drop goes under the root node of // the document in that case. if (!node.isValid() || !ModelNode::isThisOrAncestorLocked(node)) { - QByteArray data = e->mimeData()->data(QStringLiteral("application/vnd.bauhaus.itemlibraryinfo")); + QByteArray data = e->mimeData()->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); if (!data.isEmpty()) { QDataStream stream(data); stream >> m_itemLibraryEntry; diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp index 2b1f4e9503e..9f24e7d0e9f 100644 --- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp @@ -24,12 +24,12 @@ ****************************************************************************/ #include "abstractformeditortool.h" +#include "assetslibrarywidget.h" +#include "formeditorscene.h" #include "formeditorview.h" #include "formeditorwidget.h" -#include "formeditorscene.h" -#include "assetslibrarywidget.h" - -#include +#include "modelnodecontextmenu.h" +#include "qmldesignerconstants.h" #include #include @@ -236,20 +236,20 @@ void AbstractFormEditorTool::dropEvent(const QList &/*itemList*/ void AbstractFormEditorTool::dragEnterEvent(const QList &itemList, QGraphicsSceneDragDropEvent *event) { bool hasValidAssets = false; - if (event->mimeData()->hasFormat("application/vnd.bauhaus.libraryresource")) { + if (event->mimeData()->hasFormat(Constants::MIME_TYPE_ASSETS)) { const QStringList assetPaths = QString::fromUtf8(event->mimeData() - ->data("application/vnd.bauhaus.libraryresource")).split(","); + ->data(Constants::MIME_TYPE_ASSETS)).split(','); for (const QString &assetPath : assetPaths) { QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; - if (assetType == "application/vnd.bauhaus.libraryresource.image" - || assetType == "application/vnd.bauhaus.libraryresource.font") { + if (assetType == Constants::MIME_TYPE_ASSET_IMAGE + || assetType == Constants::MIME_TYPE_ASSET_FONT) { hasValidAssets = true; break; } } } - if (event->mimeData()->hasFormat(QLatin1String("application/vnd.bauhaus.itemlibraryinfo")) || hasValidAssets) { + if (event->mimeData()->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO) || hasValidAssets) { event->accept(); view()->changeToDragTool(); view()->currentTool()->dragEnterEvent(itemList, event); diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp index b3650aaae0d..f01500b6d46 100644 --- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp +++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp @@ -31,6 +31,7 @@ #include #include #include +#include "qmldesignerconstants.h" #include #include @@ -219,9 +220,7 @@ void DragTool::abort() static ItemLibraryEntry itemLibraryEntryFromMimeData(const QMimeData *mimeData) { - QByteArray data = mimeData->data(QStringLiteral("application/vnd.bauhaus.itemlibraryinfo")); - - QDataStream stream(data); + QDataStream stream(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)); ItemLibraryEntry itemLibraryEntry; stream >> itemLibraryEntry; @@ -236,7 +235,7 @@ static bool canBeDropped(const QMimeData *mimeData) static bool hasItemLibraryInfo(const QMimeData *mimeData) { - return mimeData->hasFormat(QStringLiteral("application/vnd.bauhaus.itemlibraryinfo")); + return mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); } void DragTool::dropEvent(const QList &/*itemList*/, QGraphicsSceneDragDropEvent *event) @@ -326,12 +325,12 @@ void DragTool::createDragNodes(const QMimeData *mimeData, const QPointF &scenePo scenePosition); } else { const QStringList assetPaths = QString::fromUtf8(mimeData - ->data("application/vnd.bauhaus.libraryresource")).split(","); + ->data(Constants::MIME_TYPE_ASSETS)).split(','); for (const QString &assetPath : assetPaths) { QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; - if (assetType == "application/vnd.bauhaus.libraryresource.image") + if (assetType == Constants::MIME_TYPE_ASSET_IMAGE) createQmlItemNodeFromImage(assetPath, targetContainerQmlItemNode, scenePosition); - else if (assetType == "application/vnd.bauhaus.libraryresource.font") + else if (assetType == Constants::MIME_TYPE_ASSET_FONT) createQmlItemNodeFromFont(assetPath, targetContainerQmlItemNode, scenePosition); } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp index b3d67f5c8ed..d1232432524 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp @@ -36,6 +36,7 @@ #include #include #include +#include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include #include @@ -515,9 +516,9 @@ QMimeData *ItemLibraryModel::getMimeData(const ItemLibraryEntry &itemLibraryEntr QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << itemLibraryEntry; - mimeData->setData(QStringLiteral("application/vnd.bauhaus.itemlibraryinfo"), data); + mimeData->setData(Constants::MIME_TYPE_ITEM_LIBRARY_INFO, data); - mimeData->removeFormat(QStringLiteral("text/plain")); + mimeData->removeFormat("text/plain"); return mimeData; } diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp index 5f18557d88b..9f6d33e9349 100644 --- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp +++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp @@ -40,7 +40,6 @@ #include "modelnodeoperations.h" #include #include -#include #include #include #include @@ -89,6 +88,9 @@ static QString propertyEditorResourcesPath() bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) { + auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); + Model *model = document ? document->documentModel() : nullptr; + if (event->type() == QEvent::FocusOut) { if (obj == m_itemsWidget.data()) QMetaObject::invokeMethod(m_itemsWidget->rootObject(), "closeContextMenu"); @@ -115,33 +117,19 @@ bool ItemLibraryWidget::eventFilter(QObject *obj, QEvent *event) } } } - QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator"); - if (view) { - NavigatorWidget *navView = qobject_cast(view); - if (navView) { - navView->setDragType(entry.typeName()); - navView->update(); - } + + if (model) { + model->startDrag(m_itemLibraryModel->getMimeData(entry), + Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath())); } - auto drag = new QDrag(this); - drag->setPixmap(Utils::StyleHelper::dpiSpecificImageFile(entry.libraryEntryIconPath())); - drag->setMimeData(m_itemLibraryModel->getMimeData(entry)); - drag->exec(); - drag->deleteLater(); m_itemToDrag = {}; } } } else if (event->type() == QMouseEvent::MouseButtonRelease) { m_itemToDrag = {}; - QWidget *view = QmlDesignerPlugin::instance()->viewManager().widget("Navigator"); - if (view) { - NavigatorWidget *navView = qobject_cast(view); - if (navView) { - navView->setDragType(""); - navView->update(); - } - } + if (model) + model->endDrag(); } return QObject::eventFilter(obj, event); diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp index 5f4ae027441..138a4aad844 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp @@ -27,6 +27,7 @@ #include "navigatorview.h" #include "navigatorwidget.h" #include "choosefrompropertylistdialog.h" +#include "qmldesignerconstants.h" #include "qmldesignerplugin.h" #include "assetslibrarywidget.h" @@ -71,7 +72,7 @@ namespace QmlDesigner { static QList modelNodesFromMimeData(const QMimeData *mineData, AbstractView *view) { - QByteArray encodedModelNodeData = mineData->data(QLatin1String("application/vnd.modelnode.list")); + QByteArray encodedModelNodeData = mineData->data(Constants::MIME_TYPE_MODELNODE_LIST); QDataStream modelNodeStream(&encodedModelNodeData, QIODevice::ReadOnly); QList modelNodeList; @@ -465,9 +466,9 @@ void NavigatorTreeModel::setView(NavigatorView *view) QStringList NavigatorTreeModel::mimeTypes() const { - const static QStringList types({"application/vnd.modelnode.list", - "application/vnd.bauhaus.itemlibraryinfo", - "application/vnd.bauhaus.libraryresource"}); + const static QStringList types({Constants::MIME_TYPE_MODELNODE_LIST, + Constants::MIME_TYPE_ITEM_LIBRARY_INFO, + Constants::MIME_TYPE_ASSETS}); return types; } @@ -490,7 +491,7 @@ QMimeData *NavigatorTreeModel::mimeData(const QModelIndexList &modelIndexList) c } } - mimeData->setData("application/vnd.modelnode.list", encodedModelNodeData); + mimeData->setData(Constants::MIME_TYPE_MODELNODE_LIST, encodedModelNodeData); return mimeData; } @@ -560,10 +561,10 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, widget->setDragType(""); if (dropModelIndex.model() == this) { - if (mimeData->hasFormat("application/vnd.bauhaus.itemlibraryinfo")) { + if (mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { handleItemLibraryItemDrop(mimeData, rowNumber, dropModelIndex); - } else if (mimeData->hasFormat("application/vnd.bauhaus.libraryresource")) { - const QStringList assetsPaths = QString::fromUtf8(mimeData->data("application/vnd.bauhaus.libraryresource")).split(","); + } else if (mimeData->hasFormat(Constants::MIME_TYPE_ASSETS)) { + const QStringList assetsPaths = QString::fromUtf8(mimeData->data(Constants::MIME_TYPE_ASSETS)).split(','); NodeAbstractProperty targetProperty; const QModelIndex rowModelIndex = dropModelIndex.sibling(dropModelIndex.row(), 0); @@ -579,9 +580,9 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, QSet neededImports; for (const QString &assetPath : assetsPaths) { QString assetType = AssetsLibraryWidget::getAssetTypeAndData(assetPath).first; - if (assetType == "application/vnd.bauhaus.libraryresource.shader") + if (assetType == Constants::MIME_TYPE_ASSET_SHADER) neededImports.insert("QtQuick3D"); - else if (assetType == "application/vnd.bauhaus.libraryresource.sound") + else if (assetType == Constants::MIME_TYPE_ASSET_SOUND) neededImports.insert("QtMultimedia"); if (neededImports.size() == 2) @@ -598,20 +599,20 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, auto assetTypeAndData = AssetsLibraryWidget::getAssetTypeAndData(assetPath); QString assetType = assetTypeAndData.first; QString assetData = QString::fromUtf8(assetTypeAndData.second); - if (assetType == "application/vnd.bauhaus.libraryresource.image") { + if (assetType == Constants::MIME_TYPE_ASSET_IMAGE) { currNode = handleItemLibraryImageDrop(assetPath, targetProperty, rowModelIndex, moveNodesAfter); - } else if (assetType == "application/vnd.bauhaus.libraryresource.font") { + } else if (assetType == Constants::MIME_TYPE_ASSET_FONT) { currNode = handleItemLibraryFontDrop(assetData, // assetData is fontFamily targetProperty, rowModelIndex); - } else if (assetType == "application/vnd.bauhaus.libraryresource.shader") { + } else if (assetType == Constants::MIME_TYPE_ASSET_SHADER) { currNode = handleItemLibraryShaderDrop(assetPath, assetData == "f", targetProperty, rowModelIndex, moveNodesAfter); - } else if (assetType == "application/vnd.bauhaus.libraryresource.sound") { + } else if (assetType == Constants::MIME_TYPE_ASSET_SOUND) { currNode = handleItemLibrarySoundDrop(assetPath, targetProperty, rowModelIndex); - } else if (assetType == "application/vnd.bauhaus.libraryresource.texture3d") { + } else if (assetType == Constants::MIME_TYPE_ASSET_TEXTURE3D) { currNode = handleItemLibraryTexture3dDrop(assetPath, targetProperty, rowModelIndex, moveNodesAfter); } @@ -627,7 +628,7 @@ bool NavigatorTreeModel::dropMimeData(const QMimeData *mimeData, m_view->setSelectedModelNodes(addedNodes); } } - } else if (mimeData->hasFormat("application/vnd.modelnode.list")) { + } else if (mimeData->hasFormat(Constants::MIME_TYPE_MODELNODE_LIST)) { handleInternalDrop(mimeData, rowNumber, dropModelIndex); } } @@ -673,7 +674,7 @@ void NavigatorTreeModel::handleItemLibraryItemDrop(const QMimeData *mimeData, in NodeAbstractProperty targetProperty; const ItemLibraryEntry itemLibraryEntry = - createItemLibraryEntryFromMimeData(mimeData->data("application/vnd.bauhaus.itemlibraryinfo")); + createItemLibraryEntryFromMimeData(mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)); const NodeHints hints = NodeHints::fromItemLibraryEntry(itemLibraryEntry); diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp index 0cf8b01ee98..3adfc91781d 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -57,8 +58,9 @@ #include #include -#include +#include #include +#include static inline void setScenePos(const QmlDesigner::ModelNode &modelNode,const QPointF &pos) { @@ -263,6 +265,25 @@ void NavigatorView::bindingPropertiesChanged(const QList & prop } } +void NavigatorView::dragStarted(QMimeData *mimeData) +{ + if (mimeData->hasFormat(Constants::MIME_TYPE_ITEM_LIBRARY_INFO)) { + QByteArray data = mimeData->data(Constants::MIME_TYPE_ITEM_LIBRARY_INFO); + QDataStream stream(data); + ItemLibraryEntry itemLibraryEntry; + stream >> itemLibraryEntry; + + m_widget->setDragType(itemLibraryEntry.typeName()); + m_widget->update(); + } +} + +void NavigatorView::dragEnded() +{ + m_widget->setDragType(""); + m_widget->update(); +} + void NavigatorView::customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) { diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h index a8a87c00828..ee29e7691ad 100644 --- a/src/plugins/qmldesigner/components/navigator/navigatorview.h +++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h @@ -92,6 +92,9 @@ public: void bindingPropertiesChanged(const QList &propertyList, PropertyChangeFlags) override; + void dragStarted(QMimeData *mimeData) override; + void dragEnded() override; + void customNotification(const AbstractView *view, const QString &identifier, const QList &nodeList, const QList &data) override; void handleChangedExport(const ModelNode &modelNode, bool exported); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp index 135bbca9dfa..c73290bf76a 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorqmlbackend.cpp @@ -294,7 +294,7 @@ void PropertyEditorQmlBackend::createPropertyEditorValue(const QmlObjectNode &qm valueObject = new PropertyEditorValue(&backendValuesPropertyMap()); QObject::connect(valueObject, &PropertyEditorValue::valueChanged, &backendValuesPropertyMap(), &DesignerPropertyMap::valueChanged); QObject::connect(valueObject, &PropertyEditorValue::expressionChanged, propertyEditor, &PropertyEditorView::changeExpression); - QObject::connect(valueObject, &PropertyEditorValue::exportPopertyAsAliasRequested, propertyEditor, &PropertyEditorView::exportPopertyAsAlias); + QObject::connect(valueObject, &PropertyEditorValue::exportPropertyAsAliasRequested, propertyEditor, &PropertyEditorView::exportPropertyAsAlias); QObject::connect(valueObject, &PropertyEditorValue::removeAliasExportRequested, propertyEditor, &PropertyEditorView::removeAliasExport); backendValuesPropertyMap().insert(QString::fromUtf8(propertyName), QVariant::fromValue(valueObject)); } diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp index c37549e074a..c447d6c4a96 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.cpp @@ -367,9 +367,9 @@ void PropertyEditorValue::setEnumeration(const QString &scope, const QString &na setValueWithEmit(QVariant::fromValue(newEnumeration)); } -void PropertyEditorValue::exportPopertyAsAlias() +void PropertyEditorValue::exportPropertyAsAlias() { - emit exportPopertyAsAliasRequested(nameAsQString()); + emit exportPropertyAsAliasRequested(nameAsQString()); } bool PropertyEditorValue::hasPropertyAlias() const diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h index 52de9cbb707..8e29d0ae0ef 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorvalue.h @@ -130,7 +130,7 @@ public: static void registerDeclarativeTypes(); - Q_INVOKABLE void exportPopertyAsAlias(); + Q_INVOKABLE void exportPropertyAsAlias(); Q_INVOKABLE bool hasPropertyAlias() const; Q_INVOKABLE bool isAttachedProperty() const; Q_INVOKABLE void removeAliasExport(); @@ -153,7 +153,7 @@ signals: void valueChangedQml(); void expressionChanged(const QString &name); - void exportPopertyAsAliasRequested(const QString &name); + void exportPropertyAsAliasRequested(const QString &name); void removeAliasExportRequested(const QString &name); void modelStateChanged(); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index 383baee320e..6cf8a4a7a30 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -256,6 +256,9 @@ void PropertyEditorView::changeExpression(const QString &propertyName) if (noValidSelection()) return; + QScopeGuard unlock([&](){ m_locked = false; }); + m_locked = true; + executeInTransaction("PropertyEditorView::changeExpression", [this, name](){ PropertyName underscoreName(name); underscoreName.replace('.', '_'); @@ -317,13 +320,13 @@ void PropertyEditorView::changeExpression(const QString &propertyName) return; } - if (qmlObjectNode->expression(name) != value->expression() || !qmlObjectNode->propertyAffectedByCurrentState(name)) + if (qmlObjectNode->expression(name) != value->expression() + || !qmlObjectNode->propertyAffectedByCurrentState(name)) qmlObjectNode->setBindingProperty(name, value->expression()); - }); /* end of transaction */ } -void PropertyEditorView::exportPopertyAsAlias(const QString &name) +void PropertyEditorView::exportPropertyAsAlias(const QString &name) { if (name.isNull()) return; @@ -334,7 +337,7 @@ void PropertyEditorView::exportPopertyAsAlias(const QString &name) if (noValidSelection()) return; - executeInTransaction("PropertyEditorView::exportPopertyAsAlias", [this, name](){ + executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ const QString id = m_selectedNode.validId(); QString upperCasePropertyName = name; upperCasePropertyName.replace(0, 1, upperCasePropertyName.at(0).toUpper()); @@ -362,7 +365,7 @@ void PropertyEditorView::removeAliasExport(const QString &name) if (noValidSelection()) return; - executeInTransaction("PropertyEditorView::exportPopertyAsAlias", [this, name](){ + executeInTransaction("PropertyEditorView::exportPropertyAsAlias", [this, name](){ const QString id = m_selectedNode.validId(); for (const BindingProperty &property : rootModelNode().bindingProperties()) @@ -692,6 +695,9 @@ void PropertyEditorView::variantPropertiesChanged(const QList& void PropertyEditorView::bindingPropertiesChanged(const QList& propertyList, PropertyChangeFlags /*propertyChange*/) { + if (locked()) + return; + if (noValidSelection()) return; diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h index c6dd85d98f8..06e86fd57cd 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.h @@ -89,7 +89,7 @@ public: void changeValue(const QString &name); void changeExpression(const QString &name); - void exportPopertyAsAlias(const QString &name); + void exportPropertyAsAlias(const QString &name); void removeAliasExport(const QString &name); bool locked() const; diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h index 920689857a5..23273939b49 100644 --- a/src/plugins/qmldesigner/designercore/include/abstractview.h +++ b/src/plugins/qmldesigner/designercore/include/abstractview.h @@ -229,6 +229,9 @@ public: virtual void updateImport3DSupport(const QVariantMap &supportMap); virtual void modelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); + virtual void dragStarted(QMimeData *mimeData); + virtual void dragEnded(); + void changeRootNodeType(const TypeName &type, int majorVersion, int minorVersion); NodeInstanceView *nodeInstanceView() const; diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h index 48700376b9d..d46fbfe4930 100644 --- a/src/plugins/qmldesigner/designercore/include/model.h +++ b/src/plugins/qmldesigner/designercore/include/model.h @@ -129,6 +129,9 @@ public: QString generateNewId(const QString &prefixName) const; QString generateNewId(const QString &prefixName, const QString &fallbackPrefix) const; + void startDrag(QMimeData *mimeData, const QString iconPath = {}); + void endDrag(); + protected: Model(); diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 7cfd89e7602..b22ba232f9b 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -396,6 +396,9 @@ void AbstractView::modelNodePreviewPixmapChanged(const ModelNode & /*node*/, con { } +void AbstractView::dragStarted(QMimeData * /*mimeData*/) {} +void AbstractView::dragEnded() {} + QList AbstractView::toModelNodeList(const QList &nodeList) const { return QmlDesigner::toModelNodeList(nodeList, const_cast(this)); diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp index 64a45e0a894..f4ca504cc50 100644 --- a/src/plugins/qmldesigner/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/designercore/model/model.cpp @@ -67,6 +67,7 @@ #include +#include #include /*! @@ -588,6 +589,16 @@ void ModelPrivate::notifyImport3DSupportChanged(const QVariantMap &supportMap) notifyInstanceChanges([&](AbstractView *view) { view->updateImport3DSupport(supportMap); }); } +void ModelPrivate::notifyDragStarted(QMimeData *mimeData) +{ + notifyInstanceChanges([&](AbstractView *view) { view->dragStarted(mimeData); }); +} + +void ModelPrivate::notifyDragEnded() +{ + notifyInstanceChanges([&](AbstractView *view) { view->dragEnded(); }); +} + void ModelPrivate::notifyRewriterBeginTransaction() { notifyNodeInstanceViewLast([&](AbstractView *view) { view->rewriterBeginTransaction(); }); @@ -1492,6 +1503,22 @@ QString Model::generateNewId(const QString &prefixName, const QString &fallbackP return newId; } +void Model::startDrag(QMimeData *mimeData, const QString iconPath) +{ + d->notifyDragStarted(mimeData); + + auto drag = new QDrag(this); + drag->setPixmap(iconPath); + drag->setMimeData(mimeData); + drag->exec(); + drag->deleteLater(); +} + +void Model::endDrag() +{ + d->notifyDragEnded(); +} + QString Model::generateNewId(const QString &prefixName) const { return generateNewId(prefixName, QStringLiteral("element")); diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h index 43a85858092..09595670534 100644 --- a/src/plugins/qmldesigner/designercore/model/model_p.h +++ b/src/plugins/qmldesigner/designercore/model/model_p.h @@ -183,6 +183,9 @@ public: void notifyModelNodePreviewPixmapChanged(const ModelNode &node, const QPixmap &pixmap); void notifyImport3DSupportChanged(const QVariantMap &supportMap); + void notifyDragStarted(QMimeData *mimeData); + void notifyDragEnded(); + void setDocumentMessages(const QList &errors, const QList &warnings); void notifyRewriterBeginTransaction(); diff --git a/src/plugins/qmldesigner/qmldesignerconstants.h b/src/plugins/qmldesigner/qmldesignerconstants.h index 81b9d8a4a7a..a5c7bbf33de 100644 --- a/src/plugins/qmldesigner/qmldesignerconstants.h +++ b/src/plugins/qmldesigner/qmldesignerconstants.h @@ -85,6 +85,16 @@ const char QUICK_3D_ASSET_IMPORT_DATA_OPTIONS_KEY[] = "import_options"; const char QUICK_3D_ASSET_IMPORT_DATA_SOURCE_KEY[] = "source_scene"; const char DEFAULT_ASSET_IMPORT_FOLDER[] = "/asset_imports"; +const char MIME_TYPE_ITEM_LIBRARY_INFO[] = "application/vnd.qtdesignstudio.itemlibraryinfo"; +const char MIME_TYPE_ASSETS[] = "application/vnd.qtdesignstudio.assets"; +const char MIME_TYPE_ASSET_IMAGE[] = "application/vnd.qtdesignstudio.asset.image"; +const char MIME_TYPE_ASSET_FONT[] = "application/vnd.qtdesignstudio.asset.font"; +const char MIME_TYPE_ASSET_SHADER[] = "application/vnd.qtdesignstudio.asset.shader"; +const char MIME_TYPE_ASSET_SOUND[] = "application/vnd.qtdesignstudio.asset.sound"; +const char MIME_TYPE_ASSET_VIDEO[] = "application/vnd.qtdesignstudio.asset.video"; +const char MIME_TYPE_ASSET_TEXTURE3D[] = "application/vnd.qtdesignstudio.asset.texture3d"; +const char MIME_TYPE_MODELNODE_LIST[] = "application/vnd.qtdesignstudio.modelnode.list"; + // Menus const char M_VIEW_WORKSPACES[] = "QmlDesigner.Menu.View.Workspaces"; diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp index ce54df3d8d5..f385cc74660 100644 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp +++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectfileformat.cpp @@ -91,6 +91,10 @@ QmlProjectItem *QmlProjectFileFormat::parseProjectFile(const Utils::FilePath &fi if (mainFileProperty.isValid()) projectItem->setMainFile(mainFileProperty.value.toString()); + const auto mainUiFileProperty = rootNode->property(QLatin1String("mainUiFile")); + if (mainUiFileProperty.isValid()) + projectItem->setMainUiFile(mainUiFileProperty.value.toString()); + const auto importPathsProperty = rootNode->property(QLatin1String("importPaths")); if (importPathsProperty.isValid()) { QStringList list = importPathsProperty.value.toStringList(); diff --git a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h index 02916555a7c..d437db61140 100644 --- a/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h +++ b/src/plugins/qmlprojectmanager/fileformat/qmlprojectitem.h @@ -81,6 +81,9 @@ public: QString mainFile() const { return m_mainFile; } void setMainFile(const QString &mainFilePath) { m_mainFile = mainFilePath; } + QString mainUiFile() const { return m_mainUiFile; } + void setMainUiFile(const QString &mainUiFilePath) { m_mainUiFile = mainUiFilePath; } + bool widgetApp() const { return m_widgetApp; } void setWidgetApp(bool widgetApp) { m_widgetApp = widgetApp; } @@ -107,6 +110,7 @@ protected: QStringList m_supportedLanguages; QString m_primaryLanguage; QString m_mainFile; + QString m_mainUiFile; Utils::EnvironmentItems m_environment; QVector m_content; // content property bool m_forceFreeType = false; diff --git a/src/plugins/qmlprojectmanager/qmlproject.cpp b/src/plugins/qmlprojectmanager/qmlproject.cpp index 780946e55d5..3c514aca0f9 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.cpp +++ b/src/plugins/qmlprojectmanager/qmlproject.cpp @@ -118,21 +118,38 @@ QmlProject::QmlProject(const Utils::FilePath &fileName) disconnect(m_openFileConnection); if (target && success) { - Utils::FilePaths uiFiles = getUiQmlFilesForFolder(projectDirectory() - + "/content"); - if (uiFiles.isEmpty()) - uiFiles = getUiQmlFilesForFolder(projectDirectory()); - if (!uiFiles.isEmpty()) { - Utils::FilePath currentFile; - if (auto cd = Core::EditorManager::currentDocument()) - currentFile = cd->filePath(); + auto target = activeTarget(); + if (!target) + return; - if (currentFile.isEmpty() || !isKnownFile(currentFile)) - QTimer::singleShot(1000, [uiFiles]() { - Core::EditorManager::openEditor(uiFiles.first(), + auto qmlBuildSystem = qobject_cast( + target->buildSystem()); + + const Utils::FilePath mainUiFile = qmlBuildSystem->mainUiFilePath(); + + if (mainUiFile.completeSuffix() == "qi.qml" && mainUiFile.exists()) { + QTimer::singleShot(1000, [mainUiFile]() { + Core::EditorManager::openEditor(mainUiFile, Utils::Id()); - }); + }); + } else { + Utils::FilePaths uiFiles = getUiQmlFilesForFolder(projectDirectory() + + "/content"); + if (uiFiles.isEmpty()) + uiFiles = getUiQmlFilesForFolder(projectDirectory()); + + if (!uiFiles.isEmpty()) { + Utils::FilePath currentFile; + if (auto cd = Core::EditorManager::currentDocument()) + currentFile = cd->filePath(); + + if (currentFile.isEmpty() || !isKnownFile(currentFile)) + QTimer::singleShot(1000, [uiFiles]() { + Core::EditorManager::openEditor(uiFiles.first(), + Utils::Id()); + }); + } } } }); @@ -236,6 +253,58 @@ void QmlBuildSystem::parseProject(RefreshOptions options) } } +bool QmlBuildSystem::setFileSettingInProjectFile(const QString &setting, const Utils::FilePath &mainFilePath, const QString &oldFile) +{ + // make sure to change it also in the qmlproject file + const Utils::FilePath qmlProjectFilePath = project()->projectFilePath(); + Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath); + const QList editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath); + TextEditor::TextDocument *document = nullptr; + if (!editors.isEmpty()) { + document = qobject_cast(editors.first()->document()); + if (document && document->isModified()) + if (!Core::DocumentManager::saveDocument(document)) + return false; + } + + QString fileContent; + QString error; + Utils::TextFileFormat textFileFormat; + const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 + if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error) + != Utils::TextFileFormat::ReadSuccess) { + qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error; + } + + const QString settingQmlCode = setting + ":"; + + QDir projectDir = project()->projectFilePath().toDir(); + projectDir.cdUp(); + const QString relativePath = projectDir.relativeFilePath(mainFilePath.toString()); + + if (fileContent.indexOf(settingQmlCode) < 0) { + QString addedText = QString("\n %1 \"%2\"\n").arg(settingQmlCode).arg(relativePath); + auto index = fileContent.lastIndexOf("}"); + fileContent.insert(index, addedText); + } else { + QString originalFileName = oldFile; + originalFileName.replace(".", "\\."); + const QRegularExpression expression(QString("%1\\s*\"(%2)\"").arg(settingQmlCode).arg(originalFileName)); + + const QRegularExpressionMatch match = expression.match(fileContent); + + fileContent.replace(match.capturedStart(1), + match.capturedLength(1), + relativePath); + } + + if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error)) + qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error; + + refresh(Everything); + return true; +} + void QmlBuildSystem::refresh(RefreshOptions options) { ParseGuard guard = guardParsingRun(); @@ -266,11 +335,68 @@ QString QmlBuildSystem::mainFile() const return QString(); } +QString QmlBuildSystem::mainUiFile() const +{ + if (m_projectItem) + return m_projectItem->mainUiFile(); + return QString(); +} + Utils::FilePath QmlBuildSystem::mainFilePath() const { return projectDirectory().pathAppended(mainFile()); } +Utils::FilePath QmlBuildSystem::mainUiFilePath() const +{ + return projectDirectory().pathAppended(mainUiFile()); +} + +bool QmlBuildSystem::setMainFileInProjectFile(const Utils::FilePath &newMainFilePath) +{ + + return setFileSettingInProjectFile("mainFile", newMainFilePath, mainFile()); +} + +bool QmlBuildSystem::setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath) +{ + return setMainUiFileInMainFile(newMainUiFilePath) + && setFileSettingInProjectFile("mainUiFile", newMainUiFilePath, mainUiFile()); +} + +bool QmlBuildSystem::setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath) +{ + Core::FileChangeBlocker fileChangeBlocker(mainFilePath()); + const QList editors = Core::DocumentModel::editorsForFilePath(mainFilePath()); + TextEditor::TextDocument *document = nullptr; + if (!editors.isEmpty()) { + document = qobject_cast(editors.first()->document()); + if (document && document->isModified()) + if (!Core::DocumentManager::saveDocument(document)) + return false; + } + + QString fileContent; + QString error; + Utils::TextFileFormat textFileFormat; + const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 + if (Utils::TextFileFormat::readFile(mainFilePath(), codec, &fileContent, &textFileFormat, &error) + != Utils::TextFileFormat::ReadSuccess) { + qWarning() << "Failed to read file" << mainFilePath() << ":" << error; + } + + const QString currentMain = QString("%1 {").arg(mainUiFilePath().baseName()); + const QString newMain = QString("%1 {").arg(newMainUiFilePath.baseName()); + + if (fileContent.contains(currentMain)) + fileContent.replace(currentMain, newMain); + + if (!textFileFormat.writeFile(mainFilePath(), fileContent, &error)) + qWarning() << "Failed to write file" << mainFilePath() << ":" << error; + + return true; +} + bool QmlBuildSystem::qtForMCUs() const { if (m_projectItem) @@ -646,43 +772,10 @@ bool QmlBuildSystem::deleteFiles(Node *context, const FilePaths &filePaths) bool QmlBuildSystem::renameFile(Node * context, const FilePath &oldFilePath, const FilePath &newFilePath) { if (dynamic_cast(context)) { - if (oldFilePath.endsWith(mainFile())) { - setMainFile(newFilePath.toString()); - - // make sure to change it also in the qmlproject file - const Utils::FilePath qmlProjectFilePath = project()->projectFilePath(); - Core::FileChangeBlocker fileChangeBlocker(qmlProjectFilePath); - const QList editors = Core::DocumentModel::editorsForFilePath(qmlProjectFilePath); - TextEditor::TextDocument *document = nullptr; - if (!editors.isEmpty()) { - document = qobject_cast(editors.first()->document()); - if (document && document->isModified()) - if (!Core::DocumentManager::saveDocument(document)) - return false; - } - - QString fileContent; - QString error; - Utils::TextFileFormat textFileFormat; - const QTextCodec *codec = QTextCodec::codecForName("UTF-8"); // qml files are defined to be utf-8 - if (Utils::TextFileFormat::readFile(qmlProjectFilePath, codec, &fileContent, &textFileFormat, &error) - != Utils::TextFileFormat::ReadSuccess) { - qWarning() << "Failed to read file" << qmlProjectFilePath << ":" << error; - } - - // find the mainFile and do the file name with brackets in a capture group and mask the . with \. - QString originalFileName = oldFilePath.fileName(); - originalFileName.replace(".", "\\."); - const QRegularExpression expression(QString("mainFile:\\s*\"(%1)\"").arg(originalFileName)); - const QRegularExpressionMatch match = expression.match(fileContent); - - fileContent.replace(match.capturedStart(1), match.capturedLength(1), newFilePath.fileName()); - - if (!textFileFormat.writeFile(qmlProjectFilePath, fileContent, &error)) - qWarning() << "Failed to write file" << qmlProjectFilePath << ":" << error; - - refresh(Everything); - } + if (oldFilePath.endsWith(mainFile())) + return setMainFileInProjectFile(newFilePath); + if (oldFilePath.endsWith(mainUiFile())) + return setMainUiFileInProjectFile(newFilePath); return true; } diff --git a/src/plugins/qmlprojectmanager/qmlproject.h b/src/plugins/qmlprojectmanager/qmlproject.h index 78a30187f6e..a5ce067be69 100644 --- a/src/plugins/qmlprojectmanager/qmlproject.h +++ b/src/plugins/qmlprojectmanager/qmlproject.h @@ -77,7 +77,13 @@ public: Utils::FilePath canonicalProjectDir() const; QString mainFile() const; + QString mainUiFile() const; Utils::FilePath mainFilePath() const; + Utils::FilePath mainUiFilePath() const; + + bool setMainFileInProjectFile(const Utils::FilePath &newMainFilePath); + bool setMainUiFileInProjectFile(const Utils::FilePath &newMainUiFilePath); + bool setMainUiFileInMainFile(const Utils::FilePath &newMainUiFilePath); bool qtForMCUs() const; bool qt6Project() const; @@ -116,6 +122,10 @@ public: void parseProject(RefreshOptions options); private: + bool setFileSettingInProjectFile(const QString &setting, + const Utils::FilePath &mainFilePath, + const QString &oldFile); + std::unique_ptr m_projectItem; Utils::FilePath m_canonicalProjectDir; bool m_blockFilesUpdate = false; diff --git a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp index 32b9cb1594d..5a7acca5e0d 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectplugin.cpp @@ -30,16 +30,23 @@ #include "qmlprojectrunconfiguration.h" #include "projectfilecontenttools.h" +#include +#include #include #include +#include #include #include #include #include +#include #include +#include +#include #include #include +#include #include @@ -54,11 +61,12 @@ #include #include +#include #include #include +#include #include #include -#include using namespace ProjectExplorer; @@ -230,19 +238,124 @@ void QmlProjectPlugin::openInQDSWithProject(const Utils::FilePath &filePath) } } +static QmlBuildSystem *qmlBuildSystemforFileNode(const FileNode *fileNode) +{ + if (!fileNode) + return nullptr; + + if (QmlProject *qmlProject = qobject_cast(fileNode->getProject())) { + auto target = qmlProject->activeTarget(); + if (!target) + return nullptr; + + return qobject_cast(target->buildSystem()); + + } + + return nullptr; +} + bool QmlProjectPlugin::initialize(const QStringList &, QString *errorMessage) { Q_UNUSED(errorMessage) d = new QmlProjectPluginPrivate; - if (!qmlDesignerEnabled()) { + if (!qmlDesignerEnabled()) initializeQmlLandingPage(); - } ProjectManager::registerProjectType(QmlJSTools::Constants::QMLPROJECT_MIMETYPE); Core::FileIconProvider::registerIconOverlayForSuffix(":/qmlproject/images/qmlproject.png", "qmlproject"); + + if (QmlProject::isQtDesignStudio()) { + Core::ActionContainer *menu = Core::ActionManager::actionContainer( + ProjectExplorer::Constants::M_FILECONTEXT); + QAction *mainfileAction = new QAction(tr("Set as main .qml file"), this); + mainfileAction->setEnabled(false); + + connect(mainfileAction, &QAction::triggered, this, []() { + const Node *currentNode = ProjectTree::currentNode(); + if (!currentNode || !currentNode->asFileNode() + || currentNode->asFileNode()->fileType() != FileType::QML) + return; + + const Utils::FilePath file = currentNode->filePath(); + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(currentNode->asFileNode()); + if (buildSystem) + buildSystem->setMainFileInProjectFile(file); + }); + + menu->addAction(Core::ActionManager::registerAction( + mainfileAction, + "QmlProject.setMainFile", + Core::Context(ProjectExplorer::Constants::C_PROJECT_TREE)), + ProjectExplorer::Constants::G_FILE_OTHER); + mainfileAction->setVisible(false); + connect(ProjectTree::instance(), + &ProjectTree::currentNodeChanged, + mainfileAction, + [mainfileAction](Node *node) { + const FileNode *fileNode = node ? node->asFileNode() : nullptr; + + const bool isVisible = fileNode && fileNode->fileType() == FileType::QML + && fileNode->filePath().completeSuffix() == "qml"; + + mainfileAction->setVisible(isVisible); + + if (!isVisible) + return; + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(fileNode); + + if (buildSystem) + mainfileAction->setEnabled(buildSystem->mainFilePath() + != fileNode->filePath()); + }); + + QAction *mainUifileAction = new QAction(tr("Set as main .ui.qml file"), this); + mainUifileAction->setEnabled(false); + + connect(mainUifileAction, &QAction::triggered, this, []() { + const Node *currentNode = ProjectTree::currentNode(); + if (!currentNode || !currentNode->asFileNode() + || currentNode->asFileNode()->fileType() != FileType::QML) + return; + + const Utils::FilePath file = currentNode->filePath(); + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(currentNode->asFileNode()); + if (buildSystem) + buildSystem->setMainUiFileInProjectFile(file); + }); + + menu->addAction(Core::ActionManager::registerAction( + mainUifileAction, + "QmlProject.setMainUIFile", + Core::Context(ProjectExplorer::Constants::C_PROJECT_TREE)), + ProjectExplorer::Constants::G_FILE_OTHER); + mainUifileAction->setVisible(false); + connect(ProjectTree::instance(), + &ProjectTree::currentNodeChanged, + mainUifileAction, + [mainUifileAction](Node *node) { + const FileNode *fileNode = node ? node->asFileNode() : nullptr; + const bool isVisible = fileNode && fileNode->fileType() == FileType::QML + && fileNode->filePath().completeSuffix() == "ui.qml"; + + mainUifileAction->setVisible(isVisible); + + if (!isVisible) + return; + + QmlBuildSystem *buildSystem = qmlBuildSystemforFileNode(fileNode); + if (buildSystem) + mainUifileAction->setEnabled(buildSystem->mainUiFilePath() + != fileNode->filePath()); + }); + } + return true; } diff --git a/src/plugins/studiowelcome/examplecheckout.cpp b/src/plugins/studiowelcome/examplecheckout.cpp index 86d0bc41fef..46678a21ddb 100644 --- a/src/plugins/studiowelcome/examplecheckout.cpp +++ b/src/plugins/studiowelcome/examplecheckout.cpp @@ -196,17 +196,6 @@ void FileDownloader::probeUrl() }); QNetworkReply::connect(reply, &QNetworkReply::finished, this, [this, reply]() { - QQmlData *data = QQmlData::get(this, false); - if (!data) { - qDebug() << Q_FUNC_INFO << "FileDownloader is nullptr."; - return; - } - - if (QQmlData::wasDeleted(this)) { - qDebug() << Q_FUNC_INFO << "FileDownloader was deleted."; - return; - } - if (reply->error()) return; @@ -383,7 +372,6 @@ void FileExtractor::extract() Utils::Archive *archive = new Utils::Archive(m_sourceFile, m_targetPath); QTC_ASSERT(archive->isValid(), delete archive; return); - archive->setParent(this); m_timer.start(); qint64 bytesBefore = QStorageInfo(m_targetPath.toFileInfo().dir()).bytesAvailable(); @@ -423,7 +411,8 @@ void FileExtractor::extract() emit detailedTextChanged(); }); - QObject::connect(archive, &Utils::Archive::finished, this, [this](bool ret) { + QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) { + delete archive; m_finished = ret; m_timer.stop(); @@ -436,3 +425,76 @@ void FileExtractor::extract() }); archive->unarchive(); } + +static Utils::FilePath tempFilePath() +{ + QStandardPaths::StandardLocation location = QStandardPaths::CacheLocation; + + return Utils::FilePath::fromString(QStandardPaths::writableLocation(location)) + .pathAppended("QtDesignStudio"); +} + +DataModelDownloader::DataModelDownloader(QObject * /* parent */) +{ + auto fileInfo = targetFolder().toFileInfo(); + m_birthTime = fileInfo.birthTime(); + m_exists = fileInfo.exists(); +} + +void DataModelDownloader::start() +{ + m_fileDownloader.setUrl(QUrl::fromUserInput( + "https://download.qt.io/learning/examples/qtdesignstudio/dataImports.zip")); + + connect(&m_fileDownloader, &FileDownloader::availableChanged, this, [this]() { + + m_available = m_fileDownloader.available(); + + emit availableChanged(); + + if (!m_available) { + qWarning() << m_fileDownloader.url() << "failed to download"; + return; + } + + if (!m_forceDownload && m_fileDownloader.lastModified() < m_birthTime) + return; + + m_fileDownloader.start(); + connect(&m_fileDownloader, &FileDownloader::finishedChanged, this, [this]() { + if (m_fileDownloader.finished()) { + const Utils::FilePath archiveFile = Utils::FilePath::fromString( + m_fileDownloader.tempFile()); + QTC_ASSERT(Utils::Archive::supportsFile(archiveFile), return ); + auto archive = new Utils::Archive(archiveFile, tempFilePath()); + QTC_ASSERT(archive->isValid(), delete archive; return ); + QObject::connect(archive, &Utils::Archive::finished, this, [this, archive](bool ret) { + QTC_CHECK(ret); + delete archive; + emit finished(); + }); + archive->unarchive(); + } + }); + }); +} + +bool DataModelDownloader::exists() const +{ + return m_exists; +} + +bool DataModelDownloader::available() const +{ + return m_available; +} + +Utils::FilePath DataModelDownloader::targetFolder() const +{ + return Utils::FilePath::fromUserInput(tempFilePath().toString() + "/" + "dataImports"); +} + +void DataModelDownloader::setForceDownload(bool b) +{ + m_forceDownload = b; +} diff --git a/src/plugins/studiowelcome/examplecheckout.h b/src/plugins/studiowelcome/examplecheckout.h index af2be4893ec..a6c0df3acef 100644 --- a/src/plugins/studiowelcome/examplecheckout.h +++ b/src/plugins/studiowelcome/examplecheckout.h @@ -154,5 +154,29 @@ private: int m_progress = 0; QFile m_tempFile; QDateTime m_lastModified; - bool m_available; + bool m_available = false; +}; + +class DataModelDownloader : public QObject +{ + Q_OBJECT + +public: + explicit DataModelDownloader(QObject *parent = nullptr); + void start(); + bool exists() const; + bool available() const; + Utils::FilePath targetFolder() const; + void setForceDownload(bool b); + +signals: + void finished(); + void availableChanged(); + +private: + FileDownloader m_fileDownloader; + QDateTime m_birthTime; + bool m_exists = false; + bool m_available = false; + bool m_forceDownload = false; }; diff --git a/src/plugins/studiowelcome/studiowelcomeplugin.cpp b/src/plugins/studiowelcome/studiowelcomeplugin.cpp index 800aa3cf494..246ba554188 100644 --- a/src/plugins/studiowelcome/studiowelcomeplugin.cpp +++ b/src/plugins/studiowelcome/studiowelcomeplugin.cpp @@ -447,6 +447,7 @@ public: private: QQuickWidget *m_modeWidget = nullptr; + DataModelDownloader *m_dataModelDownloader = nullptr; }; void StudioWelcomePlugin::closeSplashScreen() @@ -596,6 +597,28 @@ WelcomeMode::WelcomeMode() { setDisplayName(tr("Welcome")); + const QString welcomePagePath = Core::ICore::resourcePath("qmldesigner/welcomepage").toString(); + + m_dataModelDownloader = new DataModelDownloader(this); + if (!m_dataModelDownloader->exists()) { //Fallback if data cannot be downloaded + Utils::FileUtils::copyRecursively(Utils::FilePath::fromUserInput(welcomePagePath + + "/dataImports"), + m_dataModelDownloader->targetFolder()); + m_dataModelDownloader->setForceDownload(true); + } + Utils::FilePath readme = Utils::FilePath::fromUserInput(welcomePagePath + + "/dataImports/readme.txt"); + + if (!readme.exists()) // Only downloads contain the readme + m_dataModelDownloader->setForceDownload(true); + + m_dataModelDownloader->start(); + + connect(m_dataModelDownloader, &DataModelDownloader::finished, this, [this](){ + auto source = m_modeWidget->source(); + m_modeWidget->engine()->clearComponentCache(); + m_modeWidget->setSource(source); + }); const Utils::Icon FLAT({{":/studiowelcome/images/mode_welcome_mask.png", Utils::Theme::IconsBaseColor}}); const Utils::Icon FLAT_ACTIVE({{":/studiowelcome/images/mode_welcome_mask.png", @@ -640,8 +663,8 @@ WelcomeMode::WelcomeMode() m_modeWidget->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources/imports").toString()); - const QString welcomePagePath = Core::ICore::resourcePath("qmldesigner/welcomepage").toString(); m_modeWidget->engine()->addImportPath(welcomePagePath + "/imports"); + m_modeWidget->engine()->addImportPath(m_dataModelDownloader->targetFolder().toString()); m_modeWidget->setSource(QUrl::fromLocalFile(welcomePagePath + "/main.qml")); QShortcut *updateShortcut = nullptr; diff --git a/tests/system/README b/tests/system/README index 9e1a5d4264a..e7775af3a84 100644 --- a/tests/system/README +++ b/tests/system/README @@ -4,29 +4,7 @@ Squish tests inside this folder have several prerequisites to get them running. First - and most important - you have to own a valid Squish license. At least Squish 6.0 is required. -Second - some of the test suites/test cases expect a build of Qt 4.8.7 to be available: -[ this is optional and if Qt4 is not available some Qt5 will be tried to use instead ] - 1. Download the source code from: - * Windows: https://download.qt.io/archive/qt/4.8/4.8.7/qt-everywhere-opensource-src-4.8.7.zip - * Other: https://download.qt.io/archive/qt/4.8/4.8.7/qt-everywhere-opensource-src-4.8.7.tar.gz - 2. Extract the contents of the archive's directory qt-everywhere-opensource-src-4.8.7 to: - * Windows: C:\Qt\Qt4.8.7 - * Other: $HOME/Qt4.8.7 - 3. Apply the changes from patch.txt next to this README. - 4. In the directory you extracted the sources to, configure Qt: - * Windows (MSVC2013 32 bit): - .\configure.exe -opensource -developer-build -confirm-license -debug-and-release -nomake tests -nomake examples -nomake demos -no-webkit -no-phonon - * Linux (gcc < 6): - ./configure -opensource -developer-build -confirm-license -debug-and-release -nomake tests -nomake examples -nomake demos -no-webkit -no-phonon - * macOS: - ./configure -opensource -developer-build -confirm-license -debug-and-release -nomake tests -nomake examples -nomake demos -no-webkit -no-phonon -sdk - 5. Make: - * Windows (do not use jom): - nmake - * Other: - make -j - -Third - some of the test suites/test cases expect Qt versions to be installed in their default +Second - some of the test suites/test cases expect Qt versions to be installed in their default locations. On Linux/macOS this is ~/Qt5.x.1 and on Windows this is C:\Qt\Qt5.x.1. It's easiest to use installations of the official opensource Qt packages. Just install the Qt version for the respective toolchain with the components (if available): @@ -50,7 +28,7 @@ Qt 5.4.1 (gcc) Qt 5.10.1 (MSVC2015, 32 bit) Qt 5.14.1 (MSVC2017, 64 bit) -Fourth - you'll have to provide some additional repositories. +Third - you'll have to provide some additional repositories. These additional repositories are located inside ~/squish-data or C:\Users\\squish-data (depending on the OS you're on). You can also just provide them inside a different folder and specify the folder with the environment variable SYSTEST_SRCPATH. This folder must contain the following: @@ -58,12 +36,12 @@ This folder must contain the following: * a subfolder called 'creator-test-data' * a speedcrunch 0.11 repository (or source copy) inside 'creator-test-data' named 'speedcrunch' -Fifth - you'll have to make sure that some needed tools are available (no matter on which OS you're on). +Fourth - you'll have to make sure that some needed tools are available (no matter on which OS you're on). * cmake 3.14 or newer * wget or curl, capable of HTTPS Normally it should be okay to just install them as usual and add their executables' path(s) to the PATH variable. -Sixth - Qt Creator must be built on a Qt without Qt WebEngine or Qt WebKit. +Fifth - Qt Creator must be built on a Qt without Qt WebEngine or Qt WebKit. On macOS make sure you are using the correct keyboard layout to avoid problems when using keyboard interaction. Tested and known to be working would be 'U.S. International - PC', while pure 'U.S.' had problems. diff --git a/tests/system/settings/mac/QtProject/qtcreator/profiles.xml b/tests/system/settings/mac/QtProject/qtcreator/profiles.xml index cf36fa263fc..51024f82880 100644 --- a/tests/system/settings/mac/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/mac/QtProject/qtcreator/profiles.xml @@ -4,31 +4,6 @@ Profile.0 - - false - - - {70e26273-2c0b-4534-bbc0-eb6ca670821a} - {7c5a3673-e300-4286-9666-0f86d3e3dc38} - GenericLinuxOsType - - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - {461bb8dc-22ff-461f-82fe-ebe8b21b697f} - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - - 2 - - :///DESKTOP/// - {f16848fc-b615-43b5-b0cc-16a9f57fb573} - - Embedded Linux - false - - - - Profile.1 false @@ -55,32 +30,7 @@ - Profile.2 - - false - - - {2f514661-b9f7-4f83-8822-a9a9d0699600} - Desktop Device - Desktop - - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - {461bb8dc-22ff-461f-82fe-ebe8b21b697f} - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - - 2 - - :///DESKTOP/// - {1dcb5509-1670-470d-80a5-8a988f36e4e2} - - Desktop 4.8.7 default - false - - - - Profile.3 + Profile.1 false @@ -108,7 +58,7 @@ Profile.Count - 4 + 2 Profile.Default diff --git a/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml b/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml index 6521f404a1a..340cbe918ea 100644 --- a/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml +++ b/tests/system/settings/mac/QtProject/qtcreator/qtversion.xml @@ -4,16 +4,6 @@ QtVersion.0 - - 2 - Desktop Qt 4.8 for GCC - ~/Qt4.8.7/bin/qmake - Qt4ProjectManager.QtVersion.Desktop - false - - - - QtVersion.1 9 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) @@ -23,7 +13,7 @@ - QtVersion.2 + QtVersion.1 11 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) diff --git a/tests/system/settings/unix/QtProject/qtcreator/profiles.xml b/tests/system/settings/unix/QtProject/qtcreator/profiles.xml index 81f328703d3..54076fa5e1b 100644 --- a/tests/system/settings/unix/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/unix/QtProject/qtcreator/profiles.xml @@ -4,52 +4,6 @@ Profile.0 - - false - - - {70e26273-2c0b-4534-bbc0-eb6ca670821a} - {7c5a3673-e300-4286-9666-0f86d3e3dc38} - GenericLinuxOsType - - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - {7bfd4fd4-e64a-417f-b10f-20602e1719d1} - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - - 2 - - :///DESKTOP/// - {f16848fc-b615-43b5-b0cc-16a9f57fb573} - - Embedded Linux - false - - - - Profile.1 - - false - - - {70e26273-2c0b-4534-bbc0-eb6ca670821a} - Desktop Device - Desktop - - ProjectExplorer.ToolChain.Gcc:{c3f59b87-6997-4bd8-8067-ee04dc536371} - - 2 - - :///DESKTOP/// - {1dcb5509-1670-470d-80a5-8a988f36e4e2} - - Desktop 4.8.7 default - false - - - - Profile.2 false @@ -73,7 +27,7 @@ - Profile.3 + Profile.1 false @@ -97,7 +51,7 @@ - Profile.4 + Profile.2 false @@ -126,7 +80,7 @@ Profile.Count - 5 + 3 Profile.Default diff --git a/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml b/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml index cb208914864..829b5c07edb 100644 --- a/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml +++ b/tests/system/settings/unix/QtProject/qtcreator/qtversion.xml @@ -4,16 +4,6 @@ QtVersion.0 - - 2 - Desktop Qt 4.8 for GCC - ~/Qt4.8.7/bin/qmake - Qt4ProjectManager.QtVersion.Desktop - false - - - - QtVersion.1 13 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) @@ -23,7 +13,7 @@ - QtVersion.2 + QtVersion.1 15 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) @@ -33,7 +23,7 @@ - QtVersion.3 + QtVersion.2 17 Qt %{Qt:Version} (SQUISH_DEFAULT_COMPILER) diff --git a/tests/system/settings/windows/QtProject/qtcreator/profiles.xml b/tests/system/settings/windows/QtProject/qtcreator/profiles.xml index fab39649776..3bd4752b3dc 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/profiles.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/profiles.xml @@ -4,34 +4,6 @@ Profile.0 - - false - - - - {1b25f20a-d584-4fb7-85b3-74dd15b82f6f} - Desktop Device - Desktop - - - {7ca0887f-a9a5-4251-aba6-560a15595d20} - - {d35e7a1a-5ab8-4fd6-8a2c-634846c669bb} - {7ca0887f-a9a5-4251-aba6-560a15595d20} - - - 2 - - :///DESKTOP/// - {9b35bbe6-25a7-4cce-ba07-487c795f5265} - - Desktop 4.8.7 default - false - - - - - Profile.1 false @@ -59,7 +31,7 @@ - Profile.2 + Profile.1 false @@ -70,8 +42,7 @@ Desktop - {7ca0887f-a9a5-4251-aba6-560a15595d20} - + {ce3a8004-e9ae-46f2-b62d-d7daf69435ca} {3df7c776-a480-4a04-9099-6c75adac2dca} @@ -87,7 +58,7 @@ - Profile.3 + Profile.2 false @@ -116,7 +87,7 @@ Profile.Count - 4 + 3 Profile.Default diff --git a/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml b/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml index 2939316b75f..499c44893f7 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/qtversion.xml @@ -4,16 +4,6 @@ QtVersion.0 - - 2 - Qt 4.8 for Desktop - MSVC2013 - C:/Qt/Qt4.8.7/bin/qmake.exe - Qt4ProjectManager.QtVersion.Desktop - false - - - - QtVersion.1 22 Qt %{Qt:Version} (mingw491_32) @@ -23,7 +13,7 @@ - QtVersion.2 + QtVersion.1 24 Qt %{Qt:Version} (msvc2017_64) @@ -33,7 +23,7 @@ - QtVersion.3 + QtVersion.2 26 Qt %{Qt:Version} (msvc2015) diff --git a/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml b/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml index 49518d2d28b..a5d80d7dcbf 100644 --- a/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml +++ b/tests/system/settings/windows/QtProject/qtcreator/toolchains.xml @@ -19,18 +19,6 @@ ToolChain.1 - - x86-windows-msvc2013-pe-32bit - C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat - x86 - true - Microsoft Visual C++ Compiler 12.0 (x86) - ProjectExplorer.ToolChain.Msvc:{d35e7a1a-5ab8-4fd6-8a2c-634846c669bb} - 1 - - - - ToolChain.2 i686-w64-mingw32 C:/Qt/Qt5.4.1/Tools/mingw491_32/bin/gcc.exe @@ -47,12 +35,18 @@ - ToolChain.3 + ToolChain.2 x86-windows-msvc2017-pe-64bit C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Auxiliary/Build/vcvarsall.bat amd64 - false + + + PATH + 0 + SQUISH_MSVC2017_PATH + + MSVC2017 (amd64) ProjectExplorer.ToolChain.Msvc:{ce3a8004-e9ae-46f2-b62d-d7daf69435ca} 1 @@ -60,7 +54,7 @@ - ToolChain.4 + ToolChain.3 x86-windows-msvc2015-pe-32bit C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat @@ -73,7 +67,7 @@ - ToolChain.5 + ToolChain.4 x86-windows-msvc2015-pe-32bit C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/vcvarsall.bat @@ -86,23 +80,18 @@ - ToolChain.6 - - x86-windows-msvc2013-pe-32bit - C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat - x86 - true - Microsoft Visual C++ Compiler 12.0 (x86) - ProjectExplorer.ToolChain.Msvc:{7ca0887f-a9a5-4251-aba6-560a15595d20} - - - - ToolChain.7 + ToolChain.5 x86-windows-msvc2017-pe-64bit C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Auxiliary/Build/vcvarsall.bat amd64 - false + + + PATH + 0 + SQUISH_MSVC2017_PATH + + MSVC2017 (amd64) ProjectExplorer.ToolChain.Msvc:{3df7c776-a480-4a04-9099-6c75adac2dca} 2 @@ -111,7 +100,7 @@ ToolChain.Count - 8 + 6 Version diff --git a/tests/system/shared/build_utils.py b/tests/system/shared/build_utils.py index a58a1fa93d7..b90561fedf6 100644 --- a/tests/system/shared/build_utils.py +++ b/tests/system/shared/build_utils.py @@ -190,13 +190,12 @@ def verifyBuildConfig(currentTarget, configName, shouldBeDebug=False, enableShad # it will wait here until compilation of the debug libraries has finished. runCMakeButton = ("{type='QPushButton' text='Run CMake' unnamed='1' " "window=':Qt Creator_Core::Internal::MainWindow'}") - if currentTarget not in (Targets.DESKTOP_4_8_7_DEFAULT, Targets.EMBEDDED_LINUX): - qmlDebuggingCombo = findObject(':Qt Creator.QML debugging and profiling:_QComboBox') - if selectFromCombo(qmlDebuggingCombo, 'Enable'): - if buildSystem is None or buildSystem == "CMake": # re-run cmake to apply - clickButton(waitForObject(runCMakeButton)) - elif buildSystem == "qmake": # Don't rebuild now - clickButton(waitForObject(":QML Debugging.No_QPushButton", 5000)) + qmlDebuggingCombo = findObject(':Qt Creator.QML debugging and profiling:_QComboBox') + if selectFromCombo(qmlDebuggingCombo, 'Enable'): + if buildSystem is None or buildSystem == "CMake": # re-run cmake to apply + clickButton(waitForObject(runCMakeButton)) + elif buildSystem == "qmake": # Don't rebuild now + clickButton(waitForObject(":QML Debugging.No_QPushButton", 5000)) try: problemFound = waitForObject("{window=':Qt Creator_Core::Internal::MainWindow' " "type='QLabel' name='problemLabel' visible='1'}", 1000) @@ -205,14 +204,13 @@ def verifyBuildConfig(currentTarget, configName, shouldBeDebug=False, enableShad except: pass else: - if currentTarget not in (Targets.DESKTOP_4_8_7_DEFAULT, Targets.EMBEDDED_LINUX): - qmlDebuggingCombo = findObject(':Qt Creator.QML debugging and profiling:_QComboBox') - if selectFromCombo(qmlDebuggingCombo, "Disable"): - test.log("Qml debugging libraries are available - unchecked qml debugging.") - if buildSystem is None or buildSystem == "CMake": # re-run cmake to apply - clickButton(waitForObject(runCMakeButton)) - elif buildSystem == "qmake": # Don't rebuild now - clickButton(waitForObject(":QML Debugging.No_QPushButton", 5000)) + qmlDebuggingCombo = findObject(':Qt Creator.QML debugging and profiling:_QComboBox') + if selectFromCombo(qmlDebuggingCombo, "Disable"): + test.log("Qml debugging libraries are available - unchecked qml debugging.") + if buildSystem is None or buildSystem == "CMake": # re-run cmake to apply + clickButton(waitForObject(runCMakeButton)) + elif buildSystem == "qmake": # Don't rebuild now + clickButton(waitForObject(":QML Debugging.No_QPushButton", 5000)) clickButton(waitForObject(":scrollArea.Details_Utils::DetailsButton")) switchViewTo(ViewConstants.EDIT) diff --git a/tests/system/shared/classes.py b/tests/system/shared/classes.py index efe60daa701..9ee95e2de48 100644 --- a/tests/system/shared/classes.py +++ b/tests/system/shared/classes.py @@ -30,37 +30,27 @@ except ImportError: # for easier re-usage (because Python hasn't an enum type) class Targets: - ALL_TARGETS = tuple(range(5)) + ALL_TARGETS = tuple(range(3)) - (DESKTOP_4_8_7_DEFAULT, - EMBEDDED_LINUX, - DESKTOP_5_4_1_GCC, + (DESKTOP_5_4_1_GCC, DESKTOP_5_10_1_DEFAULT, DESKTOP_5_14_1_DEFAULT) = ALL_TARGETS __TARGET_NAME_DICT__ = dict(zip(ALL_TARGETS, - ["Desktop 4.8.7 default", - "Embedded Linux", - "Desktop 5.4.1 GCC", + ["Desktop 5.4.1 GCC", "Desktop 5.10.1 default", "Desktop 5.14.1 default"])) @staticmethod def availableTargetClasses(ignoreValidity=False): availableTargets = set(Targets.ALL_TARGETS) - if not qt4Available and not ignoreValidity: - availableTargets.remove(Targets.DESKTOP_4_8_7_DEFAULT) - if not (qt4Available or ignoreValidity) or platform.system() in ('Windows', 'Microsoft'): - availableTargets.remove(Targets.EMBEDDED_LINUX) - elif platform.system() == 'Darwin': + if platform.system() == 'Darwin': availableTargets.remove(Targets.DESKTOP_5_4_1_GCC) return availableTargets @staticmethod def desktopTargetClasses(): - desktopTargets = Targets.availableTargetClasses() - desktopTargets.discard(Targets.EMBEDDED_LINUX) - return desktopTargets + return Targets.availableTargetClasses() @staticmethod def getStringForTarget(target): diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index 4774bca9a5b..17d0580d016 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -161,11 +161,8 @@ def __createProjectHandleQtQuickSelection__(minimumQtVersion): # param buildSystem is a string holding the build system selected for the project # param checks turns tests in the function on if set to True # param available a list holding the available targets -# withoutQt4 if True Qt4 will get unchecked / not selected while checking the targets -def __selectQtVersionDesktop__(buildSystem, checks, available=None, withoutQt4=False): +def __selectQtVersionDesktop__(buildSystem, checks, available=None): wanted = Targets.desktopTargetClasses() - if withoutQt4: - wanted.discard(Targets.DESKTOP_4_8_7_DEFAULT) checkedTargets = __chooseTargets__(wanted, available) if checks: for target in checkedTargets: @@ -183,9 +180,8 @@ def __selectQtVersionDesktop__(buildSystem, checks, available=None, withoutQt4=F objectMap.realName(detailsWidget))) verifyChecked(cbObject % ("Minimum Size Release", objectMap.realName(detailsWidget))) - elif (buildSystem == "qmake" - and target not in (Targets.DESKTOP_4_8_7_DEFAULT, Targets.EMBEDDED_LINUX)): - verifyChecked(cbObject % ("Profile", objectMap.realName(detailsWidget))) + elif buildSystem == "qmake": + verifyChecked(cbObject % ("Profile", objectMap.realName(detailsWidget))) clickButton(detailsButton) clickButton(waitForObject(":Next_QPushButton")) @@ -207,11 +203,6 @@ def __verifyFileCreation__(path, expectedFiles): def __modifyAvailableTargets__(available, requiredQt, asStrings=False): versionFinder = re.compile("^Desktop (\\d{1}\.\\d{1,2}\.\\d{1,2}).*$") tmp = list(available) # we need a deep copy - if Qt5Path.toVersionTuple(requiredQt) > (4,8,7) and qt4Available: - toBeRemoved = Targets.EMBEDDED_LINUX - if asStrings: - toBeRemoved = Targets.getStringForTarget(toBeRemoved) - available.discard(toBeRemoved) for currentItem in tmp: if asStrings: item = currentItem @@ -260,7 +251,7 @@ def createProject_Qt_GUI(path, projectName, checks=True, addToVersionControl="