diff --git a/.clang-format b/.clang-format index 97f7f2b2347..e1dad0fa0c0 100644 --- a/.clang-format +++ b/.clang-format @@ -1,19 +1,3 @@ -# .clang-format for Qt Creator -# -# This is for clang-format >= 5.0. -# -# The configuration below follows the Qt Creator Coding Rules [1] as closely as -# possible. For documentation of the options, see [2]. -# -# Use ../../tests/manual/clang-format-for-qtc/test.cpp for documenting problems -# or testing changes. -# -# In case you update this configuration please also update the qtcStyle() in src\plugins\clangformat\clangformatutils.cpp -# -# [1] https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html -# [2] https://clang.llvm.org/docs/ClangFormatStyleOptions.html -# ---- Language: Cpp AccessModifierOffset: -4 AlignAfterOpenBracket: Align @@ -90,12 +74,12 @@ NamespaceIndentation: None ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true -PenaltyBreakAssignment: 150 -PenaltyBreakBeforeFirstCallParameter: 300 +PenaltyBreakAssignment: 500 +PenaltyBreakBeforeFirstCallParameter: 150 PenaltyBreakComment: 500 PenaltyBreakFirstLessLess: 400 PenaltyBreakString: 600 -PenaltyExcessCharacter: 50 +PenaltyExcessCharacter: 7 PenaltyReturnTypeOnItsOwnLine: 300 PointerAlignment: Right ReflowComments: false diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index 1bbb6f82f43..57826d6d91f 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -5,6 +5,7 @@ on: [push, pull_request] env: QT_VERSION: 5.15.0 CLANG_VERSION: 100 + ELFUTILS_VERSION: 0.175 CMAKE_VERSION: 3.17.0 NINJA_VERSION: 1.10.0 BUILD_TYPE: Release @@ -46,7 +47,10 @@ jobs: steps: - uses: actions/checkout@v2 - name: Checkout submodules - run: git submodule update --init src/plugins/help/qlitehtml/litehtml + run: | + git submodule set-url -- perfparser https://code.qt.io/qt-creator/perfparser.git + git submodule update --init src/plugins/help/qlitehtml/litehtml + git submodule update --init src/tools/perfparser - name: Download Ninja and CMake id: cmake_and_ninja @@ -261,6 +265,32 @@ jobs: file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/libclang" libclang_dir) message("::set-output name=libclang_dir::${libclang_dir}") + - name: Download elfutils + if: runner.os == 'Windows' && matrix.config.is_msvc || runner.os == 'Linux' + id: elfutils + shell: cmake -P {0} + run: | + set(elfutils_version "$ENV{ELFUTILS_VERSION}") + + if ("${{ runner.os }}" STREQUAL "Windows") + if ("${{ matrix.config.environment_script }}" MATCHES "vcvars64.bat") + set(elfutils "elfutils-release_${elfutils_version}qt-windows-x86_64.7z") + elseif ("${{ matrix.config.environment_script }}" MATCHES "vcvars32.bat") + set(elfutils "elfutils-release_${elfutils_version}qt-windows-i686.7z") + endif() + elseif ("${{ runner.os }}" STREQUAL "Linux") + set(elfutils "elfutils-release_${elfutils_version}qt-linux-x86_64.7z") + endif() + + set(elfutils_url "https://download.qt.io/development_releases/prebuilt/elfutils/${elfutils}") + file(DOWNLOAD "${elfutils_url}" ./elfutils.7z SHOW_PROGRESS) + file(MAKE_DIRECTORY elfutils) + execute_process(COMMAND ${CMAKE_COMMAND} -E tar xvf ../elfutils.7z WORKING_DIRECTORY elfutils) + + # Save the path for other steps + file(TO_CMAKE_PATH "$ENV{GITHUB_WORKSPACE}/elfutils" elfutils_dir) + message("::set-output name=elfutils_dir::${elfutils_dir}") + - name: Download ccache id: ccache shell: cmake -P {0} @@ -359,6 +389,12 @@ jobs: set(CDB_OPTION) endif() + if (NOT "${{ steps.elfutils.outputs.elfutils_dir }}" STREQUAL "") + set(ELFUTILS_OPTION "--elfutils-path;${{ steps.elfutils.outputs.elfutils_dir }}") + else() + set(ELFUTILS_OPTION) + endif() + execute_process( COMMAND python -u @@ -369,6 +405,7 @@ jobs: --llvm-path "${{ steps.libclang.outputs.libclang_dir }}" --with-tests ${CDB_OPTION} + ${ELFUTILS_OPTION} --add-config=-DCMAKE_C_COMPILER_LAUNCHER=ccache --add-config=-DCMAKE_CXX_COMPILER_LAUNCHER=ccache --add-config=-DIDE_REVISION_URL=https://github.com/$ENV{GITHUB_REPOSITORY}/commits/$ENV{GITHUB_SHA} diff --git a/cmake/QtCreatorAPIInternal.cmake b/cmake/QtCreatorAPIInternal.cmake index ea03aecdbfb..6be35cca1cb 100644 --- a/cmake/QtCreatorAPIInternal.cmake +++ b/cmake/QtCreatorAPIInternal.cmake @@ -349,6 +349,11 @@ function(enable_pch target) file(GENERATE OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/empty_pch.cpp CONTENT "/*empty file*/") + set_source_files_properties( + ${CMAKE_CURRENT_BINARY_DIR}/empty_pch.c + ${CMAKE_CURRENT_BINARY_DIR}/empty_pch.cpp + PROPERTIES GENERATED TRUE) + _add_pch_target(QtCreatorPchGui "${PROJECT_SOURCE_DIR}/src/shared/qtcreator_gui_pch.h" Qt5::Widgets) _add_pch_target(QtCreatorPchConsole diff --git a/cmake/QtCreatorTranslations.cmake b/cmake/QtCreatorTranslations.cmake index dd0dae68758..a674f37be6d 100644 --- a/cmake/QtCreatorTranslations.cmake +++ b/cmake/QtCreatorTranslations.cmake @@ -138,6 +138,8 @@ function(add_translation_targets file_prefix) add_custom_target("${_arg_ALL_QM_TARGET}" ALL COMMENT "Generate .qm-files") endif() + file(MAKE_DIRECTORY ${_arg_OUTPUT_DIRECTORY}) + foreach(l IN ITEMS ${_arg_LANGUAGES}) set(_ts_file "${CMAKE_CURRENT_SOURCE_DIR}/${file_prefix}_${l}.ts") set(_qm_file "${_arg_OUTPUT_DIRECTORY}/${file_prefix}_${l}.qm") diff --git a/dist/changes-4.13.0.md b/dist/changes-4.13.0.md new file mode 100644 index 00000000000..b526ee54a38 --- /dev/null +++ b/dist/changes-4.13.0.md @@ -0,0 +1,227 @@ +Qt Creator 4.13 +=============== + +Qt Creator version 4.13 contains bug fixes and new features. + +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/4.12..v4.13.0 + +General +------- + +* Added experimental support for Meson projects +* Added experimental support for IncrediBuild +* Moved view related menu items to separate toplevel `View` menu (QTCREATORBUG-23610) +* Added basic `Install Plugin` wizard to `About Plugins` dialog +* Added support for multiple shortcuts per action (QTCREATORBUG-72) +* Added sections to marketplace browser (QTCREATORBUG-23808) + +Help +---- + +* Adapted to new filter engine in Qt 5.15 + +Editing +------- + +* Added option for excluding file patterns from whitespace cleanup (QTCREATORBUG-13358) +* Fixed issue with resolving highlighting definitions (QTCREATORBUG-7906) + +### C++ + +* Updated to LLVM 10 +* Added editor tool button for `Analyze File` (QTCREATORBUG-23348) +* Added `Add forward declaration` refactoring action (QTCREATORBUG-23444) +* Extended `Add Include` refactoring action to non-Qt classes (QTCREATORBUG-21) +* Fixed indentation with C++11 list initialization (QTCREATORBUG-16977, QTCREATORBUG-24035) +* Fixed indentation with trailing return types (QTCREATORBUG-23502) +* Fixed issue with `std::chrono::time_point` (QTCREATORBUG-24067) +* Fixed detection of `noexcept` as change of function signature (QTCREATORBUG-23895) +* Fixed `Extract Function` with namespaces (QTCREATORBUG-23256) +* Fixed `Find Usages` for `shared_ptr` with MSVC (QTCREATORBUG-7866) +* Fixed completion for `std::pair` with MSVC +* Fixed issues with anonymous enums (QTCREATORBUG-7487) +* Fixed that inaccessible members were offered in completion (QTCREATORBUG-1984) +* Fixed refactoring for operators (QTCREATORBUG-6236) +* Fixed issues with resolving overloads in presence of default arguments (QTCREATORBUG-17807) +* Fixed sorting in completion (QTCREATORBUG-6242) +* Fixed that find usages was finding function arguments when searching functions (QTCREATORBUG-2176) +* Fixed crash in lexer (QTCREATORBUG-19525) + +### Language Client + +* Added support for renaming symbols (QTCREATORBUG-21578) +* Added highlighting of code range for diagnostics +* Added tooltips for diagnostics +* Fixed various issues with completion, outline, files with spaces, and synchronization + +### QML + +* Added support for moving functions in outline (QTCREATORBUG-21993) +* Fixed issues with Qt 5.15 imports +* Fixed updating of outline (QTCREATORBUG-21335) +* Fixed resolution of easing curve (QTCREATORBUG-24142) + +### Python + +* Added tool button for opening interactive Python, optionally importing current file + +### Diff Viewer + +* Added option to select encoding (QTCREATORBUG-23835) + +### Model Editor + +* Improved intersection computation + +### QRC + +* Added option to sort file list + +Projects +-------- + +* Improved responsiveness of project loading (QTCREATORBUG-18533) +* Improved handling of custom project settings when a kit is removed (QTCREATORBUG-23023) +* Improved editing of path list entries in environment settings (QTCREATORBUG-20965) +* Unified compile and application output parsing (QTCREATORBUG-22665) +* Added option to remove similar files when removing a file (QTCREATORBUG-23869) +* Added support for custom output parsers (QTCREATORBUG-23993) +* Fixed responsiveness with large messages in `Compile Output` (QTCREATORBUG-23944) +* Fixed issue with macros at the start of paths (QTCREATORBUG-24095) + +### Wizards + +* Added heuristics for finding header to include for non-standard base class (QTCREATORBUG-3855) +* Added information about default suffix for file wizards (QTCREATORBUG-23621) + +### CMake + +* Removed internal cache of CMake configuration options, which removes conflicts between internal + cache and existing configuration in build directory (QTCREATORBUG-23218) +* Removed support for server-mode (CMake < 3.14) (QTCREATORBUG-23915) +* Removed defaulting to `CodeBlocks` extra generator +* Added option to provide arguments for initial CMake configuration (QTCREATORBUG-16296, + QTCREATORBUG-18179) +* Added option to provide arguments to `cmake --build` (QTCREATORBUG-24088) +* Added option to build several targets simultaneously +* Fixed that special build targets were missing from target list (QTCREATORBUG-24064) +* Fixed that triggering a build did not ask for applying changed CMake configuration + (QTCREATORBUG-18504) + +### Compilation Database + +* Fixed that `-fpic` was removed from compiler commands (QTCREATORBUG-24106) + +Debugging +--------- + +* Added option to reset all formats for watches to default + +Analyzer +-------- + +### Clang + +* Re-added editor text marks for diagnostics (QTCREATORBUG-23349) +* Changed to use separate `clazy-standalone` executable +* Fixed issue with `clang-tidy` and `WarningsAsErrors` (QTCREATORBUG-23423) + +Version Control Systems +----------------------- + +### Git + +* Added option to open `git-bash` (Windows, `Tools` > `Git` > `Git Bash`) +* Added colors to log (QTCREATORBUG-19624) + +Test Integration +---------------- + +* Added support for Catch test framework (QTCREATORBUG-19740) + +### Google Test + +Code Pasting +------------ + +* Added option for public or private pastes (QTCREATORBUG-23972) + +Platforms +--------- + +### Linux + +* Fixed issues with memory not being released (QTCREATORBUG-22832) + +### Android + +* Removed Ministro deployment option (QTCREATORBUG-23761) +* Added service editor to manifest editor (QTCREATORBUG-23937) +* Added splash screen editor to manifest editor (QTCREATORBUG-24011, QTCREATORBUG-24013) +* Fixed debugging on x86 and armv7 architectures (QTCREATORBUG-24191) +* Fixed issue with long kit names (QTBUG-83875) + +### Bare Metal + +* Added support for peripheral registers view using UVSC provider +* Added support for Keil C251 toolchain +* Added support for Keil C166 toolchain +* Added support for RISC-V architecture +* Added support for various Renesas architectures + +Credits for these changes go to: +-------------------------------- +Aleksei German +Alessandro Portale +Alexis Jeandet +Alexis Murzeau +Andre Hartmann +André Pönitz +Assam Boudjelthia +Christian Kamm +Christian Kandeler +Christian Stenger +Cristian Adam +David Schulz +Denis Shienkov +Eike Ziller +Federico Guerinoni +Henning Gruendl +Ivan Komissarov +Jaroslaw Kobus +Jochen Becher +Jochen Seemann +Johanna Vanhatapio +Joni Poikelin +Junker, Gregory +Knud Dollereder +Lars Knoll +Leena Miettinen +Mahmoud Badri +Marco Bubke +Michael Brüning +Michael Winkelmann +Miikka Heikkinen +Mitch Curtis +Nikolai Kosjar +Oliver Wolff +Or Kunst +Orgad Shaneh +Philip Van Hoof +Robert Löhning +Tarja Sundqvist +Thiago Macieira +Thomas Hartmann +Tim Jenssen +Tobias Hunger +Unseon Ryu +Viacheslav Tertychnyi +Ville Nummela +Ville Voutilainen +Volodymyr Zibarov +Yuya Nishihara diff --git a/doc/config/macros.qdocconf b/doc/config/macros.qdocconf index 88a92778d63..3b2970724b5 100644 --- a/doc/config/macros.qdocconf +++ b/doc/config/macros.qdocconf @@ -16,6 +16,7 @@ macro.macos = "macOS" macro.note = "\\b{Note:}" macro.oslash.HTML = "ø" macro.ouml.HTML = "ö" +macro.Q3DS = "Qt 3D Studio" macro.QA = "Qt Assistant" macro.QB = "Qt Bridge" macro.QBPS = "Qt Bridge for Adobe Photoshop" diff --git a/doc/qtcreator/src/howto/creator-ui.qdoc b/doc/qtcreator/src/howto/creator-ui.qdoc index eeef2601a40..7b1bd855c0e 100644 --- a/doc/qtcreator/src/howto/creator-ui.qdoc +++ b/doc/qtcreator/src/howto/creator-ui.qdoc @@ -32,7 +32,7 @@ /*! \page creator-quick-tour.html \if defined(qtdesignstudio) - \previouspage exporting-from-maya.html + \previouspage exporting-from-qt3ds.html \else \previouspage creator-overview.html \endif diff --git a/doc/qtcreator/src/qtcreator-toc.qdoc b/doc/qtcreator/src/qtcreator-toc.qdoc index 2fde6213caa..e3b1a0da861 100644 --- a/doc/qtcreator/src/qtcreator-toc.qdoc +++ b/doc/qtcreator/src/qtcreator-toc.qdoc @@ -256,6 +256,7 @@ \li \l{Setting Up an Autotools Project} \li \l{Setting Up a Generic Project} \li \l{Setting Up Nimble} + \li \l{Setting Up Meson} \endlist \li \l{Using Command Line Options} \li \l{Keyboard Shortcuts} diff --git a/doc/qtdesignstudio/config/qtdesignstudio.qdocconf b/doc/qtdesignstudio/config/qtdesignstudio.qdocconf index d99c57c6098..7c74d2665e2 100644 --- a/doc/qtdesignstudio/config/qtdesignstudio.qdocconf +++ b/doc/qtdesignstudio/config/qtdesignstudio.qdocconf @@ -44,6 +44,7 @@ excludedirs += ../../qtcreator/examples/accelbubble \ ../../qtcreator/src/ios \ ../../qtcreator/src/linux-mobile \ ../../qtcreator/src/mcu \ + ../../qtcreator/src/meson \ ../../qtcreator/src/overview/creator-only \ ../../qtcreator/src/projects \ ../../qtcreator/src/qnx \ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/01-welcome-screen.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/01-welcome-screen.png new file mode 100644 index 00000000000..9771de9c127 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/01-welcome-screen.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/02-create-new-project.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/02-create-new-project.png new file mode 100644 index 00000000000..0ec116bf71d Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/02-create-new-project.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/03-folder-and-project-name.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/03-folder-and-project-name.png new file mode 100644 index 00000000000..00ee4bce79e Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/03-folder-and-project-name.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/04-project-size.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/04-project-size.png new file mode 100644 index 00000000000..b0e286d72b9 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/04-project-size.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/05-new-project-created.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/05-new-project-created.png new file mode 100644 index 00000000000..73c7e5aec53 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/05-new-project-created.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/06-check-screen-size-in-qt3ds-project.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/06-check-screen-size-in-qt3ds-project.png new file mode 100644 index 00000000000..9a5d43f03bd Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/06-check-screen-size-in-qt3ds-project.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/07-screen-size-in-text-editor.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/07-screen-size-in-text-editor.png new file mode 100644 index 00000000000..5d2c615dfe2 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/07-screen-size-in-text-editor.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/08-delete-placeholders.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/08-delete-placeholders.png new file mode 100644 index 00000000000..909778ef67d Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/08-delete-placeholders.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/09-add-new-assets.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/09-add-new-assets.png new file mode 100644 index 00000000000..e96a5982de9 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/09-add-new-assets.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/10-find-project-file.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/10-find-project-file.png new file mode 100644 index 00000000000..5c0a3060c28 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/10-find-project-file.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/11-import.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/11-import.png new file mode 100644 index 00000000000..2310d4e73cc Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/11-import.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/12-imported-image-assets.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/12-imported-image-assets.png new file mode 100644 index 00000000000..5a1d06fb578 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/12-imported-image-assets.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/13-myowncluster-in-qml-types.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/13-myowncluster-in-qml-types.png new file mode 100644 index 00000000000..c23e2ece27a Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/13-myowncluster-in-qml-types.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/14-add-myowncluster-to-project.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/14-add-myowncluster-to-project.png new file mode 100644 index 00000000000..b01087119da Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/14-add-myowncluster-to-project.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/15-drag-to-project.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/15-drag-to-project.png new file mode 100644 index 00000000000..709fd5253f0 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/15-drag-to-project.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/16-go-into-component.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/16-go-into-component.png new file mode 100644 index 00000000000..2c6b677cf43 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/16-go-into-component.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/17-offending-line.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/17-offending-line.png new file mode 100644 index 00000000000..834e8a77b68 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/17-offending-line.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/17b-commented-out.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/17b-commented-out.png new file mode 100644 index 00000000000..fa345476f3e Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/17b-commented-out.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/18-repeat-where-needed.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/18-repeat-where-needed.png new file mode 100644 index 00000000000..98e93705d64 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/18-repeat-where-needed.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/19-see-form-editor.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/19-see-form-editor.png new file mode 100644 index 00000000000..cd15c51790a Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/19-see-form-editor.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/20-drag-subpresentation-adas-into-layer.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/20-drag-subpresentation-adas-into-layer.png new file mode 100644 index 00000000000..a8f19c732ef Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/20-drag-subpresentation-adas-into-layer.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/22-go-into-subpresentation-adas.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/22-go-into-subpresentation-adas.png new file mode 100644 index 00000000000..efea13d0586 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/22-go-into-subpresentation-adas.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/23-replace-objects.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/23-replace-objects.png new file mode 100644 index 00000000000..a76635b15de Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/23-replace-objects.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/24-recreate-animations.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/24-recreate-animations.png new file mode 100644 index 00000000000..4e11e79d590 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/24-recreate-animations.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/25-adas-timeline.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/25-adas-timeline.png new file mode 100644 index 00000000000..052176a0b58 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/25-adas-timeline.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/26-recreate-2d-elements.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/26-recreate-2d-elements.png new file mode 100644 index 00000000000..0b0978e4333 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/26-recreate-2d-elements.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/27-delete-2d-layer.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/27-delete-2d-layer.png new file mode 100644 index 00000000000..261948575f3 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/27-delete-2d-layer.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/28-delete-2d-elements-from-3d-layer.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/28-delete-2d-elements-from-3d-layer.png new file mode 100644 index 00000000000..1d7ed2590cb Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/28-delete-2d-elements-from-3d-layer.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/30-find-qml-stream-files-in-file-explorer.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/30-find-qml-stream-files-in-file-explorer.png new file mode 100644 index 00000000000..e633099b33f Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/30-find-qml-stream-files-in-file-explorer.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/31-move-under-myowncluster.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/31-move-under-myowncluster.png new file mode 100644 index 00000000000..86f582b3f15 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/31-move-under-myowncluster.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/32-rename-the-qml-stream-file.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/32-rename-the-qml-stream-file.png new file mode 100644 index 00000000000..9cced22d290 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/32-rename-the-qml-stream-file.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/33-see-qml-stream-component-in-myqmlcomponents.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/33-see-qml-stream-component-in-myqmlcomponents.png new file mode 100644 index 00000000000..7db34107820 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/33-see-qml-stream-component-in-myqmlcomponents.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/34-drag-to-myowncluster-in-navigator.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/34-drag-to-myowncluster-in-navigator.png new file mode 100644 index 00000000000..54a6562d63a Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/34-drag-to-myowncluster-in-navigator.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/35-visible-states.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/35-visible-states.png new file mode 100644 index 00000000000..f79b6cee076 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/35-visible-states.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/36-recreate-qml-animations.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/36-recreate-qml-animations.png new file mode 100644 index 00000000000..4fa191492dd Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/36-recreate-qml-animations.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/37-recreate-keyframes.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/37-recreate-keyframes.png new file mode 100644 index 00000000000..87a0e148d1c Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/37-recreate-keyframes.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/38-delete-unnecessary-folders.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/38-delete-unnecessary-folders.png new file mode 100644 index 00000000000..f521a2c5569 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/38-delete-unnecessary-folders.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/39-delete-individual-files-in-qds.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/39-delete-individual-files-in-qds.png new file mode 100644 index 00000000000..cbbc252ce52 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/39-delete-individual-files-in-qds.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/40-done.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/40-done.png new file mode 100644 index 00000000000..5783f79c1d9 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/40-done.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/41-finished-qds-project.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/41-finished-qds-project.png new file mode 100644 index 00000000000..6859d34324d Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/41-finished-qds-project.png differ diff --git a/doc/qtdesignstudio/images/exporting-from-qt3ds/porting-example-myowncluster.png b/doc/qtdesignstudio/images/exporting-from-qt3ds/porting-example-myowncluster.png new file mode 100644 index 00000000000..674f08d6da8 Binary files /dev/null and b/doc/qtdesignstudio/images/exporting-from-qt3ds/porting-example-myowncluster.png differ diff --git a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc index 0d2de98a0f0..fb79bbe41eb 100644 --- a/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc +++ b/doc/qtdesignstudio/src/qtdesignstudio-toc.qdoc @@ -44,7 +44,9 @@ \li \l{Setting Up Qt Bridge for Sketch} \li \l{Using Qt Bridge for Sketch} \endlist + \li \l{Exporting from Blender} \li \l{Exporting from Maya} + \li \l{Exporting from Qt 3D Studio} \endlist \li \l{User Interface} \list diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc index 94a949d8681..1214ca81f2a 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-3d-assets.qdoc @@ -195,5 +195,6 @@ \list \li \l{Exporting from Blender}{Blender} \li \l{Exporting from Maya}{Maya} + \li \l{Exporting from Qt 3D Studio}{Qt 3D Studio} \endlist */ diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc index e1a4ccb1b41..5d6b39a3257 100644 --- a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-maya.qdoc @@ -29,7 +29,7 @@ \page exporting-from-maya.html \previouspage exporting-from-blender.html \if defined (qtdesignstudio) - \nextpage creator-quick-tour.html + \nextpage exporting-from-maya.html \else \nextpage studio-importing-3d.html \endif diff --git a/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc new file mode 100644 index 00000000000..5d588639c2f --- /dev/null +++ b/doc/qtdesignstudio/src/qtquick3d-editor/exporting-3d/exporting-from-qt3ds.qdoc @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $QT_BEGIN_LICENSE:FDL$ +** 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 Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page exporting-from-qt3ds.html + \previouspage exporting-from-maya.html + \nextpage creator-quick-tour.html + \title Exporting from Qt 3D Studio + + Use the following guidelines to achieve the best results when converting + projects from \Q3DS to \QDS. + + \section1 Best Practices + + \list + \li If something in UI is clearly 2D, do not import it from \Q3DS + presentation. Implement it directly in \QDS. + \li QML streams cannot be imported as QML elements directly into \QDS. + They need to be copied manually since there is no import functionality + for QML files in \QDS. + \li If you do import the whole \Q3DS project (\c .uia file), use only the + parts you actually need from it. Use \uicontrol + {Move Component Into Separate File} for the parts you need, and + eventually delete all the rest from the project structure. + \li There are extra levels of \c Node elements in most imported QML files, + and they should be removed for improved performance. As a rule of thumb, + each Node that has only one child is considered an extra level that + should be deleted. + \li \Q3DS \e slides become \QDS \e states, and there may be problems + with state changes. Always go through each of the state changes to make + sure they work correctly. + \endlist + + \section1 Importing Assets + + \section2 Custom Materials + Custom materials (\e something.shader files in \Q3DS) are not imported + correctly, and have to be fixed manually. + \list + \li They are imported as \c {CustomMaterial { id: something; source: + "something" }} that neither works nor does anything. + \li Remove the \e {source: "something"} altogether. + \li Vertex shader part from the \e something.shader needs to be copied to + \c {Shader { stage: Shader.Vertex; shader: "vertex shader code here" }} + inside the \c CustomMaterial. Fragment shader part needs to be copied + to \c {Shader { stage: Shader.Fragment; shader: "fragment shader code + here" }}. + \li Properties in \e Metadata of the \e something.shader need to be + introduced manually as QML properties of the same name and correct type + inside the \c CustomMaterial element. + \li Add \c {import QtQuick3D.Materials 1.15} import statement to files that + have \c CustomMaterial elements. + \li If custom material uses textures, they are not imported automatically + and must be manually added to the project structure. + \li Many custom materials are not rendered in \uicontrol {3D Editor} or + \uicontrol {Form Editor} views due to an open bug in \QDS 1.5. The + project needs to be run for them to appear. + \endlist + + \section2 Standard Materials + Some properties of standard materials may not be imported correctly. + \list + \li The sensible value ranges of some properties may have changed between + \Q3DS and \QDS and need to be redefined manually. + \li Extra properties may be added and can be removed manually. + \endlist + + \section2 Models + Models have some extra properties (tessellations), which can be removed. + + \section1 Example: Converting MyOwnCluster Project from \Q3DS to \QDS + + \image exporting-from-qt3ds/porting-example-myowncluster.png "My Own Cluster project in Qt 3D Studio" + \e {My Own Cluster project in \Q3DS} + + The following steps describe how to convert the My Own Cluster project from + \Q3DS to \QDS. + + \section2 Creating a New Project in \QDS + + \list 1 + \li To create a new project in \QDS, select \uicontrol {File > New File or + Project}, or select \uicontrol {New Project} in the Welcome mode. + \image exporting-from-qt3ds/01-welcome-screen.png "Welcome mode in Qt Design Studio" + + \li Creating a new project in \QDS is aided by a wizard that contains + templates for creating different types of projects. Choose the + \uicontrol {Qt Quick 3D Application} template to get started with your + new 3D project. + \image exporting-from-qt3ds/02-create-new-project.png "Create a new Project in Qt Design Studio" + + \li In the \uicontrol Name field, enter a name for the project. + In the \uicontrol {Create in} field, enter the path for the project + files, and then select \uicontrol Next. + \image exporting-from-qt3ds/03-folder-and-project-name.png "Project location and name" + + \li In the \uicontrol {Screen resolution} field, select the screen + resolution for previewing the UI on the desktop or on a device. + You must select one of the predefined screen resolutions, which will + later be altered to match the width and height of the original project. + In the \uicontrol {Qt Quick Controls Style} field, select one of the + predefined UI styles to use, and then select \uicontrol Finish. + \image exporting-from-qt3ds/04-project-size.png "Select the screen resolution for the project" + + \li Your new project has now been created. For more information on + creating projects in \QDS, see \l {Creating Projects}. + For more information on how to get started with \QDS, see + \l {Getting Started}. + \image exporting-from-qt3ds/05-new-project-created.png "New project in Qt Design Studio" + \endlist + + \section2 Getting Started with MyOwnCluster + + \list 1 + \li Return to your project in \Q3DS to check the size of the presentation. + Select \uicontrol {Edit} > \uicontrol {Presentation Settings} to see the + values for \uicontrol {Width x Height}. + \image exporting-from-qt3ds/06-check-screen-size-in-qt3ds-project.png "Check screen size in Qt 3D Studio presentation" + + \li Adjust the canvas size of your project in \QDS according to the screen + size in \Q3DS presentation by editing the \c Constants.qml file in + \uicontrol {Text Editor}. If you cannot see the \uicontrol {Text Editor} + view, select \uicontrol Window > \uicontrol {Views}, and then select the + \uicontrol {Text Editor} checkbox. In the \uicontrol Projects view, open + the imports subfolder, then the subfolder named after your project, and + double-click \c Constants.qml. Edit the values for \e {readonly property + int width} and \e {readonly property int height} to match the \uicontrol + {Width x Height} values in \Q3DS presentation. + \image exporting-from-qt3ds/07-screen-size-in-text-editor.png "Adjust the canvas size" + + \li To delete the placeholders created by the wizard, multiselect \uicontrol + Text and \uicontrol view3D components (\key Ctrl + mouse left click) in + \uicontrol Navigator, then right click on the selected items, and select + \uicontrol Edit > \uicontrol Delete. + \image exporting-from-qt3ds/08-delete-placeholders.png "Delete placeholders in Navigator" + \endlist + + \section2 Importing Assets + \list 1 + \li In the \uicontrol Library view, select \uicontrol {Add New Assets}. + \image exporting-from-qt3ds/09-add-new-assets.png + + \li Select the \c .uia file for the \Q3DS project you wish to import, and + then select \uicontrol Open. + \image exporting-from-qt3ds/10-find-project-file.png "Find the uia file in file explorer" + + \li Select \uicontrol Import, and after the import is complete, select \uicontrol + Close. + \image exporting-from-qt3ds/11-import.png "Import the file" + + \li The 2D assets imported from \Q3DS now appear in \uicontrol Library + under \uicontrol Assets tab. + \image exporting-from-qt3ds/12-imported-image-assets.png "Imported assets in Library" + + \li The QML components generated from the imported \Q3DS project now appear + as available imports in \uicontrol Library under \uicontrol {QML Types}. + Select \uicontrol Quick3DAssets.MyOwnCluster to import them to your + project. + \image exporting-from-qt3ds/13-myowncluster-in-qml-types.png "Available imports in QML Types" + + \li The imported QML types now appear in \uicontrol Library and can be added + to the project. + \image exporting-from-qt3ds/14-add-myowncluster-to-project.png "MyOwnCluster in Library" + \endlist + + \section2 Adding Components to the Project + + \list 1 + \li Drag \uicontrol MyOwnCluster from \uicontrol {My Quick3D Components} + in \uicontrol Library to \uicontrol {Form Editor}. + \image exporting-from-qt3ds/15-drag-to-project.png "Drag MyOwnCluster to Form Editor" + + \li In \uicontrol Navigator, right click on myOwnCluster and select \uicontrol + {Go into Component}. + \image exporting-from-qt3ds/16-go-into-component.png "Go into component My Own Cluster" + + \li Find the offending line in \uicontrol {Text Editor}. + \image exporting-from-qt3ds/17-offending-line.png + + \li Comment out the offending line by placing two slashes in the beginning + of it (or remove the line). + \image exporting-from-qt3ds/17b-commented-out.png + + \li In Navigator, go to each component of the project and comment out + (or remove) any offending lines you find. + \image exporting-from-qt3ds/18-repeat-where-needed.png + + \li You should now see some parts of the project in \uicontrol {Form Editor}. + \image exporting-from-qt3ds/19-see-form-editor.png "Project in Form Editor" + \endlist + + \section2 Converting 3D Elements + + \list 1 + \li Drag subpresentation_ADAS from \uicontrol {My QML Components} in \uicontrol + Library into layer folder in \uicontrol Navigator. + \image exporting-from-qt3ds/20-drag-subpresentation-adas-into-layer.png "Drag into layer" + + \li To delete the old subpresentation rectangle, right-click on the file + name (subpresentation_ADAS2_u52017 in this project), select + \uicontrol Edit > \uicontrol Delete. + + \li Go into the component subpresentation_ADAS. + \image exporting-from-qt3ds/22-go-into-subpresentation-adas.png "Go into component" + + \li Reposition objects and/or camera in \uicontrol {3D Editor} according to + the original \Q3DS project. The desired scene may be achieved + simply by changing the z position sign from positive to negative, or + vice versa, in some cases. + + For more information on how to edit a scene in \uicontrol {3D Editor}, + see \l {Working in 3D Editor}. + \image exporting-from-qt3ds/23-replace-objects.png "Replace objects in 3D Editor" + + \li Recreate animations in subpresentation_ADAS according to the original + project. For more information on creating animations in \QDS, see + \l {Creating Animations}. + \image exporting-from-qt3ds/24-recreate-animations.png "Recreate animations" + + \li Go to the \uicontrol Timeline View to review the timeline for the + project. + \image exporting-from-qt3ds/25-adas-timeline.png "ADAS timeline view" + \endlist + + \section2 Converting 2D Elements + + \list 1 + \li Recreate the 2D elements of the original project (in all layers) using + the 2D QML elements available in \QDS. You can use the imported 2D layer + as a guide for recreating the elements. If the 2D elements in the + original project have rotations, especially in a 3D layer, make sure to + add rotations that mimic the original ones to the \QDS project. For + example, rotation on one axis with perspective camera requires rotation + on two axes in pure 2D. You may need to use \uicontrol {Text Editor} + to achieve rotation similar to the rotation of the object in \Q3DS. + For more information on specifying advanced transformations on + \uicontrol Items, see \l Transform. + \image exporting-from-qt3ds/26-recreate-2d-elements.png "Recreate 2D elements" + + \li To delete the 2D layer (telltaleLayer_u39332) in \uicontrol Navigator + after recreating the 2D elements, right-click on the component, and + select \uicontrol Edit > \uicontrol Delete. + \image exporting-from-qt3ds/27-delete-2d-layer.png "Delete the 2D layer" + + \li Next, you need to delete the 2D elements from the 3D layer (speed_u20335 + and rPM_u10371 from layer_32325). Right-click on the element, and + select \uicontrol Edit > \uicontrol Delete. + \image exporting-from-qt3ds/28-delete-2d-elements-from-3d-layer.png "Delete old 2D elements from the 3D layer" + + \li Recreate the animations for 2D elements the same way it was done for the + 3D elements. + \endlist + + \section2 Converting QML Streams + + \list 1 + \li Find QML stream files in file explorer. + \image exporting-from-qt3ds/30-find-qml-stream-files-in-file-explorer.png "Find the QML stream file" + + \li Move the QML stream files under the import's main folder (one level up + in this example). + \image exporting-from-qt3ds/31-move-under-myowncluster.png "Move the QML stream files under the import's main folder" + + \li Make sure that the QML stream file names start with a capital letter + to enable \QDS to recognize them as QML component files. + \image exporting-from-qt3ds/32-rename-the-qml-stream-file.png "The QML stream file names should start with a capital letter" + + \li Return to \QDS and enter the MyOwnCluster component. The QML stream + component now appears in \uicontrol {My QML Components}. + \image exporting-from-qt3ds/33-see-qml-stream-component-in-myqmlcomponents.png "QML stream in My QML Components" + + \li Drag and drop the QML stream component to MyOwnCluster in \uicontrol + Navigator. + \image exporting-from-qt3ds/34-drag-to-myowncluster-in-navigator.png "Drag the QML stream component to MyOwnCluster" + + \li Go to the \uicontrol States view and use the \uicontrol Visibility tab + in the \uicontrol Properties view to make the QML stream component + visible only in the correct state. + \image exporting-from-qt3ds/35-visible-states.png "Make components visible in correct states" + + \li Next, recreate the animations for the QML stream according to the original project. + \image exporting-from-qt3ds/36-recreate-qml-animations.png "Recreate animations" + + \li Recreate all the keyframes for the imported QML stream that is now a + component. + \image exporting-from-qt3ds/37-recreate-keyframes.png "Recreate keyframes" + \endlist + + \section2 Cleaning Up the Project Structure + + \list 1 + \li Go to file explorer and clean up the project structure deleting + the folder and files that are not used in the \QDS project. + \image exporting-from-qt3ds/38-delete-unnecessary-folders.png "Delete folders in file explorer" + + \li Individual files can also be deleted in the \uicontrol {Projects} view + in \QDS. To delete a file, right-click on it, select \uicontrol + {Delete File}, and then select \uicontrol Yes. + \image exporting-from-qt3ds/39-delete-individual-files-in-qds.png "Delete individual files" + + \li This is the project structure after clean-up. + \image exporting-from-qt3ds/40-done.png "Project structure after clean-up" + \endlist + + \section2 Finished + + You have now converted your \Q3DS project to \QDS. + \image exporting-from-qt3ds/41-finished-qds-project.png +*/ diff --git a/share/qtcreator/android/sdk_definitions.json b/share/qtcreator/android/sdk_definitions.json index c88926b8739..e4bd1ac571c 100644 --- a/share/qtcreator/android/sdk_definitions.json +++ b/share/qtcreator/android/sdk_definitions.json @@ -1,15 +1,15 @@ { "common": { "sdk_tools_url": { - "linux": "https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip", - "linux_sha256": "92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9", - "windows": "https://dl.google.com/android/repository/sdk-tools-windows-4333796.zip", - "windows_sha256": "7e81d69c303e47a4f0e748a6352d85cd0c8fd90a5a95ae4e076b5e5f960d3c7a", - "mac": "https://dl.google.com/android/repository/sdk-tools-darwin-4333796.zip", - "mac_sha256": "ecb29358bc0f13d7c2fa0f9290135a5b608e38434aad9bf7067d0252c160853e" + "linux": "https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip", + "linux_sha256": "89f308315e041c93a37a79e0627c47f21d5c5edbe5e80ea8dc0aac8a649e0e92", + "windows": "https://dl.google.com/android/repository/commandlinetools-win-6609375_latest.zip", + "windows_sha256": "40bba20275180194bebf89bb58c74d712bb93cc401f36bd2f8f32383acf9826c", + "mac": "https://dl.google.com/android/repository/commandlinetools-mac-6609375_latest.zip", + "mac_sha256": "2c3822db1c916655223e5ee8ce0fbf6b73d0b99012045c9dc8eaa6a5736c0c55" }, "sdk_essential_packages": { - "default": ["platform-tools", "platforms;android-29"], + "default": ["platform-tools", "platforms;android-29", "cmdline-tools;latest"], "linux": [], "mac": [], "windows": ["extras;google;usb_driver"] diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 5e211885ce5..e66900c1ef6 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -3201,13 +3201,17 @@ class DumperBase(): val.laddress = self.pointer() if val.laddress is None and self.laddress is not None: val.laddress = self.laddress - val.type = self.dumper.nativeDynamicType(val.laddress, self.type.dereference()) + val.type = self.type.dereference() + if self.dumper.useDynamicType: + val.type = self.dumper.nativeDynamicType(val.laddress, val.type) else: val = self.dumper.nativeValueDereferenceReference(self) elif self.type.code == TypeCode.Pointer: if self.nativeValue is None: val.laddress = self.pointer() - val.type = self.dumper.nativeDynamicType(val.laddress, self.type.dereference()) + val.type = self.type.dereference() + if self.dumper.useDynamicType: + val.type = self.dumper.nativeDynamicType(val.laddress, val.type) else: val = self.dumper.nativeValueDereferencePointer(self) else: @@ -3662,7 +3666,9 @@ class DumperBase(): % type(targetTypish)) val = self.Value(self) val.ldata = self.toPointerData(targetAddress) - targetType = self.createType(targetTypish).dynamicType(targetAddress) + targetType = self.createType(targetTypish) + if self.useDynamicType: + targetType = targetType.dynamicType(targetAddress) val.type = self.createPointerType(targetType) return val @@ -3675,7 +3681,8 @@ class DumperBase(): % type(targetType)) val = self.Value(self) val.ldata = self.toPointerData(targetAddress) - targetType = targetType.dynamicType(targetAddress) + if self.useDynamicType: + targetType = targetType.dynamicType(targetAddress) val.type = self.createReferenceType(targetType) return val @@ -3848,7 +3855,8 @@ class DumperBase(): if self.isInt(datish): # Used as address. #DumperBase.warn('CREATING %s AT 0x%x' % (val.type.name, datish)) val.laddress = datish - val.type = val.type.dynamicType(datish) + if self.useDynamicType: + val.type = val.type.dynamicType(datish) return val if isinstance(datish, bytes): #DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish))) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index ae20b57ada8..c81bca1dd50 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -229,10 +229,11 @@ class Dumper(DumperBase): def fromFrameValue(self, nativeValue): #DumperBase.warn('FROM FRAME VALUE: %s' % nativeValue.address) val = nativeValue - try: - val = nativeValue.cast(nativeValue.dynamic_type) - except: - pass + if self.useDynamicType: + try: + val = nativeValue.cast(nativeValue.dynamic_type) + except: + pass return self.fromNativeValue(val) def fromNativeValue(self, nativeValue): @@ -1151,7 +1152,9 @@ class Dumper(DumperBase): def nativeValueDereferencePointer(self, value): # This is actually pretty expensive, up to 100ms. deref = value.nativeValue.dereference() - return self.fromNativeValue(deref.cast(deref.dynamic_type)) + if self.useDynamicType: + deref = deref.cast(deref.dynamic_type) + return self.fromNativeValue(deref) def nativeValueDereferenceReference(self, value): nativeValue = value.nativeValue diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml index efbfcd56421..2bf81b837f1 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/EditView3D.qml @@ -543,15 +543,60 @@ Item { MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton - onClicked: { + hoverEnabled: false + + property MouseArea3D freeDraggerArea + property point pressPoint + property bool initialMoveBlock: false + + onPressed: { if (viewRoot.editView) { var pickResult = viewRoot.editView.pick(mouse.x, mouse.y); handleObjectClicked(_generalHelper.resolvePick(pickResult.objectHit), mouse.modifiers & Qt.ControlModifier); - if (!pickResult.objectHit) + + if (pickResult.objectHit) { + if (transformMode === EditView3D.TransformMode.Move) + freeDraggerArea = moveGizmo.freeDraggerArea; + else if (transformMode === EditView3D.TransformMode.Rotate) + freeDraggerArea = rotateGizmo.freeDraggerArea; + else if (transformMode === EditView3D.TransformMode.Scale) + freeDraggerArea = scaleGizmo.freeDraggerArea; + pressPoint.x = mouse.x; + pressPoint.y = mouse.y; + initialMoveBlock = true; + } else { mouse.accepted = false; + } } } + onPositionChanged: { + if (freeDraggerArea) { + if (initialMoveBlock && Math.abs(pressPoint.x - mouse.x) + Math.abs(pressPoint.y - mouse.y) > 10) { + // Don't force press event at actual press, as that puts the gizmo + // in free-dragging state, which is bad UX if drag is not actually done + freeDraggerArea.forcePressEvent(pressPoint.x, pressPoint.y); + freeDraggerArea.forceMoveEvent(mouse.x, mouse.y); + initialMoveBlock = false; + } else { + freeDraggerArea.forceMoveEvent(mouse.x, mouse.y); + } + } + } + + function handleRelease(mouse) + { + if (freeDraggerArea) { + if (initialMoveBlock) + freeDraggerArea.forceReleaseEvent(pressPoint.x, pressPoint.y); + else + freeDraggerArea.forceReleaseEvent(mouse.x, mouse.y); + freeDraggerArea = null; + } + } + + onReleased: handleRelease(mouse) + onCanceled: handleRelease(mouse) } DropArea { diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml index 493e6cc3c25..98777031fb8 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/MoveGizmo.qml @@ -38,6 +38,7 @@ Node { || planeX.dragging || planeY.dragging || planeZ.dragging || centerBall.dragging property MouseArea3D dragHelper: null + property alias freeDraggerArea: centerBall.mouseArea position: dragHelper.pivotScenePosition(targetNode) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarDraggable.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarDraggable.qml index 9015a127d95..41a44f23863 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarDraggable.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/PlanarDraggable.qml @@ -37,6 +37,7 @@ Model { property bool dragging: mouseArea.dragging property bool active: false property MouseArea3D dragHelper: null + property alias mouseArea: mouseArea readonly property bool hovering: mouseArea.hovering diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/RotateGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/RotateGizmo.qml index 54d2d3a0a3e..8ed85f32969 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/RotateGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/RotateGizmo.qml @@ -39,6 +39,7 @@ Node { property MouseArea3D dragHelper: null property real currentAngle property point currentMousePos + property alias freeDraggerArea: mouseAreaFree position: dragHelper.pivotScenePosition(targetNode) diff --git a/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml index 3b4918e272c..ff64957699a 100644 --- a/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml +++ b/share/qtcreator/qml/qmlpuppet/mockfiles/ScaleGizmo.qml @@ -37,6 +37,7 @@ Node { || planeX.dragging || planeY.dragging || planeZ.dragging || centerMouseArea.dragging property MouseArea3D dragHelper: null + property alias freeDraggerArea: centerMouseArea position: dragHelper.pivotScenePosition(targetNode) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp index 89c33c29005..03ec0f56d99 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.cpp @@ -512,6 +512,32 @@ double MouseArea3D::getRelativeScale(QQuick3DNode *node) const return (scenePos1 - scenePos2).length() / 100.; } +void MouseArea3D::forcePressEvent(double x, double y) +{ + m_forceCaptureNextPress = true; + + Qt::MouseButtons buttons; + Qt::KeyboardModifiers mods; + QMouseEvent event(QEvent::MouseButtonPress, QPointF(x, y), Qt::LeftButton, buttons, mods); + eventFilter(m_view3D, &event); +} + +void QmlDesigner::Internal::MouseArea3D::forceMoveEvent(double x, double y) +{ + Qt::MouseButtons buttons; + Qt::KeyboardModifiers mods; + QMouseEvent event(QEvent::MouseMove, QPointF(x, y), Qt::LeftButton, buttons, mods); + eventFilter(m_view3D, &event); +} + +void QmlDesigner::Internal::MouseArea3D::forceReleaseEvent(double x, double y) +{ + Qt::MouseButtons buttons; + Qt::KeyboardModifiers mods; + QMouseEvent event(QEvent::MouseButtonRelease, QPointF(x, y), Qt::LeftButton, buttons, mods); + eventFilter(m_view3D, &event); +} + QVector3D MouseArea3D::getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const { @@ -535,7 +561,8 @@ QVector3D MouseArea3D::getMousePosInPlane(const MouseArea3D *helper, bool MouseArea3D::eventFilter(QObject *, QEvent *event) { if (!m_active || (m_grabsMouse && s_mouseGrab && s_mouseGrab != this - && (m_priority <= s_mouseGrab->m_priority || s_mouseGrab->m_dragging))) { + && (m_priority <= s_mouseGrab->m_priority || s_mouseGrab->m_dragging + || s_mouseGrab->m_forceCaptureNextPress))) { return false; } @@ -596,7 +623,8 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event) m_dragHelper->setScale(sceneScale()); } m_mousePosInPlane = getMousePosInPlane(m_dragHelper, mouseEvent->pos()); - if (mouseOnTopOfMouseArea(m_mousePosInPlane, mouseEvent->pos())) { + if (m_forceCaptureNextPress || mouseOnTopOfMouseArea(m_mousePosInPlane, mouseEvent->pos())) { + m_forceCaptureNextPress = false; setDragging(true); emit pressed(m_mousePosInPlane.toVector2D(), mouseEvent->pos(), pickAngle); if (m_grabsMouse) { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h index 11b6de3f0f9..1cb91cab337 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/editor3d/mousearea3d.h @@ -116,6 +116,10 @@ public slots: Q_INVOKABLE QVector3D pivotScenePosition(QQuick3DNode *node) const; Q_INVOKABLE double getRelativeScale(QQuick3DNode *node) const; + Q_INVOKABLE void forcePressEvent(double x, double y); + Q_INVOKABLE void forceMoveEvent(double x, double y); + Q_INVOKABLE void forceReleaseEvent(double x, double y); + signals: void view3DChanged(); @@ -171,6 +175,7 @@ private: qreal m_minAngle = 0.; QQuick3DNode *m_pickNode = nullptr; MouseArea3D *m_dragHelper = nullptr; + bool m_forceCaptureNextPress = false; }; } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomComboBoxStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomComboBoxStyle.qml deleted file mode 100644 index 363ff65600c..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomComboBoxStyle.qml +++ /dev/null @@ -1,86 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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. -** -****************************************************************************/ - -import QtQuick 2.1 -import QtQuick.Controls 1.1 as Controls -import QtQuick.Controls.Styles 1.2 -import QtQuickDesignerTheme 1.0 - -ComboBoxStyle { - property color textColor: Theme.color(Theme.PanelTextColorLight) - __editor: Item { - - } - - padding.left: 20 - - background: Item { - implicitWidth: 120 - implicitHeight: 24 - - Rectangle { - anchors.fill: parent - visible: !control.pressed - color: Theme.qmlDesignerButtonColor() - border.color: Theme.qmlDesignerBorderColor() - border.width: 1 - } - - Rectangle { - color: Theme.qmlDesignerBackgroundColorDarker() - anchors.fill: parent - visible: control.pressed - border.color: Theme.qmlDesignerBorderColor() - border.width: 1 - } - - Image { - id: imageItem - width: 8 - height: 4 - source: "image://icons/down-arrow" - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 8 - opacity: control.enabled ? 1 : 0.5 - } - } - - label: Text { - id: textitem - anchors.left: parent.left - anchors.verticalCenter: parent.verticalCenter - text: control.currentText - renderType: Text.NativeRendering - color: control.textColor - } - - __dropDownStyle: MenuStyle { - __maxPopupHeight: 600 - __menuItemType: "comboboxitem" - frame: Rectangle { - } - } -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomSpinBoxStyle.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomSpinBoxStyle.qml deleted file mode 100644 index f0a808b1479..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomSpinBoxStyle.qml +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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. -** -****************************************************************************/ - -import QtQuick 2.1 -import QtQuick.Controls 1.1 as Controls -import QtQuick.Controls.Styles 1.1 -import QtQuickDesignerTheme 1.0 - -SpinBoxStyle { - selectionColor: Theme.color(Theme.PanelTextColorLight) - selectedTextColor: Theme.color(Theme.PanelTextColorMid) - textColor: spinBox.textColor - - - padding.top: 2 - padding.bottom: 2 - padding.right: 18 - padding.left: 12 - - incrementControl: Item { - implicitWidth: 14 - implicitHeight: parent.height/2 - opacity: styleData.upEnabled ? styleData.upPressed ? 0.5 : 1 : 0.5 - Image { - width: 8 - height: 4 - source: "image://icons/up-arrow" - x: 1 - y: 5 - } - } - - decrementControl: Item { - implicitWidth: 14 - implicitHeight: parent.height/2 - opacity: styleData.downEnabled ? styleData.downPressed ? 0.5 : 1 : 0.5 - Image { - width: 8 - height: 4 - source: "image://icons/down-arrow" - x: 1 - y: 3 - } - } - - background: Rectangle { - implicitWidth: Math.max(64, styleData.contentWidth) - implicitHeight: 24 - color: Theme.qmlDesignerBackgroundColorDarker() - border.color: Theme.qmlDesignerBorderColor() - } -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionButton.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionButton.qml deleted file mode 100644 index 80ab3d39faa..00000000000 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ExtendedFunctionButton.qml +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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. -** -****************************************************************************/ - -import QtQuick 2.1 -import QtQuick.Controls 1.0 as Controls -import QtQuick.Window 2.0 -import QtQuick.Controls.Styles 1.1 -import "Constants.js" as Constants -import QtQuickDesignerTheme 1.0 - -Item { - width: 14 - height: 14 - - id: extendedFunctionButton - - property variant backendValue - - property string icon: "image://icons/placeholder" - property string hoverIcon: "image://icons/submenu" - - property bool active: true - - signal reseted - - function setIcon() { - if (backendValue == null) { - extendedFunctionButton.icon = "image://icons/placeholder" - } else if (backendValue.isBound) { - if (backendValue.isTranslated) { - //translations are a special case - extendedFunctionButton.icon = "image://icons/placeholder" - } else { - extendedFunctionButton.icon = "image://icons/expression" - } - } else { - if (backendValue.complexNode != null - && backendValue.complexNode.exists) { - - } else { - extendedFunctionButton.icon = "image://icons/placeholder" - } - } - } - - onBackendValueChanged: { - setIcon() - } - - property bool isBoundBackend: backendValue.isBound - property string backendExpression: backendValue.expression - - onActiveChanged: { - if (active) { - setIcon() - opacity = 1 - } else { - opacity = 0 - } - } - - onIsBoundBackendChanged: { - setIcon() - } - - onBackendExpressionChanged: { - setIcon() - } - - Image { - width: 14 - height: 14 - source: extendedFunctionButton.icon - anchors.centerIn: parent - } - - MouseArea { - hoverEnabled: true - anchors.fill: parent - onHoveredChanged: { - if (containsMouse) - icon = hoverIcon - else - setIcon() - } - onClicked: { - menuLoader.show() - } - } - - Loader { - id: menuLoader - - active: false - - function show() { - active = true - item.popup() - } - - sourceComponent: Component { - Controls.Menu { - - id: menu - - onAboutToShow: { - exportMenuItem.checked = backendValue.hasPropertyAlias() - exportMenuItem.enabled = !backendValue.isAttachedProperty() - } - - Controls.MenuItem { - text: qsTr("Reset") - onTriggered: { - transaction.start() - backendValue.resetValue() - backendValue.resetValue() - transaction.end() - extendedFunctionButton.reseted() - } - } - Controls.MenuItem { - text: qsTr("Set Binding") - onTriggered: expressionDialogLoader.show() - } - Controls.MenuItem { - id: exportMenuItem - text: qsTr("Export Property as Alias") - onTriggered: { - if (checked) - backendValue.exportPopertyAsAlias() - else - backendValue.removeAliasExport() - } - checkable: true - } - - Controls.MenuItem { - text: qsTr("Insert Keyframe") - visible: hasActiveTimeline - onTriggered: insertKeyframe(backendValue.name) - } - } - } - } - - Loader { - id: expressionDialogLoader - parent: itemPane - anchors.fill: parent - - visible: false - active: visible - - function show() { - expressionDialogLoader.visible = true - } - - sourceComponent: Component { - Item { - id: expressionDialog - anchors.fill: parent - - Component.onCompleted: { - textField.text = backendValue.expression - textField.forceActiveFocus() - } - - Rectangle { - anchors.fill: parent - color: Theme.qmlDesignerBackgroundColorDarker() - opacity: 0.6 - } - - MouseArea { - anchors.fill: parent - onDoubleClicked: expressionDialog.visible = false - } - - Rectangle { - x: 4 - Component.onCompleted: { - var pos = itemPane.mapFromItem( - extendedFunctionButton.parent, 0, 0) - y = pos.y + 2 - } - - width: parent.width - 8 - height: 260 - - radius: 2 - color: Theme.qmlDesignerBackgroundColorDarkAlternate() - border.color: Theme.qmlDesignerBorderColor() - - Label { - x: 8 - y: 6 - font.bold: true - text: qsTr("Binding Editor") - } - ExpressionTextField { - id: textField - onRejected: expressionDialogLoader.visible = false - onAccepted: { - backendValue.expression = textField.text.trim() - expressionDialogLoader.visible = false - } - anchors.fill: parent - anchors.leftMargin: 8 - anchors.rightMargin: 8 - anchors.topMargin: 24 - anchors.bottomMargin: 32 - } - } - } - } - } -} diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir index 21c8f0ce22a..f77c9e19348 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir @@ -14,12 +14,8 @@ ColorEditor 2.0 ColorEditor.qml ColorLine 2.0 ColorLine.qml ColorLogic 2.0 ColorLogic.qml ComboBox 2.0 ComboBox.qml -CustomCheckBoxStyle 2.0 CustomCheckBoxStyle.qml -CustomComboBoxStyle 2.0 CustomComboBoxStyle.qml -CustomSpinBoxStyle 2.0 CustomSpinBoxStyle.qml EditableListView 2.0 EditableListView.qml ExpandingSpacer 2.0 ExpandingSpacer.qml -ExtendedFunctionButton 2.0 ExtendedFunctionButton.qml ExtendedFunctionLogic 2.0 ExtendedFunctionLogic.qml FlickableSection 2.0 FlickableSection.qml FontComboBox 2.0 FontComboBox.qml @@ -28,7 +24,6 @@ FontStyleButtons 2.0 FontStyleButtons.qml GradientLine 2.0 GradientLine.qml GradientPresetList 2.0 GradientPresetList.qml GradientPresetTabContent 2.0 GradientPresetTabContent.qml -GroupBox 2.0 GroupBox.qml HueSlider 2.0 HueSlider.qml IconLabel 2.0 IconLabel.qml ItemFilterComboBox 2.0 ItemFilterComboBox.qml diff --git a/share/qtcreator/templates/wizards/classes/python/file.py b/share/qtcreator/templates/wizards/classes/python/file.py index 5698022dd85..6b16b9d9527 100644 --- a/share/qtcreator/templates/wizards/classes/python/file.py +++ b/share/qtcreator/templates/wizards/classes/python/file.py @@ -1,38 +1,24 @@ # This Python file uses the following encoding: utf-8 -@if '%{Module}' === 'PySide2' - @if '%{ImportQtCore}' -from PySide2 import QtCore +@if '%{Module}' !== '' + @if '%{ImportQtCore}' !== '' +from %{Module} import QtCore @endif - @if '%{ImportQtWidgets}' -from PySide2 import QtWidgets + @if '%{ImportQtWidgets}' !== '' +from %{Module} import QtWidgets @endif - @if '%{ImportQtQuick}' -from PySide2 import QtQuick - @endif -@else - @if '%{ImportQtCore}' -from PyQt5 import QtCore - @endif - @if '%{ImportQtWidgets}' -from PyQt5 import QtWidgets - @endif - @if '%{ImportQtQuick}' -from PyQt5 import QtQuick + @if '%{ImportQtQuick}' !== '' +from %{Module} import QtQuick @endif @endif @if '%{Base}' -class %{Class}(%{Base}): +class %{Class}(%{FullBase}): @else class %{Class}: @endif def __init__(self): -@if '%{Base}' === 'QWidget' - QtWidgets.QWidget.__init__(self) -@elif '%{Base}' === 'QMainWindow' - QtWidgets.QMainWindow.__init__(self) -@elif '%{Base}' === 'QQuickItem' - QtQuick.QQuickItem.__init__(self) +@if %{JS: [ "QObject", "QWidget", "QMainWindow", "QQuickItem" ].indexOf("%Base")} >= 0 + %{FullBase}.__init__(self) @endif pass diff --git a/share/qtcreator/templates/wizards/classes/python/wizard.json b/share/qtcreator/templates/wizards/classes/python/wizard.json index aeebd1f2a30..d8a67341447 100644 --- a/share/qtcreator/templates/wizards/classes/python/wizard.json +++ b/share/qtcreator/templates/wizards/classes/python/wizard.json @@ -12,7 +12,8 @@ "options": [ { "key": "Base", "value":"%{JS: value('BaseCB') === '' ? value('BaseEdit') : value('BaseCB')}" }, - { "key": "Imports", "value": "%{ImportQtCore}%{ImportQtWidgets}%{ImportQtDeclarative}"} + { "key": "Package", "value": "%{JS: [ 'QWidget', 'QMainWindow' ].indexOf(value('Base')) >= 0 ? 'QtWidgets' : (value('Base') === 'QQuickItem' ? 'QtQuick' : (value('Base') === 'QObject' ? 'QtCore' : ''))}" }, + { "key": "FullBase", "value": "%{JS: value('Package') === '' ? value('Base') : value('Package') + '.' + value('Base')}" } ], "pages": @@ -32,11 +33,11 @@ }, { "name": "Module", - "trDisplayName": "Python module:", + "trDisplayName": "Qt for Python module:", "type": "ComboBox", "data": { - "items": ["PySide2", "PyQt5"] + "items": ["", "PySide2", "PyQt5"] } }, { @@ -46,8 +47,10 @@ "data": { "items": [ { "trKey": "", "value": "" }, - "QObject", "QWidget", "QMainWindow", "QDeclarativeItem", "QQuickItem" ] - } + "QObject", "QWidget", "QMainWindow", "QQuickItem" ] + }, + "isComplete": "%{JS: (value('Module') === '' && value('BaseCB') === '') || value('Module') !== ''}", + "trIncompleteMessage": "You can choose Qt classes only if you select a Qt for Python module." }, { "name": "BaseEdit", @@ -67,33 +70,36 @@ "name": "ImportQtCore", "trDisplayName": "Import QtCore", "type": "CheckBox", + "enabled": "%{JS: value('Module') !== ''}", "data": { "checkedValue": "QtCore", "uncheckedValue": "", - "checked": "%{JS: value('Base') !== ''}" + "checked": "%{JS: value('BaseCB') !== ''}" } }, { - "name": "ImportQWidget", + "name": "ImportQtWidgets", "trDisplayName": "Import QtWidgets", "type": "CheckBox", + "enabled": "%{JS: value('Module') !== ''}", "data": { "checkedValue": "QtWidgets", "uncheckedValue": "", - "checked": "%{JS: value('Base') === 'QWidget'}" + "checked": "%{JS: [ 'QWidget', 'QMainWindow' ].indexOf(value('BaseCB')) >= 0}" } }, { "name": "ImportQtQuick", "trDisplayName": "Import QtQuick", "type": "CheckBox", + "enabled": "%{JS: value('Module') !== ''}", "data": { "checkedValue": "QtQuick", "uncheckedValue": "", - "checked": "%{JS: value('Base') === 'QQuickItem'}" + "checked": "%{JS: value('BaseCB') === 'QQuickItem'}" } }, { diff --git a/src/libs/clangsupport/filesystem.cpp b/src/libs/clangsupport/filesystem.cpp index db79ddda312..e6c0ed34b70 100644 --- a/src/libs/clangsupport/filesystem.cpp +++ b/src/libs/clangsupport/filesystem.cpp @@ -61,4 +61,10 @@ long long FileSystem::lastModified(FilePathId filePathId) const return 0; } +void FileSystem::remove(const FilePathIds &filePathIds) +{ + for (FilePathId filePathId : filePathIds) + QFile::remove(QString{m_filePathCache.filePath(filePathId)}); +} + } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filesystem.h b/src/libs/clangsupport/filesystem.h index ecf332de55c..827fdcfd0e8 100644 --- a/src/libs/clangsupport/filesystem.h +++ b/src/libs/clangsupport/filesystem.h @@ -41,6 +41,8 @@ public: FilePathIds directoryEntries(const QString &directoryPath) const override; long long lastModified(FilePathId filePathId) const override; + void remove(const FilePathIds &filePathIds) override; + private: FilePathCachingInterface &m_filePathCache; }; diff --git a/src/libs/clangsupport/filesysteminterface.h b/src/libs/clangsupport/filesysteminterface.h index 9d64d2a93a1..68be461657b 100644 --- a/src/libs/clangsupport/filesysteminterface.h +++ b/src/libs/clangsupport/filesysteminterface.h @@ -36,6 +36,7 @@ class FileSystemInterface public: virtual FilePathIds directoryEntries(const QString &directoryPath) const = 0; virtual long long lastModified(FilePathId filePathId) const = 0; + virtual void remove(const FilePathIds &filePathIds) = 0; protected: ~FileSystemInterface() = default; diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index 015a9384c98..2f804004a54 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -1559,11 +1559,9 @@ void PluginManagerPrivate::readPluginPaths() pluginCategories.insert(QString(), QVector()); for (const QString &pluginFile : pluginFiles(pluginPaths)) { - auto *spec = new PluginSpec; - if (!spec->d->read(pluginFile)) { // not a Qt Creator plugin - delete spec; + PluginSpec *spec = PluginSpec::read(pluginFile); + if (!spec) // not a Qt Creator plugin continue; - } // defaultDisabledPlugins and defaultEnabledPlugins from install settings // is used to override the defaults read from the plugin spec diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp index 8e5b3e02ecb..ea83933b7ed 100644 --- a/src/libs/extensionsystem/pluginspec.cpp +++ b/src/libs/extensionsystem/pluginspec.cpp @@ -547,6 +547,16 @@ void PluginSpec::setEnabledBySettings(bool value) d->setEnabledBySettings(value); } +PluginSpec *PluginSpec::read(const QString &filePath) +{ + auto spec = new PluginSpec; + if (!spec->d->read(filePath)) { // not a Qt Creator plugin + delete spec; + return nullptr; + } + return spec; +} + //==========PluginSpecPrivate================== namespace { diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h index 2180d916dd5..4ad702b2163 100644 --- a/src/libs/extensionsystem/pluginspec.h +++ b/src/libs/extensionsystem/pluginspec.h @@ -133,6 +133,8 @@ public: void setEnabledBySettings(bool value); + static PluginSpec *read(const QString &filePath); + private: PluginSpec(); diff --git a/src/libs/utils/id.h b/src/libs/utils/id.h index f3658b4a949..e0af61099c8 100644 --- a/src/libs/utils/id.h +++ b/src/libs/utils/id.h @@ -27,6 +27,7 @@ #include "utils_global.h" +#include #include #include diff --git a/src/plugins/android/CMakeLists.txt b/src/plugins/android/CMakeLists.txt index 563732fbd4f..e7bdfea6bc1 100644 --- a/src/plugins/android/CMakeLists.txt +++ b/src/plugins/android/CMakeLists.txt @@ -44,7 +44,6 @@ add_qtc_plugin(Android androidsettingswidget.cpp androidsettingswidget.h androidsettingswidget.ui androidsignaloperation.cpp androidsignaloperation.h androidtoolchain.cpp androidtoolchain.h - androidtoolmanager.cpp androidtoolmanager.h avddialog.cpp avddialog.h certificatesmodel.cpp certificatesmodel.h createandroidmanifestwizard.cpp createandroidmanifestwizard.h diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index bdf7efb38f6..f5c5f981f89 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -43,7 +43,6 @@ HEADERS += \ android_global.h \ androidbuildapkstep.h \ androidbuildapkwidget.h \ - androidtoolmanager.h \ androidsdkmanager.h \ androidavdmanager.h \ adbcommandswidget.h \ @@ -90,7 +89,6 @@ SOURCES += \ avddialog.cpp \ androidbuildapkstep.cpp \ androidbuildapkwidget.cpp \ - androidtoolmanager.cpp \ androidsdkmanager.cpp \ androidavdmanager.cpp \ adbcommandswidget.cpp \ diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index 026cc30324c..1f89461102f 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -103,8 +103,6 @@ Project { "androidsignaloperation.h", "androidtoolchain.cpp", "androidtoolchain.h", - "androidtoolmanager.cpp", - "androidtoolmanager.h", "avddialog.cpp", "avddialog.h", "certificatesmodel.cpp", diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index d396aceed7a..55ae7a5cdc6 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -24,8 +24,6 @@ ****************************************************************************/ #include "androidavdmanager.h" -#include "androidtoolmanager.h" - #include #include #include @@ -76,7 +74,7 @@ bool AndroidAvdManager::avdManagerCommand(const AndroidConfig &config, const QSt Utils::SynchronousProcess proc; auto env = AndroidConfigurations::toolsEnvironment(config).toStringList(); proc.setEnvironment(env); - qCDebug(avdManagerLog) << "Running command:" << cmd.toUserOutput(); + qCDebug(avdManagerLog) << "Running AVD Manager command:" << cmd.toUserOutput(); SynchronousProcessResponse response = proc.runBlocking(cmd); if (response.result == Utils::SynchronousProcessResponse::Finished) { if (output) @@ -138,7 +136,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig &config, const CreateA const QString avdManagerTool = config.avdManagerToolPath().toString(); qCDebug(avdManagerLog) - << "Running command:" << CommandLine(avdManagerTool, arguments).toUserOutput(); + << "Running AVD Manager command:" << CommandLine(avdManagerTool, arguments).toUserOutput(); QProcess proc; proc.start(avdManagerTool, arguments); if (!proc.waitForStarted()) { @@ -222,7 +220,6 @@ private: AndroidAvdManager::AndroidAvdManager(const AndroidConfig &config): m_config(config), - m_androidTool(new AndroidToolManager(m_config)), m_parser(new AvdManagerOutputParser) { diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 5f6ea967a67..667301a58d6 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -32,7 +32,6 @@ namespace Android { namespace Internal { -class AndroidToolManager; class AvdManagerOutputParser; class AndroidAvdManager @@ -63,7 +62,6 @@ private: private: const AndroidConfig &m_config; - std::unique_ptr m_androidTool; std::unique_ptr m_parser; }; diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 07b63bdc24e..7bba90bf9c7 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -190,7 +190,7 @@ bool AndroidBuildApkStep::init() m_packagePath = AndroidManager::apkPath(target()).toString(); } - qCDebug(buildapkstepLog) << "Package path:" << m_packagePath; + qCDebug(buildapkstepLog) << "APK or AAB path:" << m_packagePath; if (!AbstractProcessStep::init()) return false; diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 7d964b7f800..4154e2ad924 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -30,8 +30,6 @@ #include "androidmanager.h" #include "androidqtversion.h" #include "androiddevicedialog.h" -#include "androidsdkmanager.h" -#include "androidtoolmanager.h" #include "avddialog.h" #include @@ -965,11 +963,10 @@ QStringList AndroidConfig::allEssentials() const return allPackages; } -bool AndroidConfig::allEssentialsInstalled() +bool AndroidConfig::allEssentialsInstalled(AndroidSdkManager *sdkManager) { QStringList essentialPkgs(allEssentials()); - for (const AndroidSdkPackage *pkg : - AndroidConfigurations::sdkManager()->installedSdkPackages()) { + for (const AndroidSdkPackage *pkg : sdkManager->installedSdkPackages()) { if (essentialPkgs.contains(pkg->sdkStylePath())) essentialPkgs.removeOne(pkg->sdkStylePath()); if (essentialPkgs.isEmpty()) diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 24e26309a04..a727b3690ce 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -27,6 +27,8 @@ #include "android_global.h" #include "androidsdkpackage.h" +#include "androidsdkmanager.h" + #include #include @@ -134,7 +136,7 @@ public: QStringList defaultEssentials() const; QStringList essentialsFromQtVersion(const QtSupport::BaseQtVersion &version) const; QStringList allEssentials() const; - bool allEssentialsInstalled(); + bool allEssentialsInstalled(Internal::AndroidSdkManager *sdkManager); bool sdkToolsOk() const; Utils::FilePath openJDKLocation() const; diff --git a/src/plugins/android/androiddebugsupport.cpp b/src/plugins/android/androiddebugsupport.cpp index dc58202daa5..04ec22f66c0 100644 --- a/src/plugins/android/androiddebugsupport.cpp +++ b/src/plugins/android/androiddebugsupport.cpp @@ -122,8 +122,6 @@ void AndroidDebugSupport::start() setUseContinueInsteadOfRun(true); setAttachPid(m_runner->pid()); - qCDebug(androidDebugSupportLog) << "Start. Package name: " << packageName - << "PID: " << m_runner->pid().pid(); QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitAspect::qtVersion(kit); if (!Utils::HostOsInfo::isWindowsHost() && (qtVersion @@ -187,6 +185,8 @@ void AndroidDebugSupport::start() addSearchDirectory(qtVersion->qmlPath()); } + qCDebug(androidDebugSupportLog) << "Starting debugger - package name: " << packageName + << ", PID: " << m_runner->pid().pid(); DebuggerRunTool::start(); } diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index e9c3a95f218..d82d050511a 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -163,8 +163,12 @@ Utils::Id AndroidDeployQtStep::stepId() bool AndroidDeployQtStep::init() { QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target()->kit()); - if (!version) // TODO: Add error message + if (!version) { + qCDebug(deployStepLog, + "The Qt version for kit %s is not valid.", + qPrintable(target()->kit()->displayName())); return false; + } m_androiddeployqtArgs = CommandLine(); @@ -205,7 +209,7 @@ bool AndroidDeployQtStep::init() m_avdName = info.avdname; m_serialNumber = info.serialNumber; - qCDebug(deployStepLog) << "Selected Device:" << info; + qCDebug(deployStepLog) << "Selected device info:" << info; gatherFilesToPull(); @@ -242,7 +246,6 @@ bool AndroidDeployQtStep::init() emit addOutput(tr("Cannot find the androiddeployqt tool."), OutputFormat::Stderr); return false; } - qCDebug(deployStepLog) << "Using androiddeployqt"; m_command = m_command.pathAppended(HostOsInfo::withExecutableSuffix("androiddeployqt")); m_workingDirectory = bc->buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY); @@ -493,9 +496,8 @@ void AndroidDeployQtStep::gatherFilesToPull() m_filesToPull["/system/bin/" + linkerName] = buildDir + linkerName; m_filesToPull["/system/" + libDirName + "/libc.so"] = buildDir + "libc.so"; - qCDebug(deployStepLog) << "Files to pull from device:"; for (auto itr = m_filesToPull.constBegin(); itr != m_filesToPull.constEnd(); ++itr) - qCDebug(deployStepLog) << itr.key() << "to" << itr.value(); + qCDebug(deployStepLog) << "Pulling file from device:" << itr.key() << "to:" << itr.value(); } void AndroidDeployQtStep::doRun() diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index d00d275ae31..56726d774df 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -433,7 +433,7 @@ QString AndroidManager::deviceSerialNumber(const Target *target) void AndroidManager::setDeviceSerialNumber(Target *target, const QString &deviceSerialNumber) { - qCDebug(androidManagerLog) << "Device serial for the target changed" + qCDebug(androidManagerLog) << "Target device serial changed:" << target->displayName() << deviceSerialNumber; target->setNamedSettings(AndroidDeviceSn, deviceSerialNumber); } @@ -470,7 +470,7 @@ int AndroidManager::deviceApiLevel(const Target *target) void AndroidManager::setDeviceApiLevel(Target *target, int level) { - qCDebug(androidManagerLog) << "Device API level for the target changed" + qCDebug(androidManagerLog) << "Target device API level changed:" << target->displayName() << level; target->setNamedSettings(ApiLevelKey, level); } @@ -815,7 +815,7 @@ SdkToolResult AndroidManager::runCommand(const CommandLine &command, cmdResult.m_stdOut = response.stdOut().trimmed(); cmdResult.m_stdErr = response.stdErr().trimmed(); cmdResult.m_success = response.result == SynchronousProcessResponse::Finished; - qCDebug(androidManagerLog) << "Running command (sync) finshed:" << command.toUserOutput() + qCDebug(androidManagerLog) << "Command finshed (sync):" << command.toUserOutput() << "Success:" << cmdResult.m_success << "Output:" << response.allRawOutput(); if (!cmdResult.success()) diff --git a/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp b/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp index 46c7ccc6242..3cd14cdeaff 100644 --- a/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp +++ b/src/plugins/android/androidmanifesteditoriconcontainerwidget.cpp @@ -53,7 +53,7 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg auto masterIconButton = new AndroidManifestEditorIconWidget(this, lowDpiIconSize, lowDpiIconSize, - tr("Master icon"), tr("Select master icon")); + tr("Master icon"), tr("Select master icon.")); masterIconButton->setIcon(QIcon::fromTheme(QLatin1String("document-open"), Utils::Icons::OPENFILE.icon())); iconLayout->addWidget(masterIconButton); iconLayout->addStretch(1); @@ -69,7 +69,7 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg auto lIconButton = new AndroidManifestEditorIconWidget(this, lowDpiIconSize, lowDpiIconSize, - tr("Low DPI icon"), tr("Select low DPI icon"), + tr("Low DPI icon"), tr("Select low DPI icon."), textEditorWidget, lowDpiIconPath, iconFileName); @@ -80,7 +80,7 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg auto mIconButton = new AndroidManifestEditorIconWidget(this, mediumDpiIconSize, mediumDpiIconSize, - tr("Medium DPI icon"), tr("Select medium DPI icon"), + tr("Medium DPI icon"), tr("Select medium DPI icon."), textEditorWidget, mediumDpiIconPath, iconFileName); @@ -91,7 +91,7 @@ AndroidManifestEditorIconContainerWidget::AndroidManifestEditorIconContainerWidg auto hIconButton = new AndroidManifestEditorIconWidget(this, highDpiIconSize, highDpiIconSize, - tr("High DPI icon"), tr("Select high DPI icon"), + tr("High DPI icon"), tr("Select high DPI icon."), textEditorWidget, highDpiIconPath, iconFileName); diff --git a/src/plugins/android/androidmanifesteditoriconwidget.cpp b/src/plugins/android/androidmanifesteditoriconwidget.cpp index 455c773f615..486f4dfceae 100644 --- a/src/plugins/android/androidmanifesteditoriconwidget.cpp +++ b/src/plugins/android/androidmanifesteditoriconwidget.cpp @@ -87,7 +87,7 @@ AndroidManifestEditorIconWidget::AndroidManifestEditorIconWidget( m_scaleWarningLabel->setMinimumSize(clearAndWarningSize); m_scaleWarningLabel->setMaximumSize(clearAndWarningSize); m_scaleWarningLabel->setPixmap(Utils::Icons::WARNING.icon().pixmap(clearAndWarningSize)); - m_scaleWarningLabel->setToolTip(tr("Icon scaled up")); + m_scaleWarningLabel->setToolTip(tr("Icon scaled up.")); m_scaleWarningLabel->setVisible(false); } auto label = new QLabel(tr("Click to select..."), parent); diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index 49d5fbd96fb..2d4ba1e338c 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -58,6 +58,8 @@ public: BuildStepConfigWidget *createConfigWidget() final; + QString nativeAndroidBuildPath() const; + private: bool init() final; void setupOutputFormatter(Utils::OutputFormatter *formatter) override; @@ -74,8 +76,9 @@ public: AndroidPackageInstallationStepWidget(BuildStep *step) : BuildStepConfigWidget(step) { - setDisplayName(tr("Make install")); - setSummaryText("" + tr("Make install") + ""); + const QString cmd = static_cast(step) + ->nativeAndroidBuildPath(); + setSummaryText(tr("Make install: Copy App Files to %1").arg(cmd)); } }; @@ -91,17 +94,14 @@ AndroidPackageInstallationStep::AndroidPackageInstallationStep(BuildStepList *bs bool AndroidPackageInstallationStep::init() { - QString dirPath = buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY).toString(); - if (HostOsInfo::isWindowsHost()) - if (buildEnvironment().searchInPath("sh.exe").isEmpty()) - dirPath = QDir::toNativeSeparators(dirPath); - ToolChain *tc = ToolChainKitAspect::cxxToolChain(target()->kit()); QTC_ASSERT(tc, return false); - CommandLine cmd{tc->makeCommand(buildEnvironment())}; + QString dirPath = nativeAndroidBuildPath(); const QString innerQuoted = QtcProcess::quoteArg(dirPath); const QString outerQuoted = QtcProcess::quoteArg("INSTALL_ROOT=" + innerQuoted); + + CommandLine cmd{tc->makeCommand(buildEnvironment())}; cmd.addArgs(outerQuoted + " install", CommandLine::Raw); ProcessParameters *pp = processParameters(); @@ -120,6 +120,16 @@ bool AndroidPackageInstallationStep::init() return AbstractProcessStep::init(); } +QString AndroidPackageInstallationStep::nativeAndroidBuildPath() const +{ + QString buildPath = buildDirectory().pathAppended(Constants::ANDROID_BUILDDIRECTORY).toString(); + if (HostOsInfo::isWindowsHost()) + if (buildEnvironment().searchInPath("sh.exe").isEmpty()) + buildPath = QDir::toNativeSeparators(buildPath); + + return buildPath; +} + void AndroidPackageInstallationStep::setupOutputFormatter(OutputFormatter *formatter) { formatter->addLineParser(new GnuMakeParser); diff --git a/src/plugins/android/androidpotentialkit.h b/src/plugins/android/androidpotentialkit.h index b7b2e6a3fdc..0bc3f99f69e 100644 --- a/src/plugins/android/androidpotentialkit.h +++ b/src/plugins/android/androidpotentialkit.h @@ -51,7 +51,5 @@ private: void recheck(); }; -// TODO add "Download" links to the settings page? - } } diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 566bfbc820c..67c60dc37e0 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -150,14 +150,12 @@ void AndroidRunner::qmlServerPortReady(Port port) void AndroidRunner::remoteOutput(const QString &output) { - Core::MessageManager::write("LOGCAT: " + output, Core::MessageManager::Silent); appendMessage(output, Utils::StdOutFormat); m_outputParser.processOutput(output); } void AndroidRunner::remoteErrorOutput(const QString &output) { - Core::MessageManager::write("LOGCAT: " + output, Core::MessageManager::Silent); appendMessage(output, Utils::StdErrFormat); m_outputParser.processOutput(output); } diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index a4315038abc..9b1b067f76f 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -124,7 +124,6 @@ static qint64 extractPID(const QByteArray &output, const QString &packageName) static void findProcessPID(QFutureInterface &fi, QStringList selector, const QString &packageName, bool preNougat) { - qCDebug(androidRunWorkerLog) << "Finding PID. PreNougat:" << preNougat; if (packageName.isEmpty()) return; @@ -144,7 +143,7 @@ static void findProcessPID(QFutureInterface &fi, QStringList selector, } } while (processPID == -1 && !isTimedOut(start) && !fi.isCanceled()); - qCDebug(androidRunWorkerLog) << "PID found:" << processPID; + qCDebug(androidRunWorkerLog) << "PID found:" << processPID << ", PreNougat:" << preNougat; if (!fi.isCanceled()) fi.reportResult(processPID); } @@ -157,7 +156,6 @@ static void deleter(QProcess *p) p->kill(); p->waitForFinished(); } - qCDebug(androidRunWorkerLog) << "Done killing process:" << p->objectName(); // Might get deleted from its own signal handler. p->deleteLater(); } @@ -308,11 +306,11 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunWorker *runner, const QString &packa m_debugServerPath = debugServer(m_useLldb, target).toString(); qCDebug(androidRunWorkerLog) << "Device Serial:" << m_deviceSerialNumber - << "API level:" << m_apiLevel - << "Extra Start Args:" << m_amStartExtraArgs - << "Before Start ADB cmds:" << m_beforeStartAdbCommands - << "After finish ADB cmds:" << m_afterFinishAdbCommands - << "Debug server path:" << m_debugServerPath; + << ", API level:" << m_apiLevel + << ", Extra Start Args:" << m_amStartExtraArgs + << ", Before Start ADB cmds:" << m_beforeStartAdbCommands + << ", After finish ADB cmds:" << m_afterFinishAdbCommands + << ", Debug server path:" << m_debugServerPath; QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(target->kit()); m_useAppParamsForQmlDebugger = version->qtVersion() >= QtSupport::QtVersionNumber(5, 12); diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index e5df4a2d827..5bc53341ffa 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -25,9 +25,9 @@ #include "androidsdkmanager.h" +#include "androidconfigurations.h" #include "androidconstants.h" #include "androidmanager.h" -#include "androidtoolmanager.h" #include #include @@ -49,10 +49,6 @@ static Q_LOGGING_CATEGORY(sdkManagerLog, "qtc.android.sdkManager", QtWarningMsg) namespace Android { namespace Internal { -// Though sdk manager is introduced in 25.2.3 but the verbose mode is avaialble in 25.3.0 -// and android tool is supported in 25.2.3 -const QVersionNumber sdkManagerIntroVersion(25, 3 ,0); - const char installLocationKey[] = "Installed Location:"; const char revisionKey[] = "Version:"; const char descriptionKey[] = "Description:"; @@ -134,6 +130,10 @@ void watcherDeleter(QFutureWatcher *watcher) delete watcher; } +static QString sdkRootArg(const AndroidConfig &config) +{ + return "--sdk_root=" + config.sdkLocation().toString(); +} /*! Runs the \c sdkmanger tool with arguments \a args. Returns \c true if the command is successfully executed. Output is copied into \a output. The function blocks the calling thread. @@ -141,13 +141,16 @@ void watcherDeleter(QFutureWatcher *watcher) static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output, int timeout = sdkManagerCmdTimeoutS) { + QStringList newArgs = args; + newArgs.append(sdkRootArg(config)); qCDebug(sdkManagerLog) << "Running SDK Manager command (sync):" - << CommandLine(config.sdkManagerToolPath(), args).toUserOutput(); + << CommandLine(config.sdkManagerToolPath(), newArgs) + .toUserOutput(); SynchronousProcess proc; proc.setProcessEnvironment(AndroidConfigurations::toolsEnvironment(config)); proc.setTimeoutS(timeout); proc.setTimeOutMessageBoxEnabled(true); - SynchronousProcessResponse response = proc.run({config.sdkManagerToolPath(), args}); + SynchronousProcessResponse response = proc.run({config.sdkManagerToolPath(), newArgs}); if (output) *output = response.allOutput(); return response.result == SynchronousProcessResponse::Finished; @@ -164,8 +167,10 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar AndroidSdkManager::OperationOutput &output, double progressQuota, bool interruptible = true, int timeout = sdkManagerOperationTimeoutS) { + QStringList newArgs = args; + newArgs.append(sdkRootArg(config)); qCDebug(sdkManagerLog) << "Running SDK Manager command (async):" - << CommandLine(config.sdkManagerToolPath(), args).toUserOutput(); + << CommandLine(config.sdkManagerToolPath(), newArgs).toUserOutput(); int offset = fi.progressValue(); SynchronousProcess proc; proc.setProcessEnvironment(AndroidConfigurations::toolsEnvironment(config)); @@ -188,7 +193,7 @@ static void sdkManagerCommand(const AndroidConfig &config, const QStringList &ar QObject::connect(&sdkManager, &AndroidSdkManager::cancelActiveOperations, &proc, &SynchronousProcess::terminate); } - SynchronousProcessResponse response = proc.run({config.sdkManagerToolPath(), args}); + SynchronousProcessResponse response = proc.run({config.sdkManagerToolPath(), newArgs}); if (assertionFound) { output.success = false; output.stdOutput = response.stdOut(); @@ -916,23 +921,13 @@ void AndroidSdkManagerPrivate::reloadSdkPackages() return; } - if (m_config.sdkToolsVersion() < sdkManagerIntroVersion && !m_config.isCmdlineSdkToolsInstalled()) { - // Old Sdk tools. - m_packageListingSuccessful = true; - AndroidToolManager toolManager(m_config); - auto toAndroidSdkPackages = [](SdkPlatform *p) -> AndroidSdkPackage *{ - return p; - }; - m_allPackages = Utils::transform(toolManager.availableSdkPlatforms(), toAndroidSdkPackages); - } else { - QString packageListing; - QStringList args({"--list", "--verbose"}); - args << m_config.sdkManagerToolArgs(); - m_packageListingSuccessful = sdkManagerCommand(m_config, args, &packageListing); - if (m_packageListingSuccessful) { - SdkManagerOutputParser parser(m_allPackages); - parser.parsePackageListing(packageListing); - } + QString packageListing; + QStringList args({"--list", "--verbose"}); + args << m_config.sdkManagerToolArgs(); + m_packageListingSuccessful = sdkManagerCommand(m_config, args, &packageListing); + if (m_packageListingSuccessful) { + SdkManagerOutputParser parser(m_allPackages); + parser.parsePackageListing(packageListing); } emit m_sdkManager.packageReloadFinished(); } @@ -1025,7 +1020,7 @@ void AndroidSdkManagerPrivate::checkPendingLicense(SdkCmdFutureInterface &fi) fi.setProgressValue(0); AndroidSdkManager::OperationOutput result; result.type = AndroidSdkManager::LicenseCheck; - QStringList args("--licenses"); + const QStringList args = {"--licenses", sdkRootArg(m_config)}; if (!fi.isCanceled()) sdkManagerCommand(m_config, args, m_sdkManager, fi, result, 100.0); else @@ -1044,7 +1039,7 @@ void AndroidSdkManagerPrivate::getPendingLicense(SdkCmdFutureInterface &fi) QtcProcess licenseCommand; licenseCommand.setProcessEnvironment(AndroidConfigurations::toolsEnvironment(m_config)); bool reviewingLicenses = false; - licenseCommand.setCommand(CommandLine(m_config.sdkManagerToolPath(), {"--licenses"})); + licenseCommand.setCommand(CommandLine(m_config.sdkManagerToolPath(), {"--licenses", sdkRootArg(m_config)})); if (Utils::HostOsInfo::isWindowsHost()) licenseCommand.setUseCtrlCStub(true); licenseCommand.start(); diff --git a/src/plugins/android/androidsdkmanagerwidget.cpp b/src/plugins/android/androidsdkmanagerwidget.cpp index 37ac3097538..bc39aaef103 100644 --- a/src/plugins/android/androidsdkmanagerwidget.cpp +++ b/src/plugins/android/androidsdkmanagerwidget.cpp @@ -172,7 +172,7 @@ void AndroidSdkManagerWidget::installEssentials() "Install them manually after the current operation is done.\n") .arg(m_sdkModel->missingEssentials().join("\", \""))); } - m_ui->applySelectionButton->click(); + onApplyButton(); } void AndroidSdkManagerWidget::beginLicenseCheck() diff --git a/src/plugins/android/androidsdkmanagerwidget.ui b/src/plugins/android/androidsdkmanagerwidget.ui index efdee00f582..e49f7a792ef 100644 --- a/src/plugins/android/androidsdkmanagerwidget.ui +++ b/src/plugins/android/androidsdkmanagerwidget.ui @@ -105,29 +105,14 @@ Qt::Vertical - - - 20 - 40 - - - - - 0 - 0 - - Show Packages - - 12 - diff --git a/src/plugins/android/androidsdkmodel.cpp b/src/plugins/android/androidsdkmodel.cpp index 91bd62f7c82..50205e735f2 100644 --- a/src/plugins/android/androidsdkmodel.cpp +++ b/src/plugins/android/androidsdkmodel.cpp @@ -313,7 +313,8 @@ void AndroidSdkModel::selectMissingEssentials() } m_missingEssentials = pendingPkgs; - qCDebug(androidSdkModelLog) << "Couldn't find some essential packages:" << m_missingEssentials; + if (!m_missingEssentials.isEmpty()) + qCDebug(androidSdkModelLog) << "Couldn't find some essential packages:" << m_missingEssentials; } QList AndroidSdkModel::userSelection() const diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 605646a4176..7587b4ea465 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -189,15 +189,17 @@ public: { QTC_CHECK(m_detailsWidget); auto layout = new QVBoxLayout(this); - layout->setContentsMargins(12, 12, 12, 12); + layout->setContentsMargins(22, 0, 0, 12); + layout->setSpacing(4); for (auto itr = validationPoints.cbegin(); itr != validationPoints.cend(); ++itr) { RowData data; data.m_infoLabel = new InfoLabel(itr.value()); layout->addWidget(data.m_infoLabel); m_validationData[itr.key()] = data; - setPointValid(itr.key(), true); + setPointValid(itr.key(), false); } m_detailsWidget->setWidget(this); + setContentsMargins(0, 0, 0, 0); } void setPointValid(int key, bool valid) @@ -284,7 +286,7 @@ QVariant AvdModel::itemData(const AndroidDeviceInfo &info, int column, int role) AvdModel::AvdModel() { //: AVD - Android Virtual Device - setHeader({tr("AVD Name"), tr("API"), tr("CPU/ABI"), tr("Device type"), tr("Target"), tr("SD-card size")}); + setHeader({tr("AVD Name"), tr("API"), tr("CPU/ABI"), tr("Device Type"), tr("Target"), tr("SD-card Size")}); } void AndroidSettingsWidget::showEvent(QShowEvent *event) @@ -350,13 +352,18 @@ AndroidSettingsWidget::AndroidSettingsWidget() sdkMangerLayout->setContentsMargins(0, 0, 0, 0); sdkMangerLayout->addWidget(m_sdkManagerWidget); connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::updatingSdk, [this] { - m_ui.SDKLocationPathChooser->setEnabled(false); - // Disable the tab bar to restrict the user moving away from sdk manager tab untill - // operations finish. + // Disable the top level UI to keep the user from unintentionally interrupting operations + m_ui.javaSettingsGroupBox->setEnabled(false); + m_ui.androidSettingsGroupBox->setEnabled(false); + m_ui.androidOpenSSLSettingsGroupBox->setEnabled(false); + m_ui.CreateKitCheckBox->setEnabled(false); m_ui.managerTabWidget->tabBar()->setEnabled(false); }); connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::updatingSdkFinished, [this] { - m_ui.SDKLocationPathChooser->setEnabled(true); + m_ui.javaSettingsGroupBox->setEnabled(true); + m_ui.androidSettingsGroupBox->setEnabled(true); + m_ui.androidOpenSSLSettingsGroupBox->setEnabled(true); + m_ui.CreateKitCheckBox->setEnabled(true); m_ui.managerTabWidget->tabBar()->setEnabled(true); }); connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::licenseWorkflowStarted, [this] { @@ -408,7 +415,7 @@ AndroidSettingsWidget::AndroidSettingsWidget() currentSDKPath = AndroidConfig::defaultSdkPath(); m_ui.SDKLocationPathChooser->setFilePath(currentSDKPath); - m_ui.SDKLocationPathChooser->setPromptDialogTitle(tr("Select Android SDK folder")); + m_ui.SDKLocationPathChooser->setPromptDialogTitle(tr("Select Android SDK Folder")); m_ui.openSslPathChooser->setPromptDialogTitle(tr("Select OpenSSL Include Project File")); FilePath currentOpenSslPath = m_androidConfig.openSslLocation(); @@ -599,7 +606,8 @@ void AndroidSettingsWidget::validateSdk() // after AndroidSdkManager::packageReloadFinished. m_androidSummary->setPointValid(PlatformSdkInstalledRow, !m_sdkManager.installedSdkPlatforms().isEmpty()); - m_androidSummary->setPointValid(AllEssentialsInstalledRow, m_androidConfig.allEssentialsInstalled()); + m_androidSummary->setPointValid(AllEssentialsInstalledRow, + m_androidConfig.allEssentialsInstalled(&m_sdkManager)); const bool sdkToolsOk = m_androidSummary->rowsOk({SdkPathExistsRow, SdkPathWritableRow, @@ -614,7 +622,7 @@ void AndroidSettingsWidget::validateSdk() // Ask user to install essential SDK components. Works only for sdk tools version >= 26.0.0 QString message = tr("Android SDK installation is missing necessary packages. Do you " "want to install the missing packages?"); - auto userInput = QMessageBox::information(this, tr("Missing Android SDK packages"), + auto userInput = QMessageBox::information(this, tr("Missing Android SDK Packages"), message, QMessageBox::Yes | QMessageBox::No); if (userInput == QMessageBox::Yes) { m_ui.managerTabWidget->setCurrentWidget(m_ui.sdkManagerTab); @@ -692,7 +700,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent) msgBox.setText(tr("OpenSSL prebuilt libraries cloning failed. ") + msgSuffix + tr("Opening OpenSSL URL for manual download.")); msgBox.addButton(tr("OK"), QMessageBox::YesRole); - QAbstractButton *openButton = msgBox.addButton(tr("Open download URL"), QMessageBox::ActionRole); + QAbstractButton *openButton = msgBox.addButton(tr("Open Download URL"), QMessageBox::ActionRole); msgBox.exec(); if (msgBox.clickedButton() == openButton) @@ -716,7 +724,7 @@ void AndroidSettingsWidget::downloadOpenSslRepo(const bool silent) connect(gitCloner, &QtcProcess::errorOccurred, this, [=](QProcess::ProcessError error) { openSslProgressDialog->close(); if (error == QProcess::FailedToStart) { - failDialog(tr("The git tool might not be installed properly on your system. ")); + failDialog(tr("The Git tool might not be installed properly on your system. ")); } else { failDialog(); } diff --git a/src/plugins/android/androidsettingswidget.ui b/src/plugins/android/androidsettingswidget.ui index 764a41d1e06..b4542e8d4c4 100644 --- a/src/plugins/android/androidsettingswidget.ui +++ b/src/plugins/android/androidsettingswidget.ui @@ -66,7 +66,7 @@ 0 - + 0 @@ -124,7 +124,7 @@ - + Android Settings @@ -151,7 +151,7 @@ - + @@ -252,7 +252,7 @@ - + Set Up SDK @@ -263,7 +263,7 @@ - + Android OpenSSL settings (Optional) diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp index 708189b5666..d26c9f0d2a3 100644 --- a/src/plugins/android/androidtoolchain.cpp +++ b/src/plugins/android/androidtoolchain.cpp @@ -209,8 +209,6 @@ ToolChainList AndroidToolChainFactory::autodetectToolChainsFromNdks( const AndroidConfig config = AndroidConfigurations::currentConfig(); for (const Utils::FilePath &ndkLocation : ndkLocations) { - qCDebug(androidTCLog) << "Detecting toolchains from Android NDK:" << ndkLocation; - FilePath clangPath = config.clangPathFromNdk(ndkLocation); if (!clangPath.exists()) { qCDebug(androidTCLog) << "Clang toolchains detection fails. Can not find Clang" @@ -241,12 +239,12 @@ ToolChainList AndroidToolChainFactory::autodetectToolChainsFromNdks( AndroidConfig::displayName(abi), config.ndkVersion(ndkLocation).toString())); if (tc) { - qCDebug(androidTCLog) << "Tool chain already known" << abi.toString() << lang; // make sure to update the toolchain with current name format if (tc->displayName() != displayName) tc->setDisplayName(displayName); } else { - qCDebug(androidTCLog) << "New Clang toolchain found" << abi.toString() << lang; + qCDebug(androidTCLog) << "New Clang toolchain found" << abi.toString() << lang + << "for NDK" << ndkLocation; auto atc = new AndroidToolChain(); atc->setNdkLocation(ndkLocation); atc->setOriginalTargetTriple(target); diff --git a/src/plugins/android/androidtoolmanager.cpp b/src/plugins/android/androidtoolmanager.cpp deleted file mode 100644 index 4fefc5dbcfe..00000000000 --- a/src/plugins/android/androidtoolmanager.cpp +++ /dev/null @@ -1,339 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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. -** -****************************************************************************/ -#include "androidtoolmanager.h" - -#include "androidmanager.h" - -#include -#include -#include -#include -#include - -#include - -namespace { -static Q_LOGGING_CATEGORY(androidToolLog, "qtc.android.sdkManager", QtWarningMsg) -} - -namespace Android { -namespace Internal { - -using namespace Utils; - -class AndroidToolOutputParser -{ -public: - void parseTargetListing(const QString &output, const FilePath &sdkLocation, - SdkPlatformList &platformList); - - QList m_installedPlatforms; -}; - -/*! - Runs the \c android tool located at \a toolPath with arguments \a args and environment \a - environment. Returns \c true for successful execution. Command's output is copied to \a - output. - */ -static bool androidToolCommand(Utils::FilePath toolPath, const QStringList &args, - const QProcessEnvironment &environment, QString *output) -{ - SynchronousProcess proc; - proc.setProcessEnvironment(environment); - SynchronousProcessResponse response = proc.runBlocking({toolPath, args}); - if (response.result == SynchronousProcessResponse::Finished) { - if (output) - *output = response.allOutput(); - return true; - } - return false; -} - -static QStringList cleanAndroidABIs(const QStringList &abis) -{ - QStringList res; - foreach (const QString &abi, abis) { - int index = abi.lastIndexOf(QLatin1Char('/')); - if (index == -1) - res << abi; - else - res << abi.mid(index + 1); - } - return res; -} - -AndroidToolManager::AndroidToolManager(const AndroidConfig &config) : - m_config(config), - m_parser(new AndroidToolOutputParser) -{ - -} - -AndroidToolManager::~AndroidToolManager() = default; - -SdkPlatformList AndroidToolManager::availableSdkPlatforms(bool *ok) const -{ - bool success = false; - SdkPlatformList list; - QString targetListing; - if (androidToolCommand(m_config.androidToolPath(), QStringList({"list", "target"}), - AndroidConfigurations::toolsEnvironment(m_config), &targetListing)) { - m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), list); - success = true; - } else { - qCDebug(androidToolLog) << "Android tool target listing failed"; - } - - if (ok) - *ok = success; - - return list; -} - -QFuture AndroidToolManager::createAvd(CreateAvdInfo info) const -{ - return Utils::runAsync(&AndroidToolManager::createAvdImpl, info, - m_config.androidToolPath(), - AndroidConfigurations::toolsEnvironment(m_config)); -} - -bool AndroidToolManager::removeAvd(const QString &name) const -{ - SynchronousProcess proc; - proc.setTimeoutS(5); - proc.setProcessEnvironment(AndroidConfigurations::toolsEnvironment(m_config)); - SynchronousProcessResponse response - = proc.runBlocking({m_config.androidToolPath(), {"delete", "avd", "-n", name}}); - return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0; -} - -QFuture AndroidToolManager::androidVirtualDevicesFuture() const -{ - return Utils::runAsync(&AndroidToolManager::androidVirtualDevices, - m_config.androidToolPath(), m_config.sdkLocation(), - AndroidConfigurations::toolsEnvironment(m_config)); -} - -CreateAvdInfo AndroidToolManager::createAvdImpl(CreateAvdInfo info, FilePath androidToolPath, - QProcessEnvironment env) -{ - QProcess proc; - proc.setProcessEnvironment(env); - QStringList arguments; - arguments << QLatin1String("create") << QLatin1String("avd") - << QLatin1String("-t") << QString("android-%1").arg(info.systemImage->apiLevel()) - << QLatin1String("-n") << info.name - << QLatin1String("-b") << info.abi; - if (info.sdcardSize > 0) - arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize); - proc.start(androidToolPath.toString(), arguments); - if (!proc.waitForStarted()) { - info.error = tr("Could not start process \"%1 %2\"") - .arg(androidToolPath.toString(), arguments.join(QLatin1Char(' '))); - return info; - } - QTC_CHECK(proc.state() == QProcess::Running); - proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile" - - QByteArray question; - while (true) { - proc.waitForReadyRead(500); - question += proc.readAllStandardOutput(); - if (question.endsWith(QByteArray("]:"))) { - // truncate to last line - int index = question.lastIndexOf(QByteArray("\n")); - if (index != -1) - question = question.mid(index); - if (question.contains("hw.gpu.enabled")) - proc.write(QByteArray("yes\n")); - else - proc.write(QByteArray("\n")); - question.clear(); - } - - if (proc.state() != QProcess::Running) - break; - } - QTC_CHECK(proc.state() == QProcess::NotRunning); - - QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError()); - // The exit code is always 0, so we need to check stderr - // For now assume that any output at all indicates a error - if (!errorOutput.isEmpty()) { - info.error = errorOutput; - } - - return info; -} - -AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::FilePath &androidTool, - const FilePath &sdkLocationPath, - const QProcessEnvironment &env) -{ - AndroidDeviceInfoList devices; - QString output; - if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), env, &output)) - return devices; - - QStringList avds = output.split('\n'); - if (avds.empty()) - return devices; - - for (const QString &line : avds) // remove the daemon logs - if (line.startsWith("* daemon")) - avds.removeOne(line); - - avds.removeFirst(); // remove "List of devices attached" header line - - bool nextLineIsTargetLine = false; - - AndroidDeviceInfo dev; - for (int i = 0; i < avds.size(); i++) { - QString line = avds.at(i); - if (!line.contains(QLatin1String("Name:"))) - continue; - - int index = line.indexOf(QLatin1Char(':')) + 2; - if (index >= line.size()) - break; - dev.avdname = line.mid(index).trimmed(); - dev.sdk = -1; - dev.cpuAbi.clear(); - ++i; - for (; i < avds.size(); ++i) { - line = avds.at(i); - if (line.contains(QLatin1String("---------"))) - break; - - if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) { - if (line.contains(QLatin1String("Google APIs"))) { - nextLineIsTargetLine = true; - continue; - } - - nextLineIsTargetLine = false; - - int lastIndex = line.lastIndexOf(QLatin1Char(' ')); - if (lastIndex == -1) // skip line - break; - QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed(); - dev.sdk = AndroidManager::findApiLevel( - sdkLocationPath.pathAppended(QString("/platforms/android-%1").arg(tmp))); - } - if (line.contains(QLatin1String("Tag/ABI:"))) { - int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1; - if (lastIndex >= line.size()) - break; - dev.cpuAbi = QStringList(line.mid(lastIndex)); - } else if (line.contains(QLatin1String("ABI:"))) { - int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1; - if (lastIndex >= line.size()) - break; - dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed()); - } - } - // armeabi-v7a devices can also run armeabi code - if (dev.cpuAbi == QStringList("armeabi-v7a")) - dev.cpuAbi << QLatin1String("armeabi"); - dev.state = AndroidDeviceInfo::OkState; - dev.type = AndroidDeviceInfo::Emulator; - if (dev.cpuAbi.isEmpty() || dev.sdk == -1) - continue; - devices.push_back(dev); - } - Utils::sort(devices); - - return devices; -} - -void AndroidToolOutputParser::parseTargetListing(const QString &output, - const Utils::FilePath &sdkLocation, - SdkPlatformList &platformList) -{ - auto addSystemImage = [](const QStringList& abiList, SdkPlatform *platform) { - QTC_ASSERT(platform, return); - foreach (auto imageAbi, abiList) { - auto image = new SystemImage(QVersionNumber(), "", imageAbi, platform); - platform->addSystemImage(image); - } - }; - - class { - public: - QStringList abiList; - QVersionNumber revision; - int apiLevel = -1; - QString description; - Utils::FilePath installedLocation; - - void clear() { - abiList.clear(); - revision = QVersionNumber(); - apiLevel = -1; - description.clear(); - installedLocation.clear(); - } - } platformParams; - - QStringList outputLines = output.split('\n'); - for (int index = 0; index < outputLines.count(); ++index) { - const QString line = outputLines.at(index).trimmed(); - if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) { - int index = line.indexOf(QLatin1String("\"android-")); - if (index == -1) - continue; - QString androidTarget = line.mid(index + 1, line.length() - index - 2); - const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1); - platformParams.installedLocation = sdkLocation.pathAppended(QString("/platforms/android-%1").arg(tmp)); - platformParams.apiLevel = AndroidManager::findApiLevel(platformParams.installedLocation); - } else if (line.startsWith(QLatin1String("Name:"))) { - platformParams.description = line.mid(6); - } else if (line.startsWith(QLatin1String("Revision:"))) { - platformParams.revision = QVersionNumber::fromString(line.mid(10)); - } else if (line.startsWith(QLatin1String("Tag/ABIs :"))) { - platformParams.abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("ABIs"))) { - platformParams.abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", "))); - } else if (line.startsWith(QLatin1String("---")) - || line.startsWith(QLatin1String("===")) - || index == outputLines.count() - 1) { - if (platformParams.apiLevel == -1) - continue; - auto platform = new SdkPlatform(platformParams.revision, - QString("platforms;android-%1").arg(platformParams.apiLevel), - platformParams.apiLevel); - platform->setState(AndroidSdkPackage::Installed); - platform->setDescriptionText(platformParams.description); - platform->setInstalledLocation(platformParams.installedLocation); - addSystemImage(platformParams.abiList, platform); - platformList << platform; - platformParams.clear(); - } - } - Utils::sort(platformList); -} - -} // namespace Internal -} // namespace Android diff --git a/src/plugins/android/androidtoolmanager.h b/src/plugins/android/androidtoolmanager.h deleted file mode 100644 index a109d4d2b7f..00000000000 --- a/src/plugins/android/androidtoolmanager.h +++ /dev/null @@ -1,71 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** 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 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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. -** -****************************************************************************/ -#pragma once - -#include "androidconfigurations.h" - -#include - -#include - -#include - -namespace Android { -class AndroidConfig; - -namespace Internal { - -class AndroidToolOutputParser; -/*! - Wraps the \c android tool's usage. The tool itself is deprecated since SDK tools version 25.3.0. - */ -class AndroidToolManager -{ - Q_DECLARE_TR_FUNCTIONS(AndroidToolManager) - -public: - AndroidToolManager(const AndroidConfig &config); - ~AndroidToolManager(); - - SdkPlatformList availableSdkPlatforms(bool *ok = nullptr) const; - - QFuture createAvd(CreateAvdInfo info) const; - bool removeAvd(const QString &name) const; - QFuture androidVirtualDevicesFuture() const; - -// Helper methods -private: - static CreateAvdInfo createAvdImpl(CreateAvdInfo info, Utils::FilePath androidToolPath, - QProcessEnvironment env); - static AndroidDeviceInfoList androidVirtualDevices(const Utils::FilePath &androidTool, - const Utils::FilePath &sdkLlocationPath, - const QProcessEnvironment &env); -private: - const AndroidConfig &m_config; - std::unique_ptr m_parser; -}; - -} // namespace Internal -} // namespace Android diff --git a/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri b/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri index a7ef679399d..07e55b9354b 100644 --- a/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri +++ b/src/plugins/clangcodemodel/clangcodemodelunittestfiles.pri @@ -1,22 +1,28 @@ INCLUDEPATH += $$PWD SOURCES += \ - $$PWD/clangactivationsequencecontextprocessor.cpp \ - $$PWD/clangactivationsequenceprocessor.cpp \ $$PWD/clangcompletionchunkstotextconverter.cpp \ - $$PWD/clangcompletioncontextanalyzer.cpp \ $$PWD/clangdiagnosticfilter.cpp \ $$PWD/clangfixitoperation.cpp \ $$PWD/clanghighlightingresultreporter.cpp \ $$PWD/clanguiheaderondiskmanager.cpp +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES+= \ + $$PWD/clangactivationsequenceprocessor.cpp \ + $$PWD/clangactivationsequencecontextprocessor.cpp + HEADERS += \ - $$PWD/clangactivationsequencecontextprocessor.h \ - $$PWD/clangactivationsequenceprocessor.h \ $$PWD/clangcompletionchunkstotextconverter.h \ - $$PWD/clangcompletioncontextanalyzer.h \ $$PWD/clangdiagnosticfilter.h \ $$PWD/clangfixitoperation.h \ $$PWD/clanghighlightingresultreporter.h \ $$PWD/clangisdiagnosticrelatedtolocation.h \ $$PWD/clanguiheaderondiskmanager.h + +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES+= \ + $$PWD/clangactivationsequencecontextprocessor.h \ + $$PWD/clangactivationsequenceprocessor.h \ + $$PWD/clangcompletioncontextanalyzer.cpp \ + $$PWD/clangcompletioncontextanalyzer.h + + diff --git a/src/plugins/clangtools/clangtool.cpp b/src/plugins/clangtools/clangtool.cpp index c6441fd3521..49f389b4f2c 100644 --- a/src/plugins/clangtools/clangtool.cpp +++ b/src/plugins/clangtools/clangtool.cpp @@ -472,7 +472,7 @@ ClangTool::ClangTool() // Load diagnostics from file action = new QAction(this); action->setIcon(Utils::Icons::OPENFILE_TOOLBAR.icon()); - action->setToolTip(tr("Load Diagnostics from YAML Files exported with \"-export-fixes\".")); + action->setToolTip(tr("Load diagnostics from YAML files exported with \"-export-fixes\".")); connect(action, &QAction::triggered, this, &ClangTool::loadDiagnosticsFromFiles); m_loadExported = action; diff --git a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp index 535d28ee75b..84312316a3c 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildconfiguration.cpp @@ -487,7 +487,7 @@ void CMakeBuildConfiguration::runCMakeWithExtraArguments() InitialCMakeArgumentsAspect::InitialCMakeArgumentsAspect() { setSettingsKey("CMake.Initial.Parameters"); - setLabelText(tr("Initial CMake Parameters:")); + setLabelText(tr("Initial CMake parameters:")); setDisplayStyle(TextEditDisplay); } diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 4f563f232ce..6ab8e805f88 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -60,7 +60,7 @@ CMakeManager::CMakeManager() , m_runCMakeActionContextMenu(new QAction(QIcon(), tr("Run CMake"), this)) , m_rescanProjectAction(new QAction(QIcon(), tr("Rescan Project"), this)) , m_parseAndValidateCMakeReplyFileAction( - new QAction(QIcon(), tr("Parse and verify a CMake reply file"), this)) + new QAction(QIcon(), tr("Parse and verify a CMake reply file."), this)) { Core::ActionContainer *mbuild = Core::ActionManager::actionContainer(ProjectExplorer::Constants::M_BUILDPROJECT); @@ -230,7 +230,7 @@ void CMakeManager::enableBuildFileMenus(Node *node) void CMakeManager::parseAndValidateCMakeReplyFile() { QString replyFile = QFileDialog::getOpenFileName(Core::ICore::mainWindow(), - tr("Select a CMake reply file"), + tr("Select a CMake Reply File"), QString(), QString("index*.json")); if (replyFile.isEmpty()) diff --git a/src/plugins/coreplugin/CMakeLists.txt b/src/plugins/coreplugin/CMakeLists.txt index 1faf28e4884..6e5c56ae87f 100644 --- a/src/plugins/coreplugin/CMakeLists.txt +++ b/src/plugins/coreplugin/CMakeLists.txt @@ -85,7 +85,7 @@ add_qtc_plugin(Core helpmanager.cpp helpmanager.h helpmanager_implementation.h icontext.cpp icontext.h icore.cpp icore.h - id.h + id.cpp id.h idocument.cpp idocument.h idocumentfactory.cpp idocumentfactory.h ifilewizardextension.h diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index ffa9eeeb4a7..2b2133ccaaa 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -20,6 +20,7 @@ SOURCES += corejsextensions.cpp \ fancytabwidget.cpp \ generalsettings.cpp \ themechooser.cpp \ + id.cpp \ icontext.cpp \ jsexpander.cpp \ messagemanager.cpp \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index 49d2c270b5e..10f6ba2286d 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -92,6 +92,7 @@ Project { "icontext.h", "icore.cpp", "icore.h", + "id.cpp", "id.h", "idocument.cpp", "idocument.h", diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GroupBox.qml b/src/plugins/coreplugin/id.cpp similarity index 90% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GroupBox.qml rename to src/plugins/coreplugin/id.cpp index 943ec6bf375..6f8a29f40c7 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/GroupBox.qml +++ b/src/plugins/coreplugin/id.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -23,9 +23,10 @@ ** ****************************************************************************/ -import QtQuick 2.1 -import QtQuick.Controls 1.0 as Controls +#include "id.h" -Controls.GroupBox { +/*! + \typedef Core::Id -} \ No newline at end of file + Use Utils::Id instead. +*/ diff --git a/src/plugins/coreplugin/modemanager.cpp b/src/plugins/coreplugin/modemanager.cpp index b7a1f237ab2..d09ec460e51 100644 --- a/src/plugins/coreplugin/modemanager.cpp +++ b/src/plugins/coreplugin/modemanager.cpp @@ -53,15 +53,35 @@ namespace Core { /*! \class Core::ModeManager \inheaderfile coreplugin/modemanager.h + \ingroup mainclasses \inmodule QtCreator - \brief The ModeManager class implements a mode manager. + \brief The ModeManager class manages the activation of modes and the + actions in the mode selector's tool bar. - The mode manager handles everything related to the instances of IMode - that were added to the plugin manager's object pool. + Modes are implemented with the IMode class. Use the ModeManager to + force activation of a mode, or to be notified when the active mode changed. - In addition, it handles the mode buttons and the tool bar buttons in the - lower left corner of \QC. + The ModeManager also manages the actions that are visible in the mode + selector's toolbar. Adding actions to the tool bar should be done very + sparingly. +*/ + +/*! + \enum ModeManager::Style + \internal +*/ + +/*! + \fn void ModeManager::currentModeAboutToChange(Core::Id mode) + + Emitted before the current mode changes to \a mode. +*/ + +/*! + \fn void ModeManager::currentModeChanged(Core::Id mode, Core::Id oldMode) + + Emitted after the current mode changed from \a oldMode to \a mode. */ struct ModeManagerPrivate @@ -132,6 +152,12 @@ ModeManager::~ModeManager() m_instance = nullptr; } +/*! + Returns the id of the current mode. + + \sa activateMode() + \sa currentMode() +*/ Id ModeManager::currentModeId() { int currentIndex = d->m_modeStack->currentIndex(); @@ -148,6 +174,14 @@ static IMode *findMode(Id id) return nullptr; } +/*! + Makes the mode with ID \a id the current mode. + + \sa currentMode() + \sa currentModeId() + \sa currentModeAboutToChange() + \sa currentModeChanged() +*/ void ModeManager::activateMode(Id id) { d->activateModeHelper(id); @@ -254,6 +288,11 @@ void ModeManager::removeMode(IMode *mode) d->m_mainWindow->removeContextObject(mode); } +/*! + Adds the \a action to the mode selector's tool bar. + Actions are sorted by \a priority in descending order. + Use this functionality very sparingly. +*/ void ModeManager::addAction(QAction *action, int priority) { d->m_actions.insert(action, priority); @@ -268,6 +307,9 @@ void ModeManager::addAction(QAction *action, int priority) d->m_actionBar->insertAction(index, action); } +/*! + \internal +*/ void ModeManager::addProjectSelector(QAction *action) { d->m_actionBar->addProjectSelector(action); @@ -306,6 +348,9 @@ void ModeManager::currentTabChanged(int index) emit currentModeChanged(mode->id(), oldMode ? oldMode->id() : Id()); } +/*! + \internal +*/ void ModeManager::setFocusToCurrentMode() { IMode *mode = findMode(currentModeId()); @@ -319,6 +364,9 @@ void ModeManager::setFocusToCurrentMode() } } +/*! + \internal +*/ void ModeManager::setModeStyle(ModeManager::Style style) { const bool visible = style != Style::Hidden; @@ -330,22 +378,37 @@ void ModeManager::setModeStyle(ModeManager::Style style) d->m_modeStack->setSelectionWidgetVisible(visible); } +/*! + \internal +*/ void ModeManager::cycleModeStyle() { auto nextStyle = Style((int(modeStyle()) + 1) % 3); setModeStyle(nextStyle); } +/*! + \internal +*/ ModeManager::Style ModeManager::modeStyle() { return d->m_modeStyle; } +/*! + Returns the pointer to the instance. Only use for connecting to signals. +*/ ModeManager *ModeManager::instance() { return m_instance; } +/*! + Returns a pointer to the current mode. + + \sa activateMode() + \sa currentModeId() +*/ IMode *ModeManager::currentMode() { const int currentIndex = d->m_modeStack->currentIndex(); diff --git a/src/plugins/coreplugin/plugininstallwizard.cpp b/src/plugins/coreplugin/plugininstallwizard.cpp index 7d0b03e6914..59380bd191c 100644 --- a/src/plugins/coreplugin/plugininstallwizard.cpp +++ b/src/plugins/coreplugin/plugininstallwizard.cpp @@ -25,13 +25,18 @@ #include "plugininstallwizard.h" +#include "coreplugin.h" #include "icore.h" +#include + #include #include #include #include #include +#include +#include #include #include #include @@ -40,6 +45,7 @@ #include #include +#include #include #include #include @@ -50,6 +56,7 @@ #include +using namespace ExtensionSystem; using namespace Utils; struct Data @@ -59,6 +66,15 @@ struct Data bool installIntoApplication; }; +static QStringList libraryNameFilter() +{ + if (HostOsInfo().isWindowsHost()) + return {"*.dll"}; + if (HostOsInfo().isLinuxHost()) + return {"*.so"}; + return {"*.dylib"}; +} + static bool hasLibSuffix(const FilePath &path) { return (HostOsInfo().isWindowsHost() && path.endsWith(".dll")) @@ -146,6 +162,12 @@ public: class CheckArchivePage : public WizardPage { public: + struct ArchiveIssue + { + QString message; + InfoLabel::InfoType type; + }; + CheckArchivePage(Data *data, QWidget *parent) : WizardPage(parent) , m_data(data) @@ -155,6 +177,8 @@ public: setLayout(vlayout); m_label = new InfoLabel; + m_label->setElideMode(Qt::ElideNone); + m_label->setWordWrap(true); m_cancelButton = new QPushButton(PluginInstallWizard::tr("Cancel")); m_output = new QTextEdit; m_output->setReadOnly(true); @@ -177,7 +201,8 @@ public: m_tempDir = std::make_unique("plugininstall"); m_data->extractedPath = FilePath::fromString(m_tempDir->path()); m_label->setText(PluginInstallWizard::tr("Checking archive...")); - // m_label->setType(InfoLabel::None); + m_label->setType(InfoLabel::None); + m_cancelButton->setVisible(true); m_output->clear(); @@ -186,18 +211,17 @@ public: if (!m_archive) { m_label->setType(InfoLabel::Error); m_label->setText(PluginInstallWizard::tr("The file is not an archive.")); + return; } QObject::connect(m_archive, &Archive::outputReceived, this, [this](const QString &output) { m_output->append(output); }); QObject::connect(m_archive, &Archive::finished, this, [this](bool success) { - m_cancelButton->setVisible(false); - m_isComplete = success; - if (success) { - m_label->setType(InfoLabel::Ok); - m_label->setText(PluginInstallWizard::tr("Archive is ok.")); - } else { + m_archive = nullptr; // we don't own it + m_cancelButton->disconnect(); + if (!success) { // unarchiving failed + m_cancelButton->setVisible(false); if (m_canceled) { m_label->setType(InfoLabel::Information); m_label->setText(PluginInstallWizard::tr("Canceled.")); @@ -206,9 +230,31 @@ public: m_label->setText( PluginInstallWizard::tr("There was an error while unarchiving.")); } + } else { // unarchiving was successful, run a check + m_archiveCheck = Utils::runAsync( + [this](QFutureInterface &fi) { return checkContents(fi); }); + Utils::onFinished(m_archiveCheck, this, [this](const QFuture &f) { + m_cancelButton->setVisible(false); + m_cancelButton->disconnect(); + const bool ok = f.resultCount() == 0 && !f.isCanceled(); + if (f.isCanceled()) { + m_label->setType(InfoLabel::Information); + m_label->setText(PluginInstallWizard::tr("Canceled.")); + } else if (ok) { + m_label->setType(InfoLabel::Ok); + m_label->setText(PluginInstallWizard::tr("Archive is OK.")); + } else { + const ArchiveIssue issue = f.result(); + m_label->setType(issue.type); + m_label->setText(issue.message); + } + m_isComplete = ok; + emit completeChanged(); + }); + QObject::connect(m_cancelButton, &QPushButton::clicked, this, [this] { + m_archiveCheck.cancel(); + }); } - m_archive = nullptr; // we don't own it - emit completeChanged(); }); QObject::connect(m_cancelButton, &QPushButton::clicked, m_archive, [this] { m_canceled = true; @@ -216,13 +262,60 @@ public: }); } + // Async. Result is set if any issue was found. + void checkContents(QFutureInterface &fi) + { + QTC_ASSERT(m_tempDir.get(), return ); + + PluginSpec *coreplugin = CorePlugin::instance()->pluginSpec(); + + // look for plugin + QDirIterator it(m_tempDir->path(), libraryNameFilter(), QDir::Files | QDir::NoSymLinks); + while (it.hasNext()) { + if (fi.isCanceled()) + return; + it.next(); + PluginSpec *spec = PluginSpec::read(it.filePath()); + if (spec) { + // Is a Qt Creator plugin. Let's see if we find a Core dependency and check the + // version + const QVector dependencies = spec->dependencies(); + const auto found = std::find_if(dependencies.constBegin(), + dependencies.constEnd(), + [coreplugin](const PluginDependency &d) { + return d.name == coreplugin->name(); + }); + if (found != dependencies.constEnd()) { + if (!coreplugin->provides(found->name, found->version)) { + fi.reportResult({PluginInstallWizard::tr( + "Plugin requires an incompatible version of %1 (%2).") + .arg(Constants::IDE_DISPLAY_NAME) + .arg(found->version), + InfoLabel::Error}); + return; + } + } + return; // successful / no error + } + } + fi.reportResult({PluginInstallWizard::tr("Did not find %1 plugin in toplevel directory.") + .arg(Constants::IDE_DISPLAY_NAME), + InfoLabel::Error}); + } + void cleanupPage() { // back button pressed + m_cancelButton->disconnect(); if (m_archive) { + m_archive->disconnect(); m_archive->cancel(); m_archive = nullptr; // we don't own it } + if (m_archiveCheck.isRunning()) { + m_archiveCheck.cancel(); + m_archiveCheck.waitForFinished(); + } m_tempDir.reset(); } @@ -230,6 +323,7 @@ public: std::unique_ptr m_tempDir; Archive *m_archive = nullptr; + QFuture m_archiveCheck; InfoLabel *m_label = nullptr; QPushButton *m_cancelButton = nullptr; QTextEdit *m_output = nullptr; diff --git a/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui b/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui index 69671323e17..f5a80ee0475 100644 --- a/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui +++ b/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui @@ -38,7 +38,7 @@ - Apply configuration changes + Apply Configuration Changes @@ -51,11 +51,11 @@ - Wipe build directory and reconfigure using previous command line options. -Userful when build directory got corrupted, or when rebuilding with a newer version of meson. + Wipes build directory and reconfigures using previous command line options. +Useful if build directory is corrupted or when rebuilding with a newer version of Meson. - Wipe project + Wipe Project diff --git a/src/plugins/projectexplorer/buildaspects.cpp b/src/plugins/projectexplorer/buildaspects.cpp index 912f8857558..0d8a3c3bc8b 100644 --- a/src/plugins/projectexplorer/buildaspects.cpp +++ b/src/plugins/projectexplorer/buildaspects.cpp @@ -127,7 +127,7 @@ void BuildDirectoryAspect::updateProblemLabel() SeparateDebugInfoAspect::SeparateDebugInfoAspect() { - setDisplayName(tr("Separate Debug Info:")); + setDisplayName(tr("Separate debug info:")); setSettingsKey("SeparateDebugInfo"); setSetting(ProjectExplorerPlugin::buildPropertiesSettings().separateDebugInfo); } diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index d2c48827242..e4ea036d77f 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -1075,9 +1075,11 @@ QList GccToolChainFactory::detectForImport(const ToolChainDescripti { const QString fileName = tcd.compilerPath.toFileInfo().completeBaseName(); if ((tcd.language == Constants::C_LANGUAGE_ID && (fileName.startsWith("gcc") - || fileName.endsWith("gcc"))) + || fileName.endsWith("gcc") + || fileName == "cc")) || (tcd.language == Constants::CXX_LANGUAGE_ID && (fileName.startsWith("g++") - || fileName.endsWith("g++")))) + || fileName.endsWith("g++") + || fileName == "c++"))) return autoDetectToolChain(tcd, [](const ToolChain *tc) { return tc->targetAbi().osFlavor() != Abi::WindowsMSysFlavor; }); diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 9ea02a152f0..8a7d3238ef2 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -3631,7 +3631,7 @@ void ProjectExplorerPluginPrivate::removeFile() const QMessageBox::StandardButton reply = QMessageBox::question( Core::ICore::dialogParent(), tr("Remove More Files?"), - tr("Would you like to remove these files as well?\n %1") + tr("Remove these files as well?\n %1") .arg(Utils::transform(siblings, [](const NodeAndPath &np) { return np.second.toFileInfo().fileName(); }).join("\n "))); diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp index 6cf646a27a7..bd28d749b70 100644 --- a/src/plugins/python/pythonutils.cpp +++ b/src/plugins/python/pythonutils.cpp @@ -433,7 +433,7 @@ void PyLSConfigureAssistant::handlePyLSState(const FilePath &python, Utils::InfoBarEntry info(startPylsInfoBarId, message, Utils::InfoBarEntry::GlobalSuppression::Enabled); - info.setCustomButtonInfo(tr("Setup"), + info.setCustomButtonInfo(tr("Set Up"), [=]() { setupPythonLanguageServer(python, document); }); infoBar->addInfo(info); m_infoBarEntries[python] << document; diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index fba34728174..01c35a266c6 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -386,6 +386,13 @@ extend_qtc_plugin(QmlDesigner texteditorwidget.cpp texteditorwidget.h ) +extend_qtc_plugin(QmlDesigner + SOURCES_PREFIX components/listmodeleditor + SOURCES + listmodeleditordialog.cpp listmodeleditordialog.h + listmodeleditormodel.cpp listmodeleditormodel.h +) + extend_qtc_plugin(QmlDesigner SOURCES_PREFIX designercore SOURCES diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp index 53ec46697ee..3fbc85fd559 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp @@ -35,6 +35,7 @@ #include "utils/fileutils.h" #include "utils/outputformatter.h" +#include #include #include #include @@ -86,6 +87,15 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, Core::FileUtils::showInGraphicalShell(Core::ICore::mainWindow(), m_ui->exportPath->path()); }); + auto optionsWidget = new QWidget; + m_ui->advancedOptions->setSummaryText(tr("Advanced Options")); + m_ui->advancedOptions->setWidget(optionsWidget); + auto optionsLayout = new QHBoxLayout(optionsWidget); + optionsLayout->setMargin(8); + m_exportAssetsCheck = new QCheckBox(tr("Export assets"), this); + m_exportAssetsCheck->setChecked(true); + optionsLayout->addWidget(m_exportAssetsCheck); + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_ui->stackedWidget->addWidget(m_filesView); @@ -97,6 +107,7 @@ AssetExportDialog::AssetExportDialog(const Utils::FilePath &exportPath, switchView(false); connect(m_ui->buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, [this]() { + m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); m_assetExporter.cancel(); }); @@ -137,7 +148,8 @@ void AssetExportDialog::onExport() TaskHub::clearTasks(Constants::TASK_CATEGORY_ASSET_EXPORT); m_exportLogs->clear(); - m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName()); + m_assetExporter.exportQml(m_filePathModel.files(), m_ui->exportPath->fileName(), + m_exportAssetsCheck->isChecked()); } void AssetExportDialog::onExportStateChanged(AssetExporter::ParsingState newState) diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h index 7bf68b6a748..e452fff6bcb 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.h @@ -34,6 +34,7 @@ QT_BEGIN_NAMESPACE class QPushButton; +class QCheckBox; class QListView; class QPlainTextEdit; QT_END_NAMESPACE @@ -74,6 +75,7 @@ private: FilePathModel &m_filePathModel; std::unique_ptr m_ui; QPushButton *m_exportBtn = nullptr; + QCheckBox *m_exportAssetsCheck = nullptr; QListView *m_filesView = nullptr; QPlainTextEdit *m_exportLogs = nullptr; Utils::OutputFormatter *m_outputFormatter = nullptr; diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui index 38c2152098c..a4e7ec91d9c 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.ui @@ -14,9 +14,6 @@ Export QML - - - @@ -30,10 +27,10 @@ - + - + 1000 @@ -43,13 +40,26 @@ - + + + + + 0 + 8 + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Close + + + @@ -59,6 +69,12 @@
utils/pathchooser.h
1 + + Utils::DetailsWidget + QWidget +
utils/detailswidget.h
+ 1 +
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp index 26e2d2c455b..1ea1e09e923 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.cpp @@ -25,7 +25,6 @@ #include "assetexporter.h" #include "componentexporter.h" #include "exportnotification.h" -#include "assetexportpluginconstants.h" #include "rewriterview.h" #include "qmlitemnode.h" @@ -117,21 +116,26 @@ void AssetExporter::exportQml(const Utils::FilePaths &qmlFiles, const Utils::Fil ExportNotification::addInfo(tr("Exporting metadata at %1. Export assets: ") .arg(exportPath.toUserOutput()) .arg(exportAssets? tr("Yes") : tr("No"))); - // TODO Asset export notifyProgress(0.0); - Q_UNUSED(exportAssets); m_exportFiles = qmlFiles; + m_totalFileCount = m_exportFiles.count(); m_components = QJsonArray(); m_exportPath = exportPath; m_currentState.change(ParsingState::Parsing); triggerLoadNextFile(); - m_assetDumper = make_unique(); + if (exportAssets) + m_assetDumper = make_unique(); + else + m_assetDumper.reset(); } void AssetExporter::cancel() { - // TODO Cancel export - m_assetDumper.reset(); + if (!m_cancelled) { + ExportNotification::addInfo(tr("Cancelling export.")); + m_assetDumper.reset(); + m_cancelled = true; + } } bool AssetExporter::isBusy() const @@ -141,13 +145,13 @@ bool AssetExporter::isBusy() const m_currentState == AssetExporter::ParsingState::WritingJson; } -Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node) +Utils::FilePath AssetExporter::exportAsset(const QmlObjectNode &node, const QString &uuid) { - // TODO: Use this hash as UUID and add to the node. - QByteArray hash = addNodeUUID(node.modelNode()); - Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png") - .arg(QString::fromLatin1(hash))); - m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath); + if (m_cancelled) + return {}; + Utils::FilePath assetPath = m_exportPath.pathAppended(QString("assets/%1.png").arg(uuid)); + if (m_assetDumper) + m_assetDumper->dumpAsset(node.toQmlItemNode().instanceRenderPixmap(), assetPath); return assetPath; } @@ -157,6 +161,7 @@ void AssetExporter::exportComponent(const ModelNode &rootNode) Component exporter(*this, rootNode); exporter.exportComponent(); m_components.append(exporter.json()); + notifyProgress((m_totalFileCount - m_exportFiles.count()) * 0.8 / m_totalFileCount); } void AssetExporter::notifyLoadError(AssetExporterView::LoadState state) @@ -194,19 +199,13 @@ void AssetExporter::onQmlFileLoaded() triggerLoadNextFile(); } -QByteArray AssetExporter::addNodeUUID(ModelNode node) +QByteArray AssetExporter::generateUuid(const ModelNode &node) { - QByteArray uuid = node.auxiliaryData(Constants::UuidTag).toByteArray(); - qDebug() << node.id() << "UUID" << uuid; - if (uuid.isEmpty()) { - // Assign a new hash. - do { - uuid = generateHash(node.id()); - } while (m_usedHashes.contains(uuid)); - m_usedHashes.insert(uuid); - node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid)); - node.model()->rewriterView()->writeAuxiliaryData(); - } + QByteArray uuid; + do { + uuid = generateHash(node.id()); + } while (m_usedHashes.contains(uuid)); + m_usedHashes.insert(uuid); return uuid; } @@ -217,7 +216,7 @@ void AssetExporter::triggerLoadNextFile() void AssetExporter::loadNextFile() { - if (m_exportFiles.isEmpty()) { + if (m_cancelled || m_exportFiles.isEmpty()) { notifyProgress(0.8); m_currentState.change(ParsingState::ParsingFinished); writeMetadata(); @@ -233,6 +232,13 @@ void AssetExporter::loadNextFile() void AssetExporter::writeMetadata() const { + if (m_cancelled) { + notifyProgress(1.0); + ExportNotification::addInfo(tr("Export cancelled.")); + m_currentState.change(ParsingState::ExportingDone); + return; + } + Utils::FilePath metadataPath = m_exportPath.pathAppended(m_exportPath.fileName() + ".metadata"); ExportNotification::addInfo(tr("Writing metadata to file %1."). arg(metadataPath.toUserOutput())); @@ -253,7 +259,8 @@ void AssetExporter::writeMetadata() const } notifyProgress(1.0); ExportNotification::addInfo(tr("Export finished.")); - m_assetDumper->quitDumper(); + if (m_assetDumper) + m_assetDumper->quitDumper(); m_currentState.change(ParsingState::ExportingDone); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h index 2aa238fb327..01b125167be 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexporter.h @@ -70,7 +70,8 @@ public: void cancel(); bool isBusy() const; - Utils::FilePath exportAsset(const QmlObjectNode& node); + Utils::FilePath exportAsset(const QmlObjectNode& node, const QString &uuid); + QByteArray generateUuid(const ModelNode &node); signals: void stateChanged(ParsingState); @@ -87,8 +88,6 @@ private: void onQmlFileLoaded(); - QByteArray addNodeUUID(ModelNode node); - private: mutable class State { public: @@ -101,10 +100,12 @@ private: ProjectExplorer::Project *m_project = nullptr; AssetExporterView *m_view = nullptr; Utils::FilePaths m_exportFiles; + unsigned int m_totalFileCount = 0; Utils::FilePath m_exportPath; QJsonArray m_components; QSet m_usedHashes; std::unique_ptr m_assetDumper; + bool m_cancelled = false; }; QDebug operator<< (QDebug os, const QmlDesigner::AssetExporter::ParsingState& s); diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h index 1937c7126eb..a1c0e2181ca 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h +++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportpluginconstants.h @@ -61,6 +61,7 @@ const char AssetDataTag[] = "assetData"; const char AssetPathTag[] = "assetPath"; const char AssetBoundsTag[] = "assetBounds"; const char OpacityTag[] = "opacity"; +const char TypeNameTag[] = "qmlType"; const char TextDetailsTag[] = "textDetails"; const char FontFamilyTag[] = "fontFamily"; diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp index 819fa3d328f..059b6ecb161 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.cpp @@ -23,6 +23,9 @@ ** ****************************************************************************/ #include "componentexporter.h" +#include "assetexporter.h" +#include "assetexportpluginconstants.h" +#include "exportnotification.h" #include "parsers/modelnodeparser.h" #include "model.h" @@ -73,6 +76,7 @@ void Component::exportComponent() { QTC_ASSERT(m_rootNode.isValid(), return); m_json = nodeToJson(m_rootNode); + addImports(); } ModelNodeParser *Component::createNodeParser(const ModelNode &node) const @@ -102,8 +106,18 @@ QJsonObject Component::nodeToJson(const ModelNode &node) { QJsonObject jsonObject; std::unique_ptr parser(createNodeParser(node)); - if (parser) + if (parser) { + if (parser->uuid().isEmpty()) { + // Assign an unique identifier to the node. + QByteArray uuid = m_exporter.generateUuid(node); + node.setAuxiliaryData(Constants::UuidAuxTag, QString::fromLatin1(uuid)); + node.model()->rewriterView()->writeAuxiliaryData(); + } jsonObject = parser->json(*this); + } else { + ExportNotification::addError(tr("Error exporting component %1. Parser unavailable.") + .arg(node.id())); + } QJsonArray children; for (const ModelNode &childnode : node.directSubModelNodes()) @@ -112,7 +126,17 @@ QJsonObject Component::nodeToJson(const ModelNode &node) if (!children.isEmpty()) jsonObject.insert("children", children); - return jsonObject; + return jsonObject; +} + +void Component::addImports() +{ + QJsonArray importsArray; + for (const Import &import : m_rootNode.model()->imports()) + importsArray.append(import.toString()); + + if (!importsArray.empty()) + m_json.insert(Constants::ImportsTag, importsArray); } diff --git a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h index e2017b785c2..5e29e10bf0d 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h +++ b/src/plugins/qmldesigner/assetexporterplugin/componentexporter.h @@ -24,8 +24,9 @@ ****************************************************************************/ #pragma once -#include #include +#include +#include #include #include @@ -68,6 +69,8 @@ protected: class Component { + Q_DECLARE_TR_FUNCTIONS(Component); + public: Component(AssetExporter& exporter, const ModelNode &rootNode); @@ -84,6 +87,7 @@ public: private: ModelNodeParser* createNodeParser(const ModelNode &node) const; QJsonObject nodeToJson(const ModelNode &node); + void addImports(); private: AssetExporter& m_exporter; diff --git a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp index 36c175414b5..c0cfeacb972 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/filepathmodel.cpp @@ -41,8 +41,8 @@ Q_LOGGING_CATEGORY(loggerInfo, "qtc.designer.assetExportPlugin.filePathModel", Q void findQmlFiles(QFutureInterface &f, const Project *project) { - if (!project && !f.isCanceled()) - f.reportFinished({}); + if (!project || f.isCanceled()) + return; int index = 0; Utils::FilePaths qmlFiles = project->files([&f, &index](const Node* node) ->bool { @@ -54,7 +54,6 @@ void findQmlFiles(QFutureInterface &f, const Project *project) f.reportResult(path, index++); return true; }); - f.reportFinished(); } } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp index 159eccec460..a42b7300624 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/assetnodeparser.cpp @@ -55,8 +55,7 @@ QJsonObject AssetNodeParser::json(Component &component) const QJsonObject jsonObject = ItemNodeParser::json(component); QPixmap asset = objectNode().toQmlItemNode().instanceRenderPixmap(); - Utils::FilePath assetPath = component.exporter().exportAsset(objectNode()); - + Utils::FilePath assetPath = component.exporter().exportAsset(objectNode(), uuid()); QJsonObject assetData; assetData.insert(AssetPathTag, assetPath.toString()); jsonObject.insert(AssetDataTag, assetData); diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp index 355983f2215..5104732e1c0 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelitemnodeparser.cpp @@ -60,6 +60,10 @@ QJsonObject QmlDesigner::ItemNodeParser::json(QmlDesigner::Component &component) jsonObject.insert(WidthTag, size.width()); jsonObject.insert(HeightTag, size.height()); + jsonObject.insert(UuidTag, uuid()); + jsonObject.insert(ExportTypeTag, "child"); + jsonObject.insert(TypeNameTag, QString::fromLatin1(m_node.type())); + return jsonObject; } } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp index 31787b83cc3..d9cb9b61781 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.cpp @@ -23,6 +23,7 @@ ** ****************************************************************************/ #include "modelnodeparser.h" +#include "assetexportpluginconstants.h" namespace QmlDesigner { ModelNodeParser::ModelNodeParser(const QByteArrayList &lineage, const ModelNode &node) : @@ -38,4 +39,9 @@ QVariant ModelNodeParser::propertyValue(const PropertyName &name) const return m_objectNode.instanceValue(name); } +QString ModelNodeParser::uuid() const +{ + return m_node.auxiliaryData(Constants::UuidAuxTag).toString(); +} + } diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h index 4ca17746e8d..c91b2eae022 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/modelnodeparser.h @@ -47,6 +47,7 @@ public: const QByteArrayList& lineage() const { return m_lineage; } const QmlObjectNode& objectNode() const { return m_objectNode; } QVariant propertyValue(const PropertyName &name) const; + QString uuid() const; protected: const ModelNode &m_node; diff --git a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp index 12b73c4506a..bffe5ed8d5c 100644 --- a/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp +++ b/src/plugins/qmldesigner/assetexporterplugin/parsers/textnodeparser.cpp @@ -42,7 +42,7 @@ const QHash AlignMapping{ QString toJsonAlignEnum(QString value) { if (value.isEmpty() || !AlignMapping.contains(value)) - return ""; + return {}; return AlignMapping[value]; } } diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index 65866ad324a..ac863778162 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -54,7 +54,7 @@ void ActionEditor::registerDeclarativeType() qmlRegisterType("HelperWidgets", 2, 0, "ActionEditor"); } -void ActionEditor::showWidget(int x, int y) +void ActionEditor::prepareDialog() { if (s_lastActionEditor) s_lastActionEditor->hideWidget(); @@ -70,8 +70,18 @@ void ActionEditor::showWidget(int x, int y) this, &ActionEditor::rejected); m_dialog->setAttribute(Qt::WA_DeleteOnClose); +} + +void ActionEditor::showWidget() +{ + prepareDialog(); + m_dialog->showWidget(); +} + +void ActionEditor::showWidget(int x, int y) +{ + prepareDialog(); m_dialog->showWidget(x, y); - m_dialog->activateWindow(); } void ActionEditor::hideWidget() diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index b8133224fc9..7c53dffb603 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -48,6 +48,7 @@ public: static void registerDeclarativeType(); + Q_INVOKABLE void showWidget(); Q_INVOKABLE void showWidget(int x, int y); Q_INVOKABLE void hideWidget(); @@ -69,6 +70,7 @@ private: QVariant backendValue() const; QVariant modelNodeBackend() const; QVariant stateModelNode() const; + void prepareDialog(); private: QPointer m_dialog; diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 2fa19a5c243..a8db25f4176 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -181,6 +181,9 @@ const char addFlowActionToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", const char fitRootToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the root element inside the available space."); const char fitSelectionToScreenToolTip[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Fit the selected elements inside the available space."); +const char editListModelDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", + "Edit List Model..."); + const int priorityFirst = 280; const int prioritySelectionCategory = 220; const int priorityQmlPreviewCategory = 200; diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 479f40203cb..fb7d4ce2029 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -41,10 +41,14 @@ #include #include +#include +#include + #include #include #include +#include #include #include #include @@ -335,6 +339,64 @@ public: } }; +class EditListModelAction final : public ModelNodeContextMenuAction +{ +public: + EditListModelAction() + : ModelNodeContextMenuAction("EditListModel", + ComponentCoreConstants::editListModelDisplayName, + {}, + ComponentCoreConstants::rootCategory, + QKeySequence("Alt+e"), + 1001, + &openDialog, + &isListViewInBaseState, + &isListViewInBaseState) + {} + + static bool isListViewInBaseState(const SelectionContext &selectionState) + { + return selectionState.isInBaseState() && selectionState.singleNodeIsSelected() + && selectionState.currentSingleSelectedNode().metaInfo().isSubclassOf( + "QtQuick.ListView"); + } + + bool isEnabled(const SelectionContext &) const override { return true; } + + static ModelNode listModelNode(const ModelNode &listViewNode) + { + if (listViewNode.hasProperty("model")) { + if (listViewNode.hasBindingProperty("model")) + return listViewNode.bindingProperty("model").resolveToModelNode(); + else if (listViewNode.hasNodeProperty("model")) + return listViewNode.nodeProperty("model").modelNode(); + } + + ModelNode newModel = listViewNode.view()->createModelNode("QtQml.Models.ListModel", 2, 15); + listViewNode.nodeProperty("mode").reparentHere(newModel); + + return newModel; + } + + static void openDialog(const SelectionContext &selectionState) + { + ListModelEditorModel model; + + ModelNode targetNode = selectionState.targetNode(); + if (!targetNode.isValid()) + targetNode = selectionState.currentSingleSelectedNode(); + if (!targetNode.isValid()) + return; + + model.setListModel(listModelNode(targetNode)); + + ListModelEditorDialog dialog{Core::ICore::mainWindow()}; + dialog.setModel(&model); + + dialog.exec(); + } +}; + bool flowOptionVisible(const SelectionContext &context) { return QmlFlowViewNode::isValidQmlFlowViewNode(context.rootNode()); @@ -1217,6 +1279,8 @@ void DesignerActionManager::createDefaultDesignerActions() priorityGenericToolBar)); addDesignerAction(new ChangeStyleAction()); + + addDesignerAction(new EditListModelAction); } void DesignerActionManager::createDefaultAddResourceHandler() diff --git a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp index 49f47dfef27..dcd22bc06b1 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.cpp @@ -51,11 +51,11 @@ AddNewBackendDialog::~AddNewBackendDialog() delete m_ui; } -void AddNewBackendDialog::setupPossibleTypes(const QList &types) +void AddNewBackendDialog::setupPossibleTypes(const QList &types) { QSignalBlocker blocker(this); m_typeData = types; - for (const CppTypeData &typeData : types) + for (const QmlTypeData &typeData : types) m_ui->comboBox->addItem(typeData.typeName); m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(m_ui->comboBox->count() > 0); @@ -67,7 +67,7 @@ QString AddNewBackendDialog::importString() const if (m_ui->comboBox->currentIndex() < 0) return QString(); - CppTypeData typeData = m_typeData.at(m_ui->comboBox->currentIndex()); + QmlTypeData typeData = m_typeData.at(m_ui->comboBox->currentIndex()); return typeData.importUrl + " " + typeData.versionString; } @@ -100,7 +100,7 @@ void AddNewBackendDialog::invalidate() if (m_ui->comboBox->currentIndex() < 0) return; - CppTypeData typeData = m_typeData.at(m_ui->comboBox->currentIndex()); + QmlTypeData typeData = m_typeData.at(m_ui->comboBox->currentIndex()); m_ui->importLabel->setText(importString()); m_ui->checkBox->setChecked(false); diff --git a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h index 4cfa46d66e9..363721c14b9 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h +++ b/src/plugins/qmldesigner/components/connectioneditor/addnewbackenddialog.h @@ -42,7 +42,7 @@ class AddNewBackendDialog : public QDialog public: explicit AddNewBackendDialog(QWidget *parent = nullptr); ~AddNewBackendDialog() override; - void setupPossibleTypes(const QList &types); + void setupPossibleTypes(const QList &types); QString importString() const; QString type() const; bool applied() const; @@ -53,7 +53,7 @@ private: void invalidate(); Ui::AddNewBackendDialog *m_ui; - QList m_typeData; + QList m_typeData; bool m_applied = false; bool m_isSingleton = false; diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp index e17f827025b..d64a9898a82 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.cpp @@ -75,7 +75,7 @@ void BackendModel::resetModel() static const PropertyTypeList simpleTypes = {"int", "real", "color", "string"}; if (rewriterView) - for (const CppTypeData &cppTypeData : rewriterView->getCppTypes()) + for (const QmlTypeData &cppTypeData : rewriterView->getQMLTypes()) if (cppTypeData.isSingleton) { NodeMetaInfo metaInfo = m_connectionView->model()->metaInfo(cppTypeData.typeName.toUtf8()); if (metaInfo.isValid() && !metaInfo.isSubclassOf("QtQuick.Item")) { @@ -146,20 +146,20 @@ QStringList BackendModel::possibleCppTypes() const QStringList list; if (rewriterView) - foreach (const CppTypeData &cppTypeData, rewriterView->getCppTypes()) + foreach (const QmlTypeData &cppTypeData, rewriterView->getQMLTypes()) list.append(cppTypeData.typeName); return list; } -CppTypeData BackendModel::cppTypeDataForType(const QString &typeName) const +QmlTypeData BackendModel::cppTypeDataForType(const QString &typeName) const { RewriterView *rewriterView = m_connectionView->model()->rewriterView(); if (!rewriterView) - return CppTypeData(); + return QmlTypeData(); - return Utils::findOr(rewriterView->getCppTypes(), CppTypeData(), [&typeName](const CppTypeData &data) { + return Utils::findOr(rewriterView->getQMLTypes(), QmlTypeData(), [&typeName](const QmlTypeData &data) { return typeName == data.typeName; }); } @@ -173,7 +173,7 @@ void BackendModel::deletePropertyByRow(int rowNumber) /* singleton case remove the import */ if (data(index(rowNumber, 0), Qt::UserRole + 1).toBool()) { const QString typeName = data(index(rowNumber, 0), Qt::UserRole + 1).toString(); - CppTypeData cppTypeData = cppTypeDataForType(typeName); + QmlTypeData cppTypeData = cppTypeDataForType(typeName); if (cppTypeData.isSingleton) { @@ -214,7 +214,7 @@ void BackendModel::addNewBackend() QStringList availableTypes; if (rewriterView) - dialog.setupPossibleTypes(Utils::filtered(rewriterView->getCppTypes(), [model](const CppTypeData &cppTypeData) { + dialog.setupPossibleTypes(Utils::filtered(rewriterView->getQMLTypes(), [model](const QmlTypeData &cppTypeData) { return !cppTypeData.isSingleton || !model->metaInfo(cppTypeData.typeName.toUtf8()).isValid(); /* Only show singletons if the import is missing */ })); diff --git a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h index 8abbbe77fa1..2f355c71922 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h +++ b/src/plugins/qmldesigner/components/connectioneditor/backendmodel.h @@ -52,7 +52,7 @@ public: void resetModel(); QStringList possibleCppTypes() const; - CppTypeData cppTypeDataForType(const QString &typeName) const; + QmlTypeData cppTypeDataForType(const QString &typeName) const; void deletePropertyByRow(int rowNumber); diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp index f9710f1e8d2..2a17c2ec6d1 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp @@ -45,7 +45,7 @@ namespace { QStringList propertyNameListToStringList(const QmlDesigner::PropertyNameList &propertyNameList) { QStringList stringList; - foreach (QmlDesigner::PropertyName propertyName, propertyNameList) { + for (const QmlDesigner::PropertyName propertyName : propertyNameList) { stringList << QString::fromUtf8(propertyName); } stringList.removeDuplicates(); @@ -58,7 +58,6 @@ bool isConnection(const QmlDesigner::ModelNode &modelNode) || modelNode.type() == "QtQuick.Connections" || modelNode.type() == "Qt.Connections" || modelNode.type() == "QtQml.Connections"); - } } //namespace @@ -81,7 +80,7 @@ void ConnectionModel::resetModel() setHorizontalHeaderLabels(QStringList({ tr("Target"), tr("Signal Handler"), tr("Action") })); if (connectionView()->isAttached()) { - foreach (const ModelNode modelNode, connectionView()->allModelNodes()) + for (const ModelNode modelNode : connectionView()->allModelNodes()) addModelNode(modelNode); } @@ -96,8 +95,7 @@ SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt(); const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString(); - ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); - + ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId); if (modelNode.isValid()) return modelNode.signalHandlerProperty(targetPropertyName.toUtf8()); @@ -112,7 +110,7 @@ void ConnectionModel::addModelNode(const ModelNode &modelNode) void ConnectionModel::addConnection(const ModelNode &modelNode) { - foreach (const AbstractProperty &property, modelNode.properties()) { + for (const AbstractProperty &property : modelNode.properties()) { if (property.isSignalHandlerProperty() && property.name() != "target") { addSignalHandler(property.toSignalHandlerProperty()); } @@ -179,7 +177,6 @@ void ConnectionModel::updateSource(int row) m_exceptionError = e.description(); QTimer::singleShot(200, this, &ConnectionModel::handleException); } - } void ConnectionModel::updateSignalName(int rowNumber) @@ -211,8 +208,14 @@ void ConnectionModel::updateTargetNode(int rowNumber) const QString newTarget = data(index(rowNumber, TargetModelNodeRow)).toString(); ModelNode connectionNode = signalHandlerProperty.parentModelNode(); + const bool isAlias = newTarget.contains("."); + if (!newTarget.isEmpty()) { - const ModelNode parent = connectionView()->modelNodeForId(newTarget); + //if it's an alias, then let's reparent connections to alias property owner: + const ModelNode parent = connectionView()->modelNodeForId(isAlias + ? newTarget.split(".").constFirst() + : newTarget); + if (parent.isValid() && QmlItemNode::isValidQmlItemNode(parent)) parent.nodeListProperty("data").reparentHere(connectionNode); @@ -236,8 +239,28 @@ ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connectio BindingProperty bindingProperty = connection.bindingProperty("target"); if (bindingProperty.isValid()) { + bool isAlias = bindingProperty.expression().contains("."); + if (bindingProperty.expression() == QLatin1String("parent")) return connection.parentProperty().parentModelNode(); + else if (isAlias) { + QStringList substr = bindingProperty.expression().split("."); + if (substr.size() > 1) { + ModelNode aliasParent = connectionView()->modelNodeForId(substr.constFirst()); + QString aliasBody = substr.at(1); + if (aliasParent.hasProperty(aliasBody.toUtf8())) { + AbstractProperty abstractProp = aliasParent.property(aliasBody.toUtf8()); + if (abstractProp.isBindingProperty()) { + BindingProperty binding = abstractProp.toBindingProperty(); + if (connectionView()->hasId(binding.expression())) { + ModelNode resolve = connectionView()->modelNodeForId(binding.expression()); + if (resolve.isValid()) + return resolve; + } + } + } + } + } return connectionView()->modelNodeForId(bindingProperty.expression()); } @@ -376,9 +399,8 @@ QStringList ConnectionModel::getSignalsForRow(int row) const QStringList stringList; SignalHandlerProperty signalHandlerProperty = signalHandlerPropertyForRow(row); - if (signalHandlerProperty.isValid()) { + if (signalHandlerProperty.isValid()) stringList.append(getPossibleSignalsForConnection(signalHandlerProperty.parentModelNode())); - } return stringList; } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp index 00f32a29c57..25ea5814a90 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp @@ -161,7 +161,7 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) menu.addAction(tr("Open Connection Editor"), [&]() { if (index.isValid()) { - m_connectonEditor->showWidget(mapToGlobal(event->pos()).x(), mapToGlobal(event->pos()).y()); + m_connectonEditor->showWidget(); m_connectonEditor->setBindingValue(index.data().toString()); m_connectonEditor->setModelIndex(index); m_connectonEditor->updateWindowName(); @@ -460,11 +460,14 @@ void ConnectionViewWidget::editorForConnection() if (m_connectonEditor->hasModelIndex()) { ConnectionModel *connectionModel = qobject_cast(ui->connectionView->model()); if (connectionModel->connectionView()->isWidgetEnabled() - && (connectionModel->rowCount() > m_connectonEditor->modelIndex().row())) - { - SignalHandlerProperty signalHandler = - connectionModel->signalHandlerPropertyForRow(m_connectonEditor->modelIndex().row()); - signalHandler.setSource(m_connectonEditor->bindingValue()); + && (connectionModel->rowCount() > m_connectonEditor->modelIndex().row())) { + connectionModel->connectionView() + ->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() { + SignalHandlerProperty signalHandler + = connectionModel->signalHandlerPropertyForRow( + m_connectonEditor->modelIndex().row()); + signalHandler.setSource(m_connectonEditor->bindingValue()); + }); } m_connectonEditor->resetModelIndex(); } @@ -487,20 +490,24 @@ void ConnectionViewWidget::editorForBinding() if (m_bindingIndex.isValid()) { if (bindingModel->connectionView()->isWidgetEnabled() - && (bindingModel->rowCount() > m_bindingIndex.row())) - { - BindingProperty property = bindingModel->bindingPropertyForRow(m_bindingIndex.row()); + && (bindingModel->rowCount() > m_bindingIndex.row())) { + bindingModel->connectionView()->executeInTransaction( + "ConnectionView::setBindingProperty", [this, bindingModel, newValue]() { + BindingProperty property = bindingModel->bindingPropertyForRow( + m_bindingIndex.row()); - if (property.isValid()) { - if (property.isBindingProperty()) { - if (property.isDynamic()) { - property.setDynamicTypeNameAndExpression(property.dynamicTypeName(), newValue); + if (property.isValid()) { + if (property.isBindingProperty()) { + if (property.isDynamic()) { + property + .setDynamicTypeNameAndExpression(property.dynamicTypeName(), + newValue); + } else { + property.setExpression(newValue); + } + } } - else { - property.setExpression(newValue); - } - } - } + }); } } @@ -523,29 +530,34 @@ void ConnectionViewWidget::editorForDynamic() if (m_dynamicIndex.isValid()) { if (propertiesModel->connectionView()->isWidgetEnabled() - && (propertiesModel->rowCount() > m_dynamicIndex.row())) - { - AbstractProperty abProp = propertiesModel->abstractPropertyForRow(m_dynamicIndex.row()); + && (propertiesModel->rowCount() > m_dynamicIndex.row())) { + propertiesModel->connectionView()->executeInTransaction( + "ConnectionView::setBinding", [this, propertiesModel, newValue]() { + AbstractProperty abProp = propertiesModel->abstractPropertyForRow( + m_dynamicIndex.row()); - if (abProp.isValid()) { - if (abProp.isBindingProperty()) { - BindingProperty property = abProp.toBindingProperty(); - property.setDynamicTypeNameAndExpression(property.dynamicTypeName(), newValue); - } + if (abProp.isValid()) { + if (abProp.isBindingProperty()) { + BindingProperty property = abProp.toBindingProperty(); + property.setDynamicTypeNameAndExpression(property.dynamicTypeName(), + newValue); + } - //if it's a variant property, then we remove it and replace with binding - else if (abProp.isVariantProperty()) { - VariantProperty property = abProp.toVariantProperty(); - PropertyName name = property.name(); - TypeName type = property.dynamicTypeName(); - QVariant value = newValue; + //if it's a variant property, then we remove it and replace with binding + else if (abProp.isVariantProperty()) { + VariantProperty property = abProp.toVariantProperty(); + PropertyName name = property.name(); + TypeName type = property.dynamicTypeName(); + QVariant value = newValue; - BindingProperty newProperty = propertiesModel->replaceVariantWithBinding(name); - if (newProperty.isValid()) { - newProperty.setDynamicTypeNameAndExpression(type, newValue); + BindingProperty newProperty = propertiesModel + ->replaceVariantWithBinding(name); + if (newProperty.isValid()) { + newProperty.setDynamicTypeNameAndExpression(type, newValue); + } + } } - } - } + }); } } diff --git a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp index 82e97fae210..8107690cea3 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/delegates.cpp @@ -46,7 +46,7 @@ namespace Internal { QStringList prependOnForSignalHandler(const QStringList &signalNames) { QStringList signalHandlerNames; - foreach (const QString &signalName, signalNames) { + for (const QString &signalName : signalNames) { QString signalHandlerName = signalName; if (!signalHandlerName.isEmpty()) { QChar firstChar = signalHandlerName.at(0).toUpper(); @@ -284,9 +284,19 @@ QWidget *ConnectionDelegate::createEditor(QWidget *parent, const QStyleOptionVie switch (index.column()) { case ConnectionModel::TargetModelNodeRow: { - foreach (const ModelNode &modelNode, connectionModel->connectionView()->allModelNodes()) { + for (const ModelNode &modelNode : connectionModel->connectionView()->allModelNodes()) { if (!modelNode.id().isEmpty()) { connectionComboBox->addItem(modelNode.id()); + + for (const BindingProperty &property : modelNode.bindingProperties()) { + if (property.isValid()) { + if (property.isAlias()) { + connectionComboBox->addItem(modelNode.id() + + "." + + QString::fromUtf8(property.name())); + } + } + } } } } break; diff --git a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp index cdbc1ee7312..5af183e6b11 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/dynamicpropertiesmodel.cpp @@ -399,7 +399,6 @@ QStringList DynamicPropertiesModel::possibleSourceProperties(const BindingProper if (metaInfo.propertyTypeName(propertyName) == typeName) //### todo proper check possibleProperties << QString::fromUtf8(propertyName); } - return possibleProperties; } else { qWarning() << " BindingModel::possibleSourcePropertiesForRow no meta info for source node"; diff --git a/src/plugins/qmldesigner/components/debugview/debugview.cpp b/src/plugins/qmldesigner/components/debugview/debugview.cpp index 59f4f9a2de6..6733209c838 100644 --- a/src/plugins/qmldesigner/components/debugview/debugview.cpp +++ b/src/plugins/qmldesigner/components/debugview/debugview.cpp @@ -29,9 +29,10 @@ #include #include -#include #include #include +#include +#include #include #include @@ -234,6 +235,13 @@ void DebugView::selectedNodesChanged(const QList &selectedNodes /*sel message << lineBreak; + if (selectedNode.metaInfo().isValid()) { + for (const NodeMetaInfo &metaInfo : selectedNode.metaInfo().classHierarchy()) + message << metaInfo.typeName() << lineBreak; + + message << lineBreak; + } + const QHash data = selectedNode.auxiliaryData(); PropertyNameList names = data.keys(); diff --git a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp index fda18bdf45f..87117f6318a 100644 --- a/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp +++ b/src/plugins/qmldesigner/components/edit3d/edit3dview.cpp @@ -268,7 +268,7 @@ void Edit3DView::createEdit3DActions() m_showGridAction = new Edit3DAction( QmlDesigner::Constants::EDIT3D_EDIT_SHOW_GRID, View3DActionCommand::ShowGrid, - QCoreApplication::translate("ShowGridAction", "Toggle grid visibility"), + QCoreApplication::translate("ShowGridAction", "Toggle Grid Visibility"), QKeySequence(Qt::Key_G), true, true, Icons::EDIT3D_GRID_OFF.icon(), Icons::EDIT3D_GRID_ON.icon()); diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditor.pri b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditor.pri new file mode 100644 index 00000000000..cd6938aab4b --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditor.pri @@ -0,0 +1,7 @@ +SOURCES += \ + $$PWD/listmodeleditordialog.cpp \ + $$PWD/listmodeleditormodel.cpp + +HEADERS += \ + $$PWD/listmodeleditordialog.h \ + $$PWD/listmodeleditormodel.h diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp new file mode 100644 index 00000000000..283acab78bb --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#include "listmodeleditordialog.h" +#include "listmodeleditormodel.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace QmlDesigner { + +namespace { +QIcon getIcon(Theme::Icon icon) +{ + const QString fontName = "qtds_propertyIconFont.ttf"; + + return Utils::StyleHelper::getIconFromIconFont(fontName, Theme::getIconUnicode(icon), 30, 30); +} +} // namespace + +ListModelEditorDialog::ListModelEditorDialog(QWidget *parent) + : QDialog(parent) +{ + resize((Core::ICore::mainWindow()->size() * 8) / 10); + + QVBoxLayout *mainLayout = new QVBoxLayout(this); + + QToolBar *toolBar = new QToolBar(); + toolBar->setIconSize({30, 30}); + mainLayout->addWidget(toolBar); + m_tableView = new QTableView; + mainLayout->addWidget(m_tableView); + + m_addRowAction = toolBar->addAction(getIcon(Theme::Icon::addRowAfter), tr("Add Row")); + m_removeRowsAction = toolBar->addAction(getIcon(Theme::Icon::deleteRow), tr("Remove Columns")); + m_addColumnAction = toolBar->addAction(getIcon(Theme::Icon::addColumnAfter), tr("Add Column")); + m_removeColumnsAction = toolBar->addAction(getIcon(Theme::Icon::deleteColumn), + tr("Remove Columns")); +} + +ListModelEditorDialog::~ListModelEditorDialog() = default; + +void ListModelEditorDialog::setModel(ListModelEditorModel *model) +{ + m_model = model; + + connect(m_addRowAction, &QAction::triggered, m_model, &ListModelEditorModel::addRow); + connect(m_addColumnAction, &QAction::triggered, this, &ListModelEditorDialog::openColumnDialog); + connect(m_removeRowsAction, &QAction::triggered, this, &ListModelEditorDialog::removeRows); + connect(m_removeColumnsAction, &QAction::triggered, this, &ListModelEditorDialog::removeColumns); + connect(m_tableView->horizontalHeader(), + &QHeaderView::sectionDoubleClicked, + this, + &ListModelEditorDialog::changeHeader); + + m_tableView->setModel(model); + m_tableView->horizontalHeader()->setMinimumSectionSize(60); + m_tableView->verticalHeader()->setMinimumSectionSize(25); + m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); +} + +void ListModelEditorDialog::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { + for (const QModelIndex index : m_tableView->selectionModel()->selectedIndexes()) + m_model->setData(index, QVariant(), Qt::EditRole); + } +} + +void ListModelEditorDialog::openColumnDialog() +{ + bool ok; + QString columnName = QInputDialog::getText( + this, tr("Add Property"), tr("Property Name:"), QLineEdit::Normal, "", &ok); + if (ok && !columnName.isEmpty()) + m_model->addColumn(columnName); +} + +void ListModelEditorDialog::removeRows() +{ + const QList indices = m_tableView->selectionModel()->selectedRows(); + std::vector rows; + rows.reserve(indices.size()); + + for (QModelIndex index : indices) + rows.push_back(index.row()); + + std::sort(rows.begin(), rows.end()); + + rows.erase(std::unique(rows.begin(), rows.end()), rows.end()); + + std::reverse(rows.begin(), rows.end()); + + for (int row : rows) + m_model->removeRow(row); +} + +void ListModelEditorDialog::removeColumns() +{ + const QList indices = m_tableView->selectionModel()->selectedColumns(); + std::vector columns; + columns.reserve(indices.size()); + + for (QModelIndex index : indices) + columns.push_back(index.column()); + + std::sort(columns.begin(), columns.end()); + + columns.erase(std::unique(columns.begin(), columns.end()), columns.end()); + + std::reverse(columns.begin(), columns.end()); + + for (int row : columns) + m_model->removeColumn(row); +} + +void ListModelEditorDialog::changeHeader(int column) +{ + const QString propertyName = QString::fromUtf8(m_model->propertyNames()[column]); + + bool ok; + QString newPropertyName = QInputDialog::getText( + this, tr("Change Propertry"), tr("Column Name:"), QLineEdit::Normal, propertyName, &ok); + + if (ok && !newPropertyName.isEmpty()) + m_model->renameColumn(column, newPropertyName); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h new file mode 100644 index 00000000000..519d0869fae --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditordialog.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +class QTableView; +QT_END_NAMESPACE + +namespace Ui { +class ListModelEditorDialog; +} + +namespace QmlDesigner { + +class ListModelEditorModel; + +class ListModelEditorDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ListModelEditorDialog(QWidget *parent = nullptr); + ~ListModelEditorDialog(); + + void setModel(ListModelEditorModel *model); + +protected: + void keyPressEvent(QKeyEvent *) override; + +private: + void addRow(); + void openColumnDialog(); + void removeRows(); + void removeColumns(); + void changeHeader(int column); + +private: + ListModelEditorModel *m_model{}; + QAction *m_addRowAction{}; + QAction *m_removeRowsAction{}; + QAction *m_addColumnAction{}; + QAction *m_removeColumnsAction{}; + QTableView *m_tableView{}; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp new file mode 100644 index 00000000000..98722c3e8fb --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#include "listmodeleditormodel.h" + +#include +#include +#include + +#include + +#include +#include +#include + +namespace QmlDesigner { + +class ListModelItem : public QStandardItem +{ +public: + ListModelItem(ModelNode node, PropertyName propertyName) + : node(std::move(node)) + , propertyName(propertyName) + { + setEditable(true); + } + + QVariant maybeConvertToNumber(const QVariant &value) + { + bool canConvert = false; + double convertedValue = value.toDouble(&canConvert); + if (canConvert) { + return convertedValue; + } + + return value; + } + + QVariant data(int role) const override + { + if (role == Qt::BackgroundColorRole && hasInvalidValue) + return QColor{Qt::darkYellow}; + + return QStandardItem::data(role); + } + + void setData(const QVariant &value, int role) override + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + hasInvalidValue = !value.isValid(); + + if (role == Qt::EditRole) { + const QVariant &convertedValue = maybeConvertToNumber(value); + QStandardItem::setData(convertedValue, role); + if (value.isValid()) + node.variantProperty(propertyName).setValue(convertedValue); + else + node.removeProperty(propertyName); + } else { + QStandardItem::setData(value, role); + } + } + + void removeProperty() { node.removeProperty(propertyName); } + + void renameProperty(const PropertyName &newPropertyName) + { + if (node.hasProperty(propertyName)) { + node.removeProperty(propertyName); + node.variantProperty(newPropertyName).setValue(data(Qt::EditRole)); + } + propertyName = newPropertyName; + } + +public: + ModelNode node; + PropertyName propertyName; + bool hasInvalidValue = false; +}; + +namespace { +QList getPropertyNames(const ModelNode &listElementNode) +{ + auto properties = listElementNode.variantProperties(); + + QList names; + names.reserve(properties.size()); + + for (const auto &property : properties) + names.push_back(property.name()); + + std::sort(names.begin(), names.end()); + + return names; +} + +QList mergeProperyNames(const QList &first, + const QList &second) +{ + QList merged; + merged.reserve(first.size() + second.size()); + + std::set_union(first.begin(), + first.end(), + second.begin(), + second.end(), + std::back_inserter(merged)); + + return merged; +} + +std::unique_ptr createItem(const ModelNode &listElementNode, + const PropertyName &propertyName) +{ + auto item = std::make_unique(listElementNode, propertyName); + + QVariant value = listElementNode.variantProperty(propertyName).value(); + + item->setData(value, Qt::DisplayRole); + + return item; +} + +QList convertToStringList(const QList &propertyNames) +{ + QList names; + names.reserve(propertyNames.size()); + + for (const auto &propertyName : propertyNames) + names.push_back(QString::fromUtf8(propertyName)); + + return names; +} + +QList createProperyNames(const QList &listElementNodes) +{ + QList propertyNames; + propertyNames.reserve(10); + + for (const ModelNode &listElementNode : listElementNodes) + propertyNames = mergeProperyNames(getPropertyNames(listElementNode), propertyNames); + + return propertyNames; +} + +QList createColumnItems(const ModelNode &listModelNode, + const PropertyName &propertyName) +{ + QList items; + const auto listElementNodes = listModelNode.defaultNodeListProperty().toModelNodeList(); + + for (const ModelNode &listElementNode : listElementNodes) + items.push_back(createItem(listElementNode, propertyName).release()); + + return items; +} + +void renameProperties(const QStandardItemModel *model, + int columnIndex, + const PropertyName &newPropertyName) +{ + for (int rowIndex = 0; rowIndex < model->rowCount(); ++rowIndex) + static_cast(model->item(rowIndex, columnIndex))->renameProperty(newPropertyName); +} + +} // namespace + +void ListModelEditorModel::populateModel() +{ + const auto listElementNodes = m_listModelNode.defaultNodeListProperty().toModelNodeList(); + + m_propertyNames = createProperyNames(listElementNodes); + + setHorizontalHeaderLabels(convertToStringList(m_propertyNames)); + + createItems(listElementNodes); +} + +void ListModelEditorModel::createItems(const QList &listElementNodes) +{ + for (const ModelNode &listElementNode : listElementNodes) + appendItems(listElementNode); +} + +void ListModelEditorModel::appendItems(const ModelNode &listElementNode) +{ + QList row; + row.reserve(m_propertyNames.size()); + for (const PropertyName &propertyName : propertyNames()) + row.push_back(createItem(listElementNode, propertyName).release()); + + appendRow(row); +} + +void ListModelEditorModel::addRow() +{ + auto newElement = m_listModelNode.view()->createModelNode("QtQml.Models.ListElement", 2, 15); + m_listModelNode.defaultNodeListProperty().reparentHere(newElement); + + appendItems(newElement); +} + +void ListModelEditorModel::addColumn(const QString &columnName) +{ + PropertyName propertyName = columnName.toUtf8(); + + auto found = std::lower_bound(m_propertyNames.begin(), m_propertyNames.end(), propertyName); + + if (found != m_propertyNames.end() && *found == columnName) + return; + + int newIndex = static_cast(std::distance(m_propertyNames.begin(), found)); + + m_propertyNames.insert(found, propertyName); + + insertColumn(newIndex, createColumnItems(m_listModelNode, propertyName)); + + setHorizontalHeaderItem(newIndex, new QStandardItem(columnName)); +} + +bool ListModelEditorModel::setValue(int row, int column, QVariant value, Qt::ItemDataRole role) +{ + QModelIndex index = createIndex(row, column, invisibleRootItem()); + bool success = setData(index, value, role); + emit dataChanged(index, index); + + return success; +} + +void ListModelEditorModel::removeColumn(int column) +{ + QList columnItems = QStandardItemModel::takeColumn(column); + m_propertyNames.removeAt(column); + + for (QStandardItem *columnItem : columnItems) { + static_cast(columnItem)->removeProperty(); + delete columnItem; + } +} + +void ListModelEditorModel::removeRow(int row) +{ + QList rowItems = QStandardItemModel::takeRow(row); + + if (rowItems.size()) + static_cast(rowItems.front())->node.destroy(); + + qDeleteAll(rowItems); +} + +void ListModelEditorModel::renameColumn(int oldColumn, const QString &newColumnName) +{ + const PropertyName newPropertyName = newColumnName.toUtf8(); + + auto found = std::lower_bound(m_propertyNames.begin(), m_propertyNames.end(), newPropertyName); + + if (found != m_propertyNames.end() && *found == newPropertyName) + return; + + int newColumn = static_cast(std::distance(m_propertyNames.begin(), found)); + + if (oldColumn == newColumn) { + *found = newPropertyName; + renameProperties(this, newColumn, newPropertyName); + } else if (newColumn < oldColumn) { + m_propertyNames.insert(found, newPropertyName); + m_propertyNames.erase(std::next(m_propertyNames.begin(), oldColumn + 1)); + insertColumn(newColumn, takeColumn(oldColumn)); + renameProperties(this, newColumn, newPropertyName); + } else { + m_propertyNames.insert(found, newPropertyName); + m_propertyNames.erase(std::next(m_propertyNames.begin(), oldColumn)); + insertColumn(newColumn - 1, takeColumn(oldColumn)); + renameProperties(this, newColumn - 1, newPropertyName); + } + + setHorizontalHeaderLabels(convertToStringList(m_propertyNames)); +} + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h new file mode 100644 index 00000000000..35d41bee68b --- /dev/null +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +namespace QmlDesigner { + +class ListModelEditorModel : public QStandardItemModel +{ + +public: + void setListModel(ModelNode node) + { + m_listModelNode = node; + populateModel(); + } + + void addRow(); + void addColumn(const QString &columnName); + + const QList &propertyNames() const { return m_propertyNames; } + + bool setValue(int row, int column, QVariant value, Qt::ItemDataRole role = Qt::EditRole); + + void removeColumn(int column); + void removeRow(int row); + void renameColumn(int column, const QString &newColumnName); + +private: + void populateModel(); + void createItems(const QList &listElementNodes); + void appendItems(const ModelNode &listElementNode); + +private: + ModelNode m_listModelNode; + QList m_propertyNames; +}; + +} // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri b/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri index 8f9a9dec9eb..0b3a9aa9e9f 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditor.pri @@ -16,7 +16,7 @@ SOURCES += \ transitionform.cpp HEADERS += \ - transitioneditorconstants \ + transitioneditorconstants.h \ transitioneditorview.h \ transitioneditorwidget.h \ transitioneditortoolbar.h \ diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h index aba42b599d8..5dd3c29ddd9 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorpropertyitem.h @@ -67,7 +67,7 @@ private: TransitionEditorGraphicsScene *transitionEditorGraphicsScene() const; ModelNode m_animation; - TransitionEditorBarItem *m_barItem; + TransitionEditorBarItem *m_barItem = nullptr; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h index 22aa21dd288..d7ce78f56c2 100644 --- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h +++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h @@ -137,9 +137,8 @@ private: ModelNode m_targetNode; ModelNode m_animationNode; - TransitionEditorBarItem *m_barItem; - TimelineItem *m_dummyItem; - + TransitionEditorBarItem *m_barItem = nullptr; + TimelineItem *m_dummyItem = nullptr; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/designercore/exceptions/exception.cpp b/src/plugins/qmldesigner/designercore/exceptions/exception.cpp index 486e5bec2bd..66106387481 100644 --- a/src/plugins/qmldesigner/designercore/exceptions/exception.cpp +++ b/src/plugins/qmldesigner/designercore/exceptions/exception.cpp @@ -34,8 +34,8 @@ #include -#include #ifndef QMLDESIGNER_TEST +#include #include #endif @@ -160,8 +160,12 @@ QString Exception::description() const */ void Exception::showException(const QString &title) const { - QString composedTitle = title.isEmpty() ? QCoreApplication::translate("QmlDesigner", "Error") : title; + Q_UNUSED(title) +#ifndef QMLDESIGNER_TEST + QString composedTitle = title.isEmpty() ? QCoreApplication::translate("QmlDesigner", "Error") + : title; Core::AsynchronousMessageBox::warning(composedTitle, description()); +#endif } /*! diff --git a/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri b/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri index 848d7808fc2..6fbafdee3c3 100644 --- a/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri +++ b/src/plugins/qmldesigner/designercore/exceptions/exceptions.pri @@ -1,2 +1,14 @@ -SOURCES += $$PWD/exception.cpp -SOURCES += $$PWD/invalidnodeinstanceexception.cpp +SOURCES += $$PWD/exception.cpp \ + $$PWD/invalidargumentexception.cpp \ + $$PWD/invalididexception.cpp \ + $$PWD/invalidmetainfoexception.cpp \ + $$PWD/invalidmodelnodeexception.cpp \ + $$PWD/invalidmodelstateexception.cpp \ + $$PWD/invalidpropertyexception.cpp \ + $$PWD/invalidqmlsourceexception.cpp \ + $$PWD/invalidreparentingexception.cpp \ + $$PWD/invalidslideindexexception.cpp \ + $$PWD/notimplementedexception.cpp \ + $$PWD/removebasestateexception.cpp \ + $$PWD/rewritingexception.cpp + diff --git a/src/plugins/qmldesigner/designercore/include/bindingproperty.h b/src/plugins/qmldesigner/designercore/include/bindingproperty.h index cea09aeb195..c0442a678d2 100644 --- a/src/plugins/qmldesigner/designercore/include/bindingproperty.h +++ b/src/plugins/qmldesigner/designercore/include/bindingproperty.h @@ -56,8 +56,10 @@ public: static void deleteAllReferencesTo(const ModelNode &modelNode); + bool isAlias() const; bool isAliasExport() const; + protected: BindingProperty(const PropertyName &propertyName, const Internal::InternalNodePointer &internalNode, Model* model, AbstractView *view); }; diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h index 6f3d5eb6dfb..3197e746ab5 100644 --- a/src/plugins/qmldesigner/designercore/include/modelnode.h +++ b/src/plugins/qmldesigner/designercore/include/modelnode.h @@ -91,9 +91,12 @@ public: ModelNode(const Internal::InternalNodePointer &internalNode, Model *model, const AbstractView *view); ModelNode(const ModelNode &modelNode, AbstractView *view); ModelNode(const ModelNode &other); + ModelNode(ModelNode &&other); ~ModelNode(); - ModelNode& operator=(const ModelNode &other); + ModelNode &operator=(const ModelNode &other); + ModelNode &operator=(ModelNode &&other); + TypeName type() const; QString simplifiedTypeName() const; QString displayName() const; @@ -226,6 +229,15 @@ public: bool isSubclassOf(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; QIcon typeIcon() const; + friend void swap(ModelNode &first, ModelNode &second) + { + using std::swap; + + swap(first.m_internalNode, second.m_internalNode); + swap(first.m_model, second.m_model); + swap(first.m_view, second.m_view); + } + private: // functions Internal::InternalNodePointer internalNode() const; diff --git a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h index 0872c547c83..552095a8d6d 100644 --- a/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h +++ b/src/plugins/qmldesigner/designercore/include/qmldesignercorelib_global.h @@ -31,10 +31,13 @@ // Unnecessary since core isn't a dll any more. #if defined(DESIGNER_CORE_LIBRARY) -# define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT +#define QMLDESIGNERCORE_EXPORT Q_DECL_EXPORT +#elif defined(DESIGNER_STATIC_CORE_LIBRARY) +#define QMLDESIGNERCORE_EXPORT #else -# define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT +#define QMLDESIGNERCORE_EXPORT Q_DECL_IMPORT #endif + namespace QmlDesigner { using PropertyName = QByteArray; using PropertyNameList = QList; diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h index dcd85c950d0..0601da3acf4 100644 --- a/src/plugins/qmldesigner/designercore/include/rewriterview.h +++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h @@ -53,7 +53,7 @@ class ModelNodePositionStorage; } //Internal -struct CppTypeData +struct QmlTypeData { QString superClassName; QString importUrl; @@ -61,6 +61,7 @@ struct CppTypeData QString cppClassName; QString typeName; bool isSingleton = false; + bool isCppType = false; }; class QMLDESIGNERCORE_EXPORT RewriterView : public AbstractView @@ -158,7 +159,7 @@ public: QStringList autoComplete(const QString &text, int pos, bool explicitComplete = true); - QList getCppTypes(); + QList getQMLTypes() const; void setWidgetStatusCallback(std::function setWidgetStatusCallback); diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp index 2e3b5be32b4..7cf2784a7d3 100644 --- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp +++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp @@ -958,7 +958,7 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand() QVector mockupTypesVector; - for (const CppTypeData &cppTypeData : model()->rewriterView()->getCppTypes()) { + for (const QmlTypeData &cppTypeData : model()->rewriterView()->getQMLTypes()) { const QString versionString = cppTypeData.versionString; int majorVersion = -1; int minorVersion = -1; diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp index 03ac27a5b0c..3be64be127d 100644 --- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp +++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp @@ -42,6 +42,7 @@ #include #include +#include #include namespace QmlDesigner { diff --git a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp index a6b332eb6e4..f08c3962ae7 100644 --- a/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp +++ b/src/plugins/qmldesigner/designercore/model/bindingproperty.cpp @@ -277,6 +277,18 @@ void BindingProperty::deleteAllReferencesTo(const ModelNode &modelNode) } } +bool BindingProperty::isAlias() const +{ + if (!isValid()) + throw InvalidModelNodeException(__LINE__, __FUNCTION__, __FILE__); + + return isDynamic() + && dynamicTypeName() == "alias" + && !expression().isNull() + && !expression().isEmpty() + && parentModelNode().view()->modelNodeForId(expression()).isValid(); +} + bool BindingProperty::isAliasExport() const { if (!isValid()) diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp index b4c21564c72..eb2d2859974 100644 --- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp @@ -93,7 +93,25 @@ ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) m_model(modelNode.model()), m_view(view) { +} +ModelNode::ModelNode(ModelNode &&other) + : m_internalNode(std::move(other.m_internalNode)) + , m_model(std::move(other.m_model)) + , m_view(std::move(other.m_view)) +{ + other.m_model = {}; + other.m_view = {}; +} + +ModelNode &ModelNode::operator=(ModelNode &&other) +{ + ModelNode newNode; + + swap(other, newNode); + swap(*this, newNode); + + return *this; } /*! \brief contructs a invalid model node @@ -103,7 +121,6 @@ ModelNode::ModelNode(const ModelNode &modelNode, AbstractView *view) ModelNode::ModelNode(): m_internalNode(new InternalNode) { - } ModelNode::ModelNode(const ModelNode &other) = default; diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp index 5d0cd5da351..eb9c6507469 100644 --- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp +++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp @@ -960,27 +960,31 @@ QStringList RewriterView::autoComplete(const QString &text, int pos, bool explic return list; } -QList RewriterView::getCppTypes() +QList RewriterView::getQMLTypes() const { - QList cppDataList; - for (const QmlJS::ModelManagerInterface::CppData &cppData : QmlJS::ModelManagerInterface::instance()->cppData().values()) + QList qmlDataList; + + qmlDataList.append(m_textToModelMerger->getQMLSingletons()); + + for (const QmlJS::ModelManagerInterface::CppData &cppData : + QmlJS::ModelManagerInterface::instance()->cppData().values()) for (const LanguageUtils::FakeMetaObject::ConstPtr &fakeMetaObject : cppData.exportedTypes) { - for (const LanguageUtils::FakeMetaObject::Export &exportItem : fakeMetaObject->exports()) { + for (const LanguageUtils::FakeMetaObject::Export &exportItem : + fakeMetaObject->exports()) { + QmlTypeData qmlData; + qmlData.cppClassName = fakeMetaObject->className(); + qmlData.typeName = exportItem.type; + qmlData.importUrl = exportItem.package; + qmlData.versionString = exportItem.version.toString(); + qmlData.superClassName = fakeMetaObject->superclassName(); + qmlData.isSingleton = fakeMetaObject->isSingleton(); - CppTypeData cppData; - cppData.cppClassName = fakeMetaObject->className(); - cppData.typeName = exportItem.type; - cppData.importUrl = exportItem.package; - cppData.versionString = exportItem.version.toString(); - cppData.superClassName = fakeMetaObject->superclassName(); - cppData.isSingleton = fakeMetaObject->isSingleton(); - - if (cppData.importUrl != "") //ignore pure unregistered cpp types - cppDataList.append(cppData); + if (qmlData.importUrl != "") //ignore pure unregistered cpp types + qmlDataList.append(qmlData); } } - return cppDataList; + return qmlDataList; } void RewriterView::setWidgetStatusCallback(std::function setWidgetStatusCallback) @@ -990,7 +994,6 @@ void RewriterView::setWidgetStatusCallback(std::function setWidgetS void RewriterView::qmlTextChanged() { - getCppTypes(); if (inErrorState()) return; diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp index fafa408a468..94814b74367 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp @@ -2176,6 +2176,37 @@ QSet > TextToModelMerger::qrcMapping() const return m_qrcMapping; } +QList TextToModelMerger::getQMLSingletons() const +{ + QList list; + const QmlJS::Imports *imports = m_scopeChain->context()->imports( + m_scopeChain->document().data()); + + if (!imports) + return list; + + for (const QmlJS::Import &import : imports->all()) { + if (import.info.type() == ImportType::Library && !import.libraryPath.isEmpty()) { + const LibraryInfo &libraryInfo = m_scopeChain->context()->snapshot().libraryInfo( + import.libraryPath); + + for (const QmlDirParser::Component &component : libraryInfo.components()) { + if (component.singleton) { + QmlTypeData qmlData; + + qmlData.typeName = component.typeName; + qmlData.importUrl = import.info.name(); + qmlData.versionString = import.info.version().toString(); + qmlData.isSingleton = component.singleton; + + list.append(qmlData); + } + } + } + } + return list; +} + QString TextToModelMerger::textAt(const Document::Ptr &doc, const SourceLocation &location) { diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h index 2884453f8c5..c729d2aeddc 100644 --- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h +++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.h @@ -41,6 +41,8 @@ namespace QmlDesigner { class RewriterView; class DocumentMessage; +struct QmlTypeData; + namespace Internal { class DifferenceHandler; @@ -131,7 +133,9 @@ public: void delayedSetup(); - QSet > qrcMapping() const; + QSet> qrcMapping() const; + + QList getQMLSingletons() const; private: void setupCustomParserNode(const ModelNode &node); diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro index bc0da990504..506b3d743b8 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.pro +++ b/src/plugins/qmldesigner/qmldesignerplugin.pro @@ -33,7 +33,7 @@ include(components/bindingeditor/bindingeditor.pri) include(components/annotationeditor/annotationeditor.pri) include(components/richtexteditor/richtexteditor.pri) include(components/transitioneditor/transitioneditor.pri) - +include(components/listmodeleditor/listmodeleditor.pri) BUILD_PUPPET_IN_CREATOR_BINPATH = $$(BUILD_PUPPET_IN_CREATOR_BINPATH) !isEmpty(BUILD_PUPPET_IN_CREATOR_BINPATH) { diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 6ff406e8979..b391a2d57a7 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -59,6 +59,7 @@ Project { "components/stateseditor", "components/texteditor", "components/timelineeditor", + "compenents/listmodeleditor", ]) Properties { @@ -845,6 +846,10 @@ Project { "timelineeditor/timelineview.h", "timelineeditor/timelinewidget.cpp", "timelineeditor/timelinewidget.h", + "listmodeleditor/listmodeleditordialog.cpp", + "listmodeleditor/listmodeleditordialog.h", + "listmodeleditor/listmodeleditormodel.cpp", + "listmodeleditor/listmodeleditormodel.h", "transitioneditor/transitioneditorview.cpp", "transitioneditor/transitioneditorview.h", "transitioneditor/transitioneditorwidget.cpp", diff --git a/src/plugins/qmldesigner/qmldesignerunittestfiles.pri b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri new file mode 100644 index 00000000000..cd4d52e8d3a --- /dev/null +++ b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri @@ -0,0 +1,59 @@ +INCLUDEPATH += $$PWD +INCLUDEPATH += $$PWD/designercore/include +INCLUDEPATH += $$PWD/designercore +INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces +INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/types + +DEFINES += QMLDESIGNER_TEST DESIGNER_STATIC_CORE_LIBRARY + +include($$PWD/designercore/exceptions/exceptions.pri) + +SOURCES += \ + $$PWD/designercore/model/model.cpp \ + $$PWD/designercore/model/modelnode.cpp \ + $$PWD/designercore/model/import.cpp \ + $$PWD/designercore/model/abstractproperty.cpp \ + $$PWD/designercore/model/abstractview.cpp \ + $$PWD/designercore/model/internalproperty.cpp \ + $$PWD/designercore/model/internalbindingproperty.cpp \ + $$PWD/designercore/model/internalnodeabstractproperty.cpp \ + $$PWD/designercore/model/internalnodelistproperty.cpp \ + $$PWD/designercore/model/internalnodeproperty.cpp \ + $$PWD/designercore/model/internalsignalhandlerproperty.cpp \ + $$PWD/designercore/model/internalnode.cpp \ + $$PWD/designercore/model/internalvariantproperty.cpp \ + $$PWD/designercore/model/bindingproperty.cpp \ + $$PWD/designercore/model/nodeabstractproperty.cpp \ + $$PWD/designercore/model/nodelistproperty.cpp \ + $$PWD/designercore/model/nodeproperty.cpp \ + $$PWD/designercore/model/signalhandlerproperty.cpp \ + $$PWD/designercore/model/variantproperty.cpp\ + $$PWD/designercore/model/annotation.cpp \ + $$PWD/designercore/rewritertransaction.cpp \ + $$PWD/components/listmodeleditor/listmodeleditormodel.cpp + +HEADERS += \ + $$PWD/designercore/include/modelnode.h \ + $$PWD/designercore/include/model.h \ + $$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces/commondefines.h \ + $$PWD/designercore/include/import.h \ + $$PWD/designercore/include/abstractproperty.h \ + $$PWD/designercore/include/abstractview.h \ + $$PWD/designercore/model/model_p.h \ + $$PWD/designercore/include/qmldesignercorelib_global.h \ + $$PWD/designercore/model/internalbindingproperty.h \ + $$PWD/designercore/model/internalnode_p.h \ + $$PWD/designercore/model/internalnodeabstractproperty.h \ + $$PWD/designercore/model/internalnodelistproperty.h \ + $$PWD/designercore/model/internalnodeproperty.h \ + $$PWD/designercore/model/internalproperty.h \ + $$PWD/designercore/model/internalsignalhandlerproperty.h \ + $$PWD/designercore/model/internalvariantproperty.h \ + $$PWD/designercore/include/bindingproperty.h \ + $$PWD/designercore/include/nodeabstractproperty.h \ + $$PWD/designercore/include/nodelistproperty.h \ + $$PWD/designercore/include/nodeproperty.h \ + $$PWD/designercore/include/signalhandlerproperty.h \ + $$PWD/designercore/include/variantproperty.h \ + $$PWD/designercore/rewritertransaction.h \ + $$PWD/components/listmodeleditor/listmodeleditormodel.h diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index f2e275269b1..557537d9b6c 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -128,7 +128,7 @@ public: const QString row = "%1:%2"; return QString("" + row.arg(tr("Qt Version"), m_version->qtVersionString()) - + row.arg(tr("Location of qmake)"), m_version->qmakeCommand().toUserOutput()) + + row.arg(tr("Location of qmake"), m_version->qmakeCommand().toUserOutput()) + "
"); } diff --git a/src/tools/clangbackend/source/clangtooltipinfocollector.cpp b/src/tools/clangbackend/source/clangtooltipinfocollector.cpp index a4ab626a8a4..af28c7a5a9f 100644 --- a/src/tools/clangbackend/source/clangtooltipinfocollector.cpp +++ b/src/tools/clangbackend/source/clangtooltipinfocollector.cpp @@ -252,9 +252,6 @@ Utf8String ToolTipInfoCollector::text(const Cursor &cursor, const Cursor &refere if (referenced.isFunctionLike() || referenced.kind() == CXCursor_Constructor) return textForFunctionLike(referenced); - if (referenced.type().canonical().isBuiltinType()) - return referenced.type().canonical().builtinTypeToString(); - if (referenced.kind() == CXCursor_VarDecl) return referenced.type().spelling(); // e.g. "Zii" diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index e91ca0159be..c7fe8e41384 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -213,7 +213,9 @@ struct Data // because we have a cycle dependency pchCreationProgressCounter, preCompiledHeaderStorage, database, - environment}; + environment, + fileSystem, + filePathCache}; ClangBackEnd::PchTasksMerger pchTaskMerger{pchTaskQueue}; ClangBackEnd::BuildDependenciesStorage<> buildDependencyStorage{database}; ClangBackEnd::BuildDependencyCollector buildDependencyCollector{filePathCache, diff --git a/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h b/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h index 88ed3d23448..47b86e22191 100644 --- a/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h +++ b/src/tools/clangpchmanagerbackend/source/collectbuilddependencytoolaction.h @@ -60,7 +60,6 @@ public: diagnosticConsumer); } -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique( @@ -69,15 +68,6 @@ public: m_excludedIncludeUIDs, m_alreadyIncludedFileUIDs); } -#else - clang::FrontendAction *create() override - { - return new CollectBuildDependencyAction(m_buildDependency, - m_filePathCache, - m_excludedIncludeUIDs, - m_alreadyIncludedFileUIDs); - } -#endif std::vector generateExcludedIncludeFileUIDs(clang::FileManager &fileManager) const { @@ -86,16 +76,12 @@ public: for (const FilePath &filePath : m_excludedFilePaths) { NativeFilePath nativeFilePath{filePath}; - const clang::FileEntry *file = fileManager.getFile({nativeFilePath.path().data(), - nativeFilePath.path().size()}, - true) -#if LLVM_VERSION_MAJOR >= 10 - .get() -#endif - ; + auto file = fileManager.getFile({nativeFilePath.path().data(), + nativeFilePath.path().size()}, + true); if (file) - fileUIDs.push_back(file->getUID()); + fileUIDs.push_back(file.get()->getUID()); } std::sort(fileUIDs.begin(), fileUIDs.end()); diff --git a/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h b/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h index 473b8e7df8a..e9c7c4b32b9 100644 --- a/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h +++ b/src/tools/clangpchmanagerbackend/source/collectusedmacroactionfactory.h @@ -61,7 +61,6 @@ public: diagnosticConsumer); } -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique( @@ -71,16 +70,6 @@ public: m_sourceFiles, m_fileStatuses); } -#else - clang::FrontendAction *create() override - { - return new CollectUsedMacrosAction(m_usedMacros, - m_filePathCache, - m_sourceDependencies, - m_sourceFiles, - m_fileStatuses); - } -#endif private: UsedMacros &m_usedMacros; diff --git a/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h b/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h index 5dc630de182..70adc2dffb5 100644 --- a/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h +++ b/src/tools/clangpchmanagerbackend/source/collectusedmacrosandsourcespreprocessorcallbacks.h @@ -196,17 +196,15 @@ public: } void InclusionDirective(clang::SourceLocation hashLocation, - const clang::Token &/*includeToken*/, + const clang::Token & /*includeToken*/, llvm::StringRef /*fileName*/, bool /*isAngled*/, clang::CharSourceRange /*fileNameRange*/, const clang::FileEntry *file, llvm::StringRef /*searchPath*/, llvm::StringRef /*relativePath*/, - const clang::Module * /*imported*/ -#if LLVM_VERSION_MAJOR >= 7 - , clang::SrcMgr::CharacteristicKind /*fileType*/ -#endif + const clang::Module * /*imported*/, + clang::SrcMgr::CharacteristicKind /*fileType*/ ) override { if (!m_skipInclude && file) diff --git a/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h b/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h index f1e4c74457e..3993ccc6511 100644 --- a/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h +++ b/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h @@ -68,17 +68,10 @@ public: , m_fileContent(fileContent) {} -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique(m_filePath, m_fileContent); } -#else - clang::FrontendAction *create() override - { - return new GeneratePCHAction{m_filePath, m_fileContent}; - } -#endif private: llvm::StringRef m_filePath; diff --git a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp index 7a6e29c584c..8f1b53855a1 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp +++ b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.cpp @@ -26,6 +26,8 @@ #include "pchtaskqueue.h" #include +#include +#include #include #include #include @@ -107,36 +109,67 @@ void PchTaskQueue::removePchTasks(const ProjectPartIds &projectsPartIds) removePchTasksByProjectPartId(projectsPartIds, m_projectPchTasks); } -void PchTaskQueue::processProjectPchTasks() +int PchTaskQueue::processProjectPchTasks() { - uint systemRunningTaskCount = m_systemPchTaskScheduler.slotUsage().used; + auto slotUsage = m_projectPchTaskScheduler.slotUsage(); + uint freeTaskCount = slotUsage.free; - if (!systemRunningTaskCount) { - uint freeTaskCount = m_projectPchTaskScheduler.slotUsage().free; + int newTaskCount = std::min(int(freeTaskCount), int(m_projectPchTasks.size())); - auto newEnd = std::prev(m_projectPchTasks.end(), - std::min(int(freeTaskCount), int(m_projectPchTasks.size()))); - m_projectPchTaskScheduler.addTasks(createProjectTasks( - {std::make_move_iterator(newEnd), std::make_move_iterator(m_projectPchTasks.end())})); - m_projectPchTasks.erase(newEnd, m_projectPchTasks.end()); - } + auto newEnd = std::prev(m_projectPchTasks.end(), newTaskCount); + m_projectPchTaskScheduler.addTasks(createProjectTasks( + {std::make_move_iterator(newEnd), std::make_move_iterator(m_projectPchTasks.end())})); + m_projectPchTasks.erase(newEnd, m_projectPchTasks.end()); + + return newTaskCount + slotUsage.used; } -void PchTaskQueue::processSystemPchTasks() +int PchTaskQueue::processSystemPchTasks() { - uint freeTaskCount = m_systemPchTaskScheduler.slotUsage().free; + auto slotUsage = m_systemPchTaskScheduler.slotUsage(); + uint freeTaskCount = slotUsage.free; - auto newEnd = std::prev(m_systemPchTasks.end(), - std::min(int(freeTaskCount), int(m_systemPchTasks.size()))); + int newTaskCount = std::min(int(freeTaskCount), int(m_systemPchTasks.size())); + + auto newEnd = std::prev(m_systemPchTasks.end(), newTaskCount); m_systemPchTaskScheduler.addTasks(createSystemTasks( {std::make_move_iterator(newEnd), std::make_move_iterator(m_systemPchTasks.end())})); m_systemPchTasks.erase(newEnd, m_systemPchTasks.end()); + + return newTaskCount + slotUsage.used; +} + +void PchTaskQueue::deleteUnusedPchs() +{ + FilePathIds existingPchFilePathIds = m_fileSystem.directoryEntries( + QString{m_environment.pchBuildDirectory()}); + FilePathIds notAnymoreUsedPchFilePathIds; + notAnymoreUsedPchFilePathIds.reserve(existingPchFilePathIds.size()); + + FilePathIds usedPchFilePathIds = m_filePathCache.filePathIds( + m_precompiledHeaderStorage.fetchAllPchPaths()); + std::sort(usedPchFilePathIds.begin(), usedPchFilePathIds.end()); + + std::set_difference(existingPchFilePathIds.begin(), + existingPchFilePathIds.end(), + usedPchFilePathIds.begin(), + usedPchFilePathIds.end(), + std::back_inserter(notAnymoreUsedPchFilePathIds)); + + m_fileSystem.remove(notAnymoreUsedPchFilePathIds); } void PchTaskQueue::processEntries() { - processSystemPchTasks(); - processProjectPchTasks(); + int projectTaskCount = 0; + int systemTaskCount = processSystemPchTasks(); + if (systemTaskCount == 0) + projectTaskCount = processProjectPchTasks(); + + int totalTaskCount = projectTaskCount + systemTaskCount; + + if (totalTaskCount == 0) + deleteUnusedPchs(); } std::vector PchTaskQueue::createProjectTasks(PchTasks &&pchTasks) const diff --git a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h index f7be8ae012e..a7a455b3578 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h +++ b/src/tools/clangpchmanagerbackend/source/pchtaskqueue.h @@ -37,6 +37,8 @@ class PchCreatorInterface; class PrecompiledHeaderStorageInterface; class ProgressCounter; class Environment; +class FileSystemInterface; +class FilePathCachingInterface; class PchTaskQueue final : public PchTaskQueueInterface { @@ -48,13 +50,17 @@ public: ProgressCounter &progressCounter, PrecompiledHeaderStorageInterface &precompiledHeaderStorage, Sqlite::TransactionInterface &transactionsInterface, - const Environment &environment) + const Environment &environment, + FileSystemInterface &fileSystem, + FilePathCachingInterface &filePathCache) : m_systemPchTaskScheduler(systemPchTaskScheduler) , m_projectPchTaskScheduler(projectPchTaskScheduler) , m_precompiledHeaderStorage(precompiledHeaderStorage) , m_transactionsInterface(transactionsInterface) , m_progressCounter(progressCounter) , m_environment(environment) + , m_fileSystem(fileSystem) + , m_filePathCache(filePathCache) { Q_UNUSED(m_transactionsInterface) } @@ -74,8 +80,9 @@ public: private: void addPchTasks(PchTasks &&pchTasks, PchTasks &destination); void removePchTasksByProjectPartId(const ProjectPartIds &projectsPartIds, PchTasks &destination); - void processProjectPchTasks(); - void processSystemPchTasks(); + int processProjectPchTasks(); + int processSystemPchTasks(); + void deleteUnusedPchs(); private: PchTasks m_systemPchTasks; @@ -86,6 +93,8 @@ private: Sqlite::TransactionInterface &m_transactionsInterface; ProgressCounter &m_progressCounter; const Environment &m_environment; + FileSystemInterface &m_fileSystem; + FilePathCachingInterface &m_filePathCache; }; } // namespace ClangBackEnd diff --git a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h index d781c96e529..16a6a669527 100644 --- a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h +++ b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorage.h @@ -219,6 +219,22 @@ public: return {}; } + FilePaths fetchAllPchPaths() const + { + try { + Sqlite::DeferredTransaction transaction{database}; + + auto filePaths = fetchAllPchPathsStatement.template values(1024); + + transaction.commit(); + + return filePaths; + + } catch (const Sqlite::StatementIsBusy) { + return fetchAllPchPaths(); + } + } + public: Sqlite::ImmediateNonThrowingDestructorTransaction transaction; Database &database; @@ -262,6 +278,10 @@ public: "SELECT projectPchBuildTime, systemPchBuildTime FROM precompiledHeaders WHERE " "projectPartId = ?", database}; + mutable ReadStatement fetchAllPchPathsStatement{ + "SELECT DISTINCT systemPchPath FROM precompiledHeaders UNION ALL SELECT " + "DISTINCT projectPchPath FROM precompiledHeaders", + database}; }; } diff --git a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h index 92b3604cdc4..fd954a0c8a6 100644 --- a/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h +++ b/src/tools/clangpchmanagerbackend/source/precompiledheaderstorageinterface.h @@ -60,6 +60,7 @@ public: virtual FilePath fetchPrecompiledHeader(ProjectPartId projectPartId) const = 0; virtual PchPaths fetchPrecompiledHeaders(ProjectPartId projectPartId) const = 0; virtual PrecompiledHeaderTimeStamps fetchTimeStamps(ProjectPartId projectPartId) const = 0; + virtual FilePaths fetchAllPchPaths() const = 0; protected: ~PrecompiledHeaderStorageInterface() = default; diff --git a/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h b/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h index 4f36adadf5f..bf592502bc6 100644 --- a/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h +++ b/src/tools/clangrefactoringbackend/source/collectsymbolsaction.h @@ -48,11 +48,7 @@ class CollectSymbolsAction : public clang::WrapperFrontendAction public: CollectSymbolsAction(std::shared_ptr indexDataConsumer) : clang::WrapperFrontendAction( - clang::index::createIndexingAction(indexDataConsumer, createIndexingOptions() -#if LLVM_VERSION_MAJOR < 10 - , nullptr -#endif - )) + clang::index::createIndexingAction(indexDataConsumer, createIndexingOptions())) , m_indexDataConsumer(indexDataConsumer) {} diff --git a/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp b/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp index 5d21b30b9c2..abb30d61b91 100644 --- a/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp +++ b/src/tools/clangrefactoringbackend/source/indexdataconsumer.cpp @@ -118,11 +118,7 @@ bool IndexDataConsumer::isAlreadyParsed(clang::FileID fileId, SourcesManager &so return sourcesManager.alreadyParsed(filePathId(fileEntry), fileEntry->getModificationTime()); } -#if LLVM_VERSION_MAJOR >= 10 bool IndexDataConsumer::handleDeclOccurrence( -#else - bool IndexDataConsumer::handleDeclOccurence( -#endif const clang::Decl *declaration, clang::index::SymbolRoleSet symbolRoles, llvm::ArrayRef /*symbolRelations*/, @@ -180,11 +176,7 @@ SourceLocationKind macroSymbolType(clang::index::SymbolRoleSet roles) } // namespace -#if LLVM_VERSION_MAJOR >= 10 bool IndexDataConsumer::handleMacroOccurrence( -#else -bool IndexDataConsumer::handleMacroOccurence( -#endif const clang::IdentifierInfo *identifierInfo, const clang::MacroInfo *macroInfo, clang::index::SymbolRoleSet roles, diff --git a/src/tools/clangrefactoringbackend/source/indexdataconsumer.h b/src/tools/clangrefactoringbackend/source/indexdataconsumer.h index e1d3529806e..f3af3287444 100644 --- a/src/tools/clangrefactoringbackend/source/indexdataconsumer.h +++ b/src/tools/clangrefactoringbackend/source/indexdataconsumer.h @@ -57,22 +57,14 @@ public: IndexDataConsumer(const IndexDataConsumer &) = delete; IndexDataConsumer &operator=(const IndexDataConsumer &) = delete; -#if LLVM_VERSION_MAJOR >= 10 bool handleDeclOccurrence( -#else - bool handleDeclOccurence( -#endif const clang::Decl *declaration, clang::index::SymbolRoleSet symbolRoles, llvm::ArrayRef symbolRelations, clang::SourceLocation sourceLocation, ASTNodeInfo astNodeInfo) override; -#if LLVM_VERSION_MAJOR >= 10 bool handleMacroOccurrence( -#else - bool handleMacroOccurence( -#endif const clang::IdentifierInfo *identifierInfo, const clang::MacroInfo *macroInfo, clang::index::SymbolRoleSet roles, diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp index dffd5838944..8e01869b1d9 100644 --- a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp @@ -74,11 +74,7 @@ std::unique_ptr newFrontendActionFactory( : m_action(consumerFactory) {} -#if LLVM_VERSION_MAJOR >= 10 std::unique_ptr create() override { return std::make_unique(m_action); } -#else - clang::FrontendAction *create() override { return new AdaptorAction(m_action); } -#endif private: class AdaptorAction : public clang::ASTFrontendAction diff --git a/src/tools/icons/export.py b/src/tools/icons/export.py index 6943b18fd74..bf8f0baa393 100644 --- a/src/tools/icons/export.py +++ b/src/tools/icons/export.py @@ -2,7 +2,7 @@ ############################################################################ # -# Copyright (C) 2016 The Qt Company Ltd. +# Copyright (C) 2020 The Qt Company Ltd. # Contact: https://www.qt.io/licensing/ # # This file is part of Qt Creator. @@ -27,70 +27,157 @@ # This script calls Inkscape to rasterize several images into png files. # The images end up in the final position of the source tree. -# Each images is generated as normal and high resolution variant. +# Each image is generated as normal and high resolution variant. # Each png file is afterwards optimized with optipng. -import sys, os, subprocess, re, xml.etree.ElementTree as ET +import argparse +import os +import re +import subprocess +import sys +import xml.etree.ElementTree as ET + from distutils import spawn -scriptDir = os.path.dirname(os.path.abspath(sys.argv[0])) + '/' -# QTC_SRC is expected to point to the source root of Qt Creator -qtcSourceRoot = os.getenv('QTC_SRC', os.path.abspath(scriptDir + '../../..')) \ - .replace('\\', '/') + '/' +def qtcRoot(): + return os.path.abspath( + os.path.join(os.path.dirname(sys.argv[0]), '../../..')).replace('\\', '/') -svgElementFilter = "" -if len(sys.argv) > 1: - svgElementFilter = sys.argv[1] -# Inkscape is required by this script -inkscapeExecutable = spawn.find_executable("inkscape") -if not inkscapeExecutable: - sys.stderr.write("Inkscape was not found in PATH\n") - sys.exit(1) - -# The svg element IDs of images to export. They correspond to the -# path and base name of each image in the Qt Creator sources. -svgIDs = [] -svgTree = ET.ElementTree() -svgTree.parse(scriptDir + "qtcreatoricons.svg") -svgTreeRoot = svgTree.getroot() -for svgElement in svgTreeRoot.iter(): - try: - svgElementID = svgElement.attrib['id'] - if svgElementID.startswith(('src/', 'share/')): - if svgElementFilter != "": - pattern = re.compile(svgElementFilter) - if pattern.match(svgElementID): - svgIDs.append(svgElementID) - else: +def svgIDs(svgFile, svgElementFilter): + # The svg element IDs of images to export. They correspond to the + # path and base name of each image in the Qt Creator sources. + svgIDs = [] + svgTree = ET.ElementTree() + svgTree.parse(os.path.join(qtcRoot(), svgFile)) + svgTreeRoot = svgTree.getroot() + pattern = re.compile(svgElementFilter) + for svgElement in svgTreeRoot.iter(): + try: + svgElementID = svgElement.attrib['id'] + if '/' in svgElementID and pattern.match(svgElementID): svgIDs.append(svgElementID) - except: - pass + except Exception: + pass -for id in svgIDs: - pngFile = qtcSourceRoot + id + ".png" - pngAt2XFile = qtcSourceRoot + id + "@2x.png" - if not (os.path.isfile(pngFile) or os.path.isfile(pngAt2XFile)): - sys.stderr.write(id + " has not yet been exported as .png.\n") + print("\n==== {} elements found which match {}" + .format(len(svgIDs), svgElementFilter)) + return svgIDs -# The shell mode of Inkscape is used to execute several export commands -# with one launch of Inkscape. -inkscapeShellCommands = "" -pngFiles = [] -for id in svgIDs: - for scale in [1, 2]: - pngFile = qtcSourceRoot + id + ("" if scale is 1 else "@%dx" % scale) + ".png" - pngFiles.append(pngFile) - inkscapeShellCommands += "qtcreatoricons.svg --export-id=" + id + " --export-id-only --export-png=" + pngFile + " --export-dpi=%d\n" % (scale * 96) -inkscapeShellCommands += "quit\n" -inkscapeProcess = subprocess.Popen(['inkscape', '--shell'], stdin=subprocess.PIPE, shell=True, cwd=scriptDir) -inkscapeProcess.communicate(input=inkscapeShellCommands.encode()) -# Optimizing pngs via optipng -optipngExecutable = spawn.find_executable("optipng") -if not optipngExecutable: - sys.stderr.write("optipng was not found in PATH. Please do not push the unoptimized .pngs to the main repository.\n") -else: - for pngFile in pngFiles: - subprocess.call(["optipng", "-o7", "-strip", "all", pngFile]) +def pngName(svgID, scale): + # File name is relative to qtcRoot() + return svgID + ("" if scale == 1 else "@{}x".format(scale)) + ".png" + + +def checkDirectories(svgIDs): + invalidDirectories = [] + for id in svgIDs: + if not os.path.isdir(os.path.join(qtcRoot(), id, '../')): + invalidDirectories.append(id) + + if invalidDirectories: + print("\n==== {} IDs for which the output directory is missing:" + .format(len(invalidDirectories))) + print("\n".join(invalidDirectories)) + sys.exit("Output directories are missing.") + + +def printOutUnexported(svgIDs, scaleFactors): + unexported = [] + partiallyExported = [] + for id in svgIDs: + exportedCount = 0 + for scaleFactor in scaleFactors: + if os.path.isfile(os.path.join(qtcRoot(), pngName(id, scaleFactor))): + exportedCount += 1 + if exportedCount == 0: + unexported.append(id) + elif (exportedCount < len(scaleFactors)): + partiallyExported.append(id) + + if partiallyExported: + print("\n==== {} IDs for which not each .png is exported:" + .format(len(partiallyExported))) + print("\n".join(partiallyExported)) + if unexported: + print("\n==== {} IDs for which all .pngs are missing:" + .format(len(unexported))) + print("\n".join(unexported)) + if partiallyExported or unexported: + input("\nPress Enter to continue...") + + +def exportPngs(svgIDs, svgFile, scaleFactors, inkscape): + inkscapeProcess = subprocess.Popen([inkscape, '--shell'], + stdin=subprocess.PIPE, + cwd=qtcRoot()) + actions = ["file-open:" + svgFile] + for id in svgIDs: + for scale in scaleFactors: + actions += [ + "export-id:{}".format(id), + "export-id-only", + "export-dpi:{}".format(scale * 96), + "export-filename:{}".format(pngName(id, scale)), + "export-do" + ] + actions += ["quit-inkscape"] + actionLine = "; ".join(actions) + "\n" + print("Exporting pngs for {} Ids in {} scale factors." + .format(len(svgIDs), len(scaleFactors))) + inkscapeProcess.communicate(input=actionLine.encode()) + + +def optimizePngs(svgIDs, scaleFactors, optipng): + for id in svgIDs: + for scale in scaleFactors: + png = pngName(id, scale) + print("Optimizing: {}".format(png)) + try: + subprocess.check_call([optipng, + "-o7", + "-strip", "all", + png], + stderr=subprocess.DEVNULL, + cwd=qtcRoot()) + except subprocess.CalledProcessError: + sys.exit("Failed to optimize {}.".format(png)) + + +def export(svgFile, filter, scaleFactors, inkscape, optipng): + ids = svgIDs(svgFile, filter) + if not ids: + sys.exit("{} does not match any Id.".format(filter)) + + checkDirectories(ids) + printOutUnexported(ids, scaleFactors) + exportPngs(ids, svgFile, scaleFactors, inkscape) + optimizePngs(ids, scaleFactors, optipng) + + +def main(): + parser = argparse.ArgumentParser(description='Export svg elements to .png ' + 'files and optimize the png. ' + 'Requires Inkscape 1.x and optipng in Path.') + parser.add_argument('filter', + help='a RegExp filter for svg element Ids, e.g.: .*device.*') + args = parser.parse_args() + + inkscape = spawn.find_executable("inkscape") + if inkscape is None: + sys.exit("Inkscape was not found in Path.") + + optipng = spawn.find_executable("optipng") + if optipng is None: + sys.exit("Optipng was not found in Path.") + + export("src/tools/icons/qtcreatoricons.svg", args.filter, [1, 2], + inkscape, optipng) + + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 71bf9613a5d..b8038a92820 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(auto) add_subdirectory(manual) # add_subdirectory(tools) -add_subdirectory(unit) +# add_subdirectory(unit) diff --git a/tests/auto/qml/qmldesigner/coretests/coretests.pro b/tests/auto/qml/qmldesigner/coretests/coretests.pro index dd0f071b0e9..7afb0fda2e6 100644 --- a/tests/auto/qml/qmldesigner/coretests/coretests.pro +++ b/tests/auto/qml/qmldesigner/coretests/coretests.pro @@ -12,7 +12,8 @@ QTC_LIB_DEPENDS += \ QTC_PLUGIN_DEPENDS += \ coreplugin \ qmljseditor \ - qmakeprojectmanager + qmakeprojectmanager \ + qmlprojectmanager CONFIG -= qtquickcompiler diff --git a/tests/system/shared/debugger.py b/tests/system/shared/debugger.py index 930e74980f3..5c9f01d5f2c 100644 --- a/tests/system/shared/debugger.py +++ b/tests/system/shared/debugger.py @@ -51,14 +51,14 @@ def handleDebuggerWarnings(config, isMsvcBuild=False): clickButton("{text='OK' type='QPushButton' unnamed='1' visible='1' window=%s}" % msgBox) def takeDebuggerLog(): - invokeMenuItem("Window", "Views", "Global Debugger Log") + invokeMenuItem("View", "Views", "Global Debugger Log") debuggerLogWindow = waitForObject("{container=':DebugModeWidget.Debugger Log_QDockWidget' " "type='Debugger::Internal::DebuggerPane' unnamed='1' visible='1'}") debuggerLog = str(debuggerLogWindow.plainText) mouseClick(debuggerLogWindow) invokeContextMenuItem(debuggerLogWindow, "Clear Contents") waitFor("str(debuggerLogWindow.plainText)==''", 5000) - invokeMenuItem("Window", "Views", "Global Debugger Log") + invokeMenuItem("View", "Views", "Global Debugger Log") return debuggerLog # function to set breakpoints for the current project diff --git a/tests/system/suite_CSUP/tst_CSUP01/test.py b/tests/system/suite_CSUP/tst_CSUP01/test.py index 8cac9bbfb0f..7c77bf69663 100644 --- a/tests/system/suite_CSUP/tst_CSUP01/test.py +++ b/tests/system/suite_CSUP/tst_CSUP01/test.py @@ -123,6 +123,5 @@ def main(): invokeMenuItem('File', 'Revert "main.cpp" to Saved') clickButton(waitForObject(":Revert to Saved.Proceed_QPushButton")) # exit qt creator - invokeMenuItem("File", "Save All") invokeMenuItem("File", "Exit") waitForCleanShutdown() diff --git a/tests/system/suite_debugger/tst_debug_empty_main/test.py b/tests/system/suite_debugger/tst_debug_empty_main/test.py index f71cd45d445..5a639d5dacf 100644 --- a/tests/system/suite_debugger/tst_debug_empty_main/test.py +++ b/tests/system/suite_debugger/tst_debug_empty_main/test.py @@ -52,7 +52,7 @@ def main(): workingDir = tempDir() projectName = createEmptyQtProject(workingDir, "EmptyQtProj", targets) waitForProjectParsing() - addFileToProject(os.path.join(workingDir, projectName), " C++", "C++ Source File", "main.cpp") + addFileToProject(os.path.join(workingDir, projectName), " C/C++", "C/C++ Source File", "main.cpp") editor = waitForObject(":Qt Creator_CppEditor::Internal::CPPEditorWidget") typeLines(editor, ["int main() {"]) invokeMenuItem("File", "Save All") @@ -84,7 +84,7 @@ def __handleAppOutputWaitForDebuggerFinish__(): ensureChecked(":Qt Creator_AppOutput_Core::Internal::OutputPaneToggleButton") appOutput = waitForObject("{type='Core::OutputWindow' visible='1' " "windowTitle='Application Output Window'}") - if not test.verify(waitFor("str(appOutput.plainText).endswith('Debugging has finished')", 20000), + if not test.verify(waitFor("str(appOutput.plainText).rstrip().endswith('Debugging has finished')", 20000), "Verifying whether debugging has finished."): test.log("Aborting debugging to let test continue.") invokeMenuItem("Debug", "Abort Debugging") diff --git a/tests/system/suite_debugger/tst_qml_js_console/test.py b/tests/system/suite_debugger/tst_qml_js_console/test.py index 040ceed7053..206e3f8a25b 100644 --- a/tests/system/suite_debugger/tst_qml_js_console/test.py +++ b/tests/system/suite_debugger/tst_qml_js_console/test.py @@ -152,7 +152,7 @@ def main(): invokeMenuItem("View", "Output Panes", "QML Debugger Console") # Window might be too small to show Locals, so close what we don't need for view in ("Stack", "Breakpoints", "Expressions"): - invokeMenuItem("Window", "Views", view) + invokeMenuItem("View", "Views", view) # color and float values have additional ZERO WIDTH SPACE (\u200b), different usage of # whitespaces inside expressions is part of the test checks = [("color", u"#\u200b008000"), ("width", "50"), diff --git a/tests/system/suite_editors/tst_qml_indent/test.py b/tests/system/suite_editors/tst_qml_indent/test.py index 958251d0529..a81e3d558d6 100644 --- a/tests/system/suite_editors/tst_qml_indent/test.py +++ b/tests/system/suite_editors/tst_qml_indent/test.py @@ -34,7 +34,6 @@ def main(): originalText = prepareQmlFile() if originalText: testReIndent(originalText) - invokeMenuItem("File", "Save All") invokeMenuItem("File", "Exit") def prepareQmlFile(): diff --git a/tests/unit/mockup/coreplugin/helpitem.h b/tests/unit/mockup/coreplugin/helpitem.h new file mode 100644 index 00000000000..7942036c74e --- /dev/null +++ b/tests/unit/mockup/coreplugin/helpitem.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "core_global.h" + +#include + +namespace Core { + +class HelpItem +{ +public: + HelpItem() {} + HelpItem(const QString &) {} +}; + +} // namespace Core diff --git a/tests/unit/mockup/coreplugin/icontext.h b/tests/unit/mockup/coreplugin/icontext.h new file mode 100644 index 00000000000..95b55302fb7 --- /dev/null +++ b/tests/unit/mockup/coreplugin/icontext.h @@ -0,0 +1,40 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +namespace Core { + +class IContext +{ +public: + using HelpCallback = std::function; +}; + +} // namespace Core diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomCheckBoxStyle.qml b/tests/unit/mockup/qmldesigner/designercore/include/documentmessage.h similarity index 59% rename from share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomCheckBoxStyle.qml rename to tests/unit/mockup/qmldesigner/designercore/include/documentmessage.h index bb0759d3f35..c4edc8e07db 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/CustomCheckBoxStyle.qml +++ b/tests/unit/mockup/qmldesigner/designercore/include/documentmessage.h @@ -23,32 +23,48 @@ ** ****************************************************************************/ -import QtQuick 2.1 -import QtQuick.Controls 1.1 as Controls -import QtQuick.Controls.Styles 1.1 -import QtQuickDesignerTheme 1.0 +#pragma once -CheckBoxStyle { - spacing: 24 - label: Controls.Label { text: control.text ; color: checkBox.textColor } - indicator: Item { - implicitWidth: 16 - implicitHeight: 16 - Rectangle { - anchors.fill: parent - color: control.pressed - ? Theme.color(Theme.FancyToolButtonHoverColor) - : Theme.color(Theme.FancyToolButtonSelectedColor) - border.color: Theme.qmlDesignerBorderColor() - anchors.margins: 1 - } - Image { - x: 2 - y: 2 - width: 14 - height: 13 - source: "image://icons/checkbox-indicator" - visible: control.checked - } - } +#include "exception.h" + +#include + +#include +#include + +namespace QmlJS { +class DiagnosticMessage; } + +namespace QmlDesigner { + +class DocumentMessage +{ +public: + enum Type { NoError = 0, InternalError = 1, ParseError = 2 }; + +public: + DocumentMessage() {} + DocumentMessage(const QString &) {} + + Type type() const { return m_type; } + + int line() const { return m_line; } + + int column() const { return m_column; } + + QString description() const { return m_description; } + + QUrl url() const { return m_url; } + + QString toString() const { return {}; } + +private: + Type m_type; + int m_line; + int m_column; + QString m_description; + QUrl m_url; +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h new file mode 100644 index 00000000000..ad854699b4e --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryinfo.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmldesignercorelib_global.h" + +#include + +namespace QmlDesigner { + +class ItemLibraryEntry +{ +public: + QString name() const { return {}; } + TypeName typeName() const { return {}; } + QIcon typeIcon() const { return {}; } + QString libraryEntryIconPath() const { return {}; } +}; + +class ItemLibraryInfo +{ +public: + QList entries() const { return {}; } + QList entriesForType(const QByteArray &typeName, + int majorVersion, + int minorVersion) const + { + return {}; + } + ItemLibraryEntry entry(const QString &name) const { return {}; } +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryitem.h b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryitem.h new file mode 100644 index 00000000000..ccc06640eb8 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/itemlibraryitem.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include "itemlibraryinfo.h" + +namespace QmlDesigner { + +class ItemLibraryItem : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant itemLibraryEntry READ itemLibraryEntry FINAL) + Q_PROPERTY(QString itemName READ itemName FINAL) + Q_PROPERTY(QString itemLibraryIconPath READ itemLibraryIconPath FINAL) + Q_PROPERTY(bool itemVisible READ isVisible NOTIFY visibilityChanged FINAL) + +public: + ItemLibraryItem(QObject *) {} + ~ItemLibraryItem() override {} + + QString itemName() const { return {}; } + QString typeName() const { return {}; } + QString itemLibraryIconPath() const { return {}; } + + bool setVisible(bool) { return {}; } + bool isVisible() const { return {}; } + + void setItemLibraryEntry(const ItemLibraryEntry &) {} + QVariant itemLibraryEntry() const { return {}; } + +signals: + void visibilityChanged(); +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h new file mode 100644 index 00000000000..22e7c5762db --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/metainfo.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmldesignercorelib_global.h" + +#include +#include + +#include "itemlibraryinfo.h" +#include + +namespace QmlDesigner { + +class ModelNode; +class AbstractProperty; +class ItemLibraryInfo; + +inline bool operator==(const MetaInfo &first, const MetaInfo &second) +{ + return {}; +} +inline bool operator!=(const MetaInfo &first, const MetaInfo &second) +{ + return {}; +} + +class QMLDESIGNERCORE_EXPORT MetaInfo +{ +public: + ItemLibraryInfo *itemLibraryInfo() const { return {}; } + +public: + static MetaInfo global() { return {}; } + static void clearGlobal() {} + + static void setPluginPaths(const QStringList &paths) {} +}; + +} //namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h new file mode 100644 index 00000000000..246d1ede406 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmldesignercorelib_global.h" +#include "abstractview.h" + +namespace QmlDesigner { + +class NodeInstanceView : public AbstractView +{ + Q_OBJECT + +public: + NodeInstanceView(QObject *parent) {} + ~NodeInstanceView() override {} + + void modelAttached(Model *model) override {} + void modelAboutToBeDetached(Model *model) override {} + void nodeCreated(const ModelNode &createdNode) override {} + void nodeRemoved(const ModelNode &removedNode, + const NodeAbstractProperty &parentProperty, + PropertyChangeFlags propertyChange) override + {} + void propertiesAboutToBeRemoved(const QList &propertyList) override {} + void propertiesRemoved(const QList &propertyList) override {} + void variantPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override + {} + void bindingPropertiesChanged(const QList &propertyList, + PropertyChangeFlags propertyChange) override + {} + void signalHandlerPropertiesChanged(const QVector &propertyList, + PropertyChangeFlags propertyChange) override + {} + void nodeReparented(const ModelNode &node, + const NodeAbstractProperty &newPropertyParent, + const NodeAbstractProperty &oldPropertyParent, + AbstractView::PropertyChangeFlags propertyChange) override + {} + void nodeIdChanged(const ModelNode &node, const QString &newId, const QString &oldId) override + {} + void nodeOrderChanged(const NodeListProperty &listProperty, + const ModelNode &movedNode, + int oldIndex) override + {} + void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override {} + void nodeTypeChanged(const ModelNode &node, + const TypeName &type, + int majorVersion, + int minorVersion) override + {} + void customNotification(const AbstractView *view, + const QString &identifier, + const QList &nodeList, + const QList &data) override + {} + + void rewriterBeginTransaction() override {} + void rewriterEndTransaction() override {} + + void importsChanged(const QList &addedImports, const QList &removedImports) override + {} + + void sendToken(const QString &token, int number, const QVector &nodeVector) {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h new file mode 100644 index 00000000000..ed00f71d989 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/nodemetainfo.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include "qmldesignercorelib_global.h" + +QT_BEGIN_NAMESPACE +class QDeclarativeContext; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class MetaInfo; +class Model; +class AbstractProperty; + +class NodeMetaInfo +{ +public: + NodeMetaInfo() {} + NodeMetaInfo(Model *, const TypeName &, int, int) {} + + bool isValid() const { return {}; } + bool isFileComponent() const { return {}; } + bool hasProperty(const PropertyName &) const { return {}; } + PropertyNameList propertyNames() const { return {}; } + PropertyNameList signalNames() const { return {}; } + PropertyNameList directPropertyNames() const { return {}; } + PropertyName defaultPropertyName() const { return "data"; } + bool hasDefaultProperty() const { return {}; } + TypeName propertyTypeName(const PropertyName &) const { return {}; } + bool propertyIsWritable(const PropertyName &) const { return {}; } + bool propertyIsListProperty(const PropertyName &) const { return {}; } + bool propertyIsEnumType(const PropertyName &) const { return {}; } + bool propertyIsPrivate(const PropertyName &) const { return {}; } + QString propertyEnumScope(const PropertyName &) const { return {}; } + QStringList propertyKeysForEnum(const PropertyName &) const { return {}; } + QVariant propertyCastedValue(const PropertyName &, const QVariant &) const { return {}; } + + QList classHierarchy() const { return {}; } + QList superClasses() const { return {}; } + NodeMetaInfo directSuperClass() const { return {}; } + + bool defaultPropertyIsComponent() const { return {}; } + + TypeName typeName() const { return {}; } + TypeName simplifiedTypeName() const { return {}; } + int majorVersion() const { return {}; } + int minorVersion() const { return {}; } + + QString componentSource() const { return {}; } + QString componentFileName() const { return {}; } + + bool hasCustomParser() const { return {}; } + + bool availableInVersion(int, int) const { return {}; } + bool isSubclassOf(const TypeName &, int = -1, int = -1) const { return {}; } + + bool isGraphicalItem() const { return {}; } + bool isLayoutable() const { return {}; } + bool isView() const { return {}; } + bool isTabView() const { return {}; } + + QString importDirectoryPath() const { return {}; } + + static void clearCache() {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmlmodelnodefacade.h b/tests/unit/mockup/qmldesigner/designercore/include/qmlmodelnodefacade.h new file mode 100644 index 00000000000..3821943d8fd --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmlmodelnodefacade.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include +#include + +namespace QmlDesigner { + +class AbstractView; +class NodeInstanceView; + +class QmlModelNodeFacade +{ +public: + operator ModelNode() const { return {}; } + ModelNode modelNode() { return {}; } + const ModelNode modelNode() const { return {}; } + bool hasModelNode() const { return {}; } + static bool isValidQmlModelNodeFacade(const ModelNode &modelNode) { return {}; } + virtual bool isValid() const { return {}; } + + AbstractView *view() const { return {}; } + static NodeInstanceView *nodeInstanceView(const ModelNode &modelNode) { return {}; } + NodeInstanceView *nodeInstanceView() const { return {}; } + bool isRootNode() const { return {}; } + + QmlModelNodeFacade(const ModelNode &) {} + QmlModelNodeFacade() {} + ~QmlModelNodeFacade(){}; +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmlobjectnode.h b/tests/unit/mockup/qmldesigner/designercore/include/qmlobjectnode.h new file mode 100644 index 00000000000..7e2c118589a --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmlobjectnode.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmlmodelnodefacade.h" +#include + +#include + +namespace QmlDesigner { + +class QMLDESIGNERCORE_EXPORT QmlObjectNode : public QmlModelNodeFacade +{ +public: + QmlObjectNode() {} + QmlObjectNode(const ModelNode &modelNode){}; +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmlstate.h b/tests/unit/mockup/qmldesigner/designercore/include/qmlstate.h new file mode 100644 index 00000000000..50e82596b50 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmlstate.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmlmodelnodefacade.h" + +namespace QmlDesigner { + +class QmlModelState : public QmlModelNodeFacade +{ +public: + QmlModelState(); + QmlModelState(const ModelNode &) {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/qmltimeline.h b/tests/unit/mockup/qmldesigner/designercore/include/qmltimeline.h new file mode 100644 index 00000000000..0d2c05b1b56 --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/qmltimeline.h @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmlmodelnodefacade.h" +#include + +namespace QmlDesigner { + +class QmlTimeline : public QmlModelNodeFacade +{ +public: + QmlTimeline() {} + QmlTimeline(const ModelNode &) {} + + bool isValid() const override { return {}; } + + void toogleRecording(bool b) const {} + + void resetGroupRecording() const {} +}; + +} // namespace QmlDesigner diff --git a/tests/unit/mockup/qmldesigner/designercore/include/rewriterview.h b/tests/unit/mockup/qmldesigner/designercore/include/rewriterview.h new file mode 100644 index 00000000000..39c19e4e2dd --- /dev/null +++ b/tests/unit/mockup/qmldesigner/designercore/include/rewriterview.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "qmldesignercorelib_global.h" +#include "abstractview.h" + +namespace QmlJS { +class Document; +class ScopeChain; +} + +namespace QmlDesigner { + +class TextModifier; + +namespace Internal { + +class TextToModelMerger; +class ModelToTextMerger; +class ModelNodePositionStorage; + +} //Internal + +struct CppTypeData +{ + QString superClassName; + QString importUrl; + QString versionString; + QString cppClassName; + QString typeName; + bool isSingleton = false; +}; + +class RewriterView : public AbstractView +{ + Q_OBJECT + +public: + enum DifferenceHandling { + Validate, + Amend + }; + +public: + RewriterView(DifferenceHandling, QObject *) {} + ~RewriterView() override {} + + void modelAttached(Model *) override {} + void modelAboutToBeDetached(Model *) override {} + void nodeCreated(const ModelNode &) override {} + void nodeRemoved(const ModelNode &, const NodeAbstractProperty &, PropertyChangeFlags) override + {} + void propertiesAboutToBeRemoved(const QList &) override {} + void propertiesRemoved(const QList &) override {} + void variantPropertiesChanged(const QList &, PropertyChangeFlags) override {} + void bindingPropertiesChanged(const QList &, PropertyChangeFlags) override {} + void signalHandlerPropertiesChanged(const QVector &, + PropertyChangeFlags) override + {} + void nodeReparented(const ModelNode &, + const NodeAbstractProperty &, + const NodeAbstractProperty &, + AbstractView::PropertyChangeFlags) override + {} + void nodeIdChanged(const ModelNode &, const QString &, const QString &) override {} + void nodeOrderChanged(const NodeListProperty &, const ModelNode &, int) override {} + void rootNodeTypeChanged(const QString &, int, int) override {} + void nodeTypeChanged(const ModelNode &, const TypeName &, int, int) override {} + void customNotification(const AbstractView *, + const QString &, + const QList &, + const QList &) override + {} + + void rewriterBeginTransaction() override {} + void rewriterEndTransaction() override {} + + void importsChanged(const QList &, const QList &) override {} + + TextModifier *textModifier() const { return {}; } + void setTextModifier(TextModifier *) {} + QString textModifierContent() const { return {}; } + + void reactivateTextMofifierChangeSignals() {} + void deactivateTextMofifierChangeSignals() {} + + void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override + {} + + Internal::ModelNodePositionStorage *positionStorage() const {} + + QList warnings() const { return {}; } + QList errors() const { return {}; } + void clearErrorAndWarnings() {} + void setErrors(const QList &) {} + void setWarnings(const QList &) {} + void setIncompleteTypeInformation(bool) {} + bool hasIncompleteTypeInformation() const { return false; } + void addError(const DocumentMessage &) {} + + void enterErrorState(const QString &) {} + bool inErrorState() const { return false; } + void leaveErrorState() {} + void resetToLastCorrectQml() {} + + QMap extractText(const QList &) const; + int nodeOffset(const ModelNode &) const; + int nodeLength(const ModelNode &) const; + int firstDefinitionInsideOffset(const ModelNode &) const { return {}; } + int firstDefinitionInsideLength(const ModelNode &) const { return {}; } + bool modificationGroupActive() { return {}; } + ModelNode nodeAtTextCursorPosition(int) const { return {}; } + + bool renameId(const QString &, const QString &) { return {}; } + + const QmlJS::Document *document() const { return {}; } + const QmlJS::ScopeChain *scopeChain() const { return {}; } + + QString convertTypeToImportAlias(const QString &) const { return {}; } + + bool checkSemanticErrors() const { return {}; } + + void setCheckSemanticErrors(bool) {} + + QString pathForImport(const Import &) { return {}; } + + QStringList importDirectories() const { return {}; } + + QSet> qrcMapping() const { return {}; } + + void moveToComponent(const ModelNode &) {} + + QStringList autoComplete(const QString &, int, bool = true) { return {}; } + + QList getCppTypes() { return {}; } + + void setWidgetStatusCallback(std::function setWidgetStatusCallback); + + void qmlTextChanged() {} + void delayedSetup() {} + + void writeAuxiliaryData() {} + void restoreAuxiliaryData() {} + + QString getRawAuxiliaryData() const { return {}; } + QString auxiliaryDataAsQML() const { return {}; } + + ModelNode getNodeForCanonicalIndex(int) { return {}; } +}; + +} //QmlDesigner diff --git a/tests/unit/unittest/clangtooltipinfo-test.cpp b/tests/unit/unittest/clangtooltipinfo-test.cpp index 26b6596cc5d..0c9db57dc34 100644 --- a/tests/unit/unittest/clangtooltipinfo-test.cpp +++ b/tests/unit/unittest/clangtooltipinfo-test.cpp @@ -115,6 +115,16 @@ TEST_F(ToolTipInfo, LocalVariableInt) ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("int")))); } +TEST_F(ToolTipInfo, LocalVariableConstInt) +{ + ASSERT_THAT(tooltip(211, 19), IsToolTip(::ToolTipInfo(Utf8StringLiteral("const int")))); +} + +TEST_F(ToolTipInfo, FileScopeVariableConstInt) +{ + ASSERT_THAT(tooltip(206, 11), IsToolTip(::ToolTipInfo(Utf8StringLiteral("const int")))); +} + TEST_F(ToolTipInfo, LocalVariablePointerToConstInt) { const ::ToolTipInfo actual = tooltip(4, 5); diff --git a/tests/unit/unittest/creator_dependency.pri b/tests/unit/unittest/creator_dependency.pri index 4aaac22a1bf..ef00624b957 100644 --- a/tests/unit/unittest/creator_dependency.pri +++ b/tests/unit/unittest/creator_dependency.pri @@ -16,6 +16,7 @@ include($$PWD/../../../src/plugins/clangpchmanager/clangpchmanager-source.pri) include($$PWD/../../../src/plugins/cpptools/cpptoolsunittestfiles.pri) include($$PWD/../../../src/plugins/debugger/debuggerunittestfiles.pri) include($$PWD/../../../src/plugins/compilationdatabaseprojectmanager/compilationdatabaseunittestfiles.pri) +include($$PWD/../../../src/plugins/qmldesigner/qmldesignerunittestfiles.pri) !isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):include(cplusplus.pri) !isEmpty(LLVM_VERSION) { include($$PWD/../../../src/plugins/clangtools/clangtoolsunittestfiles.pri) diff --git a/tests/unit/unittest/data/tooltipinfo.cpp b/tests/unit/unittest/data/tooltipinfo.cpp index 6844a823cc5..bc1f17066ef 100644 --- a/tests/unit/unittest/data/tooltipinfo.cpp +++ b/tests/unit/unittest/data/tooltipinfo.cpp @@ -202,3 +202,11 @@ Nuu **pointers(Nuu **p1) static constexpr int calcValue() { return 1 + 2; } const auto val = calcValue() + sizeof(char); + +const int zero = 0; + +static void func() +{ + const int i = 5; + const int j = i; +} diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index 1a92ff33884..de524834039 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -65,6 +65,8 @@ using testing::Property; using testing::Return; using testing::ReturnRef; using testing::SafeMatcherCast; +using testing::SaveArg; +using testing::SaveArgPointee; using testing::Sequence; using testing::SizeIs; using testing::StrEq; diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 704c9c6b90a..13fbe941301 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ #include #include #include +#include #include @@ -1450,6 +1452,25 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag) { } // namespace Internal } // namespace ClangTools +namespace QmlDesigner { + +std::ostream &operator<<(std::ostream &out, const ModelNode &node) +{ + if (!node.isValid()) + return out << "(invalid)"; + + return out << "(" << node.id() << ")"; +} +std::ostream &operator<<(std::ostream &out, const VariantProperty &property) +{ + if (!property.isValid()) + return out << "(invalid)"; + + return out << "(" << property.parentModelNode() << ", " << property.name() << ", " + << property.value() << ")"; +} +} // namespace QmlDesigner + void setFilePathCache(ClangBackEnd::FilePathCaching *cache) { filePathCache = cache; diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index e0cb55315f4..565479be03f 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -350,4 +350,12 @@ std::ostream &operator<<(std::ostream &out, const Diagnostic &diag); } // namespace Internal } // namespace CppTools +namespace QmlDesigner { +class ModelNode; +class VariantProperty; + +std::ostream &operator<<(std::ostream &out, const ModelNode &node); +std::ostream &operator<<(std::ostream &out, const VariantProperty &property); +} // namespace QmlDesigner + void setFilePathCache(ClangBackEnd::FilePathCaching *filePathCache); diff --git a/tests/unit/unittest/gtest-qt-printing.cpp b/tests/unit/unittest/gtest-qt-printing.cpp index cd97883b164..c097fd0b4cb 100644 --- a/tests/unit/unittest/gtest-qt-printing.cpp +++ b/tests/unit/unittest/gtest-qt-printing.cpp @@ -59,9 +59,11 @@ std::ostream &operator<<(std::ostream &out, const QVariant &variant) QString output; QDebug debug(&output); - debug << variant; + debug.noquote().nospace() << variant; - return out << output; + QByteArray utf8Text = output.toUtf8(); + + return out.write(utf8Text.data(), utf8Text.size()); } std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format) @@ -88,4 +90,14 @@ void PrintTo(const QString &text, std::ostream *os) *os << text; } +void PrintTo(const QVariant &variant, std::ostream *os) +{ + *os << variant; +} + +void PrintTo(const QByteArray &text, std::ostream *os) +{ + *os << text; +} + QT_END_NAMESPACE diff --git a/tests/unit/unittest/gtest-qt-printing.h b/tests/unit/unittest/gtest-qt-printing.h index 424762273b3..ebaeb2c7851 100644 --- a/tests/unit/unittest/gtest-qt-printing.h +++ b/tests/unit/unittest/gtest-qt-printing.h @@ -35,10 +35,12 @@ class QVariant; class QString; class QTextCharFormat; -std::ostream &operator<<(std::ostream &out, const QVariant &variant); +std::ostream &operator<<(std::ostream &out, const QVariant &QVariant); std::ostream &operator<<(std::ostream &out, const QString &text); std::ostream &operator<<(std::ostream &out, const QByteArray &byteArray); std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format); void PrintTo(const QString &text, std::ostream *os); +void PrintTo(const QVariant &variant, std::ostream *os); +void PrintTo(const QByteArray &text, std::ostream *os); QT_END_NAMESPACE diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp new file mode 100644 index 00000000000..ca0913f8656 --- /dev/null +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -0,0 +1,952 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include "googletest.h" + +#include "mocklistmodeleditorview.h" + +#include +#include +#include +#include +#include + +namespace { + +using QmlDesigner::AbstractProperty; +using QmlDesigner::AbstractView; +using QmlDesigner::ModelNode; + +MATCHER_P2(HasItem, + name, + value, + std::string(negation ? "hasn't " : "has ") + "(" + name + ", " + value + ")") +{ + QStandardItem *item = arg; + + return item->data(Qt::UserRole).toString() == name && item->data(Qt::UserRole).toDouble() == value; +} + +MATCHER(IsInvalid, std::string(negation ? "isn't null" : "is null")) +{ + return !arg.isValid(); +} + +MATCHER_P3(IsVariantProperty, + node, + name, + value, + std::string(negation ? "isn't " : "is ") + "(" + name + ", " + PrintToString(value) + ")") +{ + const QmlDesigner::VariantProperty &property = arg; + + return property.parentModelNode() == node && property.name() == name && property.value() == value; +} + +MATCHER_P2(IsVariantProperty, + name, + value, + std::string(negation ? "isn't " : "is ") + "(" + name + ", " + PrintToString(value) + ")") +{ + const QmlDesigner::VariantProperty &property = arg; + + return property.name() == name && property.value() == value; +} + +MATCHER_P2(IsAbstractProperty, node, name, std::string(negation ? "isn't " : "is ") + "(" + name + ")") +{ + const QmlDesigner::AbstractProperty &property = arg; + + return property.parentModelNode() == node && property.name() == name; +} + +class ListModelEditor : public testing::Test +{ +public: + ListModelEditor() + { + designerModel->attachView(&mockView); + + emptyListModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); + + listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); + mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); + element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}}); + element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}}); + element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}}); + } + + using Entry = std::pair; + + ModelNode createElement(std::initializer_list entries) + { + auto element = mockView.createModelNode("QtQml.Models/ListElement", 2, 15); + listModelNode.defaultNodeListProperty().reparentHere(element); + + for (const auto &entry : entries) { + element.variantProperty(entry.first).setValue(entry.second); + } + + return element; + } + + QList headerLabels(const QmlDesigner::ListModelEditorModel &model) const + { + QList labels; + labels.reserve(model.columnCount()); + + for (int i = 0; i < model.columnCount(); ++i) + labels.push_back(model.headerData(i, Qt::Horizontal).toString()); + + return labels; + } + + QList> displayValues() const + { + QList> rows; + + for (int rowIndex = 0; rowIndex < model.rowCount(); ++rowIndex) { + QList row; + + for (int columnIndex = 0; columnIndex < model.columnCount(); ++columnIndex) + row.push_back(model.data(model.index(rowIndex, columnIndex), Qt::DisplayRole)); + + rows.push_back(row); + } + + return rows; + } + + QList> backgroundColors() const + { + QList> rows; + + for (int rowIndex = 0; rowIndex < model.rowCount(); ++rowIndex) { + QList row; + + for (int columnIndex = 0; columnIndex < model.columnCount(); ++columnIndex) + row.push_back( + model.data(model.index(rowIndex, columnIndex), Qt::BackgroundColorRole) + .value()); + + rows.push_back(row); + } + + return rows; + } + + QList> properties() const + { + QList> properties; + properties.reserve(10); + + auto nodes = listModelNode.defaultNodeListProperty().toModelNodeList(); + + for (const ModelNode &node : nodes) + properties.push_back(node.variantProperties()); + + return properties; + } + +protected: + std::unique_ptr designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)}; + NiceMock mockView; + QmlDesigner::ListModelEditorModel model; + ModelNode listModelNode; + ModelNode emptyListModelNode; + ModelNode element1; + ModelNode element2; + ModelNode element3; +}; + +TEST_F(ListModelEditor, CreatePropertyNameSet) +{ + model.setListModel(listModelNode); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, CreatePropertyNameSetForEmptyList) +{ + model.setListModel(emptyListModelNode); + + ASSERT_THAT(model.propertyNames(), IsEmpty()); +} + +TEST_F(ListModelEditor, HorizontalLabels) +{ + model.setListModel(listModelNode); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, HorizontalLabelsForEmptyList) +{ + model.setListModel(emptyListModelNode); + + ASSERT_THAT(headerLabels(model), IsEmpty()); +} + +TEST_F(ListModelEditor, DisplayValues) +{ + model.setListModel(listModelNode); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeValueChangesDisplayValues) +{ + model.setListModel(listModelNode); + + model.setValue(0, 1, "hello"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "hello", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, EditValueCallVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element1, "name", "hello")), + Eq(AbstractView::NoAdditionalChanges))); + + model.setValue(0, 1, "hello"); +} + +TEST_F(ListModelEditor, ChangeDisplayValueCallsVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element1, "name", "hello")), + Eq(AbstractView::NoAdditionalChanges))) + .Times(0); + + model.setValue(0, 1, "hello", Qt::DisplayRole); +} + +TEST_F(ListModelEditor, AddRowAddedInvalidRow) +{ + model.setListModel(listModelNode); + + model.addRow(); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()), + ElementsAre(IsInvalid(), IsInvalid(), IsInvalid(), IsInvalid()))); +} + +TEST_F(ListModelEditor, AddRowCreatesNewModelNodeAndReparents) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, nodeCreated(Property(&ModelNode::type, Eq("QtQml.Models.ListElement")))); + EXPECT_CALL(mockView, + nodeReparented(Property(&ModelNode::type, Eq("QtQml.Models.ListElement")), + Property(&AbstractProperty::parentModelNode, Eq(listModelNode)), + _, + _)); + + model.addRow(); +} + +TEST_F(ListModelEditor, ChangeAddedRowPropery) +{ + model.setListModel(listModelNode); + model.addRow(); + + model.setValue(3, 2, 22); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()), + ElementsAre(IsInvalid(), IsInvalid(), 22, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeAddedRowProperyCallsVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + ModelNode element4; + ON_CALL(mockView, nodeReparented(_, _, _, _)).WillByDefault(SaveArg<0>(&element4)); + model.addRow(); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element4, "value", 22)), + Eq(AbstractView::PropertiesAdded))); + + model.setValue(3, 2, 22); +} + +TEST_F(ListModelEditor, AddColumnInsertsPropertyName) +{ + model.setListModel(listModelNode); + + model.addColumn("other"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "other", "value", "value2")); +} + +TEST_F(ListModelEditor, AddColumnInsertsPropertyNameToEmptyModel) +{ + model.setListModel(emptyListModelNode); + + model.addColumn("foo"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddTwiceColumnInsertsPropertyNameToEmptyModel) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo2"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("foo", "foo2")); +} + +TEST_F(ListModelEditor, AddSameColumnInsertsPropertyName) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddColumnInsertsHeaderLabel) +{ + model.setListModel(listModelNode); + + model.addColumn("other"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "other", "value", "value2")); +} + +TEST_F(ListModelEditor, AddColumnInsertsHeaderLabelToEmptyModel) +{ + model.setListModel(emptyListModelNode); + + model.addColumn("foo"); + + ASSERT_THAT(headerLabels(model), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddTwiceColumnInsertsHeaderLabelToEmptyModel) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo2"); + + ASSERT_THAT(headerLabels(model), ElementsAre("foo", "foo2")); +} + +TEST_F(ListModelEditor, AddSameColumnInsertsHeaderLabel) +{ + model.setListModel(emptyListModelNode); + model.addColumn("foo"); + + model.addColumn("foo"); + + ASSERT_THAT(headerLabels(model), ElementsAre("foo")); +} + +TEST_F(ListModelEditor, AddColumnInsertsDisplayValues) +{ + model.setListModel(listModelNode); + + model.addColumn("other"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", IsInvalid(), 1, 42), + ElementsAre("pic.png", "bar", IsInvalid(), 4, IsInvalid()), + ElementsAre("pic.png", "poo", IsInvalid(), 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeAddColumnPropertyDisplayValue) +{ + model.setListModel(listModelNode); + model.addColumn("other"); + + model.setValue(1, 2, 22); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", IsInvalid(), 1, 42), + ElementsAre("pic.png", "bar", 22, 4, IsInvalid()), + ElementsAre("pic.png", "poo", IsInvalid(), 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, ChangeAddColumnPropertyCallsVariantPropertiesChanged) +{ + model.setListModel(listModelNode); + model.addColumn("other"); + + EXPECT_CALL(mockView, + variantPropertiesChanged(ElementsAre(IsVariantProperty(element2, "other", 434)), _)); + + model.setValue(1, 2, 434); +} + +TEST_F(ListModelEditor, RemoveColumnRemovesDisplayValues) +{ + model.setListModel(listModelNode); + + model.removeColumn(2); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 42), + ElementsAre("pic.png", "bar", IsInvalid()), + ElementsAre("pic.png", "poo", IsInvalid()))); +} + +TEST_F(ListModelEditor, RemoveColumnRemovesProperties) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, propertiesRemoved(ElementsAre(IsAbstractProperty(element2, "image")))); + EXPECT_CALL(mockView, propertiesRemoved(ElementsAre(IsAbstractProperty(element3, "image")))); + + model.removeColumn(0); +} + +TEST_F(ListModelEditor, RemoveColumnRemovesPropertyName) +{ + model.setListModel(listModelNode); + + model.removeColumn(1); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "value", "value2")); +} + +TEST_F(ListModelEditor, RemoveRowRemovesDisplayValues) +{ + model.setListModel(listModelNode); + + model.removeRow(1); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, RemoveRowRemovesElementInListModel) +{ + model.setListModel(listModelNode); + + EXPECT_CALL(mockView, nodeRemoved(Eq(element2), _, _)); + + model.removeRow(1); +} + +TEST_F(ListModelEditor, ConvertStringFloatToFloat) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25.5"); + + ASSERT_THAT(element2.variantProperty("name").value().value(), 25.5); + ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::Double); +} + +TEST_F(ListModelEditor, ConvertStringIntegerToDouble) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25"); + + ASSERT_THAT(element2.variantProperty("name").value().value(), 25); + ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::Double); +} + +TEST_F(ListModelEditor, DontConvertStringToNumber) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "hello"); + + ASSERT_THAT(element2.variantProperty("name").value().value(), "hello"); + ASSERT_THAT(element2.variantProperty("name").value().type(), QVariant::String); +} + +TEST_F(ListModelEditor, EmptyStringsRemovesProperty) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, ""); + + ASSERT_THAT(element2.variantProperty("name").value().value(), Eq("")); +} + +TEST_F(ListModelEditor, InvalidVariantRemovesProperty) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, QVariant()); + + ASSERT_FALSE(element2.hasProperty("name")); +} + +TEST_F(ListModelEditor, DispayValueIsChangedToDouble) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25.5"); + + ASSERT_THAT(displayValues()[1][1].type(), QVariant::Double); +} + +TEST_F(ListModelEditor, StringDispayValueIsNotChanged) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, "25.5a"); + + ASSERT_THAT(displayValues()[1][1].type(), QVariant::String); +} + +TEST_F(ListModelEditor, SetInvalidToDarkYellowBackgroundColor) +{ + model.setListModel(listModelNode); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Qt::darkYellow, Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, SettingValueChangesBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(0, 0, "foo"); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, SettingValueChangesByDisplayRoleBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(0, 0, "foo", Qt::DisplayRole); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, ResettingValueChangesBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, QVariant{}); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Qt::darkYellow, Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Qt::darkYellow, Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, ResettingValueChangesByDisplayRoleBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(1, 1, QVariant{}, Qt::DisplayRole); + + ASSERT_THAT( + backgroundColors(), + ElementsAre( + ElementsAre(Qt::darkYellow, Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow)), + ElementsAre(Not(Qt::darkYellow), Qt::darkYellow, Not(Qt::darkYellow), Qt::darkYellow), + ElementsAre(Not(Qt::darkYellow), Not(Qt::darkYellow), Not(Qt::darkYellow), Qt::darkYellow))); +} + +TEST_F(ListModelEditor, SettingNullValueChangesBackgroundColor) +{ + model.setListModel(listModelNode); + + model.setValue(0, 0, 0); + + ASSERT_THAT(backgroundColors(), + ElementsAre(ElementsAre(_, _, _, _), + ElementsAre(_, _, _, Qt::darkYellow), + ElementsAre(_, _, _, Qt::darkYellow))); +} + +TEST_F(ListModelEditor, DontRenamePropertyIfColumnNameExists) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, DontRenameColumnIfColumnNameExists) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "value", "value2")); +} + +TEST_F(ListModelEditor, DontRenameColumnIfColumnNameExistsDoesNotChangeDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, DontRenameColumnIfColumnNameExistsDoesNotChangeProperties) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "value2"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RenamePropertyButDontChangeOrder) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "mood", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameColumnButDontChangeOrder) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "mood", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameColumnButDontChangeOrderDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 1, 42), + ElementsAre("pic.png", "bar", 4, IsInvalid()), + ElementsAre("pic.png", "poo", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, RenameColumnButDontChangeOrderProperies) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "mood"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RemoveColumnAfterRenameColumn) +{ + model.setListModel(listModelNode); + model.renameColumn(1, "mood"); + + model.removeColumn(1); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, ChangeValueAfterRenameColumn) +{ + model.setListModel(listModelNode); + model.renameColumn(1, "mood"); + + model.setValue(1, 1, "taaa"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "taaa"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RemovePropertyAfterRenameColumn) +{ + model.setListModel(listModelNode); + model.renameColumn(1, "mood"); + + model.setValue(1, 1, {}); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("mood", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RenameToPrecedingProperty) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("alpha", "image", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameToPrecedingColumn) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(headerLabels(model), ElementsAre("alpha", "image", "value", "value2")); +} + +TEST_F(ListModelEditor, RenameToPrecedingColumnDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre("foo", IsInvalid(), 1, 42), + ElementsAre("bar", "pic.png", 4, IsInvalid()), + ElementsAre("poo", "pic.png", 111, IsInvalid()))); +} + +TEST_F(ListModelEditor, RenameToPrecedingColumnProperties) +{ + model.setListModel(listModelNode); + + model.renameColumn(1, "alpha"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("alpha", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("alpha", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("alpha", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RenameToFollowingProperty) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(model.propertyNames(), ElementsAre("image", "name", "value2", "zoo")); +} + +TEST_F(ListModelEditor, RenameToFollowingColumn) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(headerLabels(model), ElementsAre("image", "name", "value2", "zoo")); +} + +TEST_F(ListModelEditor, RenameToFollowingColumnDisplayValues) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(displayValues(), + ElementsAre(ElementsAre(IsInvalid(), "foo", 42, 1), + ElementsAre("pic.png", "bar", IsInvalid(), 4), + ElementsAre("pic.png", "poo", IsInvalid(), 111))); +} + +TEST_F(ListModelEditor, RenameToFollowingColumnProperties) +{ + model.setListModel(listModelNode); + + model.renameColumn(2, "zoo"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"), + IsVariantProperty("zoo", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("zoo", 4)), + UnorderedElementsAre(IsVariantProperty("image", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("zoo", 111)))); +} + +TEST_F(ListModelEditor, RenamePropertiesWithInvalidValue) +{ + model.setListModel(listModelNode); + + model.renameColumn(0, "mood"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("name", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, ChangeValueAfterRenamePropertiesWithInvalidValue) +{ + model.setListModel(listModelNode); + model.renameColumn(0, "mood"); + + model.setValue(0, 0, "haaa"); + + ASSERT_THAT(properties(), + ElementsAre(UnorderedElementsAre(IsVariantProperty("mood", "haaa"), + IsVariantProperty("name", "foo"), + IsVariantProperty("value", 1), + IsVariantProperty("value2", 42)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "bar"), + IsVariantProperty("value", 4)), + UnorderedElementsAre(IsVariantProperty("mood", "pic.png"), + IsVariantProperty("name", "poo"), + IsVariantProperty("value", 111)))); +} + +TEST_F(ListModelEditor, RemoveLastRow) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + + model.removeRow(0); + + ASSERT_THAT(displayValues(), IsEmpty()); +} + +TEST_F(ListModelEditor, RemoveLastColumn) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + + model.removeColumn(0); + + ASSERT_THAT(displayValues(), ElementsAre(IsEmpty())); +} + +TEST_F(ListModelEditor, RemoveLastEmptyColumn) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + model.removeRow(0); + + model.removeColumn(0); + + ASSERT_THAT(displayValues(), IsEmpty()); +} + +TEST_F(ListModelEditor, RemoveLastEmptyRow) +{ + model.setListModel(emptyListModelNode); + model.addColumn("mood"); + model.addRow(); + model.removeColumn(0); + + model.removeRow(0); + + ASSERT_THAT(displayValues(), IsEmpty()); +} + +} // namespace diff --git a/tests/unit/unittest/mockfilesystem.h b/tests/unit/unittest/mockfilesystem.h index 688edbcae57..fff5758cc62 100644 --- a/tests/unit/unittest/mockfilesystem.h +++ b/tests/unit/unittest/mockfilesystem.h @@ -34,4 +34,5 @@ class MockFileSystem : public ClangBackEnd::FileSystemInterface public: MOCK_CONST_METHOD1(directoryEntries, ClangBackEnd::FilePathIds(const QString &directoryPath)); MOCK_CONST_METHOD1(lastModified, long long(ClangBackEnd::FilePathId filePathId)); + MOCK_METHOD1(remove, void(const ClangBackEnd::FilePathIds &filePathIds)); }; diff --git a/tests/unit/unittest/mocklistmodeleditorview.h b/tests/unit/unittest/mocklistmodeleditorview.h new file mode 100644 index 00000000000..6bec164f338 --- /dev/null +++ b/tests/unit/unittest/mocklistmodeleditorview.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +#include + +class MockListModelEditorView : public QmlDesigner::AbstractView +{ +public: + MOCK_METHOD(void, + variantPropertiesChanged, + (const QList &propertyList, + PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, nodeCreated, (const QmlDesigner::ModelNode &createdNode), (override)); + MOCK_METHOD(void, + nodeReparented, + (const QmlDesigner::ModelNode &node, + const QmlDesigner::NodeAbstractProperty &newPropertyParent, + const QmlDesigner::NodeAbstractProperty &oldPropertyParent, + AbstractView::PropertyChangeFlags propertyChange), + (override)); + MOCK_METHOD(void, + propertiesRemoved, + (const QList &propertyList), + (override)); + + MOCK_METHOD(void, + nodeRemoved, + (const QmlDesigner::ModelNode &removedNode, + const QmlDesigner::NodeAbstractProperty &parentProperty, + AbstractView::PropertyChangeFlags propertyChange), + (override)); +}; diff --git a/tests/unit/unittest/mockprecompiledheaderstorage.h b/tests/unit/unittest/mockprecompiledheaderstorage.h index de755e07eb7..5dddf97fce8 100644 --- a/tests/unit/unittest/mockprecompiledheaderstorage.h +++ b/tests/unit/unittest/mockprecompiledheaderstorage.h @@ -57,4 +57,5 @@ public: MOCK_CONST_METHOD1( fetchTimeStamps, ClangBackEnd::PrecompiledHeaderTimeStamps(ClangBackEnd::ProjectPartId projectPartId)); + MOCK_CONST_METHOD0(fetchAllPchPaths, ClangBackEnd::FilePaths()); }; diff --git a/tests/unit/unittest/mocksqlitereadstatement.cpp b/tests/unit/unittest/mocksqlitereadstatement.cpp index 1a27eea50f0..1f5bc53759b 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.cpp +++ b/tests/unit/unittest/mocksqlitereadstatement.cpp @@ -107,6 +107,12 @@ FilePathIds MockSqliteReadStatement::values(std::size_ return valuesReturnFilePathIds(reserveSize, projectPartId); } +template<> +ClangBackEnd::FilePaths MockSqliteReadStatement::values(std::size_t reserveSize) +{ + return valuesReturnFilePaths(reserveSize); +} + template <> std::vector MockSqliteReadStatement::values(std::size_t reserveSize) { diff --git a/tests/unit/unittest/mocksqlitereadstatement.h b/tests/unit/unittest/mocksqlitereadstatement.h index 04e1f8effca..11a7b126e57 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.h +++ b/tests/unit/unittest/mocksqlitereadstatement.h @@ -113,6 +113,8 @@ public: MOCK_METHOD1(valueReturnFilePath, Utils::optional(int)); + MOCK_METHOD1(valuesReturnFilePaths, ClangBackEnd::FilePaths(std::size_t)); + MOCK_METHOD1(valueReturnSmallString, Utils::optional(int)); @@ -233,6 +235,9 @@ template<> FilePathIds MockSqliteReadStatement::values(std::size_t reserveSize, const int &projectPartId); +template<> +ClangBackEnd::FilePaths MockSqliteReadStatement::values(std::size_t reserveSize); + template <> std::vector MockSqliteReadStatement::values(std::size_t reserveSize); diff --git a/tests/unit/unittest/pchtaskqueue-test.cpp b/tests/unit/unittest/pchtaskqueue-test.cpp index 598a2a5132a..6a4a2211d89 100644 --- a/tests/unit/unittest/pchtaskqueue-test.cpp +++ b/tests/unit/unittest/pchtaskqueue-test.cpp @@ -25,14 +25,18 @@ #include "googletest.h" +#include "mockfilesystem.h" #include "mockpchcreator.h" #include "mockprecompiledheaderstorage.h" #include "mocksqlitetransactionbackend.h" #include "mocktaskscheduler.h" #include "testenvironment.h" +#include #include #include +#include +#include namespace { @@ -45,9 +49,26 @@ using ClangBackEnd::SlotUsage; class PchTaskQueue : public testing::Test { protected: + ClangBackEnd::FilePathId filePathId(Utils::SmallStringView path) + { + return filePathCache.filePathId(ClangBackEnd::FilePathView{path}); + } + + ClangBackEnd::FilePathIds filePathIds(const Utils::PathStringVector &paths) + { + return filePathCache.filePathIds(Utils::transform(paths, [](const Utils::PathString &path) { + return ClangBackEnd::FilePathView(path); + })); + } + +protected: + Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; + ClangBackEnd::RefactoringDatabaseInitializer initializer{database}; + ClangBackEnd::FilePathCaching filePathCache{database}; NiceMock> mockSytemPchTaskScheduler; NiceMock> mockProjectPchTaskScheduler; NiceMock mockPrecompiledHeaderStorage; + NiceMock mockFileSystem; MockSqliteTransactionBackend mockSqliteTransactionBackend; NiceMock> mockSetProgressCallback; ClangBackEnd::ProgressCounter progressCounter{mockSetProgressCallback.AsStdFunction()}; @@ -57,7 +78,9 @@ protected: progressCounter, mockPrecompiledHeaderStorage, mockSqliteTransactionBackend, - testEnvironment}; + testEnvironment, + mockFileSystem, + filePathCache}; IncludeSearchPaths systemIncludeSearchPaths{ {"/includes", 1, IncludeSearchPathType::BuiltIn}, {"/other/includes", 2, IncludeSearchPathType::System}}; @@ -390,4 +413,91 @@ TEST_F(PchTaskQueue, DeleteSystemPchEntryInDatabaseIfNoPchIsGenerated) tasks.front()(mockPchCreator); } +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfSystemTaskAreProcessed) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 1})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfProjectTaskAreProcessed) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 1})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfSystemTaskIsAdded) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + queue.addSystemPchTasks({systemTask1}); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DontDeleteUnusedPchsIfProjectTaskIsAdded) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({"/tmp/foo", "/tmp/bar"}))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{"/tmp/foo", "/tmp/poo"})); + queue.addProjectPchTasks({projectTask1}); + + EXPECT_CALL(mockFileSystem, remove(_)).Times(0); + + queue.processEntries(); +} + +TEST_F(PchTaskQueue, DeleteUnusedPchs) +{ + QString pchsDirectory{testEnvironment.pchBuildDirectory()}; + ON_CALL(mockSytemPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockProjectPchTaskScheduler, slotUsage()).WillByDefault(Return(SlotUsage{2, 0})); + ON_CALL(mockFileSystem, directoryEntries(Eq(pchsDirectory))) + .WillByDefault(Return(filePathIds({ + "/tmp/foo", + "/tmp/bar", + "/tmp/hoo", + "/tmp/too", + }))); + ON_CALL(mockPrecompiledHeaderStorage, fetchAllPchPaths()) + .WillByDefault(Return(ClangBackEnd::FilePaths{ + "/tmp/foo", + "/tmp/poo", + "/tmp/too", + })); + + EXPECT_CALL(mockFileSystem, + remove(UnorderedElementsAre(filePathId("/tmp/bar"), filePathId("/tmp/hoo")))); + + queue.processEntries(); +} + } // namespace diff --git a/tests/unit/unittest/precompiledheaderstorage-test.cpp b/tests/unit/unittest/precompiledheaderstorage-test.cpp index 8b3727decd8..77574a28b91 100644 --- a/tests/unit/unittest/precompiledheaderstorage-test.cpp +++ b/tests/unit/unittest/precompiledheaderstorage-test.cpp @@ -53,6 +53,7 @@ protected: MockSqliteReadStatement &fetchPrecompiledHeaderStatement = storage.fetchPrecompiledHeaderStatement; MockSqliteReadStatement &fetchPrecompiledHeadersStatement = storage.fetchPrecompiledHeadersStatement; MockSqliteReadStatement &fetchTimeStampsStatement = storage.fetchTimeStampsStatement; + MockSqliteReadStatement &fetchAllPchPathsStatement = storage.fetchAllPchPathsStatement; }; TEST_F(PrecompiledHeaderStorage, UseTransaction) @@ -458,6 +459,32 @@ TEST_F(PrecompiledHeaderStorage, FetchTimeStampsBusy) storage.fetchTimeStamps(23); } +TEST_F(PrecompiledHeaderStorage, FetchAllPchPaths) +{ + InSequence s; + + EXPECT_CALL(database, deferredBegin()); + EXPECT_CALL(fetchAllPchPathsStatement, valuesReturnFilePaths(_)); + EXPECT_CALL(database, commit()); + + storage.fetchAllPchPaths(); +} + +TEST_F(PrecompiledHeaderStorage, FetchAllPchPathsIsBusy) +{ + InSequence s; + + EXPECT_CALL(database, deferredBegin()); + EXPECT_CALL(fetchAllPchPathsStatement, valuesReturnFilePaths(_)) + .WillOnce(Throw(Sqlite::StatementIsBusy{""})); + EXPECT_CALL(database, rollback()); + EXPECT_CALL(database, deferredBegin()); + EXPECT_CALL(fetchAllPchPathsStatement, valuesReturnFilePaths(_)); + EXPECT_CALL(database, commit()); + + storage.fetchAllPchPaths(); +} + class PrecompiledHeaderStorageSlowTest : public testing::Test { protected: @@ -478,4 +505,18 @@ TEST_F(PrecompiledHeaderStorageSlowTest, NoFetchTimeStamps) Field(&ClangBackEnd::PrecompiledHeaderTimeStamps::system, Eq(33)))); } +TEST_F(PrecompiledHeaderStorageSlowTest, FetchAllPchPaths) +{ + storage.insertProjectPrecompiledHeader(11, "/tmp/yi", 22); + storage.insertProjectPrecompiledHeader(12, "/tmp/er", 22); + storage.insertSystemPrecompiledHeaders({11, 12}, "/tmp/se", 33); + storage.insertSystemPrecompiledHeaders({13}, "/tmp/wu", 33); + storage.insertProjectPrecompiledHeader(13, "/tmp/san", 22); + + auto filePathIds = storage.fetchAllPchPaths(); + + ASSERT_THAT(filePathIds, + UnorderedElementsAre("/tmp/er", "/tmp/san", "/tmp/se", "/tmp/wu", "/tmp/yi")); +} + } // namespace diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index e9a96ace5f0..3917237d0bf 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -1,4 +1,5 @@ INCLUDEPATH += ../mockup +INCLUDEPATH += ../mockup/qmldesigner/designercore/include QT += core network testlib widgets CONFIG += console c++14 testcase @@ -38,8 +39,11 @@ CONFIG(release, debug|release):QMAKE_LFLAGS += -Wl,--strip-debug } gcc:!clang: QMAKE_CXXFLAGS += -Wno-noexcept-type -msvc: QMAKE_CXXFLAGS += /bigobj /wd4267 /wd4141 /wd4146 +msvc{ +QMAKE_CXXFLAGS += /bigobj /wd4267 /wd4141 /wd4146 /wd4624 +QMAKE_LFLAGS += /INCREMENTAL +} # create fake CppTools.json for the mime type definitions dependencyList = "\"Dependencies\" : []" cpptoolsjson.input = $$PWD/../../../src/plugins/cpptools/CppTools.json.in @@ -65,6 +69,7 @@ SOURCES += \ gtest-qt-printing.cpp \ lastchangedrowid-test.cpp \ lineprefixer-test.cpp \ + listmodeleditor-test.cpp \ locatorfilter-test.cpp \ mimedatabase-utilities.cpp \ pchmanagerclientserverinprocess-test.cpp \ @@ -132,18 +137,15 @@ SOURCES += \ sqlstatementbuilder-test.cpp \ createtablesqlstatementbuilder-test.cpp -!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):matchingtext-test.cpp +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += matchingtext-test.cpp !isEmpty(LIBCLANG_LIBS) { SOURCES += \ - activationsequencecontextprocessor-test.cpp \ - activationsequenceprocessor-test.cpp \ chunksreportedmonitor.cpp \ clangasyncjob-base.cpp \ clangcodecompleteresults-test.cpp \ clangcodemodelserver-test.cpp \ clangcompletecodejob-test.cpp \ - clangcompletioncontextanalyzer-test.cpp \ clangdiagnosticfilter-test.cpp \ clangdocumentprocessors-test.cpp \ clangdocumentprocessor-test.cpp \ @@ -183,6 +185,12 @@ SOURCES += \ unsavedfile-test.cpp \ utf8positionfromlinecolumn-test.cpp \ readexporteddiagnostics-test.cpp + +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCE += \ + clangcompletioncontextanalyzer-test.cpp \ + activationsequencecontextprocessor-test.cpp \ + activationsequenceprocessor-test.cpp + } !isEmpty(LIBTOOLING_LIBS) { @@ -198,7 +206,6 @@ SOURCES += \ refactoringclientserverinprocess-test.cpp \ refactoringclient-test.cpp \ refactoringcompilationdatabase-test.cpp \ - refactoringengine-test.cpp \ refactoringserver-test.cpp \ sourcerangeextractor-test.cpp \ symbolindexing-test.cpp \ @@ -207,6 +214,9 @@ SOURCES += \ usedmacrocollector-test.cpp \ builddependencycollector-test.cpp \ tokenprocessor-test.cpp + +!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += refactoringengine-test.cpp + } !isEmpty(CLANGFORMAT_LIBS) { @@ -237,6 +247,7 @@ HEADERS += \ mockclangpathwatcher.h \ mockclangpathwatchernotifier.h \ mockfilesystem.h \ + mocklistmodeleditorview.h \ mockpchcreator.h \ mockpchmanagerclient.h \ mockpchmanagernotifier.h \ @@ -288,7 +299,11 @@ HEADERS += \ mockbuilddependencygenerator.h \ mockpchtasksmerger.h \ mockpchtaskqueue.h \ - mockpchtaskgenerator.h + mockpchtaskgenerator.h \ + ../mockup/qmldesigner/designercore/include/nodeinstanceview.h \ + ../mockup/qmldesigner/designercore/include/rewriterview.h \ + ../mockup/qmldesigner/designercore/include/itemlibraryitem.h + !isEmpty(LIBCLANG_LIBS) { HEADERS += \ diff --git a/tests/unit/unittest/unittest.qbs b/tests/unit/unittest/unittest.qbs index d00ec93b26d..de84177ba15 100644 --- a/tests/unit/unittest/unittest.qbs +++ b/tests/unit/unittest/unittest.qbs @@ -13,6 +13,8 @@ CppApplication { Depends { name: "libclang"; required: false } Depends { name: "clang_defines" } + Depends { name: "QmlDesigner" } + Depends { name: "sqlite_sources" } Depends { name: "Core" } Depends { name: "CPlusPlus" } @@ -97,6 +99,7 @@ CppApplication { "../../../src/tools/clangbackend/source", "../../../src/tools/clangpchmanagerbackend/source", "../../../src/tools/clangrefactoringbackend/source", + "../../../share/qtcreator/qml/qmlpuppet/types", ]; if (libclang.present) { paths.push(libclang.llvmIncludeDir);