aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Ziller <eike.ziller@qt.io>2020-10-19 15:22:37 +0200
committerEike Ziller <eike.ziller@qt.io>2020-10-19 15:22:37 +0200
commit80951d3e59f8b6ff270333692adfe2d65adf6c9c (patch)
tree8a5da6c06f7558d847e24dac386bb60775bd7d76
parentaf0e135e7328d5672e54d08ba35765308e554f23 (diff)
parent436f111a4609462fcaae501b3d6d9b072adbb0b0 (diff)
Merge remote-tracking branch 'origin/4.14' into master
Conflicts: src/plugins/cppeditor/cppquickfix_test.cpp Change-Id: Ib2984a3b3d9d071d11304b6cf132c2f8cef77e1c
-rw-r--r--cmake/CreatePythonXY.cmake79
-rw-r--r--doc/qtcreator/images/qtcreator-cmake-build-settings.pngbin31827 -> 30751 bytes
-rw-r--r--doc/qtcreator/src/cmake/creator-projects-cmake-building.qdocinc24
-rw-r--r--share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h13
-rw-r--r--share/qtcreator/qml/qmlpuppet/images/non-visual-component.pngbin0 -> 3311 bytes
-rw-r--r--share/qtcreator/qml/qmlpuppet/images/non-visual-component@2x.pngbin0 -> 7260 bytes
-rw-r--r--share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h1
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp34
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp3
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp84
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h45
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp31
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp19
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp3
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp23
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp84
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h8
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h3
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri2
-rw-r--r--share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp5
-rw-r--r--share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc2
-rw-r--r--share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml7
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml48
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml2
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir1
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml58
-rw-r--r--share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttfbin14124 -> 15012 bytes
-rw-r--r--src/libs/qtcreatorcdbext/CMakeLists.txt27
-rw-r--r--src/libs/sqlite/CMakeLists.txt2
-rw-r--r--src/libs/sqlite/sqlite-lib.pri2
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp12
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h7
-rw-r--r--src/libs/sqlite/sqliteblob.h100
-rw-r--r--src/libs/sqlite/sqlitesessionchangeset.cpp4
-rw-r--r--src/libs/sqlite/sqlitesessionchangeset.h7
-rw-r--r--src/libs/sqlite/sqlitesessions.cpp2
-rw-r--r--src/libs/sqlite/sqlitetimestamp.h47
-rw-r--r--src/libs/sqlite/sqlitevalue.h5
-rw-r--r--src/libs/utils/aspects.cpp9
-rw-r--r--src/libs/utils/aspects.h5
-rw-r--r--src/libs/utils/buildablehelperlibrary.cpp8
-rw-r--r--src/libs/utils/filesearch.cpp3
-rw-r--r--src/libs/utils/stringutils.cpp64
-rw-r--r--src/libs/utils/stringutils.h7
-rw-r--r--src/plugins/android/androidbuildapkstep.cpp2
-rw-r--r--src/plugins/android/androiddeployqtstep.cpp57
-rw-r--r--src/plugins/android/androiddeployqtstep.h14
-rw-r--r--src/plugins/android/androidqtversion.cpp3
-rw-r--r--src/plugins/autotest/boost/boosttestsettingspage.ui3
-rw-r--r--src/plugins/autotest/testconfiguration.cpp7
-rw-r--r--src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp10
-rw-r--r--src/plugins/clangcodemodel/test/clangbatchfileprocessor.h2
-rw-r--r--src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp18
-rw-r--r--src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui3
-rw-r--r--src/plugins/clangtools/clangtoolsunittests.cpp8
-rw-r--r--src/plugins/clangtools/clangtoolsunittests.h3
-rw-r--r--src/plugins/clangtools/runsettingswidget.ui3
-rw-r--r--src/plugins/cppcheck/cppchecktool.cpp3
-rw-r--r--src/plugins/cppeditor/cppeditorplugin.h2
-rw-r--r--src/plugins/cppeditor/cppquickfix_test.cpp69
-rw-r--r--src/plugins/cppeditor/cppquickfixes.cpp45
-rw-r--r--src/plugins/cpptools/cppmodelmanager_test.cpp12
-rw-r--r--src/plugins/debugger/gdb/gdbengine.cpp2
-rw-r--r--src/plugins/git/gitclient.cpp4
-rw-r--r--src/plugins/languageclient/client.cpp7
-rw-r--r--src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui3
-rw-r--r--src/plugins/mesonprojectmanager/settings/general/generalsettingswidget.ui3
-rw-r--r--src/plugins/mesonprojectmanager/settings/tools/toolitemsettings.ui3
-rw-r--r--src/plugins/mesonprojectmanager/settings/tools/toolssettingswidget.ui3
-rw-r--r--src/plugins/nim/project/nimbletaskstep.cpp3
-rw-r--r--src/plugins/projectexplorer/buildmanager.cpp3
-rw-r--r--src/plugins/projectexplorer/buildpropertiessettings.h1
-rw-r--r--src/plugins/projectexplorer/buildstep.cpp31
-rw-r--r--src/plugins/projectexplorer/buildstep.h5
-rw-r--r--src/plugins/projectexplorer/buildstepspage.cpp2
-rw-r--r--src/plugins/projectexplorer/makestep.cpp2
-rw-r--r--src/plugins/projectexplorer/projectexplorer.cpp11
-rw-r--r--src/plugins/projectexplorer/target.h6
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp26
-rw-r--r--src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h2
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp17
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp1
-rw-r--r--src/plugins/qmakeprojectmanager/qmakesettings.cpp19
-rw-r--r--src/plugins/qmakeprojectmanager/qmakesettings.h2
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt30
-rw-r--r--src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp12
-rw-r--r--src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp28
-rw-r--r--src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui6
-rw-r--r--src/plugins/qmldesigner/components/componentcore/componentcore.pri2
-rw-r--r--src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp54
-rw-r--r--src/plugins/qmldesigner/components/componentcore/navigation2d.cpp91
-rw-r--r--src/plugins/qmldesigner/components/componentcore/navigation2d.h67
-rw-r--r--src/plugins/qmldesigner/components/componentcore/theme.h4
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp27
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h7
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp29
-rw-r--r--src/plugins/qmldesigner/components/connectioneditor/connectionview.h1
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp8
-rw-r--r--src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp5
-rw-r--r--src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp5
-rw-r--r--src/plugins/qmldesigner/components/formeditor/dragtool.cpp1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp1
-rw-r--r--src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp6
-rw-r--r--src/plugins/qmldesigner/components/integration/designdocument.cpp38
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp1
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp93
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h58
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp14
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp51
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h2
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp64
-rw-r--r--src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h11
-rw-r--r--src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp6
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp43
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreemodel.h8
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp1
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.cpp89
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorview.h2
-rw-r--r--src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp4
-rw-r--r--src/plugins/qmldesigner/components/navigator/previewtooltip.ui9
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp63
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h53
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui158
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp110
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h71
-rw-r--r--src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri11
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp33
-rw-r--r--src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h6
-rw-r--r--src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp40
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.cpp11
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.h6
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp44
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h8
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp5
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h2
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp6
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp50
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h10
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp12
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelineview.h3
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp20
-rw-r--r--src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h4
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp11
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h6
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp60
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h5
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp39
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h4
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp14
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h3
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp18
-rw-r--r--src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h4
-rw-r--r--src/plugins/qmldesigner/designercore/designercore-lib.pri21
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp166
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp117
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h66
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h48
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp68
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h45
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp159
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h95
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h53
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h197
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h58
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp38
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h39
-rw-r--r--src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h44
-rw-r--r--src/plugins/qmldesigner/designercore/include/abstractview.h1
-rw-r--r--src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h3
-rw-r--r--src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h37
-rw-r--r--src/plugins/qmldesigner/designercore/include/imagecache.h113
-rw-r--r--src/plugins/qmldesigner/designercore/include/model.h4
-rw-r--r--src/plugins/qmldesigner/designercore/include/modelnode.h11
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodeinstanceview.h6
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodemetainfo.h2
-rw-r--r--src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h20
-rw-r--r--src/plugins/qmldesigner/designercore/include/rewriterview.h2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp25
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h24
-rw-r--r--src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp9
-rw-r--r--src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h5
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp20
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.h12
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h10
-rw-r--r--src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp27
-rw-r--r--src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h10
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h5
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp60
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp6
-rw-r--r--src/plugins/qmldesigner/designercore/instances/puppetcreator.h2
-rw-r--r--src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp27
-rw-r--r--src/plugins/qmldesigner/designercore/model/abstractview.cpp20
-rw-r--r--src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp42
-rw-r--r--src/plugins/qmldesigner/designercore/model/model.cpp7
-rw-r--r--src/plugins/qmldesigner/designercore/model/model_p.h2
-rw-r--r--src/plugins/qmldesigner/designercore/model/modelnode.cpp47
-rw-r--r--src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp24
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriteaction.cpp4
-rw-r--r--src/plugins/qmldesigner/designercore/model/rewriterview.cpp20
-rw-r--r--src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp11
-rw-r--r--src/plugins/qmldesigner/qmldesigner_dependencies.pri3
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.pro1
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.qbs25
-rw-r--r--src/plugins/qmldesigner/qmldesignerunittestfiles.pri12
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationwidget.cpp62
-rw-r--r--src/plugins/qmlpreview/qmldebugtranslationwidget.h7
-rw-r--r--src/plugins/texteditor/texteditor.cpp3
-rw-r--r--src/shared/proparser/qmakebuiltins.cpp8
-rw-r--r--src/shared/proparser/qmakeglobals.h1
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt1
-rw-r--r--src/tools/qml2puppet/qml2puppet.qbs2
-rw-r--r--tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h6
-rw-r--r--tests/unit/unittest/CMakeLists.txt21
-rw-r--r--tests/unit/unittest/gmock_dependency.pri6
-rw-r--r--tests/unit/unittest/google-using-declarations.h4
-rw-r--r--tests/unit/unittest/gtest-creator-printing.cpp10
-rw-r--r--tests/unit/unittest/gtest-creator-printing.h7
-rw-r--r--tests/unit/unittest/gtest-qt-printing.cpp5
-rw-r--r--tests/unit/unittest/gtest-qt-printing.h2
-rw-r--r--tests/unit/unittest/imagecache-test.cpp326
-rw-r--r--tests/unit/unittest/imagecachecollectormock.h41
-rw-r--r--tests/unit/unittest/imagecachegenerator-test.cpp240
-rw-r--r--tests/unit/unittest/imagecachestorage-test.cpp334
-rw-r--r--tests/unit/unittest/mockimagecachegenerator.h43
-rw-r--r--tests/unit/unittest/mockimagecachestorage.h50
-rw-r--r--tests/unit/unittest/mocktimestampprovider.h36
-rw-r--r--tests/unit/unittest/notification.h56
-rw-r--r--tests/unit/unittest/sqlitedatabasemock.h72
-rw-r--r--tests/unit/unittest/sqlitereadstatementmock.cpp60
-rw-r--r--tests/unit/unittest/sqlitereadstatementmock.h93
-rw-r--r--tests/unit/unittest/sqlitestatement-test.cpp50
-rw-r--r--tests/unit/unittest/sqlitestatementmock.h105
-rw-r--r--tests/unit/unittest/sqlitetransactionbackendmock.h46
-rw-r--r--tests/unit/unittest/sqlitewritestatementmock.cpp35
-rw-r--r--tests/unit/unittest/sqlitewritestatementmock.h84
-rw-r--r--tests/unit/unittest/unittest.pro19
-rw-r--r--tests/unit/unittest/unittest.qbs13
247 files changed, 6127 insertions, 701 deletions
diff --git a/cmake/CreatePythonXY.cmake b/cmake/CreatePythonXY.cmake
new file mode 100644
index 0000000000..8aeeb156cb
--- /dev/null
+++ b/cmake/CreatePythonXY.cmake
@@ -0,0 +1,79 @@
+# create_python_xy function will precompile the Python/lib/*.py files
+# and create a zip file containing all the pyc files
+function(create_python_xy PythonExe PythonZipFilePath)
+ get_filename_component(python_lib_dir "${PythonExe}" DIRECTORY)
+ get_filename_component(python_lib_dir "${python_lib_dir}/Lib" ABSOLUTE)
+ foreach(dir collections encodings importlib json urllib)
+ file(COPY ${python_lib_dir}/${dir}
+ DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/python-lib
+ FILES_MATCHING PATTERN "*.py"
+ )
+ endforeach()
+ file(GLOB python_lib_files "${python_lib_dir}/*.py")
+ foreach(not_needed
+ aifc.py imghdr.py socket.py
+ antigravity.py imp.py socketserver.py
+ argparse.py ipaddress.py ssl.py
+ asynchat.py locale.py statistics.py
+ asyncore.py lzma.py string.py
+ bdb.py mailbox.py stringprep.py
+ binhex.py mailcap.py sunau.py
+ bisect.py mimetypes.py symbol.py
+ bz2.py modulefinder.py symtable.py
+ calendar.py netrc.py tabnanny.py
+ cgi.py nntplib.py tarfile.py
+ cgitb.py nturl2path.py telnetlib.py
+ chunk.py numbers.py tempfile.py
+ cmd.py optparse.py textwrap.py
+ code.py pathlib.py this.py
+ codeop.py pdb.py timeit.py
+ colorsys.py pickle.py trace.py
+ compileall.py pickletools.py tracemalloc.py
+ configparser.py pipes.py tty.py
+ contextvars.py plistlib.py turtle.py
+ cProfile.py poplib.py typing.py
+ crypt.py pprint.py uu.py
+ csv.py profile.py uuid.py
+ dataclasses.py pstats.py wave.py
+ datetime.py pty.py webbrowser.py
+ decimal.py pyclbr.py xdrlib.py
+ difflib.py py_compile.py zipapp.py
+ doctest.py queue.py zipfile.py
+ dummy_threading.py quopri.py zipimport.py
+ filecmp.py random.py _compat_pickle.py
+ fileinput.py rlcompleter.py _compression.py
+ formatter.py runpy.py _dummy_thread.py
+ fractions.py sched.py _markupbase.py
+ ftplib.py secrets.py _osx_support.py
+ getopt.py selectors.py _pydecimal.py
+ getpass.py shelve.py _pyio.py
+ gettext.py shlex.py _py_abc.py
+ gzip.py shutil.py _strptime.py
+ hashlib.py smtpd.py _threading_local.py
+ hmac.py smtplib.py __future__.py
+ imaplib.py sndhdr.py __phello__.foo.py
+ )
+ list(FIND python_lib_files "${python_lib_dir}/${not_needed}" found_not_needed)
+ if (NOT found_not_needed STREQUAL "-1")
+ list(REMOVE_AT python_lib_files ${found_not_needed})
+ endif()
+ endforeach()
+
+ file(COPY ${python_lib_files} DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/python-lib")
+
+ set(ENV{PYTHONOPTIMIZE} "2")
+ execute_process(
+ COMMAND "${PythonExe}" -OO -m compileall "${CMAKE_CURRENT_BINARY_DIR}/python-lib" -b
+ )
+
+ file(GLOB_RECURSE python_lib_files "${CMAKE_CURRENT_BINARY_DIR}/python-lib/*.py")
+ file(REMOVE ${python_lib_files})
+
+ file(GLOB_RECURSE python_lib_files LIST_DIRECTORIES ON "${CMAKE_CURRENT_BINARY_DIR}/python-lib/*/__pycache__$")
+ file(REMOVE_RECURSE ${python_lib_files})
+
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -E tar cf "${PythonZipFilePath}" . --format=zip
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/python-lib/"
+ )
+endfunction()
diff --git a/doc/qtcreator/images/qtcreator-cmake-build-settings.png b/doc/qtcreator/images/qtcreator-cmake-build-settings.png
index 33be38d67c..e05e124f8a 100644
--- a/doc/qtcreator/images/qtcreator-cmake-build-settings.png
+++ b/doc/qtcreator/images/qtcreator-cmake-build-settings.png
Binary files differ
diff --git a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdocinc b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdocinc
index d197facbc6..9b6aa3f0d6 100644
--- a/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdocinc
+++ b/doc/qtcreator/src/cmake/creator-projects-cmake-building.qdocinc
@@ -46,24 +46,32 @@
of the parameters that are passed to CMake. Parameter names are listed
in the \uicontrol Key column. Names with a common prefix (up to the first
underscore character) are grouped under the prefix. To view all parameters,
- select the \uicontrol Advanced check box.
+ select the \uicontrol Advanced check box.
To add parameters, select \uicontrol Add, and then select the type of
the parameter that you are adding: \uicontrol Boolean, \uicontrol String,
\uicontrol Directory, or \uicontrol File.
- To change the type of the selected parameter, select
+ To change the type of the selected parameter, right-click the
+ parameter name in the \uicontrol Key column, and then select
\uicontrol {Force to bool}, \uicontrol {Force to file},
- \uicontrol {Force to directory}, or \uicontrol {Force to string}.
+ \uicontrol {Force to directory}, or \uicontrol {Force to string}
+ in the context menu.
To modify the value of a parameter, double-click it, or select it,
- and then select \uicontrol Edit. To save the changes, select
- \uicontrol {Apply Configuration Changes}. Keep in mind that a
- configuration change might trigger a follow-up configuration change.
+ and then select \uicontrol Edit.
- To remove a parameter, select \uicontrol Unset.
+ You can apply actions to multiple parameters at a time. To clear
+ the selection, select \uicontrol {Clear Selection}.
- To reset the changes that you made, select \uicontrol Reset.
+ To remove the selected parameters, select \uicontrol Unset. To undo
+ the removal, select \uicontrol Set.
+
+ To reset all the changes that you made, select \uicontrol Reset.
+
+ To save the changes, select \uicontrol {Apply Configuration Changes}.
+ Keep in mind that a configuration change might trigger a follow-up
+ configuration change.
The parameter values that you change are passed via \c -D<option>=<value>
to CMake, which stores the options in the CMakeCache.txt file. This means
diff --git a/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h b/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h
index 6c3715dd5f..0d3bbba7b7 100644
--- a/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h
+++ b/share/qtcreator/qml/qmlpuppet/commands/captureddatacommand.h
@@ -151,8 +151,19 @@ public:
qint32 nodeId = -1;
};
+ CapturedDataCommand() = default;
+
+ CapturedDataCommand(QVector<StateData> &&stateData)
+ : stateData{std::move(stateData)}
+ {}
+
+ CapturedDataCommand(QImage &&image)
+ : image{std::move(image)}
+ {}
+
friend QDataStream &operator<<(QDataStream &out, const CapturedDataCommand &command)
{
+ out << command.image;
out << command.stateData;
return out;
@@ -160,12 +171,14 @@ public:
friend QDataStream &operator>>(QDataStream &in, CapturedDataCommand &command)
{
+ in >> command.image;
in >> command.stateData;
return in;
}
public:
+ QImage image;
QVector<StateData> stateData;
};
diff --git a/share/qtcreator/qml/qmlpuppet/images/non-visual-component.png b/share/qtcreator/qml/qmlpuppet/images/non-visual-component.png
new file mode 100644
index 0000000000..f66e2b8b11
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/images/non-visual-component.png
Binary files differ
diff --git a/share/qtcreator/qml/qmlpuppet/images/non-visual-component@2x.png b/share/qtcreator/qml/qmlpuppet/images/non-visual-component@2x.png
new file mode 100644
index 0000000000..25a136ff25
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/images/non-visual-component@2x.png
Binary files differ
diff --git a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h
index 841cd8561e..b4ffb74903 100644
--- a/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h
+++ b/share/qtcreator/qml/qmlpuppet/interfaces/nodeinstanceserverinterface.h
@@ -86,6 +86,7 @@ public:
virtual void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) = 0;
virtual void changeLanguage(const ChangeLanguageCommand &command) = 0;
virtual void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) = 0;
+ virtual void dispatchCommand(const QVariant &) {}
virtual void benchmark(const QString &) {}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
index 85de00c5ff..448a67c913 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/instances.pri
@@ -9,6 +9,7 @@ HEADERS += $$PWD/qt5nodeinstanceserver.h \
$$PWD/capturenodeinstanceserverdispatcher.h \
$$PWD/capturescenecreatedcommand.h \
$$PWD/nodeinstanceserverdispatcher.h \
+ $$PWD/qt5captureimagenodeinstanceserver.h \
$$PWD/qt5capturepreviewnodeinstanceserver.h \
$$PWD/qt5testnodeinstanceserver.h \
$$PWD/qt5informationnodeinstanceserver.h \
@@ -33,11 +34,13 @@ HEADERS += $$PWD/qt5nodeinstanceserver.h \
$$PWD/layoutnodeinstance.h \
$$PWD/qt3dpresentationnodeinstance.h \
$$PWD/quick3dnodeinstance.h \
- $$PWD/quick3dtexturenodeinstance.h
+ $$PWD/quick3dtexturenodeinstance.h \
+
SOURCES += $$PWD/qt5nodeinstanceserver.cpp \
$$PWD/capturenodeinstanceserverdispatcher.cpp \
$$PWD/nodeinstanceserverdispatcher.cpp \
+ $$PWD/qt5captureimagenodeinstanceserver.cpp \
$$PWD/qt5capturepreviewnodeinstanceserver.cpp \
$$PWD/qt5testnodeinstanceserver.cpp \
$$PWD/qt5informationnodeinstanceserver.cpp \
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
index 45d5fce845..183f036bf7 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.cpp
@@ -72,22 +72,23 @@
#include <requestmodelnodepreviewimagecommand.h>
#include <changelanguagecommand.h>
+#include <designersupportdelegate.h>
+#include <QAbstractAnimation>
#include <QDebug>
-#include <QQmlEngine>
-#include <QQmlApplicationEngine>
-#include <QFileSystemWatcher>
-#include <QUrl>
-#include <QSet>
#include <QDir>
-#include <QVariant>
+#include <QFileSystemWatcher>
#include <QMetaType>
+#include <QMutableVectorIterator>
+#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
-#include <qqmllist.h>
-#include <QAbstractAnimation>
+#include <QQmlEngine>
+#include <QQuickItemGrabResult>
#include <QQuickView>
#include <QSet>
-#include <designersupportdelegate.h>
+#include <QUrl>
+#include <QVariant>
+#include <qqmllist.h>
#include <algorithm>
@@ -1461,4 +1462,19 @@ void NodeInstanceServer::disableTimer()
m_timerMode = TimerMode::DisableTimer;
}
+void NodeInstanceServer::sheduleRootItemRender()
+{
+ QSharedPointer<QQuickItemGrabResult> result = m_rootNodeInstance.createGrabResult();
+ qint32 instanceId = m_rootNodeInstance.instanceId();
+
+ if (result) {
+ connect(result.data(), &QQuickItemGrabResult::ready, [this, result, instanceId] {
+ QVector<ImageContainer> imageVector;
+ ImageContainer container(instanceId, result->image(), instanceId);
+ imageVector.append(container);
+ nodeInstanceClient()->pixmapChanged(PixmapChangedCommand(imageVector));
+ });
+ }
+}
+
} // namespace QmlDesigner
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h
index bd6349d29a..d5726a31f6 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserver.h
@@ -241,6 +241,8 @@ protected:
ComponentCompletedCommand createComponentCompletedCommand(const QList<ServerNodeInstance> &instanceList);
ChangeSelectionCommand createChangeSelectionCommand(const QList<ServerNodeInstance> &instanceList);
+ void sheduleRootItemRender();
+
void addChangedProperty(const InstancePropertyPair &property);
virtual void startRenderTimer();
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
index dd9c42c5bd..eb5aae6a7e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstanceserverdispatcher.cpp
@@ -25,6 +25,7 @@
#include "nodeinstanceserverdispatcher.h"
+#include "qt5captureimagenodeinstanceserver.h"
#include "qt5capturepreviewnodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h"
#include "qt5rendernodeinstanceserver.h"
@@ -183,6 +184,8 @@ std::unique_ptr<NodeInstanceServer> createNodeInstanceServer(
{
if (serverName == "capturemode")
return std::make_unique<Qt5CapturePreviewNodeInstanceServer>(nodeInstanceClient);
+ else if (serverName == "captureiconmode")
+ return std::make_unique<Qt5CaptureImageNodeInstanceServer>(nodeInstanceClient);
else if (serverName == "rendermode")
return std::make_unique<Qt5RenderNodeInstanceServer>(nodeInstanceClient);
else if (serverName == "editormode")
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
index 5991e2b28e..d69846d400 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp
@@ -908,6 +908,11 @@ QImage ObjectNodeInstance::renderPreviewImage(const QSize & /*previewImageSize*/
return QImage();
}
+QSharedPointer<QQuickItemGrabResult> ObjectNodeInstance::createGrabResult() const
+{
+ return {};
+}
+
QObject *ObjectNodeInstance::parent() const
{
if (!object())
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h
index 3026ffefce..29e7dc640b 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.h
@@ -84,6 +84,8 @@ public:
virtual QImage renderImage() const;
virtual QImage renderPreviewImage(const QSize &previewImageSize) const;
+ virtual QSharedPointer<QQuickItemGrabResult> createGrabResult() const;
+
virtual QObject *parent() const;
Pointer parentInstance() const;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp
new file mode 100644
index 0000000000..d24c4e5552
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.cpp
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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 "qt5captureimagenodeinstanceserver.h"
+#include "servernodeinstance.h"
+
+#include <captureddatacommand.h>
+#include <createscenecommand.h>
+#include <nodeinstanceclientinterface.h>
+
+#include <QImage>
+#include <QQuickItem>
+#include <QQuickView>
+
+namespace QmlDesigner {
+
+namespace {
+
+QImage renderImage(ServerNodeInstance rootNodeInstance)
+{
+ rootNodeInstance.updateDirtyNodeRecursive();
+
+ QSize previewImageSize = rootNodeInstance.boundingRect().size().toSize();
+ if (previewImageSize.isEmpty())
+ previewImageSize = {640, 480};
+
+ if (previewImageSize.width() > 800 || previewImageSize.height() > 800)
+ previewImageSize.scale({800, 800}, Qt::KeepAspectRatio);
+
+ QImage previewImage = rootNodeInstance.renderPreviewImage(previewImageSize);
+
+ return previewImage;
+}
+} // namespace
+
+void Qt5CaptureImageNodeInstanceServer::collectItemChangesAndSendChangeCommands()
+{
+ static bool inFunction = false;
+
+ if (!rootNodeInstance().holdsGraphical()) {
+ nodeInstanceClient()->capturedData(CapturedDataCommand{});
+ return;
+ }
+
+ if (!inFunction) {
+ inFunction = true;
+
+ auto rooNodeInstance = rootNodeInstance();
+ rooNodeInstance.rootQuickItem()->setClip(true);
+
+ DesignerSupport::polishItems(quickView());
+
+ QImage image = renderImage(rooNodeInstance);
+
+ nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(image)});
+
+ slowDownRenderTimer();
+ inFunction = false;
+ }
+}
+
+} // namespace
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h
new file mode 100644
index 0000000000..7c26e47a87
--- /dev/null
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5captureimagenodeinstanceserver.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** 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 <qt5previewnodeinstanceserver.h>
+
+namespace QmlDesigner {
+
+class Qt5CaptureImageNodeInstanceServer : public Qt5PreviewNodeInstanceServer
+{
+public:
+ explicit Qt5CaptureImageNodeInstanceServer(NodeInstanceClientInterface *nodeInstanceClient)
+ : Qt5PreviewNodeInstanceServer(nodeInstanceClient)
+ {}
+
+protected:
+ void collectItemChangesAndSendChangeCommands() override;
+
+private:
+};
+
+} // namespace QmlDesigner
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp
index c70ef76afe..7fb87defb0 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp
@@ -100,7 +100,7 @@ void Qt5CapturePreviewNodeInstanceServer::collectItemChangesAndSendChangeCommand
stateInstance.deactivateState();
}
- nodeInstanceClient()->capturedData(CapturedDataCommand{stateDatas});
+ nodeInstanceClient()->capturedData(CapturedDataCommand{std::move(stateDatas)});
slowDownRenderTimer();
inFunction = false;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
index 2963119def..ecd066d16c 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5informationnodeinstanceserver.cpp
@@ -101,6 +101,30 @@ static QVariant objectToVariant(QObject *object)
return QVariant::fromValue(object);
}
+static QImage nonVisualComponentPreviewImage()
+{
+ static double ratio = qgetenv("FORMEDITOR_DEVICE_PIXEL_RATIO").toDouble();
+ if (ratio == 1.) {
+ static const QImage image(":/qtquickplugin/images/non-visual-component.png");
+ return image;
+ } else {
+ static const QImage image(":/qtquickplugin/images/non-visual-component@2x.png");
+ return image;
+ }
+}
+
+static bool imageHasContent(const QImage &image)
+{
+ // Check if any image pixel contains non-zero data
+ const uchar *pData = image.constBits();
+ const qsizetype size = image.sizeInBytes();
+ for (qsizetype i = 0; i < size; ++i) {
+ if (*(pData++) != 0)
+ return true;
+ }
+ return false;
+}
+
QQuickView *Qt5InformationNodeInstanceServer::createAuxiliaryQuickView(const QUrl &url,
QQuickItem *&rootItem)
{
@@ -556,7 +580,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode3DImageView()
ServerNodeInstance instance = instanceForId(m_modelNodePreviewImageCommand.instanceId());
instanceObj = instance.internalObject();
}
- QSize renderSize = m_modelNodePreviewImageCommand.size() * 2;
+ QSize renderSize = m_modelNodePreviewImageCommand.size();
QMetaObject::invokeMethod(m_ModelNode3DImageViewRootItem, "createViewForObject",
Q_ARG(QVariant, objectToVariant(instanceObj)),
@@ -652,7 +676,7 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView()
// Some component may expect to always be shown at certain size, so their layouts may
// not support scaling, so let's always render at the default size if item has one and
// scale the resulting image instead.
- QSize finalSize = m_modelNodePreviewImageCommand.size() * 2;
+ QSize finalSize = m_modelNodePreviewImageCommand.size();
QRectF renderRect = itemBoundingRect(instanceItem);
QSize renderSize = renderRect.size().toSize();
if (renderSize.isEmpty()) {
@@ -665,6 +689,9 @@ void Qt5InformationNodeInstanceServer::doRenderModelNode2DImageView()
renderImage = designerSupport()->renderImageForItem(m_ModelNode2DImageViewContentItem, renderRect, renderSize);
+ if (!imageHasContent(renderImage))
+ renderImage = nonVisualComponentPreviewImage();
+
if (renderSize != finalSize)
renderImage = renderImage.scaled(finalSize, Qt::KeepAspectRatio);
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
index defbf5614a..93e6e786cd 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceclientproxy.cpp
@@ -28,11 +28,13 @@
#include <QCoreApplication>
#include "capturenodeinstanceserverdispatcher.h"
+#include "qt5captureimagenodeinstanceserver.h"
#include "qt5capturepreviewnodeinstanceserver.h"
#include "qt5informationnodeinstanceserver.h"
#include "qt5previewnodeinstanceserver.h"
#include "qt5rendernodeinstanceserver.h"
#include "qt5testnodeinstanceserver.h"
+#include "quickitemnodeinstance.h"
#include <designersupportdelegate.h>
@@ -56,7 +58,19 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) :
NodeInstanceClientProxy(parent)
{
prioritizeDown();
- DesignerSupport::activateDesignerWindowManager();
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ const bool qt6 = false;
+#else
+ const bool qt6 = true;
+#endif
+
+ const bool unifiedRenderPath = qt6 || qEnvironmentVariableIsSet("QMLPUPPET_UNIFIED_RENDER_PATH");
+
+ if (unifiedRenderPath)
+ Internal::QuickItemNodeInstance::enableUnifiedRenderPath(true);
+ else
+ DesignerSupport::activateDesignerWindowManager();
+
if (QCoreApplication::arguments().at(1) == QLatin1String("--readcapturedstream")) {
qputenv("DESIGNER_DONT_USE_SHARED_MEMORY", "1");
setNodeInstanceServer(std::make_unique<Qt5TestNodeInstanceServer>(this));
@@ -79,6 +93,9 @@ Qt5NodeInstanceClientProxy::Qt5NodeInstanceClientProxy(QObject *parent) :
} else if (QCoreApplication::arguments().at(2) == QLatin1String("capturemode")) {
setNodeInstanceServer(std::make_unique<Qt5CapturePreviewNodeInstanceServer>(this));
initializeSocket();
+ } else if (QCoreApplication::arguments().at(2) == QLatin1String("captureiconmode")) {
+ setNodeInstanceServer(std::make_unique<Qt5CaptureImageNodeInstanceServer>(this));
+ initializeSocket();
}
}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
index 8a4b086aa5..c2015299c5 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5nodeinstanceserver.cpp
@@ -64,6 +64,8 @@ void Qt5NodeInstanceServer::initializeView()
m_quickView = new QQuickView;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ /* enables grab window without show */
QSurfaceFormat surfaceFormat = m_quickView->requestedFormat();
surfaceFormat.setVersion(4, 1);
surfaceFormat.setProfile(QSurfaceFormat::CoreProfile);
@@ -72,6 +74,7 @@ void Qt5NodeInstanceServer::initializeView()
m_quickView->setFormat(surfaceFormat);
DesignerSupport::createOpenGLContext(m_quickView.data());
+#endif
if (qEnvironmentVariableIsSet("QML_FILE_SELECTORS")) {
QQmlFileSelector *fileSelector = new QQmlFileSelector(engine(), engine());
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
index 08b20ccd22..fd3446f396 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5rendernodeinstanceserver.cpp
@@ -87,17 +87,27 @@ void Qt5RenderNodeInstanceServer::collectItemChangesAndSendChangeCommands()
if (ancestorInstance.isValid())
m_dirtyInstanceSet.insert(ancestorInstance);
}
- DesignerSupport::updateDirtyNode(item);
+ Internal::QuickItemNodeInstance::updateDirtyNode(item);
}
}
clearChangedPropertyList();
- if (!m_dirtyInstanceSet.isEmpty()) {
- nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand(QtHelpers::toList(m_dirtyInstanceSet)));
- m_dirtyInstanceSet.clear();
+ if (Internal::QuickItemNodeInstance::unifiedRenderPath()) {
+ /* QQuickItem::grabToImage render path */
+ /* TODO implement QQuickItem::grabToImage based rendering */
+ /* sheduleRootItemRender(); */
+ nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand({rootNodeInstance()}));
+ } else {
+ if (!m_dirtyInstanceSet.isEmpty()) {
+ nodeInstanceClient()->pixmapChanged(
+ createPixmapChangedCommand(QtHelpers::toList(m_dirtyInstanceSet)));
+ m_dirtyInstanceSet.clear();
+ }
}
+ m_dirtyInstanceSet.clear();
+
resetAllItems();
slowDownRenderTimer();
@@ -137,6 +147,11 @@ void Qt5RenderNodeInstanceServer::createScene(const CreateSceneCommand &command)
}
nodeInstanceClient()->pixmapChanged(createPixmapChangedCommand(instanceList));
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+#else
+ quickView()->show();
+#endif
}
void Qt5RenderNodeInstanceServer::clearScene(const ClearSceneCommand &command)
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
index 6e67d58b92..1f54bffdbd 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.cpp
@@ -41,6 +41,7 @@ namespace QmlDesigner {
namespace Internal {
bool QuickItemNodeInstance::s_createEffectItem = false;
+bool QuickItemNodeInstance::s_unifiedRenderPath = false;
QuickItemNodeInstance::QuickItemNodeInstance(QQuickItem *item)
: ObjectNodeInstance(item),
@@ -58,7 +59,7 @@ QuickItemNodeInstance::QuickItemNodeInstance(QQuickItem *item)
QuickItemNodeInstance::~QuickItemNodeInstance()
{
- if (quickItem())
+ if (quickItem() && checkIfRefFromEffect(instanceId()))
designerSupport()->derefFromEffectItem(quickItem());
}
@@ -156,6 +157,19 @@ void QuickItemNodeInstance::createEffectItem(bool createEffectItem)
s_createEffectItem = createEffectItem;
}
+void QuickItemNodeInstance::enableUnifiedRenderPath(bool unifiedRenderPath)
+{
+ s_unifiedRenderPath = unifiedRenderPath;
+}
+
+bool QuickItemNodeInstance::checkIfRefFromEffect(qint32 id)
+{
+ if (s_unifiedRenderPath)
+ return false;
+
+ return (s_createEffectItem || id == 0);
+}
+
void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
InstanceContainer::NodeFlags flags)
{
@@ -166,10 +180,10 @@ void QuickItemNodeInstance::initialize(const ObjectNodeInstance::Pointer &object
quickItem()->setParentItem(qobject_cast<QQuickItem*>(nodeInstanceServer()->quickView()->rootObject()));
}
- if (quickItem()->window()) {
- if (s_createEffectItem || instanceId() == 0)
- designerSupport()->refFromEffectItem(quickItem(),
- !flags.testFlag(InstanceContainer::ParentTakesOverRendering));
+ if (quickItem()->window() && checkIfRefFromEffect(instanceId())) {
+ designerSupport()->refFromEffectItem(quickItem(),
+ !flags.testFlag(
+ InstanceContainer::ParentTakesOverRendering));
}
ObjectNodeInstance::initialize(objectNodeInstance, flags);
@@ -246,6 +260,20 @@ QStringList QuickItemNodeInstance::allStates() const
return list;
}
+void QuickItemNodeInstance::updateDirtyNode(QQuickItem *item)
+{
+ if (s_unifiedRenderPath) {
+ item->update();
+ return;
+ }
+ DesignerSupport::updateDirtyNode(item);
+}
+
+bool QuickItemNodeInstance::unifiedRenderPath()
+{
+ return s_unifiedRenderPath;
+}
+
QRectF QuickItemNodeInstance::contentItemBoundingBox() const
{
if (contentItem()) {
@@ -378,6 +406,9 @@ double QuickItemNodeInstance::y() const
QImage QuickItemNodeInstance::renderImage() const
{
+ if (s_unifiedRenderPath && !isRootNodeInstance())
+ return {};
+
updateDirtyNodesRecursive(quickItem());
QRectF renderBoundingRect = boundingRect();
@@ -390,7 +421,16 @@ QImage QuickItemNodeInstance::renderImage() const
nodeInstanceServer()->quickView()->beforeSynchronizing();
nodeInstanceServer()->quickView()->beforeRendering();
- QImage renderImage = designerSupport()->renderImageForItem(quickItem(), renderBoundingRect, size);
+ QImage renderImage;
+
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ if (s_unifiedRenderPath)
+ renderImage = nodeInstanceServer()->quickView()->grabWindow();
+ else
+ renderImage = designerSupport()->renderImageForItem(quickItem(), renderBoundingRect, size);
+#else
+ renderImage = nodeInstanceServer()->quickView()->grabWindow();
+#endif
nodeInstanceServer()->quickView()->afterRendering();
@@ -411,7 +451,20 @@ QImage QuickItemNodeInstance::renderPreviewImage(const QSize &previewImageSize)
nodeInstanceServer()->quickView()->beforeSynchronizing();
nodeInstanceServer()->quickView()->beforeRendering();
- QImage image = designerSupport()->renderImageForItem(quickItem(), previewItemBoundingRect, size);
+ QImage image;
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
+ if (s_unifiedRenderPath)
+ image = nodeInstanceServer()->quickView()->grabWindow();
+ else
+ image = designerSupport()->renderImageForItem(quickItem(),
+ previewItemBoundingRect,
+ size);
+
+#else
+ image = nodeInstanceServer()->quickView()->grabWindow();
+#endif
+
+ image = image.scaledToWidth(size.width());
nodeInstanceServer()->quickView()->afterRendering();
@@ -426,6 +479,11 @@ QImage QuickItemNodeInstance::renderPreviewImage(const QSize &previewImageSize)
return QImage();
}
+QSharedPointer<QQuickItemGrabResult> QuickItemNodeInstance::createGrabResult() const
+{
+ return quickItem()->grabToImage(size().toSize());
+}
+
void QuickItemNodeInstance::updateAllDirtyNodesRecursive()
{
updateAllDirtyNodesRecursive(quickItem());
@@ -490,10 +548,11 @@ void QuickItemNodeInstance::updateDirtyNodesRecursive(QQuickItem *parentItem) co
void QuickItemNodeInstance::updateAllDirtyNodesRecursive(QQuickItem *parentItem) const
{
- foreach (QQuickItem *childItem, parentItem->childItems())
- updateAllDirtyNodesRecursive(childItem);
+ const QList<QQuickItem *> children = parentItem->childItems();
+ for (QQuickItem *childItem : children)
+ updateAllDirtyNodesRecursive(childItem);
- DesignerSupport::updateDirtyNode(parentItem);
+ updateDirtyNode(parentItem);
}
static inline bool isRectangleSane(const QRectF &rect)
@@ -507,7 +566,7 @@ QRectF QuickItemNodeInstance::boundingRectWithStepChilds(QQuickItem *parentItem)
boundingRect = boundingRect.united(QRectF(QPointF(0, 0), size()));
- foreach (QQuickItem *childItem, parentItem->childItems()) {
+ for (QQuickItem *childItem : parentItem->childItems()) {
if (!nodeInstanceServer()->hasInstanceForObject(childItem)) {
QRectF transformedRect = childItem->mapRectToItem(parentItem, boundingRectWithStepChilds(childItem));
if (isRectangleSane(transformedRect))
@@ -515,6 +574,9 @@ QRectF QuickItemNodeInstance::boundingRectWithStepChilds(QQuickItem *parentItem)
}
}
+ if (boundingRect.isEmpty())
+ QRectF{0, 0, 640, 480};
+
return boundingRect;
}
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h
index f42e45a8ff..2c68fe7974 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/quickitemnodeinstance.h
@@ -45,6 +45,7 @@ public:
static Pointer create(QObject *objectToBeWrapped);
static void createEffectItem(bool createEffectItem);
+ static void enableUnifiedRenderPath(bool createEffectItem);
void initialize(const ObjectNodeInstance::Pointer &objectNodeInstance,
InstanceContainer::NodeFlags flags) override;
@@ -70,6 +71,8 @@ public:
QImage renderImage() const override;
QImage renderPreviewImage(const QSize &previewImageSize) const override;
+ QSharedPointer<QQuickItemGrabResult> createGrabResult() const override;
+
void updateAllDirtyNodesRecursive() override;
@@ -98,6 +101,9 @@ public:
QList<QQuickItem*> allItemsRecursive() const override;
QStringList allStates() const override;
+ static void updateDirtyNode(QQuickItem *item);
+ static bool unifiedRenderPath();
+
protected:
explicit QuickItemNodeInstance(QQuickItem*);
QQuickItem *quickItem() const;
@@ -118,6 +124,7 @@ protected:
double x() const;
double y() const;
+ bool checkIfRefFromEffect(qint32 id);
private: //variables
QPointer<QQuickItem> m_contentItem;
@@ -131,6 +138,7 @@ private: //variables
double m_width;
double m_height;
static bool s_createEffectItem;
+ static bool s_unifiedRenderPath;
};
} // namespace Internal
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
index 69c854f3e9..511a89b62b 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.cpp
@@ -120,6 +120,11 @@ QImage ServerNodeInstance::renderPreviewImage(const QSize &previewImageSize) con
return m_nodeInstance->renderPreviewImage(previewImageSize);
}
+QSharedPointer<QQuickItemGrabResult> ServerNodeInstance::createGrabResult() const
+{
+ return m_nodeInstance->createGrabResult();
+}
+
bool ServerNodeInstance::isRootNodeInstance() const
{
return isValid() && m_nodeInstance->isRootNodeInstance();
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
index 975d0e8c37..9150b2bd4e 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/servernodeinstance.h
@@ -40,6 +40,7 @@ class QGraphicsItem;
class QGraphicsTransform;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
class QQuickItem;
+class QQuickItemGrabResult;
#endif
QT_END_NAMESPACE
@@ -102,6 +103,8 @@ public:
QImage renderImage() const;
QImage renderPreviewImage(const QSize &previewImageSize) const;
+ QSharedPointer<QQuickItemGrabResult> createGrabResult() const;
+
ServerNodeInstance parent() const;
bool hasParent() const;
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri
index f772fd8a55..1f4714d4d2 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri
@@ -1,7 +1,7 @@
QT += core gui widgets qml quick network
QT += core-private qml-private quick-private gui-private
-CONFIG += c++11
+CONFIG += c++17
DEFINES -= QT_CREATOR
diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
index 98df3de295..1f94bc3e59 100644
--- a/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
+++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppetmain.cpp
@@ -42,9 +42,11 @@
#endif
#ifdef Q_OS_WIN
-#include <windows.h>
+#include <Windows.h>
#endif
+namespace {
+
int internalMain(QGuiApplication *application)
{
QCoreApplication::setOrganizationName("QtProject");
@@ -138,6 +140,7 @@ int internalMain(QGuiApplication *application)
return application->exec();
}
+} // namespace
int main(int argc, char *argv[])
{
diff --git a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc
index d5a50ed566..e094303014 100644
--- a/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc
+++ b/share/qtcreator/qml/qmlpuppet/qmlpuppet.qrc
@@ -3,6 +3,8 @@
<file>images/template_image.png</file>
<file>html/welcome.html</file>
<file>images/webkit.png</file>
+ <file>images/non-visual-component.png</file>
+ <file>images/non-visual-component@2x.png</file>
<file>mockfiles/Window.qml</file>
<file>mockfiles/SwipeView.qml</file>
<file>mockfiles/GenericBackend.qml</file>
diff --git a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
index 1cbec5d802..b7bc9b0b58 100644
--- a/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
+++ b/share/qtcreator/qmldesigner/itemLibraryQmlSources/ItemDelegate.qml
@@ -46,8 +46,6 @@ Item {
width: itemLibraryIconWidth // to be set in Qml context
height: itemLibraryIconHeight // to be set in Qml context
source: itemLibraryIconPath // to be set by model
-
- cache: false // Allow thumbnail to be dynamically updated
}
Text {
@@ -71,10 +69,11 @@ Item {
renderType: Text.NativeRendering
}
- ToolTipArea {
+ ImagePreviewTooltipArea {
id: mouseRegion
+
anchors.fill: parent
- tooltip: itemName
+
onPressed: {
rootView.startDragAndDrop(mouseRegion, itemLibraryEntry)
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml
new file mode 100644
index 0000000000..51194cc3cb
--- /dev/null
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/ImagePreviewTooltipArea.qml
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** 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 HelperWidgets 2.0
+import QtQuick.Layouts 1.0
+
+MouseArea {
+ id: mouseArea
+
+ onExited: tooltipBackend.hideTooltip()
+ onCanceled: tooltipBackend.hideTooltip()
+ onClicked: forceActiveFocus()
+
+ hoverEnabled: true
+
+ Timer {
+ interval: 1000
+ running: mouseArea.containsMouse
+ onTriggered: {
+ tooltipBackend.componentName = itemName
+ tooltipBackend.componentPath = componentPath
+ tooltipBackend.showTooltip()
+ }
+ }
+}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
index a1627fb874..3c51254337 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/UrlChooser.qml
@@ -221,7 +221,7 @@ RowLayout {
onClicked: {
fileModel.openFileDialog()
if (fileModel.path !== "")
- urlChooser.backendValue.value = fileModel.path
+ urlChooser.backendValue.value = fileModel.fileName
}
}
}
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
index 59573217c5..0da6489551 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/qmldir
@@ -47,3 +47,4 @@ ExpressionTextField 2.0 ExpressionTextField.qml
MarginSection 2.0 MarginSection.qml
HorizontalScrollBar 2.0 HorizontalScrollBar.qml
VerticalScrollBar 2.0 VerticalScrollBar.qml
+ImagePreviewTooltipArea 2.0 ImagePreviewTooltipArea.qml
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml
index 808da702d5..928b8c4df7 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/InternalConstants.qml
@@ -98,33 +98,37 @@ QtObject {
readonly property string idAliasOff: "\u005B"
readonly property string idAliasOn: "\u005C"
readonly property string listView: "\u005D"
- readonly property string mergeCells: "\u005E"
- readonly property string minus: "\u005F"
- readonly property string plus: "\u0060"
- readonly property string redo: "\u0061"
- readonly property string splitColumns: "\u0062"
- readonly property string splitRows: "\u0063"
- readonly property string startNode: "\u0064"
- readonly property string testIcon: "\u0065"
- readonly property string textAlignBottom: "\u0066"
- readonly property string textAlignCenter: "\u0067"
- readonly property string textAlignLeft: "\u0068"
- readonly property string textAlignMiddle: "\u0069"
- readonly property string textAlignRight: "\u006A"
- readonly property string textAlignTop: "\u006B"
- readonly property string textBulletList: "\u006C"
- readonly property string textFullJustification: "\u006D"
- readonly property string textNumberedList: "\u006E"
- readonly property string tickIcon: "\u006F"
- readonly property string triState: "\u0070"
- readonly property string undo: "\u0071"
- readonly property string upDownIcon: "\u0072"
- readonly property string upDownSquare2: "\u0073"
- readonly property string wildcard: "\u0074"
- readonly property string zoomAll: "\u0075"
- readonly property string zoomIn: "\u0076"
- readonly property string zoomOut: "\u0077"
- readonly property string zoomSelection: "\u0078"
+ readonly property string lockOff: "\u005E"
+ readonly property string lockOn: "\u005F"
+ readonly property string mergeCells: "\u0060"
+ readonly property string minus: "\u0061"
+ readonly property string plus: "\u0062"
+ readonly property string redo: "\u0063"
+ readonly property string splitColumns: "\u0064"
+ readonly property string splitRows: "\u0065"
+ readonly property string startNode: "\u0066"
+ readonly property string testIcon: "\u0067"
+ readonly property string textAlignBottom: "\u0068"
+ readonly property string textAlignCenter: "\u0069"
+ readonly property string textAlignLeft: "\u006A"
+ readonly property string textAlignMiddle: "\u006B"
+ readonly property string textAlignRight: "\u006C"
+ readonly property string textAlignTop: "\u006D"
+ readonly property string textBulletList: "\u006E"
+ readonly property string textFullJustification: "\u006F"
+ readonly property string textNumberedList: "\u0070"
+ readonly property string tickIcon: "\u0071"
+ readonly property string triState: "\u0072"
+ readonly property string undo: "\u0073"
+ readonly property string upDownIcon: "\u0074"
+ readonly property string upDownSquare2: "\u0075"
+ readonly property string visibilityOff: "\u0076"
+ readonly property string visibilityOn: "\u0077"
+ readonly property string wildcard: "\u0078"
+ readonly property string zoomAll: "\u0079"
+ readonly property string zoomIn: "\u007A"
+ readonly property string zoomOut: "\u007B"
+ readonly property string zoomSelection: "\u007C"
readonly property font iconFont: Qt.font({
"family": controlIcons.name,
diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf
index e548392747..b8addaf835 100644
--- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf
+++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/StudioTheme/icons.ttf
Binary files differ
diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt
index 0a45ef0cc3..f024eaa482 100644
--- a/src/libs/qtcreatorcdbext/CMakeLists.txt
+++ b/src/libs/qtcreatorcdbext/CMakeLists.txt
@@ -59,8 +59,12 @@ if (_library_enabled)
foreach(lib IN LISTS PYTHON_LIBRARIES)
if (lib MATCHES ${PythonRegex})
+ set(PythonZipFileName "python${CMAKE_MATCH_4}.zip")
+
set(PythonDll "${CMAKE_MATCH_1}/${CMAKE_MATCH_3}${CMAKE_SHARED_LIBRARY_SUFFIX}")
- set(PythonZip "${CMAKE_MATCH_1}/python${CMAKE_MATCH_4}.zip")
+ set(PythonExe "${CMAKE_MATCH_1}/python${CMAKE_EXECUTABLE_SUFFIX}")
+ set(PythonZip "${CMAKE_MATCH_1}/${PythonZipFileName}")
+
break()
endif()
endforeach()
@@ -85,16 +89,27 @@ if (_library_enabled)
pyvalue.cpp pyvalue.h
)
- install(FILES
- "${PythonDll}"
- "${PythonZip}"
+ if (NOT EXISTS "${PythonZip}" AND
+ NOT EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${PythonZipFileName}")
+ include(CreatePythonXY)
+ create_python_xy("${PythonExe}" "${CMAKE_CURRENT_BINARY_DIR}/${PythonZipFileName}")
+ endif()
+
+ if (NOT EXISTS "${PythonZip}" AND
+ EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${PythonZipFileName}")
+ set(PythonZip "${CMAKE_CURRENT_BINARY_DIR}/${PythonZipFileName}")
+ endif()
+
+ list(APPEND deployPythonFiles "${PythonDll}")
+ list(APPEND deployPythonFiles "${PythonZip}")
+
+ install(FILES ${deployPythonFiles}
DESTINATION lib/qtcreatorcdbext${ArchSuffix}/
COMPONENT qtcreatorcdbext)
add_custom_target(copy_python_dll ALL VERBATIM)
add_custom_command(TARGET copy_python_dll POST_BUILD
- COMMAND "${CMAKE_COMMAND}" -E copy "${PythonDll}" "${PROJECT_BINARY_DIR}/lib/qtcreatorcdbext${ArchSuffix}/"
- COMMAND "${CMAKE_COMMAND}" -E copy "${PythonZip}" "${PROJECT_BINARY_DIR}/lib/qtcreatorcdbext${ArchSuffix}/"
+ COMMAND "${CMAKE_COMMAND}" -E copy ${deployPythonFiles} "${PROJECT_BINARY_DIR}/lib/qtcreatorcdbext${ArchSuffix}/"
VERBATIM
)
endif()
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 30e5edf12f..f684fa0849 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -44,4 +44,6 @@ add_qtc_library(Sqlite
tableconstraints.h
utf8string.cpp utf8string.h
utf8stringvector.cpp utf8stringvector.h
+ sqliteblob.h
+ sqlitetimestamp.h
)
diff --git a/src/libs/sqlite/sqlite-lib.pri b/src/libs/sqlite/sqlite-lib.pri
index 8ded8ca106..fd02c8801f 100644
--- a/src/libs/sqlite/sqlite-lib.pri
+++ b/src/libs/sqlite/sqlite-lib.pri
@@ -27,6 +27,8 @@ SOURCES += \
$$PWD/sqlitebasestatement.cpp
HEADERS += \
$$PWD/constraints.h \
+ $$PWD/sqliteblob.h \
+ $$PWD/sqlitetimestamp.h \
$$PWD/tableconstraints.h \
$$PWD/createtablesqlstatementbuilder.h \
$$PWD/lastchangedrowid.h \
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index dc4dd465e7..1bd7ef62a2 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -191,12 +191,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
checkForBindingError(resultCode);
}
-void BaseStatement::bind(int index, Utils::span<const byte> bytes)
+void BaseStatement::bind(int index, BlobView blobView)
{
int resultCode = sqlite3_bind_blob64(m_compiledStatement.get(),
index,
- bytes.data(),
- static_cast<long long>(bytes.size()),
+ blobView.data(),
+ blobView.size(),
SQLITE_STATIC);
if (resultCode != SQLITE_OK)
checkForBindingError(resultCode);
@@ -498,7 +498,7 @@ StringType textForColumn(sqlite3_stmt *sqlStatment, int column)
return StringType(text, size);
}
-Utils::span<const byte> blobForColumn(sqlite3_stmt *sqlStatment, int column)
+BlobView blobForColumn(sqlite3_stmt *sqlStatment, int column)
{
const byte *blob = reinterpret_cast<const byte *>(sqlite3_column_blob(sqlStatment, column));
std::size_t size = std::size_t(sqlite3_column_bytes(sqlStatment, column));
@@ -506,7 +506,7 @@ Utils::span<const byte> blobForColumn(sqlite3_stmt *sqlStatment, int column)
return {blob, size};
}
-Utils::span<const byte> convertToBlobForColumn(sqlite3_stmt *sqlStatment, int column)
+BlobView convertToBlobForColumn(sqlite3_stmt *sqlStatment, int column)
{
int dataType = sqlite3_column_type(sqlStatment, column);
if (dataType == SQLITE_BLOB)
@@ -571,7 +571,7 @@ double BaseStatement::fetchDoubleValue(int column) const
return sqlite3_column_double(m_compiledStatement.get(), column);
}
-Utils::span<const byte> BaseStatement::fetchBlobValue(int column) const
+BlobView BaseStatement::fetchBlobValue(int column) const
{
return convertToBlobForColumn(m_compiledStatement.get(), column);
}
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index bae41665d5..0b990f7d93 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -27,6 +27,7 @@
#include "sqliteglobal.h"
+#include "sqliteblob.h"
#include "sqliteexception.h"
#include "sqlitevalue.h"
@@ -70,7 +71,7 @@ public:
double fetchDoubleValue(int column) const;
Utils::SmallStringView fetchSmallStringViewValue(int column) const;
ValueView fetchValueView(int column) const;
- Utils::span<const byte> fetchBlobValue(int column) const;
+ BlobView fetchBlobValue(int column) const;
template<typename Type>
Type fetchValue(int column) const;
int columnCount() const;
@@ -82,7 +83,7 @@ public:
void bind(int index, void *pointer);
void bind(int index, Utils::SmallStringView fetchValue);
void bind(int index, const Value &fetchValue);
- void bind(int index, Utils::span<const byte> bytes);
+ void bind(int index, BlobView blobView);
void bind(int index, uint value) { bind(index, static_cast<long long>(value)); }
@@ -358,7 +359,7 @@ private:
operator long long() { return statement.fetchLongLongValue(column); }
operator double() { return statement.fetchDoubleValue(column); }
operator Utils::SmallStringView() { return statement.fetchSmallStringViewValue(column); }
- operator Utils::span<const Sqlite::byte>() { return statement.fetchBlobValue(column); }
+ operator BlobView() { return statement.fetchBlobValue(column); }
operator ValueView() { return statement.fetchValueView(column); }
StatementImplementation &statement;
diff --git a/src/libs/sqlite/sqliteblob.h b/src/libs/sqlite/sqliteblob.h
new file mode 100644
index 0000000000..17d9426ad0
--- /dev/null
+++ b/src/libs/sqlite/sqliteblob.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** 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 "sqliteglobal.h"
+
+#include <utils/span.h>
+
+#include <QByteArray>
+
+#include <algorithm>
+#include <cstring>
+#include <vector>
+
+namespace Sqlite {
+
+class BlobView
+{
+public:
+ BlobView() = default;
+
+ BlobView(const byte *data, std::size_t size)
+ : m_data(data)
+ , m_size(size)
+ {}
+
+ BlobView(const QByteArray &byteArray)
+ : m_data(reinterpret_cast<const byte *>(byteArray.constData()))
+ , m_size(static_cast<std::size_t>(byteArray.size()))
+ {}
+
+ BlobView(const std::vector<Sqlite::byte> &bytes)
+ : m_data(bytes.data())
+ , m_size(static_cast<std::size_t>(bytes.size()))
+ {}
+
+ const byte *data() const { return m_data; }
+ const char *cdata() const { return reinterpret_cast<const char *>(m_data); }
+ std::size_t size() const { return m_size; }
+ int sisize() const { return static_cast<int>(m_size); }
+ long long ssize() const { return static_cast<long long>(m_size); }
+ bool empty() const { return !m_size; }
+
+ friend bool operator==(Sqlite::BlobView first, Sqlite::BlobView second)
+ {
+ return first.size() == second.size()
+ && std::memcmp(first.data(), second.data(), first.size()) == 0;
+ }
+
+private:
+ const byte *m_data{};
+ std::size_t m_size{};
+};
+
+class Blob
+{
+public:
+ Blob(BlobView blobView)
+ {
+ bytes.reserve(blobView.size());
+ std::copy_n(blobView.data(), blobView.size(), std::back_inserter(bytes));
+ }
+
+ std::vector<Sqlite::byte> bytes;
+};
+
+class ByteArrayBlob
+{
+public:
+ ByteArrayBlob(BlobView blobView)
+ : byteArray{blobView.cdata(), blobView.sisize()}
+ {}
+
+ QByteArray byteArray;
+};
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitesessionchangeset.cpp b/src/libs/sqlite/sqlitesessionchangeset.cpp
index 3aa43c625f..430cbae222 100644
--- a/src/libs/sqlite/sqlitesessionchangeset.cpp
+++ b/src/libs/sqlite/sqlitesessionchangeset.cpp
@@ -46,7 +46,7 @@ void checkResultCode(int resultCode)
} // namespace
-SessionChangeSet::SessionChangeSet(Utils::span<const byte> blob)
+SessionChangeSet::SessionChangeSet(BlobView blob)
: data(sqlite3_malloc64(blob.size()))
, size(int(blob.size()))
{
@@ -64,7 +64,7 @@ SessionChangeSet::~SessionChangeSet()
sqlite3_free(data);
}
-Utils::span<const byte> SessionChangeSet::asSpan() const
+BlobView SessionChangeSet::asBlobView() const
{
return {static_cast<const byte *>(data), static_cast<std::size_t>(size)};
}
diff --git a/src/libs/sqlite/sqlitesessionchangeset.h b/src/libs/sqlite/sqlitesessionchangeset.h
index 65396622a9..8ea2dba008 100644
--- a/src/libs/sqlite/sqlitesessionchangeset.h
+++ b/src/libs/sqlite/sqlitesessionchangeset.h
@@ -25,10 +25,9 @@
#pragma once
+#include "sqliteblob.h"
#include "sqliteglobal.h"
-#include <utils/span.h>
-
#include <memory>
#include <vector>
@@ -41,7 +40,7 @@ class Sessions;
class SessionChangeSet
{
public:
- SessionChangeSet(Utils::span<const byte> blob);
+ SessionChangeSet(BlobView blob);
SessionChangeSet(Sessions &session);
~SessionChangeSet();
SessionChangeSet(const SessionChangeSet &) = delete;
@@ -54,7 +53,7 @@ public:
}
void operator=(SessionChangeSet &);
- Utils::span<const byte> asSpan() const;
+ BlobView asBlobView() const;
friend void swap(SessionChangeSet &first, SessionChangeSet &second) noexcept
{
diff --git a/src/libs/sqlite/sqlitesessions.cpp b/src/libs/sqlite/sqlitesessions.cpp
index e377ef9b72..69c0e1fe4b 100644
--- a/src/libs/sqlite/sqlitesessions.cpp
+++ b/src/libs/sqlite/sqlitesessions.cpp
@@ -103,7 +103,7 @@ void Sessions::commit()
if (session && !sqlite3session_isempty(session.get())) {
SessionChangeSet changeSet{*this};
- insertSession.write(changeSet.asSpan());
+ insertSession.write(changeSet.asBlobView());
}
session.reset();
diff --git a/src/libs/sqlite/sqlitetimestamp.h b/src/libs/sqlite/sqlitetimestamp.h
new file mode 100644
index 0000000000..f30d71fb29
--- /dev/null
+++ b/src/libs/sqlite/sqlitetimestamp.h
@@ -0,0 +1,47 @@
+/****************************************************************************
+**
+** 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
+
+namespace Sqlite {
+
+class TimeStamp
+{
+public:
+ TimeStamp() = default;
+ TimeStamp(long long value)
+ : value(value)
+ {}
+
+ friend bool operator==(TimeStamp first, TimeStamp second)
+ {
+ return first.value == second.value;
+ }
+
+public:
+ long long value = -1;
+};
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index 934db37642..8ace8a7bf1 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -23,6 +23,8 @@
**
****************************************************************************/
+#pragma once
+
#include "sqliteexception.h"
#include <utils/smallstring.h>
@@ -32,9 +34,6 @@
#include <cstddef>
-
-#pragma once
-
namespace Sqlite {
enum class ValueType : unsigned char { Null, Integer, Float, String };
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp
index 77b4eda306..9abb0193ec 100644
--- a/src/libs/utils/aspects.cpp
+++ b/src/libs/utils/aspects.cpp
@@ -1161,13 +1161,14 @@ void IntegerAspect::setToolTip(const QString &tooltip)
Its visual representation is a QComboBox with three items.
*/
-TriStateAspect::TriStateAspect()
+TriStateAspect::TriStateAspect(const QString onString, const QString &offString,
+ const QString &defaultString)
{
setDisplayStyle(DisplayStyle::ComboBox);
setDefaultValue(2);
- addOption(tr("Enable"));
- addOption(tr("Disable"));
- addOption(tr("Leave at Default"));
+ addOption(onString);
+ addOption(offString);
+ addOption(defaultString);
}
TriState TriStateAspect::setting() const
diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h
index 8313bcc83d..dc259e84a6 100644
--- a/src/libs/utils/aspects.h
+++ b/src/libs/utils/aspects.h
@@ -322,7 +322,10 @@ class QTCREATOR_UTILS_EXPORT TriStateAspect : public SelectionAspect
{
Q_OBJECT
public:
- TriStateAspect();
+ TriStateAspect(
+ const QString onString = tr("Enable"),
+ const QString &offString = tr("Disable"),
+ const QString &defaultString = tr("Leave at Default"));
TriState setting() const;
void setSetting(TriState setting);
diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp
index b1988bf8c3..fdebca957e 100644
--- a/src/libs/utils/buildablehelperlibrary.cpp
+++ b/src/libs/utils/buildablehelperlibrary.cpp
@@ -179,7 +179,13 @@ QStringList BuildableHelperLibrary::possibleQMakeCommands()
// On Unix some distributions renamed qmake with a postfix to avoid clashes
// On OS X, Qt 4 binary packages also has renamed qmake. There are also symbolic links that are
// named "qmake", but the file dialog always checks against resolved links (native Cocoa issue)
- return QStringList(HostOsInfo::withExecutableSuffix("qmake*"));
+ QStringList commands(HostOsInfo::withExecutableSuffix("qmake*"));
+
+ // Qt 6 CMake built targets, such as Android, are dependent on the host installation
+ // and use a script wrapper around the host qmake executable
+ if (HostOsInfo::isWindowsHost())
+ commands.append("qmake*.bat");
+ return commands;
}
// Copy helper source files to a target directory, replacing older files.
diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp
index 5601b763a3..bb096e59bf 100644
--- a/src/libs/utils/filesearch.cpp
+++ b/src/libs/utils/filesearch.cpp
@@ -29,6 +29,7 @@
#include "fileutils.h"
#include "mapreduce.h"
#include "qtcassert.h"
+#include "stringutils.h"
#include <QCoreApplication>
#include <QMutex>
@@ -474,7 +475,7 @@ QString matchCaseReplacement(const QString &originalText, const QString &replace
static QList<QRegularExpression> filtersToRegExps(const QStringList &filters)
{
return Utils::transform(filters, [](const QString &filter) {
- return QRegularExpression(QRegularExpression::wildcardToRegularExpression(filter),
+ return QRegularExpression(Utils::wildcardToRegularExpression(filter),
QRegularExpression::CaseInsensitiveOption);
});
}
diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index 888428790b..d8483df552 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -393,4 +393,68 @@ QString formatElapsedTime(qint64 elapsed)
return QCoreApplication::translate("StringUtils", "Elapsed time: %1.").arg(time);
}
+/*
+ * Basically QRegularExpression::wildcardToRegularExpression(), but let wildcards match
+ * path separators as well
+ */
+QString wildcardToRegularExpression(const QString &original)
+{
+ const qsizetype wclen = original.size();
+ QString rx;
+ rx.reserve(wclen + wclen / 16);
+ qsizetype i = 0;
+ const QChar *wc = original.data();
+
+ const QLatin1String starEscape(".*");
+ const QLatin1String questionMarkEscape(".");
+
+ while (i < wclen) {
+ const QChar c = wc[i++];
+ switch (c.unicode()) {
+ case '*':
+ rx += starEscape;
+ break;
+ case '?':
+ rx += questionMarkEscape;
+ break;
+ case '\\':
+ case '$':
+ case '(':
+ case ')':
+ case '+':
+ case '.':
+ case '^':
+ case '{':
+ case '|':
+ case '}':
+ rx += QLatin1Char('\\');
+ rx += c;
+ break;
+ case '[':
+ rx += c;
+ // Support for the [!abc] or [!a-c] syntax
+ if (i < wclen) {
+ if (wc[i] == QLatin1Char('!')) {
+ rx += QLatin1Char('^');
+ ++i;
+ }
+
+ if (i < wclen && wc[i] == QLatin1Char(']'))
+ rx += wc[i++];
+
+ while (i < wclen && wc[i] != QLatin1Char(']')) {
+ if (wc[i] == QLatin1Char('\\'))
+ rx += QLatin1Char('\\');
+ rx += wc[i++];
+ }
+ }
+ break;
+ default:
+ rx += c;
+ break;
+ }
+ }
+
+ return QRegularExpression::anchoredPattern(rx);
+}
} // namespace Utils
diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h
index 21a01de359..fbafa7708c 100644
--- a/src/libs/utils/stringutils.h
+++ b/src/libs/utils/stringutils.h
@@ -104,4 +104,11 @@ T makeUniquelyNumbered(const T &preferred, const Container &reserved)
QTCREATOR_UTILS_EXPORT QString formatElapsedTime(qint64 elapsed);
+/* This function is only necessary if you need to match the wildcard expression against a
+ * string that might contain path separators - otherwise
+ * QRegularExpression::wildcardToRegularExpression() can be used.
+ * Working around QRegularExpression::wildcardToRegularExpression() taking native separators
+ * into account and handling them to disallow matching a wildcard characters.
+ */
+QTCREATOR_UTILS_EXPORT QString wildcardToRegularExpression(const QString &original);
} // namespace Utils
diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp
index d3331d5994..4c33d6b567 100644
--- a/src/plugins/android/androidbuildapkstep.cpp
+++ b/src/plugins/android/androidbuildapkstep.cpp
@@ -324,7 +324,7 @@ QWidget *AndroidBuildApkWidget::createAdvancedGroup()
auto vbox = new QVBoxLayout(group);
QtSupport::BaseQtVersion *version = QtSupport::QtKitAspect::qtVersion(m_step->kit());
if (version && version->supportsMultipleQtAbis()) {
- auto buildAAB = new QCheckBox(tr("Build .aab (Android App Bundle)"), group);
+ auto buildAAB = new QCheckBox(tr("Build Android App Bundle (*.aab)"), group);
buildAAB->setChecked(m_step->buildAAB());
connect(buildAAB, &QAbstractButton::toggled, m_step, &AndroidBuildApkStep::setBuildAAB);
vbox->addWidget(buildAAB);
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp
index 0de5dc0d46..3776b55c33 100644
--- a/src/plugins/android/androiddeployqtstep.cpp
+++ b/src/plugins/android/androiddeployqtstep.cpp
@@ -51,6 +51,7 @@
#include <qtsupport/qtkitinformation.h>
#include <utils/algorithm.h>
+#include <utils/layoutbuilder.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/synchronousprocess.h>
@@ -86,8 +87,18 @@ AndroidDeployQtStep::AndroidDeployQtStep(BuildStepList *parent, Utils::Id id)
: BuildStep(parent, id)
{
setImmutable(true);
+
+ m_uninstallPreviousPackage = addAspect<BoolAspect>();
+ m_uninstallPreviousPackage->setSettingsKey(UninstallPreviousPackageKey);
+ m_uninstallPreviousPackage->setLabel(tr("Uninstall the existing app first"));
+ m_uninstallPreviousPackage->setValue(false);
+
const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
- m_uninstallPreviousPackage = qt && qt->qtVersion() < QtSupport::QtVersionNumber(5, 4, 0);
+ const bool forced = qt && qt->qtVersion() < QtSupport::QtVersionNumber(5, 4, 0);
+ if (forced) {
+ m_uninstallPreviousPackage->setValue(true);
+ m_uninstallPreviousPackage->setEnabled(false);
+ }
connect(this, &AndroidDeployQtStep::askForUninstall,
this, &AndroidDeployQtStep::slotAskForUninstall,
@@ -167,7 +178,7 @@ bool AndroidDeployQtStep::init()
emit addOutput(tr("Deploying to %1").arg(m_serialNumber), OutputFormat::Stdout);
- m_uninstallPreviousPackageRun = m_uninstallPreviousPackage;
+ m_uninstallPreviousPackageRun = m_uninstallPreviousPackage->value();
if (m_uninstallPreviousPackageRun)
m_manifestName = AndroidManager::manifestPath(target());
@@ -480,14 +491,6 @@ QWidget *AndroidDeployQtStep::createConfigWidget()
setDisplayName(QString("<b>%1</b>").arg(displayName()));
setSummaryText(displayName());
- auto uninstallPreviousCheckBox = new QCheckBox(widget);
- uninstallPreviousCheckBox->setText(tr("Uninstall the existing app first"));
- uninstallPreviousCheckBox->setChecked(uninstallPreviousPackage() > Keep);
- uninstallPreviousCheckBox->setEnabled(uninstallPreviousPackage() != ForceUninstall);
-
- connect(uninstallPreviousCheckBox, &QAbstractButton::toggled,
- this, &AndroidDeployQtStep::setUninstallPreviousPackage);
-
auto resetDefaultDevices = new QPushButton(widget);
resetDefaultDevices->setText(tr("Reset Default Deployment Devices"));
@@ -508,10 +511,10 @@ QWidget *AndroidDeployQtStep::createConfigWidget()
AndroidManager::installQASIPackage(target(), packagePath);
});
- auto layout = new QVBoxLayout(widget);
- layout->addWidget(uninstallPreviousCheckBox);
- layout->addWidget(resetDefaultDevices);
- layout->addWidget(installCustomApkButton);
+ LayoutBuilder builder(widget);
+ builder.addRow(m_uninstallPreviousPackage);
+ builder.addRow(resetDefaultDevices);
+ builder.addRow(installCustomApkButton);
return widget;
}
@@ -569,32 +572,6 @@ AndroidDeployQtStep::DeployErrorCode AndroidDeployQtStep::parseDeployErrors(QStr
return errorCode;
}
-bool AndroidDeployQtStep::fromMap(const QVariantMap &map)
-{
- m_uninstallPreviousPackage = map.value(UninstallPreviousPackageKey, m_uninstallPreviousPackage).toBool();
- return ProjectExplorer::BuildStep::fromMap(map);
-}
-
-QVariantMap AndroidDeployQtStep::toMap() const
-{
- QVariantMap map = ProjectExplorer::BuildStep::toMap();
- map.insert(UninstallPreviousPackageKey, m_uninstallPreviousPackage);
- return map;
-}
-
-void AndroidDeployQtStep::setUninstallPreviousPackage(bool uninstall)
-{
- m_uninstallPreviousPackage = uninstall;
-}
-
-AndroidDeployQtStep::UninstallType AndroidDeployQtStep::uninstallPreviousPackage()
-{
- const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
- if (qt && qt->qtVersion() < QtSupport::QtVersionNumber(5, 4, 0))
- return ForceUninstall;
- return m_uninstallPreviousPackage ? Uninstall : Keep;
-}
-
// AndroidDeployQtStepFactory
AndroidDeployQtStepFactory::AndroidDeployQtStepFactory()
diff --git a/src/plugins/android/androiddeployqtstep.h b/src/plugins/android/androiddeployqtstep.h
index 450213de5a..3090bbb2e8 100644
--- a/src/plugins/android/androiddeployqtstep.h
+++ b/src/plugins/android/androiddeployqtstep.h
@@ -59,20 +59,8 @@ class AndroidDeployQtStep : public ProjectExplorer::BuildStep
};
public:
- enum UninstallType {
- Keep,
- Uninstall,
- ForceUninstall
- };
-
AndroidDeployQtStep(ProjectExplorer::BuildStepList *bc, Utils::Id id);
- bool fromMap(const QVariantMap &map) override;
- QVariantMap toMap() const override;
-
- UninstallType uninstallPreviousPackage();
- void setUninstallPreviousPackage(bool uninstall);
-
signals:
void askForUninstall(DeployErrorCode errorCode);
@@ -105,7 +93,7 @@ private:
QMap<QString, QString> m_filesToPull;
QStringList m_androidABIs;
- bool m_uninstallPreviousPackage = false;
+ Utils::BoolAspect *m_uninstallPreviousPackage = nullptr;
bool m_uninstallPreviousPackageRun = false;
bool m_useAndroiddeployqt = false;
bool m_askForUninstall = false;
diff --git a/src/plugins/android/androidqtversion.cpp b/src/plugins/android/androidqtversion.cpp
index 32a58ffc66..2f306671eb 100644
--- a/src/plugins/android/androidqtversion.cpp
+++ b/src/plugins/android/androidqtversion.cpp
@@ -86,7 +86,8 @@ QString AndroidQtVersion::invalidReason() const
bool AndroidQtVersion::supportsMultipleQtAbis() const
{
- return qtVersion() >= QtSupport::QtVersionNumber{5, 14};
+ return qtVersion() >= QtSupport::QtVersionNumber{5, 14}
+ && qtVersion() < QtSupport::QtVersionNumber{6, 0};
}
Abis AndroidQtVersion::detectQtAbis() const
diff --git a/src/plugins/autotest/boost/boosttestsettingspage.ui b/src/plugins/autotest/boost/boosttestsettingspage.ui
index 1596cd7d41..22ddef51f6 100644
--- a/src/plugins/autotest/boost/boosttestsettingspage.ui
+++ b/src/plugins/autotest/boost/boosttestsettingspage.ui
@@ -10,9 +10,6 @@
<height>284</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp
index 10709e38a6..785abe4f75 100644
--- a/src/plugins/autotest/testconfiguration.cpp
+++ b/src/plugins/autotest/testconfiguration.cpp
@@ -185,8 +185,11 @@ void TestConfiguration::completeTestInformation(TestRunMode runMode)
qCDebug(LOG) << " LocalExecutable" << localExecutable;
qCDebug(LOG) << " DeployedExecutable" << deployedExecutable;
- qCDebug(LOG) << "Iterating run configurations";
- for (RunConfiguration *runConfig : target->runConfigurations()) {
+ qCDebug(LOG) << "Iterating run configurations - prefer active over others";
+ QList<RunConfiguration *> runConfigurations = target->runConfigurations();
+ runConfigurations.removeOne(target->activeRunConfiguration());
+ runConfigurations.prepend(target->activeRunConfiguration());
+ for (RunConfiguration *runConfig : qAsConst(runConfigurations)) {
qCDebug(LOG) << "RunConfiguration" << runConfig->id();
if (!isLocal(target)) { // TODO add device support
qCDebug(LOG) << " Skipped as not being local";
diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
index b1ce0cd6eb..bb45147eb8 100644
--- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
+++ b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.cpp
@@ -58,10 +58,11 @@
#include <QThread>
using namespace ClangBackEnd;
-using namespace ClangCodeModel;
-using namespace ClangCodeModel::Internal;
using namespace ProjectExplorer;
+namespace ClangCodeModel {
+namespace Internal {
+
static Q_LOGGING_CATEGORY(debug, "qtc.clangcodemodel.batch", QtWarningMsg);
static int timeOutFromEnvironmentVariable()
@@ -78,7 +79,7 @@ static int timeOutFromEnvironmentVariable()
return intervalAsInt;
}
-static int timeOutInMs()
+int timeOutInMs()
{
static int timeOut = timeOutFromEnvironmentVariable();
return timeOut;
@@ -747,9 +748,6 @@ bool BatchFileParser::parseLine(const QString &line)
} // anonymous namespace
-namespace ClangCodeModel {
-namespace Internal {
-
static QString applySubstitutions(const QString &filePath, const QString &text)
{
const QString dirPath = QFileInfo(filePath).absolutePath();
diff --git a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
index 40f0bceee3..8608229f98 100644
--- a/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
+++ b/src/plugins/clangcodemodel/test/clangbatchfileprocessor.h
@@ -30,6 +30,8 @@
namespace ClangCodeModel {
namespace Internal {
+int timeOutInMs();
+
bool runClangBatchFile(const QString &filePath);
} // namespace Internal
diff --git a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
index 0a38744a38..e32e22d8e1 100644
--- a/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
+++ b/src/plugins/clangcodemodel/test/clangcodecompletion_test.cpp
@@ -26,6 +26,7 @@
#include "clangcodecompletion_test.h"
#include "clangautomationutils.h"
+#include "clangbatchfileprocessor.h"
#include "../clangcompletionassistinterface.h"
#include "../clangmodelmanagersupport.h"
@@ -344,7 +345,7 @@ public:
if (!textToInsert.isEmpty())
openEditor.editor()->insert(textToInsert);
- proposal = completionResults(openEditor.editor(), includePaths, 15000);
+ proposal = completionResults(openEditor.editor(), includePaths, timeOutInMs());
}
TextEditor::ProposalModelPtr proposal;
@@ -657,7 +658,8 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCode()
OpenEditorAtCursorPosition openEditor(testDocument);
QVERIFY(openEditor.succeeded());
- TextEditor::ProposalModelPtr proposal = completionResults(openEditor.editor());
+ TextEditor::ProposalModelPtr proposal = completionResults(openEditor.editor(), {},
+ timeOutInMs());
QVERIFY(hasItem(proposal, "projectConfiguration1"));
}
@@ -670,7 +672,8 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProje
QVERIFY(openEditor.succeeded());
// Check completion without project
- TextEditor::ProposalModelPtr proposal = completionResults(openEditor.editor());
+ TextEditor::ProposalModelPtr proposal = completionResults(openEditor.editor(), {},
+ timeOutInMs());
QVERIFY(hasItem(proposal, "noProjectConfigurationDetected"));
{
@@ -681,7 +684,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProje
QVERIFY(projectLoader.load());
openEditor.waitUntilProjectPartChanged(QLatin1String("myproject.project"));
- proposal = completionResults(openEditor.editor());
+ proposal = completionResults(openEditor.editor(), {}, timeOutInMs());
QVERIFY(hasItem(proposal, "projectConfiguration1"));
QVERIFY(!hasItem(proposal, "projectConfiguration2"));
@@ -689,7 +692,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProje
// Check completion with project configuration 2
QVERIFY(projectLoader.updateProject({{"PROJECT_CONFIGURATION_2"}}));
openEditor.waitUntilBackendIsNotified();
- proposal = completionResults(openEditor.editor());
+ proposal = completionResults(openEditor.editor(), {}, timeOutInMs());
QVERIFY(!hasItem(proposal, "projectConfiguration1"));
QVERIFY(hasItem(proposal, "projectConfiguration2"));
@@ -697,7 +700,7 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeAfterChangingProje
// Check again completion without project
openEditor.waitUntilProjectPartChanged(QLatin1String(""));
- proposal = completionResults(openEditor.editor());
+ proposal = completionResults(openEditor.editor(), {}, timeOutInMs());
QVERIFY(hasItem(proposal, "noProjectConfigurationDetected"));
}
@@ -723,7 +726,8 @@ void ClangCodeCompletionTest::testCompleteProjectDependingCodeInGeneratedUiFile(
QVERIFY(openSource.succeeded());
// ...and check comletions
- TextEditor::ProposalModelPtr proposal = completionResults(openSource.editor());
+ TextEditor::ProposalModelPtr proposal = completionResults(openSource.editor(), {},
+ timeOutInMs());
QVERIFY(hasItem(proposal, "menuBar"));
QVERIFY(hasItem(proposal, "statusBar"));
QVERIFY(hasItem(proposal, "centralWidget"));
diff --git a/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui
index b10fd91bee..404d207d5a 100644
--- a/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui
+++ b/src/plugins/clangpchmanager/clangindexingprojectsettingswidget.ui
@@ -10,9 +10,6 @@
<height>300</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
diff --git a/src/plugins/clangtools/clangtoolsunittests.cpp b/src/plugins/clangtools/clangtoolsunittests.cpp
index 1e76c577e1..512e6e7135 100644
--- a/src/plugins/clangtools/clangtoolsunittests.cpp
+++ b/src/plugins/clangtools/clangtoolsunittests.cpp
@@ -125,7 +125,7 @@ void ClangToolsUnitTests::testProject()
ClangToolsSettings::instance()->runSettings(),
diagnosticConfig);
QSignalSpy waitForFinishedTool(tool, &ClangTool::finished);
- QVERIFY(waitForFinishedTool.wait(90000));
+ QVERIFY(waitForFinishedTool.wait(m_timeout));
// Check for errors
const QString errorText = waitForFinishedTool.takeFirst().first().toString();
@@ -186,5 +186,11 @@ void ClangToolsUnitTests::addTestRow(const QByteArray &relativeFilePath,
<< absoluteFilePath << expectedDiagCount << diagnosticConfig;
}
+int ClangToolsUnitTests::getTimeout()
+{
+ const int t = qEnvironmentVariableIntValue("QTC_CLANGTOOLS_TEST_TIMEOUT");
+ return t > 0 ? t : 480000;
+}
+
} // namespace Internal
} // namespace ClangTools
diff --git a/src/plugins/clangtools/clangtoolsunittests.h b/src/plugins/clangtools/clangtoolsunittests.h
index 87dd5a114e..20da247894 100644
--- a/src/plugins/clangtools/clangtoolsunittests.h
+++ b/src/plugins/clangtools/clangtoolsunittests.h
@@ -56,8 +56,11 @@ private:
const CppTools::ClangDiagnosticConfig &diagnosticConfig);
private:
+ static int getTimeout();
+
CppTools::Tests::TemporaryCopiedDir *m_tmpDir = nullptr;
ProjectExplorer::Kit *m_kit = nullptr;
+ int m_timeout = getTimeout();
};
} // namespace Internal
diff --git a/src/plugins/clangtools/runsettingswidget.ui b/src/plugins/clangtools/runsettingswidget.ui
index 370932c9a8..8dc283c8d0 100644
--- a/src/plugins/clangtools/runsettingswidget.ui
+++ b/src/plugins/clangtools/runsettingswidget.ui
@@ -10,9 +10,6 @@
<height>125</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
diff --git a/src/plugins/cppcheck/cppchecktool.cpp b/src/plugins/cppcheck/cppchecktool.cpp
index 2c17247d0a..91660020bb 100644
--- a/src/plugins/cppcheck/cppchecktool.cpp
+++ b/src/plugins/cppcheck/cppchecktool.cpp
@@ -38,6 +38,7 @@
#include <utils/algorithm.h>
#include <utils/macroexpander.h>
#include <utils/qtcassert.h>
+#include <utils/stringutils.h>
#include <QThread>
@@ -67,7 +68,7 @@ void CppcheckTool::updateOptions(const CppcheckOptions &options)
if (trimmedPattern.isEmpty())
continue;
- const QRegularExpression re(QRegularExpression::wildcardToRegularExpression(trimmedPattern));
+ const QRegularExpression re(Utils::wildcardToRegularExpression(trimmedPattern));
if (re.isValid())
m_filters.push_back(re);
}
diff --git a/src/plugins/cppeditor/cppeditorplugin.h b/src/plugins/cppeditor/cppeditorplugin.h
index 9adc6a9fc4..a68c2edf72 100644
--- a/src/plugins/cppeditor/cppeditorplugin.h
+++ b/src/plugins/cppeditor/cppeditorplugin.h
@@ -226,6 +226,8 @@ private slots:
void test_quickfix_removeUsingNamespace_data();
void test_quickfix_removeUsingNamespace();
+ void test_quickfix_removeUsingNamespace_simple_data();
+ void test_quickfix_removeUsingNamespace_simple();
void test_quickfix_removeUsingNamespace_differentSymbols();
void test_quickfix_InsertVirtualMethods_data();
diff --git a/src/plugins/cppeditor/cppquickfix_test.cpp b/src/plugins/cppeditor/cppquickfix_test.cpp
index 05b8e1b2d9..fc026a6c25 100644
--- a/src/plugins/cppeditor/cppquickfix_test.cpp
+++ b/src/plugins/cppeditor/cppquickfix_test.cpp
@@ -606,6 +606,32 @@ void CppEditorPlugin::test_quickfix_data()
""
);
+ // Checks: complete switch statement where enum is goes via a template type parameter
+ QTest::newRow("CompleteSwitchCaseStatement_QTCREATORBUG-24752")
+ << CppQuickFixFactoryPtr(new CompleteSwitchCaseStatement) << _(
+ "enum E {EA, EB};\n"
+ "template<typename T> struct S {\n"
+ " static T theType() { return T(); }\n"
+ "};\n"
+ "int main() {\n"
+ " @switch (S<E>::theType()) {\n"
+ " }\n"
+ "}\n"
+ ) << _(
+ "enum E {EA, EB};\n"
+ "template<typename T> struct S {\n"
+ " static T theType() { return T(); }\n"
+ "};\n"
+ "int main() {\n"
+ " switch (S<E>::theType()) {\n"
+ " case EA:\n"
+ " break;\n"
+ " case EB:\n"
+ " break;\n"
+ " }\n"
+ "}\n"
+ );
+
// Checks: No special treatment for reference to non const.
// Check: Quick fix is not triggered on a member function.
@@ -7083,6 +7109,49 @@ void CppEditorPlugin::test_quickfix_removeUsingNamespace()
QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths(), operation);
}
+void CppEditorPlugin::test_quickfix_removeUsingNamespace_simple_data()
+{
+ QTest::addColumn<QByteArray>("header");
+ QTest::addColumn<QByteArray>("expected");
+
+ const QByteArray common = R"--(
+namespace N{
+ template<typename T>
+ struct vector{
+ using iterator = T*;
+ };
+ using int_vector = vector<int>;
+}
+)--";
+ const QByteArray header = common + R"--(
+using namespace N@;
+int_vector ints;
+int_vector::iterator intIter;
+using vec = vector<int>;
+vec::iterator it;
+)--";
+ const QByteArray expected = common + R"--(
+N::int_vector ints;
+N::int_vector::iterator intIter;
+using vec = N::vector<int>;
+vec::iterator it;
+)--";
+
+ QTest::newRow("nested typedefs with Namespace") << header << expected;
+}
+
+void CppEditorPlugin::test_quickfix_removeUsingNamespace_simple()
+{
+ QFETCH(QByteArray, header);
+ QFETCH(QByteArray, expected);
+
+ QList<QuickFixTestDocument::Ptr> testDocuments;
+ testDocuments << QuickFixTestDocument::create("header.h", header, expected);
+
+ RemoveUsingNamespace factory;
+ QuickFixOperationTest(testDocuments, &factory, ProjectExplorer::HeaderPaths());
+}
+
void CppEditorPlugin::test_quickfix_removeUsingNamespace_differentSymbols()
{
QByteArray header = "namespace test{\n"
diff --git a/src/plugins/cppeditor/cppquickfixes.cpp b/src/plugins/cppeditor/cppquickfixes.cpp
index f5a89d4bb4..47a0575994 100644
--- a/src/plugins/cppeditor/cppquickfixes.cpp
+++ b/src/plugins/cppeditor/cppquickfixes.cpp
@@ -2752,6 +2752,7 @@ Enum *conditionEnum(const CppQuickFixInterface &interface, SwitchStatementAST *s
Block *block = statement->symbol;
Scope *scope = interface.semanticInfo().doc->scopeAt(block->line(), block->column());
TypeOfExpression typeOfExpression;
+ typeOfExpression.setExpandTemplates(true);
typeOfExpression.init(interface.semanticInfo().doc, interface.snapshot());
const QList<LookupItem> results = typeOfExpression(statement->condition,
interface.semanticInfo().doc,
@@ -7795,6 +7796,33 @@ private:
};
/**
+ * @brief getBaseName returns the base name of a qualified name or nullptr.
+ * E.g.: foo::bar => foo; bar => bar
+ * @param name The Name, maybe qualified
+ * @return The base name of the qualified name or nullptr
+ */
+const Identifier *getBaseName(const Name *name)
+{
+ class GetBaseName : public NameVisitor
+ {
+ void visit(const Identifier *name) override { baseName = name; }
+ void visit(const QualifiedNameId *name) override
+ {
+ if (name->base())
+ accept(name->base());
+ else
+ accept(name->name());
+ }
+
+ public:
+ const Identifier *baseName = nullptr;
+ };
+ GetBaseName getter;
+ getter.accept(name);
+ return getter.baseName;
+}
+
+/**
* @brief countNames counts the parts of the Name.
* E.g. if the name is std::vector, the function returns 2, if the name is variant, returns 1
* @param name The name that should be counted
@@ -7993,11 +8021,24 @@ private:
{
if (m_start) {
Scope *scope = m_file->scopeAt(ast->firstToken());
- const QList<LookupItem> lookups = m_context.lookup(ast->name->name, scope);
+ const Name *wantToLookup = ast->name->name;
+ // first check if the base name is a typedef. Consider the following example:
+ // using namespace std;
+ // using vec = std::vector<int>;
+ // vec::iterator it; // we have to lookup 'vec' and not iterator (would result in
+ // std::vector<int>::iterator => std::vec::iterator, which is wrong)
+ const Name *baseName = getBaseName(wantToLookup);
+ QList<LookupItem> typedefCandidates = m_context.lookup(baseName, scope);
+ if (!typedefCandidates.isEmpty()) {
+ if (typedefCandidates.front().declaration()->isTypedef())
+ wantToLookup = baseName;
+ }
+
+ const QList<LookupItem> lookups = m_context.lookup(wantToLookup, scope);
if (!lookups.empty()) {
QList<const Name *> fullName = m_context.fullyQualifiedName(
lookups.first().declaration());
- const int currentNameCount = countNames(ast->name->name);
+ const int currentNameCount = countNames(wantToLookup);
const bool needNamespace = needMissingNamespaces(std::move(fullName),
currentNameCount);
if (needNamespace)
diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp
index a23c73f18a..78de5f1583 100644
--- a/src/plugins/cpptools/cppmodelmanager_test.cpp
+++ b/src/plugins/cpptools/cppmodelmanager_test.cpp
@@ -1117,12 +1117,12 @@ void CppToolsPlugin::test_modelmanager_renameIncludesInEditor()
const MyTestDataDir testDir2(_("testdata_project2"));
QFile foobar2000Header(testDir2.file("foobar2000.h"));
- QVERIFY(foobar2000Header.open(QFile::ReadOnly));
+ QVERIFY(foobar2000Header.open(QFile::ReadOnly | QFile::Text));
const auto foobar2000HeaderContents = foobar2000Header.readAll();
foobar2000Header.close();
QFile renamedHeader(renamedHeaderWithNormalGuard);
- QVERIFY(renamedHeader.open(QFile::ReadOnly));
+ QVERIFY(renamedHeader.open(QFile::ReadOnly | QFile::Text));
auto renamedHeaderContents = renamedHeader.readAll();
renamedHeader.close();
QCOMPARE(renamedHeaderContents, foobar2000HeaderContents);
@@ -1133,12 +1133,12 @@ void CppToolsPlugin::test_modelmanager_renameIncludesInEditor()
Core::HandleIncludeGuards::Yes));
QFile foobar4000Header(testDir2.file("foobar4000.h"));
- QVERIFY(foobar4000Header.open(QFile::ReadOnly));
+ QVERIFY(foobar4000Header.open(QFile::ReadOnly | QFile::Text));
const auto foobar4000HeaderContents = foobar4000Header.readAll();
foobar4000Header.close();
renamedHeader.setFileName(renamedHeaderWithUnderscoredGuard);
- QVERIFY(renamedHeader.open(QFile::ReadOnly));
+ QVERIFY(renamedHeader.open(QFile::ReadOnly | QFile::Text));
renamedHeaderContents = renamedHeader.readAll();
renamedHeader.close();
QCOMPARE(renamedHeaderContents, foobar4000HeaderContents);
@@ -1146,7 +1146,7 @@ void CppToolsPlugin::test_modelmanager_renameIncludesInEditor()
// test the renaming of a header with a malformed guard to verify we do not make
// accidental refactors
renamedHeader.setFileName(headerWithMalformedGuard);
- QVERIFY(renamedHeader.open(QFile::ReadOnly));
+ QVERIFY(renamedHeader.open(QFile::ReadOnly | QFile::Text));
auto originalMalformedGuardContents = renamedHeader.readAll();
renamedHeader.close();
@@ -1154,7 +1154,7 @@ void CppToolsPlugin::test_modelmanager_renameIncludesInEditor()
Core::HandleIncludeGuards::Yes));
renamedHeader.setFileName(renamedHeaderWithMalformedGuard);
- QVERIFY(renamedHeader.open(QFile::ReadOnly));
+ QVERIFY(renamedHeader.open(QFile::ReadOnly | QFile::Text));
renamedHeaderContents = renamedHeader.readAll();
renamedHeader.close();
QCOMPARE(renamedHeaderContents, originalMalformedGuardContents);
diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp
index e538aa6505..3e76d5a782 100644
--- a/src/plugins/debugger/gdb/gdbengine.cpp
+++ b/src/plugins/debugger/gdb/gdbengine.cpp
@@ -4166,7 +4166,7 @@ void GdbEngine::setupInferior()
}
if (!symbolFile.isEmpty()) {
- runCommand({"-file-exec-and-symbols \"" + symbolFile + '"',
+ runCommand({"-file-symbol-file \"" + symbolFile + '"',
CB(handleFileExecAndSymbols)});
}
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 593f16fc4d..09053e77ba 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -2951,14 +2951,12 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory,
const SynchronousProcessResponse resp = vcsSynchronousExec(repositoryDirectory, arguments,
VcsCommand::NoFullySync);
- const QString stdErr = resp.stdErr();
if (resp.result == SynchronousProcessResponse::Finished) {
VcsOutputWindow::appendMessage(msgCommitted(amendSHA1, commitCount));
- VcsOutputWindow::appendError(stdErr);
GitPlugin::updateCurrentBranch();
return true;
} else {
- VcsOutputWindow::appendError(tr("Cannot commit %n files: %1\n", nullptr, commitCount).arg(stdErr));
+ VcsOutputWindow::appendError(tr("Cannot commit %n files\n", nullptr, commitCount));
return false;
}
}
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index fd39b03d37..c238ba454a 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -325,10 +325,9 @@ void Client::sendContent(const IContent &content)
QString error;
if (!QTC_GUARD(content.isValid(&error)))
Core::MessageManager::write(error);
- LanguageClientManager::logBaseMessage(LspLogMessage::ClientMessage,
- name(),
- content.toBaseMessage());
- m_clientInterface->sendMessage(content.toBaseMessage());
+ const BaseMessage message = content.toBaseMessage();
+ LanguageClientManager::logBaseMessage(LspLogMessage::ClientMessage, name(), message);
+ m_clientInterface->sendMessage(message);
}
void Client::sendContent(const DocumentUri &uri, const IContent &content)
diff --git a/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui b/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui
index 6efac5a04f..bedfa074fe 100644
--- a/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui
+++ b/src/plugins/mesonprojectmanager/project/buildoptions/mesonbuildsettingswidget.ui
@@ -10,9 +10,6 @@
<height>300</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
diff --git a/src/plugins/mesonprojectmanager/settings/general/generalsettingswidget.ui b/src/plugins/mesonprojectmanager/settings/general/generalsettingswidget.ui
index 6323bf302e..78a3c14d77 100644
--- a/src/plugins/mesonprojectmanager/settings/general/generalsettingswidget.ui
+++ b/src/plugins/mesonprojectmanager/settings/general/generalsettingswidget.ui
@@ -10,9 +10,6 @@
<height>349</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="widget" native="true">
diff --git a/src/plugins/mesonprojectmanager/settings/tools/toolitemsettings.ui b/src/plugins/mesonprojectmanager/settings/tools/toolitemsettings.ui
index 5e1b2a5953..bedf7296c2 100644
--- a/src/plugins/mesonprojectmanager/settings/tools/toolitemsettings.ui
+++ b/src/plugins/mesonprojectmanager/settings/tools/toolitemsettings.ui
@@ -10,9 +10,6 @@
<height>70</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="_nameLbl">
diff --git a/src/plugins/mesonprojectmanager/settings/tools/toolssettingswidget.ui b/src/plugins/mesonprojectmanager/settings/tools/toolssettingswidget.ui
index ed6409ab13..137bfced8f 100644
--- a/src/plugins/mesonprojectmanager/settings/tools/toolssettingswidget.ui
+++ b/src/plugins/mesonprojectmanager/settings/tools/toolssettingswidget.ui
@@ -10,9 +10,6 @@
<height>349</height>
</rect>
</property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
diff --git a/src/plugins/nim/project/nimbletaskstep.cpp b/src/plugins/nim/project/nimbletaskstep.cpp
index 1cb0505089..f41d53da90 100644
--- a/src/plugins/nim/project/nimbletaskstep.cpp
+++ b/src/plugins/nim/project/nimbletaskstep.cpp
@@ -124,9 +124,6 @@ QWidget *NimbleTaskStep::createConfigWidget()
connect(buildSystem, &NimbleBuildSystem::tasksChanged, this, &NimbleTaskStep::updateTaskList);
- connect(m_taskName, &StringAspect::changed, this, &BuildStep::recreateSummary);
- connect(m_taskArgs, &StringAspect::changed, this, &BuildStep::recreateSummary);
-
setSummaryUpdater([this] {
return QString("<b>%1:</b> nimble %2 %3")
.arg(displayName(), m_taskName->value(), m_taskArgs->value());
diff --git a/src/plugins/projectexplorer/buildmanager.cpp b/src/plugins/projectexplorer/buildmanager.cpp
index 16ae265bca..9b257c00f0 100644
--- a/src/plugins/projectexplorer/buildmanager.cpp
+++ b/src/plugins/projectexplorer/buildmanager.cpp
@@ -477,6 +477,7 @@ void BuildManager::finish()
{
const QString elapsedTime = Utils::formatElapsedTime(d->m_elapsed.elapsed());
m_instance->addToOutputWindow(elapsedTime, BuildStep::OutputFormat::NormalMessage);
+ d->m_outputWindow->flush();
QApplication::alert(ICore::dialogParent(), 3000);
}
@@ -696,7 +697,7 @@ void BuildManager::nextStep()
}
static const auto finishedHandler = [](bool success) {
- d->m_outputWindow->outputFormatter()->flush();
+ d->m_outputWindow->flush();
d->m_lastStepSucceeded = success;
disconnect(d->m_currentBuildStep, nullptr, instance(), nullptr);
BuildManager::nextBuildQueue();
diff --git a/src/plugins/projectexplorer/buildpropertiessettings.h b/src/plugins/projectexplorer/buildpropertiessettings.h
index 17432b45e0..63c289b5fb 100644
--- a/src/plugins/projectexplorer/buildpropertiessettings.h
+++ b/src/plugins/projectexplorer/buildpropertiessettings.h
@@ -35,6 +35,7 @@ class PROJECTEXPLORER_EXPORT BuildPropertiesSettings
{
public:
QString buildDirectoryTemplate;
+ QString buildDirectoryTemplateOld; // TODO: Remove in ~4.16
Utils::TriState separateDebugInfo;
Utils::TriState qmlDebugging;
Utils::TriState qtQuickCompiler;
diff --git a/src/plugins/projectexplorer/buildstep.cpp b/src/plugins/projectexplorer/buildstep.cpp
index d5aaf6ea78..6e20887d70 100644
--- a/src/plugins/projectexplorer/buildstep.cpp
+++ b/src/plugins/projectexplorer/buildstep.cpp
@@ -157,6 +157,26 @@ void BuildStep::cancel()
doCancel();
}
+QWidget *BuildStep::doCreateConfigWidget()
+{
+ QWidget *widget = createConfigWidget();
+
+ const auto recreateSummary = [this] {
+ if (m_summaryUpdater)
+ setSummaryText(m_summaryUpdater());
+ };
+
+ for (BaseAspect *aspect : qAsConst(m_aspects))
+ connect(aspect, &BaseAspect::changed, widget, recreateSummary);
+
+ connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged,
+ widget, recreateSummary);
+
+ recreateSummary();
+
+ return widget;
+}
+
QWidget *BuildStep::createConfigWidget()
{
auto widget = new QWidget;
@@ -165,12 +185,8 @@ QWidget *BuildStep::createConfigWidget()
for (BaseAspect *aspect : qAsConst(m_aspects)) {
if (aspect->isVisible())
aspect->addToLayout(builder.finishRow());
- connect(aspect, &BaseAspect::changed, this, &BuildStep::recreateSummary);
}
- connect(buildConfiguration(), &BuildConfiguration::buildDirectoryChanged,
- this, &BuildStep::recreateSummary);
-
if (m_addMacroExpander)
VariableChooser::addSupportForChildWidgets(widget, macroExpander());
@@ -500,13 +516,6 @@ void BuildStep::setSummaryText(const QString &summaryText)
void BuildStep::setSummaryUpdater(const std::function<QString()> &summaryUpdater)
{
m_summaryUpdater = summaryUpdater;
- recreateSummary();
-}
-
-void BuildStep::recreateSummary()
-{
- if (m_summaryUpdater)
- setSummaryText(m_summaryUpdater());
}
} // ProjectExplorer
diff --git a/src/plugins/projectexplorer/buildstep.h b/src/plugins/projectexplorer/buildstep.h
index 6fda3e7b03..78d5331034 100644
--- a/src/plugins/projectexplorer/buildstep.h
+++ b/src/plugins/projectexplorer/buildstep.h
@@ -70,7 +70,6 @@ public:
virtual bool init() = 0;
void run();
void cancel();
- virtual QWidget *createConfigWidget();
bool fromMap(const QVariantMap &map) override;
QVariantMap toMap() const override;
@@ -120,7 +119,7 @@ public:
QString summaryText() const;
void setSummaryText(const QString &summaryText);
- void recreateSummary();
+ QWidget *doCreateConfigWidget();
signals:
void updateSummary();
@@ -141,6 +140,8 @@ signals:
void finished(bool result);
protected:
+ virtual QWidget *createConfigWidget();
+
void runInThread(const std::function<bool()> &syncImpl);
std::function<bool()> cancelChecker() const;
diff --git a/src/plugins/projectexplorer/buildstepspage.cpp b/src/plugins/projectexplorer/buildstepspage.cpp
index cc642d0d93..f23434d6ac 100644
--- a/src/plugins/projectexplorer/buildstepspage.cpp
+++ b/src/plugins/projectexplorer/buildstepspage.cpp
@@ -170,7 +170,7 @@ void ToolWidget::setDownVisible(bool b)
BuildStepsWidgetData::BuildStepsWidgetData(BuildStep *s) :
step(s), widget(nullptr), detailsWidget(nullptr)
{
- widget = s->createConfigWidget();
+ widget = s->doCreateConfigWidget();
Q_ASSERT(widget);
detailsWidget = new DetailsWidget;
diff --git a/src/plugins/projectexplorer/makestep.cpp b/src/plugins/projectexplorer/makestep.cpp
index 86c1fe74a8..c411ac34c9 100644
--- a/src/plugins/projectexplorer/makestep.cpp
+++ b/src/plugins/projectexplorer/makestep.cpp
@@ -416,8 +416,6 @@ QWidget *MakeStep::createConfigWidget()
m_nonOverrideWarning->setVisible(makeflagsJobCountMismatch()
&& !jobCountOverridesMakeflags());
disableInSubDirsCheckBox->setChecked(!m_enabledForSubDirs);
-
- recreateSummary();
};
updateDetails();
diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp
index 344f9c1766..b10a492970 100644
--- a/src/plugins/projectexplorer/projectexplorer.cpp
+++ b/src/plugins/projectexplorer/projectexplorer.cpp
@@ -259,7 +259,8 @@ const char PROJECT_OPEN_LOCATIONS_CONTEXT_MENU[] = "Project.P.OpenLocation.CtxM
// Default directories:
const char DEFAULT_BUILD_DIRECTORY_TEMPLATE[] = "../%{JS: Util.asciify(\"build-%{Project:Name}-%{Kit:FileSystemName}-%{BuildConfig:Name}\")}";
-const char DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY[] = "Directories/BuildDirectory.Template";
+const char DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY_OLD[] = "Directories/BuildDirectory.Template"; // TODO: Remove in ~4.16
+const char DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY[] = "Directories/BuildDirectory.TemplateV2";
const char BUILD_BEFORE_DEPLOY_SETTINGS_KEY[] = "ProjectExplorer/Settings/BuildBeforeDeploy";
const char DEPLOY_BEFORE_RUN_SETTINGS_KEY[] = "ProjectExplorer/Settings/DeployBeforeRun";
@@ -1532,8 +1533,14 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er
dd->m_projectExplorerSettings.lowBuildPriority
= s->value(Constants::LOW_BUILD_PRIORITY_SETTINGS_KEY, false).toBool();
+ dd->m_buildPropertiesSettings.buildDirectoryTemplateOld
+ = s->value(Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY_OLD).toString();
dd->m_buildPropertiesSettings.buildDirectoryTemplate
= s->value(Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY).toString();
+ if (dd->m_buildPropertiesSettings.buildDirectoryTemplate.isEmpty()) {
+ dd->m_buildPropertiesSettings.buildDirectoryTemplate
+ = dd->m_buildPropertiesSettings.buildDirectoryTemplateOld;
+ }
if (dd->m_buildPropertiesSettings.buildDirectoryTemplate.isEmpty())
dd->m_buildPropertiesSettings.buildDirectoryTemplate = Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE;
// TODO: Remove in ~4.16
@@ -2110,6 +2117,8 @@ void ProjectExplorerPluginPrivate::savePersistentSettings()
s->setValue(Constants::STOP_BEFORE_BUILD_SETTINGS_KEY, int(dd->m_projectExplorerSettings.stopBeforeBuild));
// Store this in the Core directory scope for backward compatibility!
+ s->setValue(Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY_OLD,
+ dd->m_buildPropertiesSettings.buildDirectoryTemplateOld);
s->setValue(Constants::DEFAULT_BUILD_DIRECTORY_TEMPLATE_KEY,
dd->m_buildPropertiesSettings.buildDirectoryTemplate);
diff --git a/src/plugins/projectexplorer/target.h b/src/plugins/projectexplorer/target.h
index 8de1d9168d..ce8440fa38 100644
--- a/src/plugins/projectexplorer/target.h
+++ b/src/plugins/projectexplorer/target.h
@@ -53,9 +53,11 @@ class PROJECTEXPLORER_EXPORT Target : public QObject
friend class SessionManager; // for setActiveBuild and setActiveDeployConfiguration
Q_OBJECT
- struct _constructor_tag { explicit _constructor_tag() = default; };
-
public:
+ struct _constructor_tag
+ {
+ explicit _constructor_tag() = default;
+ };
Target(Project *parent, Kit *k, _constructor_tag);
~Target() override;
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index 3d3ec6af01..64a12ab081 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -75,6 +75,17 @@ using namespace QmakeProjectManager::Internal;
namespace QmakeProjectManager {
+class RunSystemAspect : public TriStateAspect
+{
+ Q_OBJECT
+public:
+ RunSystemAspect() : TriStateAspect(tr("Run"), tr("Ignore"), tr("Use global setting"))
+ {
+ setSettingsKey("RunSystemFunction");
+ setDisplayName(tr("qmake system() behavior when parsing:"));
+ }
+};
+
QmakeExtraBuildInfo::QmakeExtraBuildInfo()
{
const BuildPropertiesSettings &settings = ProjectExplorerPlugin::buildPropertiesSettings();
@@ -198,6 +209,8 @@ QmakeBuildConfiguration::QmakeBuildConfiguration(Target *target, Utils::Id id)
emit qmakeBuildConfigurationChanged();
qmakeBuildSystem()->scheduleUpdateAllNowOrLater();
});
+
+ addAspect<RunSystemAspect>();
}
QmakeBuildConfiguration::~QmakeBuildConfiguration()
@@ -439,6 +452,17 @@ void QmakeBuildConfiguration::forceQtQuickCompiler(bool enable)
aspect<QtQuickCompilerAspect>()->setSetting(enable ? TriState::Enabled : TriState::Disabled);
}
+bool QmakeBuildConfiguration::runSystemFunction() const
+{
+ switch (aspect<RunSystemAspect>()->value()) {
+ case 0:
+ return true;
+ case 1:
+ return false;
+ }
+ return QmakeSettings::runSystemFunction();
+}
+
QStringList QmakeBuildConfiguration::configCommandLineArguments() const
{
QStringList result;
@@ -875,3 +899,5 @@ void QmakeBuildConfiguration::restrictNextBuild(const RunConfiguration *rc)
}
} // namespace QmakeProjectManager
+
+#include <qmakebuildconfiguration.moc>
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h
index 71f88256a9..19fbf519a5 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h
@@ -106,6 +106,8 @@ public:
Utils::TriState useQtQuickCompiler() const;
void forceQtQuickCompiler(bool enable);
+ bool runSystemFunction() const;
+
signals:
/// emitted for setQMakeBuildConfig, not emitted for Qt version changes, even
/// if those change the qmakebuildconfig
diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
index 3a564fd20b..0babec02e8 100644
--- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp
@@ -1839,15 +1839,24 @@ QStringList QmakeProFile::includePaths(QtSupport::ProFileReader *reader, const F
}
bool tryUnfixified = false;
+
+ // These paths should not be checked for existence, to ensure consistent include path lists
+ // before and after building.
+ const QString mocDir = mocDirPath(reader, buildDir);
+ const QString uiDir = uiDirPath(reader, buildDir);
+
foreach (const ProFileEvaluator::SourceFile &el,
reader->fixifiedValues(QLatin1String("INCLUDEPATH"), projectDir, buildDir.toString(),
false)) {
const QString sysrootifiedPath = sysrootify(el.fileName, sysroot.toString(), projectDir,
buildDir.toString());
- if (IoUtils::isAbsolutePath(sysrootifiedPath) && IoUtils::exists(sysrootifiedPath))
+ if (IoUtils::isAbsolutePath(sysrootifiedPath)
+ && (IoUtils::exists(sysrootifiedPath) || sysrootifiedPath == mocDir
+ || sysrootifiedPath == uiDir)) {
paths << sysrootifiedPath;
- else
+ } else {
tryUnfixified = true;
+ }
}
// If sysrootifying a fixified path does not yield a valid path, try again with the
@@ -1862,10 +1871,6 @@ QStringList QmakeProFile::includePaths(QtSupport::ProFileReader *reader, const F
}
}
- // paths already contains moc dir and ui dir, due to corrrectly parsing uic.prf and moc.prf
- // except if those directories don't exist at the time of parsing
- // thus we add those directories manually (without checking for existence)
- paths << mocDirPath(reader, buildDir) << uiDirPath(reader, buildDir);
paths.removeDuplicates();
return paths;
}
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index da0df14b70..da6dcd0eb6 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -797,6 +797,7 @@ QtSupport::ProFileReader *QmakeBuildSystem::createProFileReader(const QmakeProFi
m_qmakeGlobals->environment.insert(env.key(eit), env.expandedValueForKey(env.key(eit)));
m_qmakeGlobals->setCommandLineArguments(buildDir(rootProFile()->filePath()).toString(), qmakeArgs);
+ m_qmakeGlobals->runSystemFunction = bc->runSystemFunction();
QtSupport::ProFileCacheManager::instance()->incRefCount();
diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.cpp b/src/plugins/qmakeprojectmanager/qmakesettings.cpp
index dd2fdb4e5d..b0edf3a404 100644
--- a/src/plugins/qmakeprojectmanager/qmakesettings.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakesettings.cpp
@@ -38,11 +38,13 @@ namespace Internal {
const char BUILD_DIR_WARNING_KEY[] = "QmakeProjectManager/WarnAgainstUnalignedBuildDir";
const char ALWAYS_RUN_QMAKE_KEY[] = "QmakeProjectManager/AlwaysRunQmake";
+const char RUN_SYSTEM_KEY[] = "QmakeProjectManager/RunSystemFunction";
static bool operator==(const QmakeSettingsData &s1, const QmakeSettingsData &s2)
{
return s1.warnAgainstUnalignedBuildDir == s2.warnAgainstUnalignedBuildDir
- && s1.alwaysRunQmake == s2.alwaysRunQmake;
+ && s1.alwaysRunQmake == s2.alwaysRunQmake
+ && s1.runSystemFunction == s2.runSystemFunction;
}
static bool operator!=(const QmakeSettingsData &s1, const QmakeSettingsData &s2)
{
@@ -59,6 +61,11 @@ bool QmakeSettings::alwaysRunQmake()
return instance().m_settings.alwaysRunQmake;
}
+bool QmakeSettings::runSystemFunction()
+{
+ return instance().m_settings.runSystemFunction;
+}
+
QmakeSettings &QmakeSettings::instance()
{
static QmakeSettings theSettings;
@@ -85,6 +92,7 @@ void QmakeSettings::loadSettings()
m_settings.warnAgainstUnalignedBuildDir = s->value(
BUILD_DIR_WARNING_KEY, Utils::HostOsInfo::isWindowsHost()).toBool();
m_settings.alwaysRunQmake = s->value(ALWAYS_RUN_QMAKE_KEY, false).toBool();
+ m_settings.runSystemFunction = s->value(RUN_SYSTEM_KEY, false).toBool();
}
void QmakeSettings::storeSettings() const
@@ -92,6 +100,7 @@ void QmakeSettings::storeSettings() const
QSettings * const s = Core::ICore::settings();
s->setValue(BUILD_DIR_WARNING_KEY, warnAgainstUnalignedBuildDir());
s->setValue(ALWAYS_RUN_QMAKE_KEY, alwaysRunQmake());
+ s->setValue(RUN_SYSTEM_KEY, runSystemFunction());
}
class QmakeSettingsPage::SettingsWidget : public QWidget
@@ -110,9 +119,15 @@ public:
m_alwaysRunQmakeCheckbox.setToolTip(tr("This option can help to prevent failures on "
"incremental builds, but might slow them down unnecessarily in the general case."));
m_alwaysRunQmakeCheckbox.setChecked(QmakeSettings::alwaysRunQmake());
+ m_ignoreSystemCheckbox.setText(tr("Ignore qmake's system() function "
+ "when parsing a project"));
+ m_ignoreSystemCheckbox.setToolTip(tr("Unchecking this option can help getting more exact "
+ "parsing results, but can have unwanted side effects."));
+ m_ignoreSystemCheckbox.setChecked(!QmakeSettings::runSystemFunction());
const auto layout = new QVBoxLayout(this);
layout->addWidget(&m_warnAgainstUnalignedBuildDirCheckbox);
layout->addWidget(&m_alwaysRunQmakeCheckbox);
+ layout->addWidget(&m_ignoreSystemCheckbox);
layout->addStretch(1);
}
@@ -121,12 +136,14 @@ public:
QmakeSettingsData settings;
settings.warnAgainstUnalignedBuildDir = m_warnAgainstUnalignedBuildDirCheckbox.isChecked();
settings.alwaysRunQmake = m_alwaysRunQmakeCheckbox.isChecked();
+ settings.runSystemFunction = !m_ignoreSystemCheckbox.isChecked();
QmakeSettings::setSettingsData(settings);
}
private:
QCheckBox m_warnAgainstUnalignedBuildDirCheckbox;
QCheckBox m_alwaysRunQmakeCheckbox;
+ QCheckBox m_ignoreSystemCheckbox;
};
QmakeSettingsPage::QmakeSettingsPage()
diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.h b/src/plugins/qmakeprojectmanager/qmakesettings.h
index b2b1a75a9d..0225a00bd7 100644
--- a/src/plugins/qmakeprojectmanager/qmakesettings.h
+++ b/src/plugins/qmakeprojectmanager/qmakesettings.h
@@ -37,6 +37,7 @@ class QmakeSettingsData {
public:
bool warnAgainstUnalignedBuildDir = false;
bool alwaysRunQmake = false;
+ bool runSystemFunction = false;
};
class QmakeSettings : public QObject
@@ -46,6 +47,7 @@ public:
static QmakeSettings &instance();
static bool warnAgainstUnalignedBuildDir();
static bool alwaysRunQmake();
+ static bool runSystemFunction();
static void setSettingsData(const QmakeSettingsData &settings);
signals:
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 11be30c80e..ca98f2c424 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -6,7 +6,7 @@ endif()
add_qtc_plugin(QmlDesigner
DEPENDS
QmlJS LanguageUtils QmlEditorWidgets AdvancedDockingSystem
- Qt5::QuickWidgets Qt5::CorePrivate
+ Qt5::QuickWidgets Qt5::CorePrivate Sqlite
DEFINES
DESIGNER_CORE_LIBRARY
IDE_LIBRARY_BASENAME=\"${IDE_LIBRARY_BASE_PATH}\"
@@ -208,6 +208,7 @@ extend_qtc_plugin(QmlDesigner
modelnodecontextmenu.cpp modelnodecontextmenu.h
modelnodecontextmenu_helper.cpp modelnodecontextmenu_helper.h
modelnodeoperations.cpp modelnodeoperations.h
+ navigation2d.cpp navigation2d.h
qmldesignericonprovider.cpp qmldesignericonprovider.h
selectioncontext.cpp selectioncontext.h
theme.cpp theme.h
@@ -316,6 +317,7 @@ extend_qtc_plugin(QmlDesigner
itemlibraryassetimportdialog.cpp itemlibraryassetimportdialog.h
itemlibraryassetimportdialog.ui
itemlibraryassetimporter.cpp itemlibraryassetimporter.h
+ itemlibraryiconimageprovider.cpp itemlibraryiconimageprovider.h
)
find_package(Qt5 COMPONENTS Quick3DAssetImport QUIET)
@@ -501,6 +503,7 @@ extend_qtc_plugin(QmlDesigner
include/textmodifier.h
include/variantproperty.h
include/viewmanager.h
+ include/imagecache.h
)
extend_qtc_plugin(QmlDesigner
@@ -585,6 +588,21 @@ extend_qtc_plugin(QmlDesigner
pluginmanager/widgetpluginmanager.cpp pluginmanager/widgetpluginmanager.h
pluginmanager/widgetpluginpath.cpp pluginmanager/widgetpluginpath.h
rewritertransaction.cpp rewritertransaction.h
+
+ imagecache/imagecachecollector.h
+ imagecache/imagecachecollector.cpp
+ imagecache/imagecache.cpp
+ imagecache/imagecachecollectorinterface.h
+ imagecache/imagecacheconnectionmanager.cpp
+ imagecache/imagecacheconnectionmanager.h
+ imagecache/imagecachegenerator.cpp
+ imagecache/imagecachegenerator.h
+ imagecache/imagecachestorage.h
+ imagecache/imagecachegeneratorinterface.h
+ imagecache/imagecachestorageinterface.h
+ imagecache/timestampproviderinterface.h
+ imagecache/timestampprovider.h
+ imagecache/timestampprovider.cpp
)
extend_qtc_plugin(QmlDesigner
@@ -645,6 +663,16 @@ extend_qtc_plugin(QmlDesigner
)
extend_qtc_plugin(QmlDesigner
+ SOURCES_PREFIX components/previewtooltip
+ SOURCES
+ previewimagetooltip.cpp
+ previewimagetooltip.h
+ previewimagetooltip.ui
+ previewtooltipbackend.cpp
+ previewtooltipbackend.h
+)
+
+extend_qtc_plugin(QmlDesigner
SOURCES_PREFIX components/richtexteditor
SOURCES
hyperlinkdialog.cpp hyperlinkdialog.h hyperlinkdialog.ui
diff --git a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
index 5c46f76f05..c04a666165 100644
--- a/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
+++ b/src/plugins/qmldesigner/assetexporterplugin/assetexportdialog.cpp
@@ -28,12 +28,12 @@
#include "assetexportpluginconstants.h"
#include "filepathmodel.h"
-#include "coreplugin/fileutils.h"
-#include "coreplugin/icore.h"
-#include "projectexplorer/task.h"
-#include "projectexplorer/taskhub.h"
-#include "utils/fileutils.h"
-#include "utils/outputformatter.h"
+#include <coreplugin/fileutils.h>
+#include <coreplugin/icore.h>
+#include <projectexplorer/task.h>
+#include <projectexplorer/taskhub.h>
+#include <utils/fileutils.h>
+#include <utils/outputformatter.h>
#include <QCheckBox>
#include <QPushButton>
diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp
index 4f958d59f6..c7e1ab192c 100644
--- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp
+++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.cpp
@@ -28,18 +28,34 @@
#include "richtexteditor/richtexteditor.h"
+#include "QStringListModel"
+
namespace QmlDesigner {
-AnnotationCommentTab::AnnotationCommentTab(QWidget *parent) :
- QWidget(parent),
- ui(new Ui::AnnotationCommentTab)
+AnnotationCommentTab::AnnotationCommentTab(QWidget *parent)
+ : QWidget(parent)
+ , ui(new Ui::AnnotationCommentTab)
{
ui->setupUi(this);
m_editor = new RichTextEditor;
ui->formLayout->setWidget(3, QFormLayout::FieldRole, m_editor);
- connect(ui->titleEdit, &QLineEdit::textEdited,
+ ui->titleEdit->setModel(new QStringListModel{QStringList{"Description",
+ "Display Condition",
+ "helper_lines"
+ "highlight"
+ "project author",
+ "project confirmed",
+ "project developer",
+ "project distributor",
+ "project modified",
+ "project type"
+ "project version",
+ "Screen Description"
+ "Section"}});
+
+ connect(ui->titleEdit, &QComboBox::currentTextChanged,
this, &AnnotationCommentTab::commentTitleChanged);
}
@@ -52,7 +68,7 @@ Comment AnnotationCommentTab::currentComment() const
{
Comment result;
- result.setTitle(ui->titleEdit->text().trimmed());
+ result.setTitle(ui->titleEdit->currentText().trimmed());
result.setAuthor(ui->authorEdit->text().trimmed());
result.setText(m_editor->richText().trimmed());
@@ -77,7 +93,7 @@ void AnnotationCommentTab::setComment(const Comment &comment)
void AnnotationCommentTab::resetUI()
{
- ui->titleEdit->setText(m_comment.title());
+ ui->titleEdit->setCurrentText(m_comment.title());
ui->authorEdit->setText(m_comment.author());
m_editor->setRichText(m_comment.deescapedText());
diff --git a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui
index 4a69703d57..b3a9a85e6c 100644
--- a/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui
+++ b/src/plugins/qmldesigner/components/annotationeditor/annotationcommenttab.ui
@@ -24,7 +24,11 @@
</widget>
</item>
<item row="1" column="1">
- <widget class="QLineEdit" name="titleEdit"/>
+ <widget class="QComboBox" name="titleEdit">
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="textLabel">
diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore.pri b/src/plugins/qmldesigner/components/componentcore/componentcore.pri
index 14337508ec..decf24a5a8 100644
--- a/src/plugins/qmldesigner/components/componentcore/componentcore.pri
+++ b/src/plugins/qmldesigner/components/componentcore/componentcore.pri
@@ -14,6 +14,7 @@ SOURCES += modelnodecontextmenu_helper.cpp
SOURCES += selectioncontext.cpp
SOURCES += designeractionmanager.cpp
SOURCES += modelnodeoperations.cpp
+SOURCES += navigation2d.cpp
SOURCES += crumblebar.cpp
SOURCES += qmldesignericonprovider.cpp
SOURCES += zoomaction.cpp
@@ -33,6 +34,7 @@ HEADERS += selectioncontext.h
HEADERS += componentcore_constants.h
HEADERS += designeractionmanager.h
HEADERS += modelnodeoperations.h
+HEADERS += navigation2d.h
HEADERS += actioninterface.h
HEADERS += crumblebar.h
HEADERS += qmldesignericonprovider.h
diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
index f7487d8a3f..ea34b091cd 100644
--- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
+++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp
@@ -347,25 +347,27 @@ public:
&& !selectionContext().currentSingleSelectedNode().isRootNode()
&& selectionContext().currentSingleSelectedNode().hasParentProperty()) {
- ActionTemplate *selectionAction = new ActionTemplate(QString(), &ModelNodeOperations::select);
- selectionAction->setParent(menu());
-
parentNode = selectionContext().currentSingleSelectedNode().parentProperty().parentModelNode();
- selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select parent: %1")).arg(
- captionForModelNode(parentNode)));
+ if (!ModelNode::isThisOrAncestorLocked(parentNode)) {
+ ActionTemplate *selectionAction = new ActionTemplate(QString(), &ModelNodeOperations::select);
+ selectionAction->setParent(menu());
+ selectionAction->setText(QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select parent: %1")).arg(
+ captionForModelNode(parentNode)));
- SelectionContext nodeSelectionContext = selectionContext();
- nodeSelectionContext.setTargetNode(parentNode);
- selectionAction->setSelectionContext(nodeSelectionContext);
+ SelectionContext nodeSelectionContext = selectionContext();
+ nodeSelectionContext.setTargetNode(parentNode);
+ selectionAction->setSelectionContext(nodeSelectionContext);
- menu()->addAction(selectionAction);
+ menu()->addAction(selectionAction);
+ }
}
- foreach (const ModelNode &node, selectionContext().view()->allModelNodes()) {
+ for (const ModelNode &node : selectionContext().view()->allModelNodes()) {
if (node != selectionContext().currentSingleSelectedNode()
&& node != parentNode
&& contains(node, selectionContext().scenePosition())
- && !node.isRootNode()) {
+ && !node.isRootNode()
+ && !ModelNode::isThisOrAncestorLocked(node)) {
selectionContext().setTargetNode(node);
QString what = QString(QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Select: %1")).arg(captionForModelNode(node));
ActionTemplate *selectionAction = new ActionTemplate(what, &ModelNodeOperations::select);
@@ -377,6 +379,9 @@ public:
menu()->addAction(selectionAction);
}
}
+
+ if (menu()->isEmpty())
+ action()->setEnabled(false);
}
}
};
@@ -574,11 +579,6 @@ bool multiSelection(const SelectionContext &context)
return !singleSelection(context) && selectionNotEmpty(context);
}
-bool singleSelectionAndInBaseState(const SelectionContext &context)
-{
- return singleSelection(context) && inBaseState(context);
-}
-
bool multiSelectionAndInBaseState(const SelectionContext &context)
{
return multiSelection(context) && inBaseState(context);
@@ -828,6 +828,11 @@ bool studioComponentsAvailable(const SelectionContext &context)
return context.view()->model()->isImportPossible(import, true, true);
}
+bool studioComponentsAvailableAndSelectionCanBeLayouted(const SelectionContext &context)
+{
+ return selectionCanBeLayouted(context) && studioComponentsAvailable(context);
+}
+
bool singleSelectedAndUiFile(const SelectionContext &context)
{
if (!singleSelection(context))
@@ -882,6 +887,12 @@ bool raiseAvailable(const SelectionContext &selectionState)
return parentProperty.indexOf(modelNode) < parentProperty.count() - 1;
}
+bool anchorsMenuEnabled(const SelectionContext &context)
+{
+ return singleSelectionItemIsNotAnchoredAndSingleSelectionNotRoot(context)
+ || singleSelectionItemIsAnchored(context);
+}
+
void DesignerActionManager::createDefaultDesignerActions()
{
using namespace SelectionContextFunctors;
@@ -996,11 +1007,10 @@ void DesignerActionManager::createDefaultDesignerActions()
&setVisible,
&singleSelectedItem));
- addDesignerAction(new ActionGroup(
- anchorsCategoryDisplayName,
- anchorsCategory,
- priorityAnchorsCategory,
- &singleSelectionAndInBaseState));
+ addDesignerAction(new ActionGroup(anchorsCategoryDisplayName,
+ anchorsCategory,
+ priorityAnchorsCategory,
+ &anchorsMenuEnabled));
addDesignerAction(new ModelNodeAction(
anchorsFillCommandId,
@@ -1042,7 +1052,7 @@ void DesignerActionManager::createDefaultDesignerActions()
addDesignerAction(new ActionGroup(groupCategoryDisplayName,
groupCategory,
priorityGroupCategory,
- &studioComponentsAvailable));
+ &studioComponentsAvailableAndSelectionCanBeLayouted));
addDesignerAction(new ActionGroup(
flowCategoryDisplayName,
diff --git a/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp b/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp
new file mode 100644
index 0000000000..94d6b5ad38
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/navigation2d.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 "navigation2d.h"
+
+#include <QGestureEvent>
+#include <QWheelEvent>
+
+namespace QmlDesigner {
+
+Navigation2dScrollBar::Navigation2dScrollBar(QWidget *parent)
+ : QScrollBar(parent)
+{}
+
+bool Navigation2dScrollBar::postEvent(QEvent *event)
+{
+ if (event->type() == QEvent::Wheel) {
+ wheelEvent(static_cast<QWheelEvent *>(event));
+ return true;
+ }
+ return false;
+}
+
+void Navigation2dScrollBar::wheelEvent(QWheelEvent *event)
+{
+ if (!event->angleDelta().isNull())
+ QScrollBar::wheelEvent(event);
+}
+
+
+Navigation2dFilter::Navigation2dFilter(QWidget *parent, Navigation2dScrollBar *scrollbar)
+ : QObject(parent)
+ , m_scrollbar(scrollbar)
+{
+ if (parent)
+ parent->grabGesture(Qt::PinchGesture);
+}
+
+bool Navigation2dFilter::eventFilter(QObject *, QEvent *event)
+{
+ if (event->type() == QEvent::Gesture)
+ return gestureEvent(static_cast<QGestureEvent *>(event));
+ else if (event->type() == QEvent::Wheel && m_scrollbar)
+ return wheelEvent(static_cast<QWheelEvent *>(event));
+
+ return QObject::event(event);
+}
+
+bool Navigation2dFilter::gestureEvent(QGestureEvent *event)
+{
+ if (QPinchGesture *pinch = static_cast<QPinchGesture *>(event->gesture(Qt::PinchGesture))) {
+ QPinchGesture::ChangeFlags changeFlags = pinch->changeFlags();
+ if (changeFlags & QPinchGesture::ScaleFactorChanged) {
+ emit zoomChanged(-(1.0 - pinch->scaleFactor()), pinch->startCenterPoint());
+ event->accept();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Navigation2dFilter::wheelEvent(QWheelEvent *event)
+{
+ if (m_scrollbar->postEvent(event))
+ event->ignore();
+
+ return false;
+}
+
+} // End namespace QmlDesigner.
diff --git a/src/plugins/qmldesigner/components/componentcore/navigation2d.h b/src/plugins/qmldesigner/components/componentcore/navigation2d.h
new file mode 100644
index 0000000000..434b59055c
--- /dev/null
+++ b/src/plugins/qmldesigner/components/componentcore/navigation2d.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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 <QScrollBar>
+
+QT_FORWARD_DECLARE_CLASS(QGestureEvent)
+QT_FORWARD_DECLARE_CLASS(QWheelEvent)
+
+namespace QmlDesigner {
+
+class Navigation2dScrollBar : public QScrollBar
+{
+ Q_OBJECT
+
+public:
+ Navigation2dScrollBar(QWidget *parent = nullptr);
+
+ bool postEvent(QEvent *event);
+
+protected:
+ void wheelEvent(QWheelEvent *event) override;
+};
+
+
+class Navigation2dFilter : public QObject
+{
+ Q_OBJECT
+
+signals:
+ void zoomChanged(double scale, const QPointF &pos);
+
+public:
+ Navigation2dFilter(QWidget *parent = nullptr, Navigation2dScrollBar *scrollbar = nullptr);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event) override;
+
+private:
+ bool gestureEvent(QGestureEvent *event);
+ bool wheelEvent(QWheelEvent *event);
+ Navigation2dScrollBar *m_scrollbar = nullptr;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/componentcore/theme.h b/src/plugins/qmldesigner/components/componentcore/theme.h
index f6a1df97cc..513965b18f 100644
--- a/src/plugins/qmldesigner/components/componentcore/theme.h
+++ b/src/plugins/qmldesigner/components/componentcore/theme.h
@@ -107,6 +107,8 @@ public:
idAliasOff,
idAliasOn,
listView,
+ lockOff,
+ lockOn,
mergeCells,
minus,
plus,
@@ -129,6 +131,8 @@ public:
undo,
upDownIcon,
upDownSquare2,
+ visibilityOff,
+ visibilityOn,
wildcard,
zoomAll,
zoomIn,
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
index 69e0e202f4..e325b02034 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.cpp
@@ -75,6 +75,23 @@ ConnectionModel::ConnectionModel(ConnectionView *parent)
connect(this, &QStandardItemModel::dataChanged, this, &ConnectionModel::handleDataChanged);
}
+Qt::ItemFlags ConnectionModel::flags(const QModelIndex &modelIndex) const
+{
+ if (!modelIndex.isValid())
+ return Qt::ItemIsEnabled;
+
+ if (!m_connectionView || !m_connectionView->model())
+ return Qt::ItemIsEnabled;
+
+ const int internalId = data(index(modelIndex.row(), TargetModelNodeRow), UserRoles::InternalIdRole).toInt();
+ ModelNode modelNode = m_connectionView->modelNodeForInternalId(internalId);
+
+ if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode))
+ return Qt::ItemIsEnabled;
+
+ return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
+}
+
void ConnectionModel::resetModel()
{
beginResetModel();
@@ -82,7 +99,7 @@ void ConnectionModel::resetModel()
setHorizontalHeaderLabels(QStringList({ tr("Target"), tr("Signal Handler"), tr("Action") }));
if (connectionView()->isAttached()) {
- for (const ModelNode modelNode : connectionView()->allModelNodes())
+ for (const ModelNode &modelNode : connectionView()->allModelNodes())
addModelNode(modelNode);
}
@@ -94,8 +111,8 @@ void ConnectionModel::resetModel()
SignalHandlerProperty ConnectionModel::signalHandlerPropertyForRow(int rowNumber) const
{
- const int internalId = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 1).toInt();
- const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), Qt::UserRole + 2).toString();
+ const int internalId = data(index(rowNumber, TargetModelNodeRow), UserRoles::InternalIdRole).toInt();
+ const QString targetPropertyName = data(index(rowNumber, TargetModelNodeRow), UserRoles::TargetPropertyNameRole).toString();
ModelNode modelNode = connectionView()->modelNodeForInternalId(internalId);
if (modelNode.isValid())
@@ -256,8 +273,8 @@ void ConnectionModel::updateTargetNode(int rowNumber)
void ConnectionModel::updateCustomData(QStandardItem *item, const SignalHandlerProperty &signalHandlerProperty)
{
- item->setData(signalHandlerProperty.parentModelNode().internalId(), Qt::UserRole + 1);
- item->setData(signalHandlerProperty.name(), Qt::UserRole + 2);
+ item->setData(signalHandlerProperty.parentModelNode().internalId(), UserRoles::InternalIdRole);
+ item->setData(signalHandlerProperty.name(), UserRoles::TargetPropertyNameRole);
}
ModelNode ConnectionModel::getTargetNodeForConnection(const ModelNode &connection) const
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
index b7d24db719..5acf4f6402 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionmodel.h
@@ -48,7 +48,14 @@ public:
TargetPropertyNameRow = 1,
SourceRow = 2
};
+ enum UserRoles {
+ InternalIdRole = Qt::UserRole + 1,
+ TargetPropertyNameRole
+ };
ConnectionModel(ConnectionView *parent = nullptr);
+
+ Qt::ItemFlags flags(const QModelIndex &modelIndex) const override;
+
void resetModel();
SignalHandlerProperty signalHandlerPropertyForRow(int rowNumber) const;
ConnectionView *connectionView() const;
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
index 0410ccf908..88dd6a971d 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.cpp
@@ -36,6 +36,8 @@
#include <variantproperty.h>
#include <signalhandlerproperty.h>
+#include <QTableView>
+
namespace QmlDesigner {
namespace Internal {
@@ -162,6 +164,33 @@ void ConnectionView::selectedNodesChanged(const QList<ModelNode> & selectedNodeL
emit connectionViewWidget()->setEnabledAddButton(selectedNodeList.count() == 1);
}
+void ConnectionView::auxiliaryDataChanged(const ModelNode &node,
+ const PropertyName &name,
+ const QVariant &data)
+{
+ Q_UNUSED(node)
+
+ // Check if the auxiliary data is actually the locked property or if it is unlocked
+ if (name != QmlDesigner::lockedProperty || !data.toBool())
+ return;
+
+ QItemSelectionModel *selectionModel = connectionTableView()->selectionModel();
+ if (!selectionModel->hasSelection())
+ return;
+
+ QModelIndex modelIndex = selectionModel->currentIndex();
+ if (!modelIndex.isValid() || !model())
+ return;
+
+ const int internalId = connectionModel()->data(connectionModel()->index(modelIndex.row(),
+ ConnectionModel::TargetModelNodeRow),
+ ConnectionModel::UserRoles::InternalIdRole).toInt();
+ ModelNode modelNode = modelNodeForInternalId(internalId);
+
+ if (modelNode.isValid() && ModelNode::isThisOrAncestorLocked(modelNode))
+ selectionModel->clearSelection();
+}
+
void ConnectionView::importsChanged(const QList<Import> & /*addedImports*/, const QList<Import> & /*removedImports*/)
{
backendModel()->resetModel();
diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
index 905fa02a58..dda496263d 100644
--- a/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
+++ b/src/plugins/qmldesigner/components/connectioneditor/connectionview.h
@@ -69,6 +69,7 @@ public:
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
+ void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override;
void importsChanged(const QList<Import> &addedImports, const QList<Import> &removedImports) override;
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp
index 36bae9be0c..9fec8b2fc5 100644
--- a/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/graphicsview.cpp
@@ -27,6 +27,7 @@
#include "axis.h"
#include "curveeditormodel.h"
#include "curveitem.h"
+#include "navigation2d.h"
#include "treeitem.h"
#include "utils.h"
@@ -79,6 +80,13 @@ GraphicsView::GraphicsView(CurveEditorModel *model, QWidget *parent)
applyZoom(m_zoomX, m_zoomY);
update();
+
+ QmlDesigner::Navigation2dFilter *filter = new QmlDesigner::Navigation2dFilter(this);
+ auto zoomChanged = &QmlDesigner::Navigation2dFilter::zoomChanged;
+ connect(filter, zoomChanged, [this](double scale, const QPointF &pos) {
+ applyZoom(m_zoomX + scale, m_zoomY, mapToGlobal(pos.toPoint()));
+ });
+ installEventFilter(filter);
}
GraphicsView::~GraphicsView()
diff --git a/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp
index 891f6896ea..0fdb00ff1b 100644
--- a/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp
+++ b/src/plugins/qmldesigner/components/curveeditor/detail/handleitem.cpp
@@ -41,8 +41,11 @@ struct HandleGeometry
handle = QRectF(topLeft, -topLeft);
toKeyframe = QLineF(QPointF(0.0, 0.0), -pos);
angle = -toKeyframe.angle() + 45.0;
+ bbox = handle.united(QRectF(-pos, QSizeF(1.0,1.0)));
}
+ QRectF bbox;
+
QRectF handle;
QLineF toKeyframe;
@@ -97,7 +100,7 @@ HandleItem::Slot HandleItem::slot() const
QRectF HandleItem::boundingRect() const
{
HandleGeometry geom(pos(), m_style);
- return geom.handle;
+ return geom.bbox;
}
bool HandleItem::contains(const QPointF &point) const
diff --git a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp
index 0ed7abaefe..fc7e8b97c7 100644
--- a/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/abstractformeditortool.cpp
@@ -189,7 +189,7 @@ FormEditorItem *AbstractFormEditorTool::topMovableFormEditorItem(const QList<QGr
FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &point, const QList<QGraphicsItem*> &itemList)
{
FormEditorItem* nearestItem = nullptr;
- foreach (QGraphicsItem *item, itemList) {
+ for (QGraphicsItem *item : itemList) {
FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
if (formEditorItem && formEditorItem->flowHitTest(point))
@@ -201,6 +201,9 @@ FormEditorItem* AbstractFormEditorTool::nearestFormEditorItem(const QPointF &poi
if (formEditorItem->parentItem() && !formEditorItem->parentItem()->isContentVisible())
continue;
+ if (formEditorItem && ModelNode::isThisOrAncestorLocked(formEditorItem->qmlItemNode().modelNode()))
+ continue;
+
if (!nearestItem)
nearestItem = formEditorItem;
else if (formEditorItem->selectionWeigth(point, 1) < nearestItem->selectionWeigth(point, 0))
diff --git a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
index 9a643edef8..8bd2d827f5 100644
--- a/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/dragtool.cpp
@@ -250,7 +250,6 @@ void DragTool::dropEvent(const QList<QGraphicsItem *> &/*itemList*/, QGraphicsSc
if (m_dragNode.isValid())
view()->setSelectedModelNode(m_dragNode);
-
m_dragNode = QmlItemNode();
view()->changeToSelectionTool();
diff --git a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
index b621dacb4a..4cc7c027b2 100644
--- a/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/formeditoritem.cpp
@@ -286,6 +286,7 @@ bool FormEditorItem::flowHitTest(const QPointF & ) const
void FormEditorItem::setFrameColor(const QColor &color)
{
m_frameColor = color;
+ update();
}
FormEditorItem::~FormEditorItem()
diff --git a/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp
index 242b5649d2..fa399f8860 100644
--- a/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp
+++ b/src/plugins/qmldesigner/components/formeditor/rubberbandselectionmanipulator.cpp
@@ -90,10 +90,10 @@ void RubberBandSelectionManipulator::select(SelectionType selectionType)
if (!m_beginFormEditorItem)
return;
- QList<QGraphicsItem*> itemList = m_editorView->scene()->items(m_selectionRectangleElement.rect(), Qt::IntersectsItemBoundingRect);
+ QList<QGraphicsItem *> itemList = m_editorView->scene()->items(m_selectionRectangleElement.rect(), Qt::IntersectsItemBoundingRect);
QList<QmlItemNode> newNodeList;
- foreach (QGraphicsItem* item, itemList)
+ for (QGraphicsItem *item : itemList)
{
FormEditorItem *formEditorItem = FormEditorItem::fromQGraphicsItem(item);
@@ -137,7 +137,7 @@ void RubberBandSelectionManipulator::select(SelectionType selectionType)
}
-void RubberBandSelectionManipulator::setItems(const QList<FormEditorItem*> &itemList)
+void RubberBandSelectionManipulator::setItems(const QList<FormEditorItem *> &itemList)
{
m_itemList = itemList;
}
diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp
index 05929fbd0e..b572d55f0b 100644
--- a/src/plugins/qmldesigner/components/integration/designdocument.cpp
+++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp
@@ -49,6 +49,7 @@
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
#include <coreplugin/editormanager/editormanager.h>
+#include <utils/algorithm.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -57,6 +58,7 @@
#include <QDebug>
#include <QApplication>
+#include <QMessageBox>
#include <QPlainTextEdit>
#include <QRandomGenerator>
@@ -375,9 +377,41 @@ void DesignDocument::deleteSelected()
if (!currentModel())
return;
+ QStringList lockedNodes;
+ for (const ModelNode &modelNode : view()->selectedModelNodes()) {
+ for (const ModelNode &node : modelNode.allSubModelNodesAndThisNode()) {
+ if (node.isValid() && !node.isRootNode() && node.locked())
+ lockedNodes.push_back(node.id());
+ }
+ }
+
+ if (!lockedNodes.empty()) {
+ Utils::sort(lockedNodes);
+ QString detailedText = QString("<b>" + tr("Locked items:") + "</b><br>");
+
+ for (const auto &id : qAsConst(lockedNodes))
+ detailedText.append("- " + id + "<br>");
+
+ detailedText.chop(QString("<br>").size());
+
+ QMessageBox msgBox;
+ msgBox.setTextFormat(Qt::RichText);
+ msgBox.setIcon(QMessageBox::Question);
+ msgBox.setWindowTitle(tr("Delete/Cut Item"));
+ msgBox.setText(QString(tr("Deleting or cutting this item will modify locked items.") + "<br><br>%1")
+ .arg(detailedText));
+ msgBox.setInformativeText(tr("Do you want to continue by removing the item (Delete) or removing it and copying it to the clipboard (Cut)?"));
+ msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+ msgBox.setDefaultButton(QMessageBox::Ok);
+
+ if (msgBox.exec() == QMessageBox::Cancel)
+ return;
+ }
+
rewriterView()->executeInTransaction("DesignDocument::deleteSelected", [this](){
QList<ModelNode> toDelete = view()->selectedModelNodes();
- foreach (ModelNode node, toDelete) {
+
+ for (ModelNode node : toDelete) {
if (node.isValid() && !node.isRootNode() && QmlObjectNode::isValidQmlObjectNode(node))
QmlObjectNode(node).destroy();
}
@@ -545,7 +579,7 @@ void DesignDocument::paste()
}
view.setSelectedModelNodes({pastedNode});
});
- NodeMetaInfo::clearCache();
+ view.model()->clearMetaInfoCache();
}
}
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
index 0e6219dd9a..8eaa9ce983 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrary.pri
@@ -7,6 +7,7 @@ qtHaveModule(quick3dassetimport) {
# Input
HEADERS += itemlibraryview.h \
+ $$PWD/itemlibraryiconimageprovider.h \
itemlibrarywidget.h \
itemlibrarymodel.h \
itemlibraryresourceview.h \
@@ -19,6 +20,7 @@ HEADERS += itemlibraryview.h \
customfilesystemmodel.h
SOURCES += itemlibraryview.cpp \
+ $$PWD/itemlibraryiconimageprovider.cpp \
itemlibrarywidget.cpp \
itemlibrarymodel.cpp \
itemlibraryresourceview.cpp \
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
index 1fc817cd01..ca00b66f46 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryassetimporter.cpp
@@ -502,7 +502,6 @@ bool ItemLibraryAssetImporter::generateComponentIcon(int size, const QString &ic
QProcessUniquePointer process = puppetCreator.createPuppetProcess(
"custom",
{},
- this,
std::function<void()>(),
[&](int exitCode, QProcess::ExitStatus exitStatus) {
processFinished(exitCode, exitStatus);
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
new file mode 100644
index 0000000000..e0254111a9
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.cpp
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** 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 "itemlibraryiconimageprovider.h"
+
+#include <projectexplorer/target.h>
+#include <utils/stylehelper.h>
+
+#include <QMetaObject>
+#include <QQuickImageResponse>
+
+namespace QmlDesigner {
+
+class ImageRespose : public QQuickImageResponse
+{
+public:
+ QQuickTextureFactory *textureFactory() const override
+ {
+ return QQuickTextureFactory::textureFactoryForImage(m_image);
+ }
+
+ void setImage(const QImage &image)
+ {
+ m_image = image;
+
+ emit finished();
+ }
+
+ void abort()
+ {
+ m_image = QImage{
+ Utils::StyleHelper::dpiSpecificImageFile(":/ItemLibrary/images/item-default-icon.png")};
+
+ emit finished();
+ }
+
+private:
+ QImage m_image;
+};
+
+
+QQuickImageResponse *ItemLibraryIconImageProvider::requestImageResponse(const QString &id,
+ const QSize &)
+{
+ auto response = std::make_unique<ImageRespose>();
+
+ m_cache.requestIcon(
+ id,
+ [response = QPointer<ImageRespose>(response.get())](const QImage &image) {
+ QMetaObject::invokeMethod(
+ response,
+ [response, image] {
+ if (response)
+ response->setImage(image);
+ },
+ Qt::QueuedConnection);
+ },
+ [response = QPointer<ImageRespose>(response.get())] {
+ QMetaObject::invokeMethod(
+ response,
+ [response] {
+ if (response)
+ response->abort();
+ },
+ Qt::QueuedConnection);
+ });
+
+ return response.release();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h
new file mode 100644
index 0000000000..9e60246d4d
--- /dev/null
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryiconimageprovider.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 <nodeinstanceview.h>
+#include <rewriterview.h>
+
+#include <coreplugin/icore.h>
+#include <imagecache.h>
+#include <imagecache/imagecachecollector.h>
+#include <imagecache/imagecacheconnectionmanager.h>
+#include <imagecache/imagecachegenerator.h>
+#include <imagecache/imagecachestorage.h>
+#include <imagecache/timestampprovider.h>
+
+#include <sqlitedatabase.h>
+
+#include <QQuickAsyncImageProvider>
+
+namespace QmlDesigner {
+
+class ItemLibraryIconImageProvider : public QQuickAsyncImageProvider
+{
+public:
+ ItemLibraryIconImageProvider(ImageCache &imageCache)
+ : m_cache{imageCache}
+ {}
+
+ QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override;
+
+private:
+ ImageCache &m_cache;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp
index d67631531c..0d46140f12 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.cpp
@@ -47,8 +47,18 @@ QString ItemLibraryItem::typeName() const
QString ItemLibraryItem::itemLibraryIconPath() const
{
- //Prepend image provider prefix
- return QStringLiteral("image://qmldesigner_itemlibrary/") + m_itemLibraryEntry.libraryEntryIconPath();
+ if (m_itemLibraryEntry.customComponentSource().isEmpty()) {
+ return QStringLiteral("image://qmldesigner_itemlibrary/")
+ + m_itemLibraryEntry.libraryEntryIconPath();
+ } else {
+ return QStringLiteral("image://itemlibrary_preview/")
+ + m_itemLibraryEntry.customComponentSource();
+ }
+}
+
+QString ItemLibraryItem::componentPath() const
+{
+ return m_itemLibraryEntry.customComponentSource();
}
bool ItemLibraryItem::setVisible(bool isVisible)
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h
index fa1fa92257..859cbe51dd 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryitem.h
@@ -42,6 +42,7 @@ class ItemLibraryItem: public QObject {
Q_PROPERTY(QString itemName READ itemName FINAL)
Q_PROPERTY(QString itemLibraryIconPath READ itemLibraryIconPath FINAL)
Q_PROPERTY(bool itemVisible READ isVisible NOTIFY visibilityChanged FINAL)
+ Q_PROPERTY(QString componentPath READ componentPath FINAL)
public:
ItemLibraryItem(QObject *parent);
@@ -50,6 +51,7 @@ public:
QString itemName() const;
QString typeName() const;
QString itemLibraryIconPath() const;
+ QString componentPath() const;
bool setVisible(bool isVisible);
bool isVisible() const;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
index a01ce38fc7..19506042bb 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarymodel.cpp
@@ -29,6 +29,8 @@
#include "itemlibraryitem.h"
#include "itemlibrarysection.h"
+#include <components/previewtooltip/previewtooltipbackend.h>
+
#include <model.h>
#include <nodehints.h>
#include <nodemetainfo.h>
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
index 5ffece61cd..79d559c0a0 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.cpp
@@ -25,24 +25,47 @@
#include "itemlibraryview.h"
#include "itemlibrarywidget.h"
+#include "metainfo.h"
+#include <bindingproperty.h>
+#include <coreplugin/icore.h>
+#include <imagecache.h>
+#include <imagecache/imagecachecollector.h>
+#include <imagecache/imagecacheconnectionmanager.h>
+#include <imagecache/imagecachegenerator.h>
+#include <imagecache/imagecachestorage.h>
+#include <imagecache/timestampprovider.h>
#include <import.h>
#include <importmanagerview.h>
-#include <qmlitemnode.h>
-#include <rewriterview.h>
-#include <bindingproperty.h>
#include <nodelistproperty.h>
+#include <projectexplorer/kit.h>
+#include <projectexplorer/target.h>
+#include <rewriterview.h>
+#include <sqlitedatabase.h>
#include <utils/algorithm.h>
#include <qmldesignerplugin.h>
-#include "metainfo.h"
+#include <qmlitemnode.h>
namespace QmlDesigner {
+class ImageCacheData
+{
+public:
+ Sqlite::Database database{
+ Utils::PathString{Core::ICore::cacheResourcePath() + "/imagecache-v1.db"}};
+ ImageCacheStorage<Sqlite::Database> storage{database};
+ ImageCacheConnectionManager connectionManager;
+ ImageCacheCollector collector{connectionManager};
+ ImageCacheGenerator generator{collector, storage};
+ TimeStampProvider timeStampProvider;
+ ImageCache cache{storage, generator, timeStampProvider};
+};
+
ItemLibraryView::ItemLibraryView(QObject* parent)
: AbstractView(parent),
m_importManagerView(new ImportManagerView(this))
{
-
+ m_imageCacheData = std::make_unique<ImageCacheData>();
}
ItemLibraryView::~ItemLibraryView() = default;
@@ -55,7 +78,7 @@ bool ItemLibraryView::hasWidget() const
WidgetInfo ItemLibraryView::widgetInfo()
{
if (m_widget.isNull()) {
- m_widget = new ItemLibraryWidget;
+ m_widget = new ItemLibraryWidget{m_imageCacheData->cache};
m_widget->setImportsWidget(m_importManagerView->widgetInfo().widget);
}
@@ -70,6 +93,16 @@ WidgetInfo ItemLibraryView::widgetInfo()
void ItemLibraryView::modelAttached(Model *model)
{
AbstractView::modelAttached(model);
+ auto target = QmlDesignerPlugin::instance()->currentDesignDocument()->currentTarget();
+ m_imageCacheData->cache.clean();
+
+ if (target) {
+ auto clonedTarget = std::make_unique<ProjectExplorer::Target>(
+ target->project(), target->kit()->clone(), ProjectExplorer::Target::_constructor_tag{});
+
+ m_imageCacheData->collector.setTarget(std::move(clonedTarget));
+ }
+
m_widget->clearSearchFilter();
m_widget->setModel(model);
updateImports();
@@ -83,6 +116,8 @@ void ItemLibraryView::modelAboutToBeDetached(Model *model)
{
model->detachView(m_importManagerView);
+ m_imageCacheData->collector.setTarget({});
+
AbstractView::modelAboutToBeDetached(model);
m_widget->setModel(nullptr);
@@ -124,7 +159,7 @@ void ItemLibraryView::importsChanged(const QList<Import> &addedImports, const QL
void ItemLibraryView::setResourcePath(const QString &resourcePath)
{
if (m_widget.isNull())
- m_widget = new ItemLibraryWidget;
+ m_widget = new ItemLibraryWidget{m_imageCacheData->cache};
m_widget->setResourcePath(resourcePath);
}
@@ -142,4 +177,4 @@ void ItemLibraryView::updateImports()
m_widget->delayedUpdateModel();
}
-} //QmlDesigner
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h
index 09535c3230..3d4af5d21c 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibraryview.h
@@ -34,6 +34,7 @@ namespace QmlDesigner {
class ItemLibraryWidget;
class ImportManagerView;
+class ImageCacheData;
class ItemLibraryView : public AbstractView
{
@@ -58,6 +59,7 @@ protected:
void updateImports();
private:
+ std::unique_ptr<ImageCacheData> m_imageCacheData;
QPointer<ItemLibraryWidget> m_widget;
ImportManagerView *m_importManagerView;
bool m_hasErrors = false;
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
index 45e5601135..10a253f5b6 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.cpp
@@ -27,19 +27,21 @@
#include "customfilesystemmodel.h"
#include "itemlibraryassetimportdialog.h"
+#include "itemlibraryiconimageprovider.h"
#include <theme.h>
-#include <itemlibrarymodel.h>
+#include <designeractionmanager.h>
+#include <designermcumanager.h>
#include <itemlibraryimageprovider.h>
#include <itemlibraryinfo.h>
+#include <itemlibrarymodel.h>
#include <metainfo.h>
#include <model.h>
+#include <previewtooltip/previewtooltipbackend.h>
#include <rewritingexception.h>
-#include <qmldesignerplugin.h>
#include <qmldesignerconstants.h>
-#include <designeractionmanager.h>
-#include <designermcumanager.h>
+#include <qmldesignerplugin.h>
#include <utils/algorithm.h>
#include <utils/flowlayout.h>
@@ -81,14 +83,14 @@ static QString propertyEditorResourcesPath() {
return Core::ICore::resourcePath() + QStringLiteral("/qmldesigner/propertyEditorQmlSources");
}
-ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
- QFrame(parent),
- m_itemIconSize(24, 24),
- m_itemViewQuickWidget(new QQuickWidget(this)),
- m_resourcesView(new ItemLibraryResourceView(this)),
- m_importTagsWidget(new QWidget(this)),
- m_addResourcesWidget(new QWidget(this)),
- m_filterFlag(QtBasic)
+ItemLibraryWidget::ItemLibraryWidget(ImageCache &imageCache)
+ : m_itemIconSize(24, 24)
+ , m_itemViewQuickWidget(new QQuickWidget(this))
+ , m_resourcesView(new ItemLibraryResourceView(this))
+ , m_importTagsWidget(new QWidget(this))
+ , m_addResourcesWidget(new QWidget(this))
+ , m_imageCache{imageCache}
+ , m_filterFlag(QtBasic)
{
m_compressionTimer.setInterval(200);
m_compressionTimer.setSingleShot(true);
@@ -102,16 +104,20 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
m_itemViewQuickWidget->engine()->addImportPath(propertyEditorResourcesPath() + "/imports");
m_itemLibraryModel = new ItemLibraryModel(this);
- m_itemViewQuickWidget->rootContext()->setContextProperties(
- QVector<QQmlContext::PropertyPair>{
- {{"itemLibraryModel"}, QVariant::fromValue(m_itemLibraryModel.data())},
- {{"itemLibraryIconWidth"}, m_itemIconSize.width()},
- {{"itemLibraryIconHeight"}, m_itemIconSize.height()},
- {{"rootView"}, QVariant::fromValue(this)},
- {{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()}
- }
- );
- m_itemViewQuickWidget->setClearColor(Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
+ m_itemViewQuickWidget->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{
+ {{"itemLibraryModel"}, QVariant::fromValue(m_itemLibraryModel.data())},
+ {{"itemLibraryIconWidth"}, m_itemIconSize.width()},
+ {{"itemLibraryIconHeight"}, m_itemIconSize.height()},
+ {{"rootView"}, QVariant::fromValue(this)},
+ {{"highlightColor"}, Utils::StyleHelper::notTooBrightHighlightColor()},
+ });
+
+ m_previewTooltipBackend = std::make_unique<PreviewTooltipBackend>(m_imageCache);
+ m_itemViewQuickWidget->rootContext()->setContextProperty("tooltipBackend",
+ m_previewTooltipBackend.get());
+
+ m_itemViewQuickWidget->setClearColor(
+ Theme::getColor(Theme::Color::QmlDesigner_BackgroundColorDarkAlternate));
/* create Resources view and its model */
m_resourcesFileSystemModel = new CustomFileSystemModel(this);
@@ -119,6 +125,7 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
/* create image provider for loading item icons */
m_itemViewQuickWidget->engine()->addImageProvider(QStringLiteral("qmldesigner_itemlibrary"), new Internal::ItemLibraryImageProvider);
+
Theme::setupTheme(m_itemViewQuickWidget->engine());
/* other widgets */
@@ -243,6 +250,8 @@ ItemLibraryWidget::ItemLibraryWidget(QWidget *parent) :
reloadQmlSource();
}
+ItemLibraryWidget::~ItemLibraryWidget() = default;
+
void ItemLibraryWidget::setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo)
{
if (m_itemLibraryInfo.data() == itemLibraryInfo)
@@ -306,9 +315,14 @@ void ItemLibraryWidget::delayedUpdateModel()
void ItemLibraryWidget::setModel(Model *model)
{
+ m_itemViewQuickWidget->engine()->removeImageProvider("itemlibrary_preview");
m_model = model;
if (!model)
return;
+
+ m_itemViewQuickWidget->engine()->addImageProvider("itemlibrary_preview",
+ new ItemLibraryIconImageProvider{m_imageCache});
+
setItemLibraryInfo(model->metaInfo().itemLibraryInfo());
}
@@ -318,7 +332,8 @@ void ItemLibraryWidget::setCurrentIndexOfStackedWidget(int index)
m_filterLineEdit->setVisible(false);
m_importTagsWidget->setVisible(true);
m_addResourcesWidget->setVisible(false);
- } if (index == 1) {
+ }
+ if (index == 1) {
m_filterLineEdit->setVisible(true);
m_importTagsWidget->setVisible(false);
m_addResourcesWidget->setVisible(true);
@@ -564,5 +579,4 @@ void ItemLibraryWidget::addResources()
}
}
}
-
-}
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
index 11dea7d0c1..bfe9106a23 100644
--- a/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
+++ b/src/plugins/qmldesigner/components/itemlibrary/itemlibrarywidget.h
@@ -37,6 +37,8 @@
#include <QQmlPropertyMap>
#include <QTimer>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QStackedWidget;
class QShortcut;
@@ -52,6 +54,9 @@ class CustomFileSystemModel;
class ItemLibraryModel;
class ItemLibraryResourceView;
+class PreviewTooltipBackend;
+class ImageCache;
+class ImageCacheCollector;
class ItemLibraryWidget : public QFrame
{
@@ -63,7 +68,8 @@ class ItemLibraryWidget : public QFrame
};
public:
- ItemLibraryWidget(QWidget *parent = nullptr);
+ ItemLibraryWidget(ImageCache &imageCache);
+ ~ItemLibraryWidget();
void setItemLibraryInfo(ItemLibraryInfo *itemLibraryInfo);
QList<QToolButton *> createToolBarWidgets();
@@ -115,9 +121,10 @@ private:
QScopedPointer<ItemLibraryResourceView> m_resourcesView;
QScopedPointer<QWidget> m_importTagsWidget;
QScopedPointer<QWidget> m_addResourcesWidget;
+ std::unique_ptr<PreviewTooltipBackend> m_previewTooltipBackend;
QShortcut *m_qmlSourceUpdateShortcut;
-
+ ImageCache &m_imageCache;
QPointer<Model> m_model;
FilterChangeFlag m_filterFlag;
ItemLibraryEntry m_currentitemLibraryEntry;
diff --git a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp
index 0371d0f1bb..5cd5721812 100644
--- a/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp
+++ b/src/plugins/qmldesigner/components/navigator/iconcheckboxitemdelegate.cpp
@@ -82,6 +82,12 @@ void IconCheckboxItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &styleOption,
const QModelIndex &modelIndex) const
{
+ bool isVisibilityIcon = modelIndex.column() != NavigatorTreeModel::ColumnType::Visibility;
+ // We need to invert the check status if visibility icon
+ bool checked = isVisibilityIcon ? isChecked(modelIndex) : !isChecked(modelIndex);
+ if (!(styleOption.state & QStyle::State_MouseOver) && !checked)
+ return;
+
if (rowIsPropertyRole(modelIndex.model(), modelIndex))
return; //Do not paint icons for property rows
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
index 5538a9cb8d..602a0f7316 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.cpp
@@ -200,10 +200,10 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
if (!modelNode.isValid())
return QVariant();
- if (role == ItemIsVisibleRole) //independent of column
+ if (role == ItemIsVisibleRole) // independent of column
return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked;
- if (index.column() == 0) {
+ if (index.column() == ColumnType::Name) {
if (role == Qt::DisplayRole) {
return modelNode.displayName();
} else if (role == Qt::DecorationRole) {
@@ -240,18 +240,24 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
} else if (role == ModelNodeRole) {
return QVariant::fromValue<ModelNode>(modelNode);
}
- } else if (index.column() == 1) { //export
+ } else if (index.column() == ColumnType::Alias) { // export
if (role == Qt::CheckStateRole)
- return currentQmlObjectNode.isAliasExported() ? Qt::Checked : Qt::Unchecked;
+ return currentQmlObjectNode.isAliasExported() ? Qt::Checked : Qt::Unchecked;
else if (role == Qt::ToolTipRole)
return tr("Toggles whether this item is exported as an "
"alias property of the root item.");
- } else if (index.column() == 2) { //visible
+ } else if (index.column() == ColumnType::Visibility) { // visible
if (role == Qt::CheckStateRole)
return m_view->isNodeInvisible(modelNode) ? Qt::Unchecked : Qt::Checked;
else if (role == Qt::ToolTipRole)
return tr("Toggles the visibility of this item in the form editor.\n"
"This is independent of the visibility property in QML.");
+ } else if (index.column() == ColumnType::Lock) { // lock
+ if (role == Qt::CheckStateRole)
+ return modelNode.locked() ? Qt::Checked : Qt::Unchecked;
+ else if (role == Qt::ToolTipRole)
+ return tr("Toggles whether this item is locked.\n"
+ "Locked items can't be modified or selected.");
}
return QVariant();
@@ -259,7 +265,16 @@ QVariant NavigatorTreeModel::data(const QModelIndex &index, int role) const
Qt::ItemFlags NavigatorTreeModel::flags(const QModelIndex &index) const
{
- if (index.column() == 0)
+ if (index.column() == ColumnType::Alias
+ || index.column() == ColumnType::Visibility
+ || index.column() == ColumnType::Lock)
+ return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren;
+
+ const ModelNode modelNode = modelNodeForIndex(index);
+ if (ModelNode::isThisOrAncestorLocked(modelNode))
+ return Qt::NoItemFlags;
+
+ if (index.column() == ColumnType::Name)
return Qt::ItemIsEditable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled | Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable
@@ -378,7 +393,7 @@ int NavigatorTreeModel::columnCount(const QModelIndex &parent) const
if (parent.column() > 0)
return 0;
- return 3;
+ return ColumnType::Count;
}
ModelNode NavigatorTreeModel::modelNodeForIndex(const QModelIndex &index) const
@@ -755,7 +770,8 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProper
auto doMoveNodesInteractive = [&parentProperty, modelNodes, targetIndex](){
const TypeName propertyQmlType = parentProperty.parentModelNode().metaInfo().propertyTypeName(parentProperty.name());
- foreach (const ModelNode &modelNode, modelNodes) {
+ int idx = targetIndex;
+ for (const ModelNode &modelNode : modelNodes) {
if (modelNode.isValid()
&& modelNode != parentProperty.parentModelNode()
&& !modelNode.isAncestorOf(parentProperty.parentModelNode())
@@ -764,10 +780,9 @@ void NavigatorTreeModel::moveNodesInteractive(NodeAbstractProperty &parentProper
//once the MetaInfo is part of instances we can do this right
bool nodeCanBeMovedToParentProperty = removeModelNodeFromNodeProperty(parentProperty, modelNode);
-
if (nodeCanBeMovedToParentProperty) {
reparentModelNodeToNodeProperty(parentProperty, modelNode);
- slideModelNodeInList(parentProperty, modelNode, targetIndex);
+ slideModelNodeInList(parentProperty, modelNode, idx++);
}
}
}
@@ -792,11 +807,13 @@ Qt::DropActions NavigatorTreeModel::supportedDragActions() const
bool NavigatorTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
ModelNode modelNode = modelNodeForIndex(index);
- if (index.column() == 1 && role == Qt::CheckStateRole) {
+ if (index.column() == ColumnType::Alias && role == Qt::CheckStateRole) {
QTC_ASSERT(m_view, return false);
m_view->handleChangedExport(modelNode, value.toInt() != 0);
- } else if (index.column() == 2 && role == Qt::CheckStateRole) {
+ } else if (index.column() == ColumnType::Visibility && role == Qt::CheckStateRole) {
QmlVisualNode(modelNode).setVisibilityOverride(value.toInt() == 0);
+ } else if (index.column() == ColumnType::Lock && role == Qt::CheckStateRole) {
+ modelNode.setLocked(value.toInt() != 0);
}
return true;
@@ -806,7 +823,7 @@ void NavigatorTreeModel::notifyDataChanged(const ModelNode &modelNode)
{
const QModelIndex index = indexForModelNode(modelNode);
const QAbstractItemModel *model = index.model();
- const QModelIndex sibling = model ? model->sibling(index.row(), 2, index) : QModelIndex();
+ const QModelIndex sibling = model ? model->sibling(index.row(), ColumnType::Count - 1, index) : QModelIndex();
emit dataChanged(index, sibling);
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
index d474ee98b9..bb2712148d 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreemodel.h
@@ -49,6 +49,14 @@ class NavigatorTreeModel : public QAbstractItemModel, public NavigatorModelInter
public:
+ enum ColumnType {
+ Name = 0,
+ Alias,
+ Visibility,
+ Lock,
+ Count
+ };
+
explicit NavigatorTreeModel(QObject *parent = nullptr);
~NavigatorTreeModel() override;
diff --git a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp
index ddf8862c89..8be526278c 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatortreeview.cpp
@@ -168,6 +168,7 @@ NavigatorTreeView::NavigatorTreeView(QWidget *parent)
setMinimumWidth(240);
setRootIsDecorated(false);
setIndentation(indentation() * 0.5);
+ viewport()->setAttribute(Qt::WA_Hover);
m_toolTipHideTimer.setSingleShot(true);
connect(&m_toolTipHideTimer, &QTimer::timeout, [this]() {
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
index 87952613b1..e1702bdac4 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.cpp
@@ -41,6 +41,7 @@
#include <qmlitemnode.h>
#include <rewritingexception.h>
#include <nodeinstanceview.h>
+#include <theme.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
@@ -48,6 +49,7 @@
#include <utils/algorithm.h>
#include <utils/icon.h>
#include <utils/utilsicons.h>
+#include <utils/stylehelper.h>
#include <QHeaderView>
#include <QTimer>
@@ -138,13 +140,14 @@ void NavigatorView::modelAttached(Model *model)
QTreeView *treeView = treeWidget();
- treeView->header()->setSectionResizeMode(0, QHeaderView::Stretch);
- treeView->header()->resizeSection(1,26);
+ treeView->header()->setSectionResizeMode(NavigatorTreeModel::ColumnType::Name, QHeaderView::Stretch);
+ treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Alias, 26);
+ treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Visibility, 26);
+ treeView->header()->resizeSection(NavigatorTreeModel::ColumnType::Lock, 26);
treeView->setIndentation(20);
m_currentModelInterface->setFilter(false);
-
QTimer::singleShot(0, this, [this, treeView]() {
m_currentModelInterface->setFilter(
DesignerSettings::getValue(DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS).toBool());
@@ -166,10 +169,6 @@ void NavigatorView::modelAttached(Model *model)
}
}
});
-
-#ifdef _LOCK_ITEMS_
- treeView->header()->resizeSection(2,20);
-#endif
}
void NavigatorView::modelAboutToBeDetached(Model *model)
@@ -304,7 +303,7 @@ void NavigatorView::nodeIdChanged(const ModelNode& modelNode, const QString & /*
m_currentModelInterface->notifyDataChanged(modelNode);
}
-void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty>& /*propertyList*/)
+void NavigatorView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &/*propertyList*/)
{
}
@@ -321,7 +320,7 @@ void NavigatorView::propertiesRemoved(const QList<AbstractProperty> &propertyLis
m_currentModelInterface->notifyModelNodesRemoved(modelNodes);
}
-void NavigatorView::rootNodeTypeChanged(const QString & /*type*/, int /*majorVersion*/, int /*minorVersion*/)
+void NavigatorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorVersion*/, int /*minorVersion*/)
{
m_currentModelInterface->notifyDataChanged(rootModelNode());
}
@@ -332,9 +331,12 @@ void NavigatorView::nodeTypeChanged(const ModelNode &modelNode, const TypeName &
}
void NavigatorView::auxiliaryDataChanged(const ModelNode &modelNode,
- const PropertyName & /*name*/,
- const QVariant & /*data*/)
+ const PropertyName &name,
+ const QVariant &data)
{
+ Q_UNUSED(name)
+ Q_UNUSED(data)
+
m_currentModelInterface->notifyDataChanged(modelNode);
}
@@ -344,8 +346,8 @@ void NavigatorView::instanceErrorChanged(const QVector<ModelNode> &errorNodeList
m_currentModelInterface->notifyDataChanged(modelNode);
}
-void NavigatorView::nodeOrderChanged(const NodeListProperty & listProperty,
- const ModelNode & /*node*/,
+void NavigatorView::nodeOrderChanged(const NodeListProperty &listProperty,
+ const ModelNode &/*node*/,
int /*oldIndex*/)
{
m_currentModelInterface->notifyModelNodesMoved(listProperty.directSubNodes());
@@ -613,33 +615,50 @@ void NavigatorView::setupWidget()
connect(m_widget.data(), &NavigatorWidget::reverseOrderToggled, this, &NavigatorView::reverseOrderToggled);
#ifndef QMLDESIGNER_TEST
+ const QString fontName = "qtds_propertyIconFont.ttf";
+
+ const QIcon visibilityOnIcon =
+ Utils::StyleHelper::getIconFromIconFont(fontName,
+ Theme::getIconUnicode(Theme::Icon::visibilityOn),
+ 28, 28, QColor(Qt::white));
+ const QIcon visibilityOffIcon =
+ Utils::StyleHelper::getIconFromIconFont(fontName,
+ Theme::getIconUnicode(Theme::Icon::visibilityOff),
+ 28, 28, QColor(Qt::white));
+
+ const QIcon aliasOnIcon =
+ Utils::StyleHelper::getIconFromIconFont(fontName,
+ Theme::getIconUnicode(Theme::Icon::idAliasOn),
+ 28, 28, QColor(Qt::red));
+ const QIcon aliasOffIcon =
+ Utils::StyleHelper::getIconFromIconFont(fontName,
+ Theme::getIconUnicode(Theme::Icon::idAliasOff),
+ 28, 28, QColor(Qt::white));
+
+ const QIcon lockOnIcon =
+ Utils::StyleHelper::getIconFromIconFont(fontName,
+ Theme::getIconUnicode(Theme::Icon::lockOn),
+ 28, 28, QColor(Qt::white));
+ const QIcon lockOffIcon =
+ Utils::StyleHelper::getIconFromIconFont(fontName,
+ Theme::getIconUnicode(Theme::Icon::lockOff),
+ 28, 28, QColor(Qt::white));
+
auto idDelegate = new NameItemDelegate(this);
- IconCheckboxItemDelegate *showDelegate =
- new IconCheckboxItemDelegate(this,
- Utils::Icons::EYE_OPEN_TOOLBAR.icon(),
- Utils::Icons::EYE_CLOSED_TOOLBAR.icon());
- IconCheckboxItemDelegate *exportDelegate =
- new IconCheckboxItemDelegate(this,
- Icons::EXPORT_CHECKED.icon(),
- Icons::EXPORT_UNCHECKED.icon());
+ IconCheckboxItemDelegate *visibilityDelegate =
+ new IconCheckboxItemDelegate(this, visibilityOnIcon, visibilityOffIcon);
-#ifdef _LOCK_ITEMS_
- IconCheckboxItemDelegate *lockDelegate =
- new IconCheckboxItemDelegate(this,
- Utils::Icons::LOCKED_TOOLBAR.icon(),
- Utils::Icons::UNLOCKED_TOOLBAR.icon());
-#endif
+ IconCheckboxItemDelegate *aliasDelegate =
+ new IconCheckboxItemDelegate(this, aliasOnIcon, aliasOffIcon);
+ IconCheckboxItemDelegate *lockDelegate =
+ new IconCheckboxItemDelegate(this, lockOnIcon, lockOffIcon);
- treeWidget()->setItemDelegateForColumn(0, idDelegate);
-#ifdef _LOCK_ITEMS_
- treeWidget()->setItemDelegateForColumn(1,lockDelegate);
- treeWidget()->setItemDelegateForColumn(2,showDelegate);
-#else
- treeWidget()->setItemDelegateForColumn(1, exportDelegate);
- treeWidget()->setItemDelegateForColumn(2, showDelegate);
-#endif
+ treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Name, idDelegate);
+ treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Alias, aliasDelegate);
+ treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Visibility, visibilityDelegate);
+ treeWidget()->setItemDelegateForColumn(NavigatorTreeModel::ColumnType::Lock, lockDelegate);
#endif //QMLDESIGNER_TEST
}
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorview.h b/src/plugins/qmldesigner/components/navigator/navigatorview.h
index 67042634e9..6189b24559 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorview.h
+++ b/src/plugins/qmldesigner/components/navigator/navigatorview.h
@@ -82,7 +82,7 @@ public:
void propertiesRemoved(const QList<AbstractProperty>& propertyList) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList ,
- const QList<ModelNode> &lastSelectedNodeList) override;
+ const QList<ModelNode> &lastSelectedNodeList) override;
void auxiliaryDataChanged(const ModelNode &node, const PropertyName &name, const QVariant &data) override;
void instanceErrorChanged(const QVector<ModelNode> &errorNodeList) override;
diff --git a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp
index 3813881f55..649b53b5ff 100644
--- a/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp
+++ b/src/plugins/qmldesigner/components/navigator/navigatorwidget.cpp
@@ -78,7 +78,7 @@ NavigatorWidget::NavigatorWidget(NavigatorView *view)
#endif
}
-void NavigatorWidget::setTreeModel(QAbstractItemModel* model)
+void NavigatorWidget::setTreeModel(QAbstractItemModel *model)
{
m_treeView->setModel(model);
}
@@ -92,7 +92,6 @@ QList<QToolButton *> NavigatorWidget::createToolBarWidgets()
{
QList<QToolButton *> buttons;
-
auto button = new QToolButton();
button->setIcon(Icons::ARROW_LEFT.icon());
button->setToolTip(tr("Become last sibling of parent (CTRL + Left)."));
@@ -180,7 +179,6 @@ void NavigatorWidget::enableNavigator()
m_treeView->setEnabled(true);
}
-
NavigatorView *NavigatorWidget::navigatorView() const
{
return m_navigatorView.data();
diff --git a/src/plugins/qmldesigner/components/navigator/previewtooltip.ui b/src/plugins/qmldesigner/components/navigator/previewtooltip.ui
index ccfcb0a6c4..65e7cb01ce 100644
--- a/src/plugins/qmldesigner/components/navigator/previewtooltip.ui
+++ b/src/plugins/qmldesigner/components/navigator/previewtooltip.ui
@@ -82,12 +82,6 @@
</property>
<item>
<widget class="QLabel" name="imageLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
<property name="minimumSize">
<size>
<width>150</width>
@@ -100,9 +94,6 @@
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
- <property name="text">
- <string notr="true">&lt;image&gt;</string>
- </property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
new file mode 100644
index 0000000000..d3c972c217
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.cpp
@@ -0,0 +1,63 @@
+/****************************************************************************
+**
+** 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 "previewimagetooltip.h"
+#include "ui_previewimagetooltip.h"
+
+#include <utils/theme/theme.h>
+
+#include <QtGui/qpixmap.h>
+
+namespace QmlDesigner {
+
+PreviewImageTooltip::PreviewImageTooltip(QWidget *parent)
+ : QWidget(parent)
+ , m_ui(std::make_unique<Ui::PreviewImageTooltip>())
+{
+ // setAttribute(Qt::WA_TransparentForMouseEvents);
+ setWindowFlags(Qt::ToolTip);
+ m_ui->setupUi(this);
+ setStyleSheet(QString("QWidget { background-color: %1 }").arg(Utils::creatorTheme()->color(Utils::Theme::BackgroundColorNormal).name()));
+}
+
+PreviewImageTooltip::~PreviewImageTooltip() = default;
+
+void PreviewImageTooltip::setComponentPath(const QString &path)
+{
+ m_ui->componentPathLabel->setText(path);
+}
+
+void PreviewImageTooltip::setComponentName(const QString &name)
+{
+ m_ui->componentNameLabel->setText(name);
+}
+
+void PreviewImageTooltip::setImage(const QImage &image)
+{
+ resize(image.width() + 20 + m_ui->componentNameLabel->width(),
+ std::max(image.height() + 20, height()));
+ m_ui->imageLabel->setPixmap(QPixmap::fromImage({image}));
+}
+}
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h
new file mode 100644
index 0000000000..e05b8a0727
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** 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 <QtWidgets/qwidget.h>
+#include <QtGui/qpixmap.h>
+
+#include <memory>
+
+namespace QmlDesigner {
+namespace Ui {
+class PreviewImageTooltip;
+}
+
+class PreviewImageTooltip : public QWidget
+{
+ Q_OBJECT
+
+public:
+ explicit PreviewImageTooltip(QWidget *parent = {});
+ ~PreviewImageTooltip();
+
+ void setComponentPath(const QString &path);
+ void setComponentName(const QString &name);
+ void setImage(const QImage &pixmap);
+
+private:
+ std::unique_ptr<Ui::PreviewImageTooltip> m_ui;
+};
+}
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui
new file mode 100644
index 0000000000..16f34fae07
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewimagetooltip.ui
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QmlDesigner::PreviewImageTooltip</class>
+ <widget class="QWidget" name="QmlDesigner::PreviewImageTooltip">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>200</width>
+ <height>200</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>1000</width>
+ <height>1000</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string/>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="sizeGripEnabled" stdset="0">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>1</number>
+ </property>
+ <property name="topMargin">
+ <number>1</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QFrame" name="frame">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="lineWidth">
+ <number>1</number>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="1" column="1">
+ <widget class="QLabel" name="componentPathLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" rowspan="2">
+ <widget class="QLabel" name="imageLabel">
+ <property name="frameShape">
+ <enum>QFrame::Box</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Plain</enum>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="Utils::ElidingLabel" name="componentNameLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>1</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <pointsize>12</pointsize>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string notr="true"/>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::NoTextInteraction</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>Utils::ElidingLabel</class>
+ <extends>QLabel</extends>
+ <header location="global">utils/elidinglabel.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
new file mode 100644
index 0000000000..559cd5c17b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.cpp
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 "previewtooltipbackend.h"
+
+#include "previewimagetooltip.h"
+
+#include <coreplugin/icore.h>
+#include <imagecache.h>
+
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QMetaObject>
+
+namespace QmlDesigner {
+
+PreviewTooltipBackend::PreviewTooltipBackend(ImageCache &cache)
+ : m_cache{cache}
+{}
+
+PreviewTooltipBackend::~PreviewTooltipBackend()
+{
+ hideTooltip();
+}
+
+void PreviewTooltipBackend::showTooltip()
+{
+ if (m_componentPath.isEmpty())
+ return;
+
+ m_tooltip = std::make_unique<PreviewImageTooltip>();
+
+ m_tooltip->setComponentName(m_componentName);
+ m_tooltip->setComponentPath(m_componentPath);
+
+ m_cache.requestImage(
+ m_componentPath,
+ [tooltip = QPointer<PreviewImageTooltip>(m_tooltip.get())](const QImage &image) {
+ QMetaObject::invokeMethod(tooltip, [tooltip, image] {
+ if (tooltip)
+ tooltip->setImage(image);
+ });
+ },
+ [] {});
+
+ auto desktopWidget = QApplication::desktop();
+ auto mousePosition = desktopWidget->cursor().pos();
+
+ mousePosition += {20, 20};
+ m_tooltip->move(mousePosition);
+ m_tooltip->show();
+}
+
+void PreviewTooltipBackend::hideTooltip()
+{
+ if (m_tooltip)
+ m_tooltip->hide();
+
+ m_tooltip.reset();
+}
+
+QString QmlDesigner::PreviewTooltipBackend::componentPath() const
+{
+ return m_componentPath;
+}
+
+void QmlDesigner::PreviewTooltipBackend::setComponentPath(const QString &path)
+{
+ m_componentPath = path;
+
+ if (m_componentPath != path)
+ emit componentPathChanged();
+}
+
+QString QmlDesigner::PreviewTooltipBackend::componentName() const
+{
+ return m_componentName;
+}
+
+void QmlDesigner::PreviewTooltipBackend::setComponentName(const QString &name)
+{
+ m_componentName = name;
+
+ if (m_componentName != name)
+ emit componentNameChanged();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h
new file mode 100644
index 0000000000..b5f777662b
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 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 <QObject>
+#include <QQmlEngine>
+
+#include <memory>
+
+namespace QmlDesigner {
+
+class PreviewImageTooltip;
+class ImageCache;
+
+class PreviewTooltipBackend : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString componentPath READ componentPath WRITE setComponentPath NOTIFY componentPathChanged)
+ Q_PROPERTY(QString componentName READ componentName WRITE setComponentName NOTIFY componentNameChanged)
+
+public:
+ PreviewTooltipBackend(ImageCache &cache);
+ ~PreviewTooltipBackend();
+
+ Q_INVOKABLE void showTooltip();
+ Q_INVOKABLE void hideTooltip();
+
+ QString componentPath() const;
+ void setComponentPath(const QString &path);
+
+ QString componentName() const;
+ void setComponentName(const QString &path);
+
+signals:
+ void componentPathChanged();
+ void componentNameChanged();
+
+private:
+ QString m_componentPath;
+ QString m_componentName;
+ std::unique_ptr<PreviewImageTooltip> m_tooltip;
+ ImageCache &m_cache;
+};
+
+}
+
+QML_DECLARE_TYPE(QmlDesigner::PreviewTooltipBackend)
diff --git a/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri
new file mode 100644
index 0000000000..a33c5d9853
--- /dev/null
+++ b/src/plugins/qmldesigner/components/previewtooltip/previewtooltipbackend.pri
@@ -0,0 +1,11 @@
+HEADERS += \
+ $$PWD/previewtooltipbackend.h \
+ $$PWD/previewimagetooltip.h
+
+SOURCES += \
+ $$PWD/previewtooltipbackend.cpp \
+ $$PWD/previewimagetooltip.cpp
+
+FORMS += $$PWD/previewimagetooltip.ui
+
+
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
index a42db492d9..71e2c99627 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.cpp
@@ -29,15 +29,21 @@
#include <model.h>
+#include <utils/algorithm.h>
+
#include <QFileDialog>
#include <QDirIterator>
#include <qmlmodelnodeproxy.h>
static QString s_lastBrowserPath;
-FileResourcesModel::FileResourcesModel(QObject *parent) :
- QObject(parent), m_filter(QLatin1String("(*.*)")), m_lock(false)
+FileResourcesModel::FileResourcesModel(QObject *parent)
+ : QObject(parent)
+ , m_filter(QLatin1String("(*.*)"))
+ , m_fileSystemWatcher(new Utils::FileSystemWatcher(this))
{
+ connect(m_fileSystemWatcher, &Utils::FileSystemWatcher::directoryChanged,
+ this, &FileResourcesModel::refreshModel);
}
void FileResourcesModel::setModelNodeBackend(const QVariant &modelNodeBackend)
@@ -163,7 +169,6 @@ QVariant FileResourcesModel::modelNodeBackend() const
bool filterMetaIcons(const QString &fileName)
{
-
QFileInfo info(fileName);
if (info.dir().path().split('/').contains("designer")) {
@@ -189,12 +194,20 @@ bool filterMetaIcons(const QString &fileName)
void FileResourcesModel::setupModel()
{
- m_lock = true;
+ m_dirPath = QFileInfo(m_path.toLocalFile()).dir();
+
+ refreshModel();
+
+ m_fileSystemWatcher->removeDirectories(m_fileSystemWatcher->directories());
+ m_fileSystemWatcher->addDirectory(m_dirPath.absolutePath(),
+ Utils::FileSystemWatcher::WatchAllChanges);
+}
+
+void FileResourcesModel::refreshModel()
+{
m_fullPathModel.clear();
m_fileNameModel.clear();
- m_dirPath = QFileInfo(m_path.toLocalFile()).dir();
-
QStringList filterList = m_filter.split(QLatin1Char(' '));
QDirIterator it(m_dirPath.absolutePath(), filterList, QDir::Files, QDirIterator::Subdirectories);
@@ -203,11 +216,15 @@ void FileResourcesModel::setupModel()
if (filterMetaIcons(absolutePath)) {
QString filePath = m_dirPath.relativeFilePath(absolutePath);
m_fullPathModel.append(filePath);
- m_fileNameModel.append(filePath.mid(filePath.lastIndexOf('/') + 1));
}
}
- m_lock = false;
+ Utils::sort(m_fullPathModel, [](const QString &s1, const QString &s2) {
+ return s1.mid(s1.lastIndexOf('/') + 1).toLower() < s2.mid(s2.lastIndexOf('/') + 1).toLower();
+ });
+
+ for (const QString &fullPath : qAsConst(m_fullPathModel))
+ m_fileNameModel.append(fullPath.mid(fullPath.lastIndexOf('/') + 1));
emit fullPathModelChanged();
emit fileNameModelChanged();
diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
index e0a1964347..226421f441 100644
--- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
+++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h
@@ -27,6 +27,8 @@
#include <qmlitemnode.h>
+#include <utils/filesystemwatcher.h>
+
#include <QDir>
#include <QObject>
#include <QStringList>
@@ -60,6 +62,7 @@ public:
QStringList fullPathModel() const;
QStringList fileNameModel() const;
void setupModel();
+ void refreshModel();
Q_INVOKABLE void openFileDialog();
@@ -79,12 +82,11 @@ private:
QUrl m_path;
QDir m_dirPath;
QString m_filter;
- bool m_lock;
QString m_currentPath;
QString m_lastModelPath;
QStringList m_fullPathModel;
QStringList m_fileNameModel;
-
+ Utils::FileSystemWatcher *m_fileSystemWatcher;
};
QML_DECLARE_TYPE(FileResourcesModel)
diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
index 3a15283663..a8792ef204 100644
--- a/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
+++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorview.cpp
@@ -30,6 +30,7 @@
#include <QDebug>
#include <QRegularExpression>
+#include <QMessageBox>
#include <cmath>
#include <memory>
@@ -42,6 +43,7 @@
#include <qmlitemnode.h>
#include <qmlstate.h>
#include <annotationeditor/annotationeditor.h>
+#include <utils/algorithm.h>
namespace QmlDesigner {
@@ -92,11 +94,46 @@ void StatesEditorView::removeState(int nodeId)
if (nodeId > 0 && hasModelNodeForInternalId(nodeId)) {
ModelNode stateNode(modelNodeForInternalId(nodeId));
Q_ASSERT(stateNode.metaInfo().isSubclassOf("QtQuick.State"));
+
+ QmlModelState modelState(stateNode);
+ if (modelState.isValid()) {
+ QStringList lockedTargets;
+ const auto propertyChanges = modelState.propertyChanges();
+ for (const QmlPropertyChanges &change : propertyChanges) {
+ const ModelNode target = change.target();
+ if (target.locked())
+ lockedTargets.push_back(target.id());
+ }
+
+ if (!lockedTargets.empty()) {
+ Utils::sort(lockedTargets);
+ QString detailedText = QString("<b>" + tr("Locked items:") + "</b><br>");
+
+ for (const auto &id : qAsConst(lockedTargets))
+ detailedText.append("- " + id + "<br>");
+
+ detailedText.chop(QString("<br>").size());
+
+ QMessageBox msgBox;
+ msgBox.setTextFormat(Qt::RichText);
+ msgBox.setIcon(QMessageBox::Question);
+ msgBox.setWindowTitle(tr("Remove State"));
+ msgBox.setText(QString(tr("Removing this state will modify locked items.") + "<br><br>%1")
+ .arg(detailedText));
+ msgBox.setInformativeText(tr("Do you want to continue by removing the state?"));
+ msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
+ msgBox.setDefaultButton(QMessageBox::Ok);
+
+ if (msgBox.exec() == QMessageBox::Cancel)
+ return;
+ }
+ }
+
NodeListProperty parentProperty = stateNode.parentProperty().toNodeListProperty();
if (parentProperty.count() <= 1) {
setCurrentState(baseState());
- } else if (parentProperty.isValid()){
+ } else if (parentProperty.isValid()) {
int index = parentProperty.indexOf(stateNode);
if (index == 0)
setCurrentState(parentProperty.at(1));
@@ -104,7 +141,6 @@ void StatesEditorView::removeState(int nodeId)
setCurrentState(parentProperty.at(index - 1));
}
-
stateNode.destroy();
}
} catch (const RewritingException &e) {
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.cpp
index a0f0eedf29..007e8ae571 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.cpp
@@ -63,6 +63,11 @@ TimelineGraphicsLayout::TimelineGraphicsLayout(TimelineGraphicsScene *scene, Tim
TimelineGraphicsLayout::~TimelineGraphicsLayout() = default;
+int TimelineGraphicsLayout::zoom() const
+{
+ return m_rulerItem->zoom();
+}
+
double TimelineGraphicsLayout::rulerWidth() const
{
return m_rulerItem->preferredWidth();
@@ -133,12 +138,12 @@ void TimelineGraphicsLayout::setTimeline(const QmlTimeline &timeline)
if (auto *scene = timelineScene())
if (auto *view = scene->timelineView())
if (!timeline.isValid() && view->isAttached())
- emit scaleFactorChanged(0);
+ emit zoomChanged(0);
}
-void TimelineGraphicsLayout::setRulerScaleFactor(int factor)
+void TimelineGraphicsLayout::setZoom(int factor)
{
- m_rulerItem->setRulerScaleFactor(factor);
+ m_rulerItem->setZoom(factor);
}
void TimelineGraphicsLayout::invalidate()
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.h b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.h
index f4d2ae5836..ce2423dfe9 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicslayout.h
@@ -44,7 +44,7 @@ class TimelineGraphicsLayout : public TimelineItem
signals:
void rulerClicked(const QPointF &pos);
- void scaleFactorChanged(int factor);
+ void zoomChanged(int factor);
public:
TimelineGraphicsLayout(TimelineGraphicsScene *scene, TimelineItem *parent = nullptr);
@@ -52,6 +52,8 @@ public:
~TimelineGraphicsLayout() override;
public:
+ int zoom() const;
+
double rulerWidth() const;
double rulerScaling() const;
@@ -66,7 +68,7 @@ public:
void setTimeline(const QmlTimeline &timeline);
- void setRulerScaleFactor(int factor);
+ void setZoom(int factor);
void invalidate();
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
index 3b87f22c51..5c2621300f 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.cpp
@@ -123,9 +123,9 @@ TimelineGraphicsScene::TimelineGraphicsScene(TimelineWidget *parent)
auto changeScale = [this](int factor) {
timelineWidget()->changeScaleFactor(factor);
- setRulerScaling(qreal(factor));
+ setZoom(factor);
};
- connect(m_layout, &TimelineGraphicsLayout::scaleFactorChanged, changeScale);
+ connect(m_layout, &TimelineGraphicsLayout::zoomChanged, changeScale);
}
TimelineGraphicsScene::~TimelineGraphicsScene()
@@ -144,7 +144,7 @@ void TimelineGraphicsScene::onShow()
setCurrentFrame(cf);
}
- emit m_layout->scaleFactorChanged(0);
+ emit m_layout->zoomChanged(0);
}
}
@@ -271,6 +271,11 @@ void TimelineGraphicsScene::setEndFrame(int frame)
timeline.modelNode().variantProperty("endFrame").setValue(frame);
}
+int TimelineGraphicsScene::zoom() const
+{
+ return m_layout->zoom();
+}
+
qreal TimelineGraphicsScene::rulerScaling() const
{
return m_layout->rulerScaling();
@@ -332,15 +337,20 @@ QVector<qreal> TimelineGraphicsScene::keyframePositions(const QmlTimelineKeyfram
return positions;
}
-void TimelineGraphicsScene::setRulerScaling(int scaleFactor)
+void TimelineGraphicsScene::setZoom(int scaleFactor)
+{
+ setZoom(scaleFactor, currentFramePosition());
+}
+
+void TimelineGraphicsScene::setZoom(int scaleFactor, double pivot)
{
const qreal oldOffset = scrollOffset();
const qreal oldScaling = m_layout->rulerScaling();
- const qreal oldPosition = mapToScene(currentFramePosition());
- m_layout->setRulerScaleFactor(scaleFactor);
+ const qreal oldPosition = mapToScene(pivot);
+ m_layout->setZoom(scaleFactor);
const qreal newScaling = m_layout->rulerScaling();
- const qreal newPosition = mapToScene(currentFramePosition());
+ const qreal newPosition = mapToScene(pivot);
const qreal newOffset = oldOffset + (newPosition - oldPosition);
@@ -428,6 +438,18 @@ void TimelineGraphicsScene::invalidateKeyframesForTarget(const ModelNode &target
TimelineSectionItem::updateFramesForTarget(child, target);
}
+void TimelineGraphicsScene::invalidateHeightForTarget(const ModelNode &target)
+{
+ if (!target.isValid())
+ return;
+
+ const auto children = m_layout->childItems();
+ for (auto child : children)
+ TimelineSectionItem::updateHeightForTarget(child, target);
+
+ invalidateLayout();
+}
+
void TimelineGraphicsScene::invalidateScene()
{
ModelNode node = timelineView()->modelNodeForId(
@@ -502,7 +524,7 @@ QRectF AbstractScrollGraphicsScene::selectionBounds() const
}
void AbstractScrollGraphicsScene::selectKeyframes(const SelectionMode &mode,
- const QList<TimelineKeyframeItem *> &items)
+ const QList<TimelineKeyframeItem *> &items)
{
if (mode == SelectionMode::Remove || mode == SelectionMode::Toggle) {
for (auto *item : items) {
@@ -731,7 +753,7 @@ void TimelineGraphicsScene::deleteKeyframeGroup(const ModelNode &group)
if (!QmlTimelineKeyframeGroup::isValidQmlTimelineKeyframeGroup(group))
return;
- timelineView()->executeInTransaction("TimelineGraphicsScene::handleKeyframeGroupDeletion", [group](){
+ timelineView()->executeInTransaction("TimelineGraphicsScene::handleKeyframeGroupDeletion", [group]() {
ModelNode nonConst = group;
nonConst.destroy();
});
@@ -739,7 +761,7 @@ void TimelineGraphicsScene::deleteKeyframeGroup(const ModelNode &group)
void TimelineGraphicsScene::deleteKeyframes(const QList<ModelNode> &frames)
{
- timelineView()->executeInTransaction("TimelineGraphicsScene::handleKeyframeDeletion", [frames](){
+ timelineView()->executeInTransaction("TimelineGraphicsScene::handleKeyframeDeletion", [frames]() {
for (auto keyframe : frames) {
if (keyframe.isValid()) {
ModelNode frame = keyframe;
@@ -764,7 +786,7 @@ AbstractView *TimelineGraphicsScene::abstractView() const
int AbstractScrollGraphicsScene::getScrollOffset(QGraphicsScene *scene)
{
- auto scrollScene = qobject_cast<AbstractScrollGraphicsScene*>(scene);
+ auto scrollScene = qobject_cast<AbstractScrollGraphicsScene *>(scene);
if (scrollScene)
return scrollScene->scrollOffset();
return 0;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h
index 7413cb1dbb..fc4da39f32 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinegraphicsscene.h
@@ -58,7 +58,6 @@ class AbstractScrollGraphicsScene : public QGraphicsScene
public:
AbstractScrollGraphicsScene(QWidget *parent);
- ;
int scrollOffset() const;
void setScrollOffset(int offset);
@@ -74,6 +73,7 @@ public:
bool isKeyframeSelected(TimelineKeyframeItem *keyframe) const;
bool multipleKeyframesSelected() const;
+ virtual int zoom() const = 0;
virtual qreal rulerScaling() const = 0;
virtual int rulerWidth() const = 0;
virtual qreal rulerDuration() const = 0;
@@ -134,6 +134,7 @@ public:
TimelineWidget *timelineWidget() const;
TimelineToolBar *toolBar() const;
+ int zoom() const override;
qreal rulerScaling() const override;
int rulerWidth() const override;
qreal rulerDuration() const override;
@@ -152,12 +153,14 @@ public:
qreal snap(qreal frame, bool snapToPlayhead = true) override;
- void setRulerScaling(int scaling);
+ void setZoom(int scaling);
+ void setZoom(int scaling, double pivot);
void commitCurrentFrame(qreal frame);
void invalidateSectionForTarget(const ModelNode &modelNode);
void invalidateKeyframesForTarget(const ModelNode &modelNode);
+ void invalidateHeightForTarget(const ModelNode &modelNode);
void invalidateScene();
void invalidateScrollbar() override;
@@ -203,7 +206,6 @@ private:
QList<QGraphicsItem *> itemsAt(const QPointF &pos);
private:
-
TimelineWidget *m_parent = nullptr;
TimelineGraphicsLayout *m_layout = nullptr;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp
index 12345a404e..1b0b2cb7e5 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.cpp
@@ -156,4 +156,9 @@ TimelineFrameHandle *TimelineMovableAbstractItem::asTimelineFrameHandle()
return nullptr;
}
+bool TimelineMovableAbstractItem::isLocked() const
+{
+ return false;
+}
+
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h
index 199a78ad99..d79101b4d9 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovableabstractitem.h
@@ -69,6 +69,8 @@ public:
virtual TimelineKeyframeItem *asTimelineKeyframeItem();
virtual TimelineFrameHandle *asTimelineFrameHandle();
+ virtual bool isLocked() const;
+
protected:
int scrollOffset() const;
void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp
index 5dc52bbc9b..0508606c11 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinemovetool.cpp
@@ -70,6 +70,9 @@ void TimelineMoveTool::mousePressEvent(TimelineMovableAbstractItem *item,
{
Q_UNUSED(item)
+ if (currentItem() && currentItem()->isLocked())
+ return;
+
if (auto *current = currentItem()->asTimelineKeyframeItem()) {
const qreal sourceFrame = qRound(current->mapFromSceneToFrame(current->rect().center().x()));
const qreal targetFrame = qRound(current->mapFromSceneToFrame(event->scenePos().x()));
@@ -85,6 +88,9 @@ void TimelineMoveTool::mouseMoveEvent(TimelineMovableAbstractItem *item,
if (!currentItem())
return;
+ if (currentItem()->isLocked())
+ return;
+
if (auto *current = currentItem()->asTimelineKeyframeItem()) {
// prevent dragging if deselecting a keyframe (Ctrl+click and drag a selected keyframe)
if (!current->highlighted())
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp
index c77d466585..1cff801235 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.cpp
@@ -171,6 +171,17 @@ void TimelineSectionItem::updateFramesForTarget(QGraphicsItem *item, const Model
}
}
+void TimelineSectionItem::updateHeightForTarget(QGraphicsItem *item, const ModelNode &target)
+{
+ if (!target.isValid())
+ return;
+
+ if (auto sectionItem = qgraphicsitem_cast<TimelineSectionItem *>(item)) {
+ if (sectionItem->targetNode() == target)
+ sectionItem->updateHeight();
+ }
+}
+
void TimelineSectionItem::moveAllFrames(qreal offset)
{
if (m_timeline.isValid())
@@ -313,7 +324,8 @@ void TimelineSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
if (event->button() == Qt::LeftButton) {
event->accept();
- toggleCollapsed();
+ if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
+ toggleCollapsed();
}
}
@@ -345,7 +357,8 @@ void TimelineSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
if (m_targetNode.isValid())
m_targetNode.view()->setSelectedModelNode(m_targetNode);
} else {
- toggleCollapsed();
+ if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
+ toggleCollapsed();
}
update();
}
@@ -414,6 +427,12 @@ void TimelineSectionItem::updateFrames()
update();
}
+void TimelineSectionItem::updateHeight()
+{
+ invalidateHeight();
+ update();
+}
+
void TimelineSectionItem::invalidateHeight()
{
int height = 0;
@@ -464,7 +483,8 @@ void TimelineSectionItem::invalidateFrames()
bool TimelineSectionItem::collapsed() const
{
- return m_targetNode.isValid() && !m_targetNode.hasAuxiliaryData("timeline_expanded");
+ return m_targetNode.isValid()
+ && (!m_targetNode.hasAuxiliaryData("timeline_expanded") || m_targetNode.locked());
}
void TimelineSectionItem::createPropertyItems()
@@ -549,9 +569,9 @@ void TimelineRulerSectionItem::invalidateRulerSize(const qreal length)
m_end = length;
}
-void TimelineRulerSectionItem::setRulerScaleFactor(int scaling)
+void TimelineRulerSectionItem::setZoom(int zoom)
{
- qreal blend = qreal(scaling) / 100.0;
+ qreal blend = qreal(zoom) / 100.0;
qreal width = size().width() - qreal(TimelineConstants::sectionWidth);
qreal duration = rulerDuration();
@@ -572,7 +592,7 @@ void TimelineRulerSectionItem::setRulerScaleFactor(int scaling)
update();
}
-int TimelineRulerSectionItem::getRulerScaleFactor() const
+int TimelineRulerSectionItem::zoom() const
{
qreal width = size().width() - qreal(TimelineConstants::sectionWidth);
qreal duration = rulerDuration();
@@ -589,7 +609,7 @@ int TimelineRulerSectionItem::getRulerScaleFactor() const
qreal rcount = width / m_scaling;
qreal rblend = TimelineUtils::reverseLerp(rcount, minCount, maxCount);
- int rfactor = std::round(rblend * 100);
+ int rfactor = static_cast<int>(std::round(rblend * 100));
return TimelineUtils::clamp(rfactor, 0, 100);
}
@@ -766,7 +786,7 @@ void TimelineRulerSectionItem::resizeEvent(QGraphicsSceneResizeEvent *event)
{
QGraphicsWidget::resizeEvent(event);
- auto factor = getRulerScaleFactor();
+ auto factor = zoom();
if (factor < 0) {
if (event->oldSize().width() < event->newSize().width())
@@ -775,7 +795,7 @@ void TimelineRulerSectionItem::resizeEvent(QGraphicsSceneResizeEvent *event)
factor = 100;
}
- emit scaleFactorChanged(factor);
+ emit zoomChanged(factor);
}
void TimelineRulerSectionItem::setSizeHints(int width)
@@ -845,6 +865,11 @@ void TimelineBarItem::commitPosition(const QPointF & /*point*/)
m_oldRect = QRectF();
}
+bool TimelineBarItem::isLocked() const
+{
+ return sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked();
+}
+
void TimelineBarItem::scrollOffsetChanged()
{
sectionItem()->invalidateBar();
@@ -904,7 +929,9 @@ void TimelineBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
const auto p = event->pos();
QRectF left, right;
- if (handleRects(rect(), left, right)) {
+ if (isLocked() && rect().contains(p)) {
+ setCursor(QCursor(Qt::ForbiddenCursor));
+ } else if (handleRects(rect(), left, right)) {
if (left.contains(p) || right.contains(p)) {
if (cursor().shape() != Qt::SizeHorCursor)
setCursor(QCursor(Qt::SizeHorCursor));
@@ -920,6 +947,9 @@ void TimelineBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
void TimelineBarItem::contextMenuEvent(QGraphicsSceneContextMenuEvent* event)
{
+ if (isLocked())
+ return;
+
QMenu menu;
QAction* overrideColor = menu.addAction(tr("Override Color"));
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h
index e5403bcb74..956ef31ef7 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinesectionitem.h
@@ -50,6 +50,8 @@ public:
void itemMoved(const QPointF &start, const QPointF &end) override;
void commitPosition(const QPointF &point) override;
+ bool isLocked() const override;
+
protected:
void scrollOffsetChanged() override;
void paint(QPainter *painter,
@@ -100,6 +102,7 @@ public:
static void updateData(QGraphicsItem *item);
static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b);
static void updateFramesForTarget(QGraphicsItem *item, const ModelNode &target);
+ static void updateHeightForTarget(QGraphicsItem *item, const ModelNode &target);
void moveAllFrames(qreal offset);
void scaleAllFrames(qreal scale);
@@ -121,6 +124,7 @@ protected:
private:
void updateData();
void updateFrames();
+ void updateHeight();
void invalidateHeight();
void invalidateProperties();
void invalidateFrames();
@@ -145,7 +149,7 @@ class TimelineRulerSectionItem : public TimelineItem
signals:
void rulerClicked(const QPointF &pos);
- void scaleFactorChanged(int scale);
+ void zoomChanged(int zoom);
public:
static TimelineRulerSectionItem *create(QGraphicsScene *parentScene, TimelineItem *parent);
@@ -153,9 +157,9 @@ public:
void invalidateRulerSize(const QmlTimeline &timeline);
void invalidateRulerSize(const qreal length);
- void setRulerScaleFactor(int scaling);
+ void setZoom(int zoom);
- int getRulerScaleFactor() const;
+ int zoom() const;
qreal getFrameTick() const;
qreal rulerScaling() const;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
index d0679c80bb..dab580a146 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.cpp
@@ -236,6 +236,18 @@ void TimelineView::selectedNodesChanged(const QList<ModelNode> & /*selectedNodeL
m_timelineWidget->graphicsScene()->update();
}
+void TimelineView::auxiliaryDataChanged(const ModelNode &modelNode,
+ const PropertyName &name,
+ const QVariant &data)
+{
+ if (name == QmlDesigner::lockedProperty && data.toBool() && modelNode.isValid()) {
+ for (const auto &node : modelNode.allSubModelNodesAndThisNode()) {
+ if (node.hasAuxiliaryData("timeline_expanded"))
+ m_timelineWidget->graphicsScene()->invalidateHeightForTarget(node);
+ }
+ }
+}
+
void TimelineView::propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList)
{
for (const auto &property : propertyList) {
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelineview.h b/src/plugins/qmldesigner/components/timelineeditor/timelineview.h
index fe3f5903ff..f1f39b6a35 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelineview.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelineview.h
@@ -62,6 +62,9 @@ public:
PropertyChangeFlags propertyChange) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
+ void auxiliaryDataChanged(const ModelNode &node,
+ const PropertyName &name,
+ const QVariant &data) override;
void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
index e740fb924e..6c7c15451d 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.cpp
@@ -34,6 +34,7 @@
#include "timelinepropertyitem.h"
#include "timelinetoolbar.h"
#include "timelineview.h"
+#include "navigation2d.h"
#include <qmldesignerplugin.h>
#include <qmlstate.h>
@@ -60,6 +61,8 @@
#include <QtGlobal>
#include <QSpacerItem>
+#include <cmath>
+
namespace QmlDesigner {
class Eventfilter : public QObject
@@ -114,7 +117,7 @@ TimelineWidget::TimelineWidget(TimelineView *view)
, m_toolbar(new TimelineToolBar(this))
, m_rulerView(new QGraphicsView(this))
, m_graphicsView(new QGraphicsView(this))
- , m_scrollbar(new QScrollBar(this))
+ , m_scrollbar(new Navigation2dScrollBar(this))
, m_statusBar(new QLabel(this))
, m_timelineView(view)
, m_graphicsScene(new TimelineGraphicsScene(this))
@@ -153,6 +156,7 @@ TimelineWidget::TimelineWidget(TimelineView *view)
m_graphicsView->setFrameShape(QFrame::NoFrame);
m_graphicsView->setFrameShadow(QFrame::Plain);
m_graphicsView->setLineWidth(0);
+ m_graphicsView->setVerticalScrollBar(new Navigation2dScrollBar);
m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -247,6 +251,14 @@ TimelineWidget::TimelineWidget(TimelineView *view)
connect(m_addButton, &QPushButton::clicked, this, [this]() {
m_timelineView->addNewTimelineDialog();
});
+
+ Navigation2dFilter *filter = new Navigation2dFilter(this, m_scrollbar);
+ connect(filter, &Navigation2dFilter::zoomChanged, [this](double scale, const QPointF& pos) {
+ int s = static_cast<int>(std::round(scale*100.));
+ double ps = m_graphicsScene->mapFromScene(pos.x());
+ m_graphicsScene->setZoom(std::clamp(m_graphicsScene->zoom() + s, 0, 100), ps);
+ });
+ installEventFilter(filter);
}
void TimelineWidget::connectToolbar()
@@ -258,8 +270,8 @@ void TimelineWidget::connectToolbar()
connect(graphicsScene(), &TimelineGraphicsScene::scroll, this, &TimelineWidget::scroll);
- auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); };
- connect(m_toolbar, &TimelineToolBar::scaleFactorChanged, setRulerScaling);
+ auto setZoomFactor = [this](int val) { m_graphicsScene->setZoom(val); };
+ connect(m_toolbar, &TimelineToolBar::scaleFactorChanged, setZoomFactor);
auto setToFirstFrame = [this]() {
graphicsScene()->setCurrentFrame(graphicsScene()->startFrame());
@@ -428,7 +440,7 @@ void TimelineWidget::init()
// setScaleFactor uses QSignalBlocker.
m_toolbar->setScaleFactor(0);
- m_graphicsScene->setRulerScaling(0);
+ m_graphicsScene->setZoom(0);
}
void TimelineWidget::reset()
diff --git a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h
index b546452e56..b1e24caf93 100644
--- a/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h
+++ b/src/plugins/qmldesigner/components/timelineeditor/timelinewidget.h
@@ -36,7 +36,6 @@ QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QGraphicsView)
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QResizeEvent)
-QT_FORWARD_DECLARE_CLASS(QScrollBar)
QT_FORWARD_DECLARE_CLASS(QShowEvent)
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QPushButton)
@@ -47,6 +46,7 @@ class TimelineToolBar;
class TimelineView;
class TimelineGraphicsScene;
class QmlTimeline;
+class Navigation2dScrollBar;
class TimelineWidget : public QWidget
{
@@ -94,7 +94,7 @@ private:
QGraphicsView *m_graphicsView = nullptr;
- QScrollBar *m_scrollbar = nullptr;
+ Navigation2dScrollBar *m_scrollbar = nullptr;
QLabel *m_statusBar = nullptr;
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp
index 02e1258dfd..5abff4d611 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.cpp
@@ -65,6 +65,11 @@ TransitionEditorGraphicsLayout::TransitionEditorGraphicsLayout(QGraphicsScene *s
TransitionEditorGraphicsLayout::~TransitionEditorGraphicsLayout() = default;
+int TransitionEditorGraphicsLayout::zoom() const
+{
+ return m_rulerItem->zoom();
+}
+
double TransitionEditorGraphicsLayout::rulerWidth() const
{
return m_rulerItem->preferredWidth();
@@ -133,7 +138,7 @@ void TransitionEditorGraphicsLayout::setTransition(const ModelNode &transition)
if (auto *scene = timelineScene())
if (auto *view = scene->timelineView())
if (!transition.isValid() && view->isAttached())
- emit scaleFactorChanged(0);
+ emit zoomChanged(0);
}
void TransitionEditorGraphicsLayout::setDuration(qreal duration)
@@ -141,9 +146,9 @@ void TransitionEditorGraphicsLayout::setDuration(qreal duration)
m_rulerItem->invalidateRulerSize(duration);
}
-void TransitionEditorGraphicsLayout::setRulerScaleFactor(int factor)
+void TransitionEditorGraphicsLayout::setZoom(int factor)
{
- m_rulerItem->setRulerScaleFactor(factor);
+ m_rulerItem->setZoom(factor);
}
void TransitionEditorGraphicsLayout::invalidate()
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h
index 9362abffdf..67495db20d 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicslayout.h
@@ -44,7 +44,7 @@ class TransitionEditorGraphicsLayout : public TimelineItem
signals:
void rulerClicked(const QPointF &pos);
- void scaleFactorChanged(int factor);
+ void zoomChanged(int factor);
public:
TransitionEditorGraphicsLayout(QGraphicsScene *scene, TimelineItem *parent = nullptr);
@@ -52,6 +52,8 @@ public:
~TransitionEditorGraphicsLayout() override;
public:
+ int zoom() const;
+
double rulerWidth() const;
double rulerScaling() const;
@@ -66,7 +68,7 @@ public:
void setDuration(qreal duration);
- void setRulerScaleFactor(int factor);
+ void setZoom(int factor);
void invalidate();
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
index 036fe173f5..c080a49314 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.cpp
@@ -104,9 +104,9 @@ TransitionEditorGraphicsScene::TransitionEditorGraphicsScene(TransitionEditorWid
auto changeScale = [this](int factor) {
transitionEditorWidget()->changeScaleFactor(factor);
- setRulerScaling(qreal(factor));
+ setZoom(factor);
};
- connect(m_layout, &TransitionEditorGraphicsLayout::scaleFactorChanged, changeScale);
+ connect(m_layout, &TransitionEditorGraphicsLayout::zoomChanged, changeScale);
}
TransitionEditorGraphicsScene::~TransitionEditorGraphicsScene()
@@ -125,7 +125,7 @@ void TransitionEditorGraphicsScene::invalidateScrollbar()
void TransitionEditorGraphicsScene::onShow()
{
- emit m_layout->scaleFactorChanged(0);
+ emit m_layout->zoomChanged(0);
}
void TransitionEditorGraphicsScene::setTransition(const ModelNode &transition)
@@ -157,7 +157,12 @@ void TransitionEditorGraphicsScene::setDuration(int duration)
m_transition.setAuxiliaryData("transitionDuration", duration);
m_layout->setDuration(duration);
qreal scaling = m_layout->rulerScaling();
- setRulerScaling(scaling);
+ setZoom(scaling);
+}
+
+int TransitionEditorGraphicsScene::zoom() const
+{
+ return m_layout->zoom();
}
qreal TransitionEditorGraphicsScene::rulerScaling() const
@@ -199,9 +204,9 @@ qreal TransitionEditorGraphicsScene::mapFromScene(qreal x) const
return xPosOffset / rulerScaling() + startFrame();
}
-void TransitionEditorGraphicsScene::setRulerScaling(int scaleFactor)
+void TransitionEditorGraphicsScene::setZoom(int scaleFactor)
{
- m_layout->setRulerScaleFactor(scaleFactor);
+ m_layout->setZoom(scaleFactor);
setScrollOffset(0);
invalidateSections();
@@ -209,6 +214,35 @@ void TransitionEditorGraphicsScene::setRulerScaling(int scaleFactor)
update();
}
+void TransitionEditorGraphicsScene::setZoom(int scaling, double pivot)
+{
+ const qreal oldOffset = scrollOffset();
+ const qreal oldScaling = m_layout->rulerScaling();
+ const qreal oldPosition = mapToScene(pivot);
+ m_layout->setZoom(scaling);
+
+ const qreal newScaling = m_layout->rulerScaling();
+ const qreal newPosition = mapToScene(pivot);
+
+ const qreal newOffset = oldOffset + (newPosition - oldPosition);
+
+ if (std::isinf(oldScaling) || std::isinf(newScaling))
+ setScrollOffset(0);
+ else {
+ setScrollOffset(std::round(newOffset));
+
+ const qreal start = mapToScene(startFrame());
+ const qreal head = TimelineConstants::sectionWidth + TimelineConstants::timelineLeftOffset;
+
+ if (start - head > 0)
+ setScrollOffset(0);
+ }
+
+ invalidateSections();
+ invalidateScrollbar();
+ update();
+}
+
void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &target)
{
if (!target.isValid())
@@ -218,7 +252,7 @@ void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &
const QList<QGraphicsItem *> items = m_layout->childItems();
for (auto child : items)
- TimelineSectionItem::updateDataForTarget(child, target, &found);
+ TransitionEditorSectionItem::updateDataForTarget(child, target, &found);
if (!found)
invalidateScene();
@@ -227,6 +261,18 @@ void TransitionEditorGraphicsScene::invalidateSectionForTarget(const ModelNode &
invalidateLayout();
}
+void TransitionEditorGraphicsScene::invalidateHeightForTarget(const ModelNode &target)
+{
+ if (!target.isValid())
+ return;
+
+ const auto children = m_layout->childItems();
+ for (auto child : children)
+ TransitionEditorSectionItem::updateHeightForTarget(child, target);
+
+ invalidateLayout();
+}
+
void TransitionEditorGraphicsScene::invalidateScene()
{
invalidateScrollbar();
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h
index 2f04c5b729..d0ade7064c 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorgraphicsscene.h
@@ -81,6 +81,7 @@ public:
TransitionEditorWidget *transitionEditorWidget() const;
TransitionEditorToolBar *toolBar() const;
+ int zoom() const override;
qreal rulerScaling() const override;
int rulerWidth() const override;
qreal rulerDuration() const override;
@@ -90,9 +91,11 @@ public:
qreal mapToScene(qreal x) const;
qreal mapFromScene(qreal x) const;
- void setRulerScaling(int scaling);
+ void setZoom(int scaling);
+ void setZoom(int scaling, double pivot);
void invalidateSectionForTarget(const ModelNode &modelNode);
+ void invalidateHeightForTarget(const ModelNode &modelNode);
void invalidateScene();
void invalidateCurrentValues();
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp
index 86442059d9..7172465e0d 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.cpp
@@ -196,6 +196,17 @@ void TransitionEditorSectionItem::updateData(QGraphicsItem *item)
sectionItem->updateData();
}
+void TransitionEditorSectionItem::updateHeightForTarget(QGraphicsItem *item, const ModelNode &target)
+{
+ if (!target.isValid())
+ return;
+
+ if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item)) {
+ if (sectionItem->targetNode() == target)
+ sectionItem->updateHeight();
+ }
+}
+
void TransitionEditorSectionItem::invalidateBar(QGraphicsItem *item)
{
if (auto sectionItem = qgraphicsitem_cast<TransitionEditorSectionItem *>(item))
@@ -360,7 +371,8 @@ void TransitionEditorSectionItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent
if (event->button() == Qt::LeftButton) {
event->accept();
- toggleCollapsed();
+ if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
+ toggleCollapsed();
}
}
@@ -392,7 +404,8 @@ void TransitionEditorSectionItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *ev
if (m_targetNode.isValid())
m_targetNode.view()->setSelectedModelNode(m_targetNode);
} else {
- toggleCollapsed();
+ if (!ModelNode::isThisOrAncestorLocked(m_targetNode))
+ toggleCollapsed();
}
update();
}
@@ -417,6 +430,12 @@ void TransitionEditorSectionItem::updateData()
update();
}
+void TransitionEditorSectionItem::updateHeight()
+{
+ invalidateHeight();
+ update();
+}
+
const QList<QGraphicsItem *> TransitionEditorSectionItem::propertyItems() const
{
QList<QGraphicsItem *> list;
@@ -488,7 +507,8 @@ void TransitionEditorSectionItem::invalidateProperties()
bool TransitionEditorSectionItem::collapsed() const
{
- return m_targetNode.isValid() && !m_targetNode.hasAuxiliaryData("timeline_expanded");
+ return m_targetNode.isValid()
+ && (!m_targetNode.hasAuxiliaryData("transition_expanded") || m_targetNode.locked());
}
qreal TransitionEditorSectionItem::rulerWidth() const
@@ -501,9 +521,9 @@ void TransitionEditorSectionItem::toggleCollapsed()
QTC_ASSERT(m_targetNode.isValid(), return );
if (collapsed())
- m_targetNode.setAuxiliaryData("timeline_expanded", true);
+ m_targetNode.setAuxiliaryData("transition_expanded", true);
else
- m_targetNode.removeAuxiliaryData("timeline_expanded");
+ m_targetNode.removeAuxiliaryData("transition_expanded");
invalidateHeight();
}
@@ -592,6 +612,11 @@ void TransitionEditorBarItem::commitPosition(const QPointF & /*point*/)
scrollOffsetChanged();
}
+bool TransitionEditorBarItem::isLocked() const
+{
+ return sectionItem() && sectionItem()->targetNode().isValid() && sectionItem()->targetNode().locked();
+}
+
void TransitionEditorBarItem::scrollOffsetChanged()
{
if (sectionItem())
@@ -637,7 +662,9 @@ void TransitionEditorBarItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
const auto p = event->pos();
QRectF left, right;
- if (handleRects(rect(), left, right)) {
+ if (isLocked() && rect().contains(p)) {
+ setCursor(QCursor(Qt::ForbiddenCursor));
+ } else if (handleRects(rect(), left, right)) {
if (left.contains(p) || right.contains(p)) {
if (cursor().shape() != Qt::SizeHorCursor)
setCursor(QCursor(Qt::SizeHorCursor));
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h
index d7ce78f56c..63828b52f5 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorsectionitem.h
@@ -54,6 +54,8 @@ public:
void itemMoved(const QPointF &start, const QPointF &end) override;
void commitPosition(const QPointF &point) override;
+ bool isLocked() const override;
+
protected:
void scrollOffsetChanged() override;
void paint(QPainter *painter,
@@ -106,6 +108,7 @@ public:
static void updateData(QGraphicsItem *item);
static void invalidateBar(QGraphicsItem *item);
static void updateDataForTarget(QGraphicsItem *item, const ModelNode &target, bool *b);
+ static void updateHeightForTarget(QGraphicsItem *item, const ModelNode &target);
void moveAllDurations(qreal offset);
void scaleAllDurations(qreal scale);
@@ -125,6 +128,7 @@ protected:
void contextMenuEvent(QGraphicsSceneContextMenuEvent *event) override;
private:
+ void updateHeight();
void invalidateHeight();
void invalidateProperties();
bool collapsed() const;
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
index ef83b17d90..e9cfb3fd72 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.cpp
@@ -142,6 +142,18 @@ void TransitionEditorView::selectedNodesChanged(const QList<ModelNode> & /*selec
}
+void TransitionEditorView::auxiliaryDataChanged(const ModelNode &modelNode,
+ const PropertyName &name,
+ const QVariant &data)
+{
+ if (name == QmlDesigner::lockedProperty && data.toBool() && modelNode.isValid()) {
+ for (const auto &node : modelNode.allSubModelNodesAndThisNode()) {
+ if (node.hasAuxiliaryData("transition_expanded"))
+ m_transitionEditorWidget->graphicsScene()->invalidateHeightForTarget(node);
+ }
+ }
+}
+
void TransitionEditorView::propertiesAboutToBeRemoved(
const QList<AbstractProperty> & /*propertyList */)
{
@@ -217,7 +229,7 @@ ModelNode TransitionEditorView::addNewTransition()
QStringList newlist = idPropertyList.value(targetId);
for (const QString &str :locList)
if (!newlist.contains(str))
- newlist.append(str);
+ newlist.append(str);
idPropertyList.insert(targetId, newlist);
} else {
if (!locList.isEmpty())
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h
index 1476a07353..857467e3cd 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorview.h
@@ -60,6 +60,9 @@ public:
PropertyChangeFlags propertyChange) override;
void selectedNodesChanged(const QList<ModelNode> &selectedNodeList,
const QList<ModelNode> &lastSelectedNodeList) override;
+ void auxiliaryDataChanged(const ModelNode &node,
+ const PropertyName &name,
+ const QVariant &data) override;
void propertiesAboutToBeRemoved(const QList<AbstractProperty> &propertyList) override;
void propertiesRemoved(const QList<AbstractProperty> &propertyList) override;
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp
index 3d7e4ec421..b2cb67c988 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.cpp
@@ -29,6 +29,7 @@
#include "transitioneditorpropertyitem.h"
#include "transitioneditortoolbar.h"
#include "transitioneditorview.h"
+#include "navigation2d.h"
#include <timelineeditor/easingcurvedialog.h>
#include <timelineeditor/timelineconstants.h>
@@ -63,6 +64,8 @@
#include <QVBoxLayout>
#include <QtGlobal>
+#include <cmath>
+
namespace QmlDesigner {
class Eventfilter : public QObject
@@ -87,7 +90,7 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view)
, m_toolbar(new TransitionEditorToolBar(this))
, m_rulerView(new QGraphicsView(this))
, m_graphicsView(new QGraphicsView(this))
- , m_scrollbar(new QScrollBar(this))
+ , m_scrollbar(new Navigation2dScrollBar(this))
, m_statusBar(new QLabel(this))
, m_transitionEditorView(view)
, m_graphicsScene(new TransitionEditorGraphicsScene(this))
@@ -126,6 +129,7 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view)
m_graphicsView->setFrameShape(QFrame::NoFrame);
m_graphicsView->setFrameShadow(QFrame::Plain);
m_graphicsView->setLineWidth(0);
+ m_graphicsView->setVerticalScrollBar(new Navigation2dScrollBar);
m_graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
m_graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
@@ -218,6 +222,14 @@ TransitionEditorWidget::TransitionEditorWidget(TransitionEditorView *view)
connect(m_addButton, &QPushButton::clicked, this, [this]() {
m_transitionEditorView->addNewTransition();
});
+
+ Navigation2dFilter *filter = new Navigation2dFilter(this, m_scrollbar);
+ connect(filter, &Navigation2dFilter::zoomChanged, [this](double scale, const QPointF& pos) {
+ int s = static_cast<int>(std::round(scale*100.));
+ double ps = m_graphicsScene->mapFromScene(pos.x());
+ m_graphicsScene->setZoom(std::clamp(m_graphicsScene->zoom() + s, 0, 100), ps);
+ });
+ installEventFilter(filter);
}
void TransitionEditorWidget::setTransitionActive(bool b)
@@ -258,7 +270,7 @@ void TransitionEditorWidget::connectToolbar()
this,
&TransitionEditorWidget::scroll);
- auto setRulerScaling = [this](int val) { m_graphicsScene->setRulerScaling(val); };
+ auto setRulerScaling = [this](int val) { m_graphicsScene->setZoom(val); };
connect(m_toolbar, &TransitionEditorToolBar::scaleFactorChanged, setRulerScaling);
auto setDuration = [this](int end) { graphicsScene()->setDuration(end); };
@@ -335,7 +347,7 @@ void TransitionEditorWidget::init()
m_toolbar->setDuration(duration);
- m_graphicsScene->setRulerScaling(40);
+ m_graphicsScene->setZoom(40);
}
void TransitionEditorWidget::updateData(const ModelNode &transition)
diff --git a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h
index dbbe87a1ff..f1c4174418 100644
--- a/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h
+++ b/src/plugins/qmldesigner/components/transitioneditor/transitioneditorwidget.h
@@ -37,7 +37,6 @@ QT_FORWARD_DECLARE_CLASS(QComboBox)
QT_FORWARD_DECLARE_CLASS(QGraphicsView)
QT_FORWARD_DECLARE_CLASS(QLabel)
QT_FORWARD_DECLARE_CLASS(QResizeEvent)
-QT_FORWARD_DECLARE_CLASS(QScrollBar)
QT_FORWARD_DECLARE_CLASS(QShowEvent)
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QPushButton)
@@ -48,6 +47,7 @@ class TransitionEditorView;
class TransitionEditorToolBar;
class TransitionEditorGraphicsScene;
class ModelNode;
+class Navigation2dScrollBar;
class TransitionEditorWidget : public QWidget
{
@@ -88,7 +88,7 @@ private:
QGraphicsView *m_graphicsView = nullptr;
- QScrollBar *m_scrollbar = nullptr;
+ Navigation2dScrollBar *m_scrollbar = nullptr;
QLabel *m_statusBar = nullptr;
diff --git a/src/plugins/qmldesigner/designercore/designercore-lib.pri b/src/plugins/qmldesigner/designercore/designercore-lib.pri
index 08842527ee..981025d1fb 100644
--- a/src/plugins/qmldesigner/designercore/designercore-lib.pri
+++ b/src/plugins/qmldesigner/designercore/designercore-lib.pri
@@ -14,6 +14,7 @@ include (../../../../share/qtcreator/qml/qmlpuppet/container/container.pri)
include (../../../../share/qtcreator/qml/qmlpuppet/types/types.pri)
SOURCES += $$PWD/model/abstractview.cpp \
+ $$PWD/imagecache/imagecachecollector.cpp \
$$PWD/model/rewriterview.cpp \
$$PWD/model/documentmessage.cpp \
$$PWD/metainfo/metainfo.cpp \
@@ -84,9 +85,15 @@ SOURCES += $$PWD/model/abstractview.cpp \
$$PWD/model/qmltimeline.cpp \
$$PWD/model/qmltimelinekeyframegroup.cpp \
$$PWD/model/annotation.cpp \
- $$PWD/model/stylesheetmerger.cpp
+ $$PWD/model/stylesheetmerger.cpp \
+ $$PWD/imagecache/imagecache.cpp \
+ $$PWD/imagecache/imagecacheconnectionmanager.cpp \
+ $$PWD/imagecache/imagecachegenerator.cpp \
+ $$PWD/imagecache/timestampprovider.cpp
+
HEADERS += $$PWD/include/qmldesignercorelib_global.h \
+ $$PWD/imagecache/imagecachecollector.h \
$$PWD/include/abstractview.h \
$$PWD/include/nodeinstanceview.h \
$$PWD/include/rewriterview.h \
@@ -162,7 +169,17 @@ HEADERS += $$PWD/include/qmldesignercorelib_global.h \
$$PWD/include/qmltimeline.h \
$$PWD/include/qmltimelinekeyframegroup.h \
$$PWD/include/annotation.h \
- $$PWD/include/stylesheetmerger.h
+ $$PWD/include/stylesheetmerger.h \
+ $$PWD/include/imagecache.h \
+ $$PWD/imagecache/imagecachecollectorinterface.h \
+ $$PWD/imagecache/imagecacheconnectionmanager.h \
+ $$PWD/imagecache/imagecachegeneratorinterface.h \
+ $$PWD/imagecache/imagecachestorageinterface.h \
+ $$PWD/imagecache/imagecachegenerator.h \
+ $$PWD/imagecache/imagecachestorage.h \
+ $$PWD/imagecache/timestampprovider.h \
+ $$PWD/imagecache/timestampproviderinterface.h
+
FORMS += \
$$PWD/instances/puppetdialog.ui
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp
new file mode 100644
index 0000000000..20409eb7fa
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecache.cpp
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** 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 "imagecache.h"
+
+#include "imagecachegenerator.h"
+#include "imagecachestorage.h"
+#include "timestampprovider.h"
+
+#include <thread>
+
+namespace QmlDesigner {
+
+ImageCache::ImageCache(ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider)
+ : m_storage(storage)
+ , m_generator(generator)
+ , m_timeStampProvider(timeStampProvider)
+{
+ m_backgroundThread = std::thread{[this] {
+ while (isRunning()) {
+ if (auto [hasEntry, entry] = getEntry(); hasEntry) {
+ request(entry.name,
+ entry.requestType,
+ std::move(entry.captureCallback),
+ std::move(entry.abortCallback),
+ m_storage,
+ m_generator,
+ m_timeStampProvider);
+ }
+
+ waitForEntries();
+ }
+ }};
+}
+
+ImageCache::~ImageCache()
+{
+ clean();
+ stopThread();
+ m_condition.notify_all();
+ if (m_backgroundThread.joinable())
+ m_backgroundThread.join();
+}
+
+void ImageCache::request(Utils::SmallStringView name,
+ ImageCache::RequestType requestType,
+ ImageCache::CaptureCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider)
+{
+ const auto timeStamp = timeStampProvider.timeStamp(name);
+ const auto entry = requestType == RequestType::Image ? storage.fetchImage(name, timeStamp)
+ : storage.fetchIcon(name, timeStamp);
+
+ if (entry.hasEntry) {
+ if (entry.image.isNull())
+ abortCallback();
+ else
+ captureCallback(entry.image);
+ } else {
+ generator.generateImage(name, timeStamp, std::move(captureCallback), std::move(abortCallback));
+ }
+}
+
+void ImageCache::requestImage(Utils::PathString name,
+ ImageCache::CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+{
+ addEntry(std::move(name), std::move(captureCallback), std::move(abortCallback), RequestType::Image);
+ m_condition.notify_all();
+}
+
+void ImageCache::requestIcon(Utils::PathString name,
+ ImageCache::CaptureCallback captureCallback,
+ ImageCache::AbortCallback abortCallback)
+{
+ addEntry(std::move(name), std::move(captureCallback), std::move(abortCallback), RequestType::Icon);
+ m_condition.notify_all();
+}
+
+void ImageCache::clean()
+{
+ clearEntries();
+ m_generator.clean();
+}
+
+std::tuple<bool, ImageCache::Entry> ImageCache::getEntry()
+{
+ std::unique_lock lock{m_mutex};
+
+ if (m_entries.empty())
+ return {false, Entry{}};
+
+ Entry entry = m_entries.back();
+ m_entries.pop_back();
+
+ return {true, entry};
+}
+
+void ImageCache::addEntry(Utils::PathString &&name,
+ ImageCache::CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback,
+ RequestType requestType)
+{
+ std::unique_lock lock{m_mutex};
+
+ m_entries.emplace_back(std::move(name),
+ std::move(captureCallback),
+ std::move(abortCallback),
+ requestType);
+}
+
+void ImageCache::clearEntries()
+{
+ std::unique_lock lock{m_mutex};
+ for (Entry &entry : m_entries)
+ entry.abortCallback();
+ m_entries.clear();
+}
+
+void ImageCache::waitForEntries()
+{
+ std::unique_lock lock{m_mutex};
+ if (m_entries.empty())
+ m_condition.wait(lock, [&] { return m_entries.size() || m_finishing; });
+}
+
+void ImageCache::stopThread()
+{
+ std::unique_lock lock{m_mutex};
+ m_finishing = true;
+}
+
+bool ImageCache::isRunning()
+{
+ std::unique_lock lock{m_mutex};
+ return !m_finishing;
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
new file mode 100644
index 0000000000..1bb7262d17
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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 "imagecachecollector.h"
+#include "imagecacheconnectionmanager.h"
+
+#include <metainfo.h>
+#include <model.h>
+#include <nodeinstanceview.h>
+#include <plaintexteditmodifier.h>
+#include <rewriterview.h>
+
+#include <projectexplorer/project.h>
+#include <projectexplorer/target.h>
+#include <utils/fileutils.h>
+
+#include <QPlainTextEdit>
+
+namespace QmlDesigner {
+
+namespace {
+
+QByteArray fileToByteArray(QString const &filename)
+{
+ QFile file(filename);
+ QFileInfo fleInfo(file);
+
+ if (fleInfo.exists() && file.open(QFile::ReadOnly))
+ return file.readAll();
+
+ return {};
+}
+
+QString fileToString(const QString &filename)
+{
+ return QString::fromUtf8(fileToByteArray(filename));
+}
+
+} // namespace
+
+ImageCacheCollector::ImageCacheCollector(ImageCacheConnectionManager &connectionManager)
+ : m_connectionManager{connectionManager}
+{}
+
+ImageCacheCollector::~ImageCacheCollector() = default;
+
+void ImageCacheCollector::start(Utils::SmallStringView name,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+{
+ RewriterView rewriterView{RewriterView::Amend, nullptr};
+ NodeInstanceView nodeInstanceView{m_connectionManager};
+
+ const QString filePath{name};
+ std::unique_ptr<Model> model{QmlDesigner::Model::create("QtQuick/Item", 2, 1)};
+ model->setFileUrl(QUrl::fromLocalFile(filePath));
+
+ auto textDocument = std::make_unique<QTextDocument>(fileToString(filePath));
+
+ auto modifier = std::make_unique<NotIndentingTextEditModifier>(textDocument.get(),
+ QTextCursor{textDocument.get()});
+
+ rewriterView.setTextModifier(modifier.get());
+
+ model->setRewriterView(&rewriterView);
+
+ if (rewriterView.inErrorState() || !rewriterView.rootModelNode().metaInfo().isGraphicalItem()) {
+ abortCallback();
+ return;
+ }
+
+ m_connectionManager.setCallback(std::move(captureCallback));
+
+ nodeInstanceView.setTarget(m_target.get());
+ nodeInstanceView.setCrashCallback(abortCallback);
+ model->setNodeInstanceView(&nodeInstanceView);
+
+ bool capturedDataArrived = m_connectionManager.waitForCapturedData();
+
+ m_connectionManager.setCallback({});
+ m_connectionManager.setCrashCallback({});
+
+ model->setNodeInstanceView({});
+ model->setRewriterView({});
+
+ if (!capturedDataArrived)
+ abortCallback();
+}
+
+void ImageCacheCollector::setTarget(std::unique_ptr<ProjectExplorer::Target> target)
+{
+ m_target = std::move(target);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
new file mode 100644
index 0000000000..e39f95f573
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollector.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** 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 "imagecachecollectorinterface.h"
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+class QTextDocument;
+QT_END_NAMESPACE
+
+namespace ProjectExplorer {
+class Target;
+}
+
+namespace QmlDesigner {
+
+class Model;
+class NotIndentingTextEditModifier;
+class ImageCacheConnectionManager;
+class RewriterView;
+class NodeInstanceView;
+
+class ImageCacheCollector final : public ImageCacheCollectorInterface
+{
+public:
+ ImageCacheCollector(ImageCacheConnectionManager &connectionManager);
+
+ ~ImageCacheCollector();
+
+ void start(Utils::SmallStringView filePath,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback) override;
+
+ void setTarget(std::unique_ptr<ProjectExplorer::Target> target);
+
+private:
+ ImageCacheConnectionManager &m_connectionManager;
+ std::unique_ptr<ProjectExplorer::Target> m_target;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
new file mode 100644
index 0000000000..e6528f2ec3
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachecollectorinterface.h
@@ -0,0 +1,48 @@
+/****************************************************************************
+**
+** 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 <utils/smallstringview.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class ImageCacheCollectorInterface
+{
+public:
+ using CaptureCallback = std::function<void(QImage &&image)>;
+ using AbortCallback = std::function<void()>;
+
+ virtual void start(Utils::SmallStringView filePath,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback)
+ = 0;
+
+protected:
+ ~ImageCacheCollectorInterface() = default;
+};
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
new file mode 100644
index 0000000000..2c7982bd1f
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#include "imagecacheconnectionmanager.h"
+
+#include <captureddatacommand.h>
+
+#include <QLocalSocket>
+
+namespace QmlDesigner {
+
+ImageCacheConnectionManager::ImageCacheConnectionManager()
+{
+ connections().emplace_back("Capture icon", "captureiconmode");
+}
+
+void ImageCacheConnectionManager::setCallback(ImageCacheConnectionManager::Callback callback)
+{
+ m_captureCallback = std::move(callback);
+}
+
+bool ImageCacheConnectionManager::waitForCapturedData()
+{
+ if (connections().empty())
+ return false;
+
+ disconnect(connections().front().socket.get(), &QIODevice::readyRead, nullptr, nullptr);
+
+ while (!m_capturedDataArrived) {
+ bool dataArrived = connections().front().socket->waitForReadyRead(600000);
+
+ if (!dataArrived)
+ return false;
+
+ readDataStream(connections().front());
+ }
+
+ m_capturedDataArrived = false;
+
+ return true;
+}
+
+void ImageCacheConnectionManager::dispatchCommand(const QVariant &command,
+ ConnectionManagerInterface::Connection &)
+{
+ static const int capturedDataCommandType = QMetaType::type("CapturedDataCommand");
+
+ if (command.userType() == capturedDataCommandType) {
+ m_captureCallback(command.value<CapturedDataCommand>().image);
+ m_capturedDataArrived = true;
+ }
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h
new file mode 100644
index 0000000000..5788f6f31d
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecacheconnectionmanager.h
@@ -0,0 +1,45 @@
+/****************************************************************************
+**
+** 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.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <designercore/instances/connectionmanager.h>
+
+namespace QmlDesigner {
+
+class CapturedDataCommand;
+
+class ImageCacheConnectionManager : public ConnectionManager
+{
+public:
+ using Callback = std::function<void(QImage &&)>;
+
+ ImageCacheConnectionManager();
+
+ void setCallback(Callback captureCallback);
+
+ bool waitForCapturedData();
+
+protected:
+ void dispatchCommand(const QVariant &command, Connection &connection) override;
+
+private:
+ Callback m_captureCallback;
+ bool m_capturedDataArrived = false;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
new file mode 100644
index 0000000000..a6783fbf48
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.cpp
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** 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 "imagecachegenerator.h"
+
+#include "imagecachecollectorinterface.h"
+#include "imagecachestorage.h"
+
+#include <QThread>
+
+namespace QmlDesigner {
+
+ImageCacheGenerator::~ImageCacheGenerator()
+{
+ std::lock_guard threadLock{*m_threadMutex.get()};
+
+ if (m_backgroundThread)
+ m_backgroundThread->wait();
+
+ clean();
+}
+
+void ImageCacheGenerator::generateImage(Utils::SmallStringView name,
+ Sqlite::TimeStamp timeStamp,
+ ImageCacheGeneratorInterface::CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback)
+{
+ {
+ std::lock_guard lock{m_dataMutex};
+ m_tasks.emplace_back(name, timeStamp, std::move(captureCallback), std::move(abortCallback));
+ }
+
+ startGenerationAsynchronously();
+}
+
+void ImageCacheGenerator::clean()
+{
+ std::lock_guard dataLock{m_dataMutex};
+ m_tasks.clear();
+}
+
+class ReleaseProcessing
+{
+public:
+ ReleaseProcessing(std::atomic_flag &processing)
+ : m_processing(processing)
+ {
+ m_processing.test_and_set(std::memory_order_acquire);
+ }
+
+ ~ReleaseProcessing() { m_processing.clear(std::memory_order_release); }
+
+private:
+ std::atomic_flag &m_processing;
+};
+
+void ImageCacheGenerator::startGeneration(std::shared_ptr<std::mutex> threadMutex)
+{
+ ReleaseProcessing guard(m_processing);
+
+ while (true) {
+ Task task;
+
+ {
+ std::unique_lock threadLock{*threadMutex.get(), std::defer_lock_t{}};
+
+ if (!threadLock.try_lock())
+ return;
+
+ std::lock_guard dataLock{m_dataMutex};
+
+ if (m_tasks.empty()) {
+ m_storage.walCheckpointFull();
+ return;
+ }
+
+ task = std::move(m_tasks.back());
+
+ m_tasks.pop_back();
+ }
+
+ m_collector.start(
+ task.filePath,
+ [this, threadMutex, task](QImage &&image) {
+ std::unique_lock lock{*threadMutex.get(), std::defer_lock_t{}};
+
+ if (!lock.try_lock())
+ return;
+
+ if (threadMutex.use_count() == 1)
+ return;
+
+ if (image.isNull())
+ task.abortCallback();
+ else
+ task.captureCallback(image);
+
+ m_storage.storeImage(std::move(task.filePath), task.timeStamp, image);
+ },
+ [this, threadMutex, task] {
+ std::unique_lock lock{*threadMutex.get(), std::defer_lock_t{}};
+
+ if (!lock.try_lock())
+ return;
+
+ if (threadMutex.use_count() == 1)
+ return;
+
+ task.abortCallback();
+ m_storage.storeImage(std::move(task.filePath), task.timeStamp, {});
+ });
+ }
+}
+
+void ImageCacheGenerator::startGenerationAsynchronously()
+{
+ if (m_processing.test_and_set(std::memory_order_acquire))
+ return;
+
+ std::unique_lock lock{*m_threadMutex.get(), std::defer_lock_t{}};
+
+ if (!lock.try_lock())
+ return;
+
+ if (m_backgroundThread)
+ m_backgroundThread->wait();
+
+ m_backgroundThread.reset(QThread::create(
+ [this](std::shared_ptr<std::mutex> threadMutex) { startGeneration(threadMutex); },
+ m_threadMutex));
+ m_backgroundThread->start();
+ // m_backgroundThread = std::thread(
+ // [this](std::shared_ptr<std::mutex> threadMutex) { startGeneration(threadMutex); },
+ // m_threadMutex);
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
new file mode 100644
index 0000000000..207622714b
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegenerator.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** 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 "imagecachegeneratorinterface.h"
+
+#include <utils/smallstring.h>
+
+#include <QThread>
+
+#include <memory>
+#include <mutex>
+
+QT_BEGIN_NAMESPACE
+class QPlainTextEdit;
+QT_END_NAMESPACE
+
+namespace QmlDesigner {
+
+class ImageCacheCollectorInterface;
+class ImageCacheStorageInterface;
+
+class ImageCacheGenerator final : public ImageCacheGeneratorInterface
+{
+public:
+ ImageCacheGenerator(ImageCacheCollectorInterface &collector, ImageCacheStorageInterface &storage)
+ : m_collector{collector}
+ , m_storage(storage)
+ {}
+
+ ~ImageCacheGenerator();
+
+ void generateImage(Utils::SmallStringView filePath,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback) override;
+ void clean() override;
+
+private:
+ struct Task
+ {
+ Task() = default;
+ Task(Utils::SmallStringView filePath,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback)
+ : filePath(filePath)
+ , captureCallback(std::move(captureCallback))
+ , abortCallback(std::move(abortCallback))
+ , timeStamp(timeStamp)
+ {}
+
+ Utils::PathString filePath;
+ CaptureCallback captureCallback;
+ AbortCallback abortCallback;
+ Sqlite::TimeStamp timeStamp;
+ };
+
+ void startGeneration(std::shared_ptr<std::mutex> threadMutex);
+ void startGenerationAsynchronously();
+
+private:
+ std::unique_ptr<QThread> m_backgroundThread;
+ std::mutex m_dataMutex;
+ std::shared_ptr<std::mutex> m_threadMutex{std::make_shared<std::mutex>()};
+ std::vector<Task> m_tasks;
+ ImageCacheCollectorInterface &m_collector;
+ ImageCacheStorageInterface &m_storage;
+ std::atomic_flag m_processing = ATOMIC_FLAG_INIT;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
new file mode 100644
index 0000000000..26b9621995
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachegeneratorinterface.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** 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 <sqlitetimestamp.h>
+#include <utils/smallstringview.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class ImageCacheGeneratorInterface
+{
+public:
+ using CaptureCallback = std::function<void(const QImage &image)>;
+ using AbortCallback = std::function<void()>;
+
+ virtual void generateImage(Utils::SmallStringView name,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback)
+ = 0;
+
+ virtual void clean() = 0;
+
+protected:
+ ~ImageCacheGeneratorInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
new file mode 100644
index 0000000000..90900cf19e
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorage.h
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** 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 "imagecachestorageinterface.h"
+
+#include <createtablesqlstatementbuilder.h>
+
+#include <sqliteblob.h>
+#include <sqlitereadstatement.h>
+#include <sqlitetable.h>
+#include <sqlitetransaction.h>
+#include <sqlitewritestatement.h>
+
+#include <QBuffer>
+#include <QImageReader>
+#include <QImageWriter>
+
+namespace QmlDesigner {
+
+template<typename DatabaseType>
+class ImageCacheStorage : public ImageCacheStorageInterface
+{
+public:
+ using ReadStatement = typename DatabaseType::ReadStatement;
+ using WriteStatement = typename DatabaseType::WriteStatement;
+
+ ImageCacheStorage(DatabaseType &database)
+ : database(database)
+ {
+ transaction.commit();
+ }
+
+ Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
+ {
+ try {
+ Sqlite::DeferredTransaction transaction{database};
+
+ auto optionalBlob = selectImageStatement.template value<Sqlite::ByteArrayBlob>(
+ name, minimumTimeStamp.value);
+
+ transaction.commit();
+
+ if (optionalBlob) {
+ QBuffer buffer{&optionalBlob->byteArray};
+ QImageReader reader{&buffer, "PNG"};
+
+ return Entry{reader.read(), true};
+ }
+
+ return {};
+
+ } catch (const Sqlite::StatementIsBusy &) {
+ return fetchImage(name, minimumTimeStamp);
+ }
+ }
+
+ Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const override
+ {
+ try {
+ Sqlite::DeferredTransaction transaction{database};
+
+ auto optionalBlob = selectIconStatement.template value<Sqlite::ByteArrayBlob>(
+ name, minimumTimeStamp.value);
+
+ transaction.commit();
+
+ if (optionalBlob) {
+ QBuffer buffer{&optionalBlob->byteArray};
+ QImageReader reader{&buffer, "PNG"};
+
+ return Entry{reader.read(), true};
+ }
+
+ return {};
+
+ } catch (const Sqlite::StatementIsBusy &) {
+ return fetchIcon(name, minimumTimeStamp);
+ }
+ }
+
+ void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) override
+ {
+ try {
+ Sqlite::ImmediateTransaction transaction{database};
+
+ if (image.isNull()) {
+ upsertImageStatement.write(name,
+ newTimeStamp.value,
+ Sqlite::NullValue{},
+ Sqlite::NullValue{});
+ } else {
+ QSize iconSize = image.size().scaled(QSize{96, 96}.boundedTo(image.size()),
+ Qt::KeepAspectRatio);
+ QImage icon = image.scaled(iconSize);
+ upsertImageStatement.write(name,
+ newTimeStamp.value,
+ Sqlite::BlobView{createImageBuffer(image)->data()},
+ Sqlite::BlobView{createImageBuffer(icon)->data()});
+ }
+ transaction.commit();
+
+ } catch (const Sqlite::StatementIsBusy &) {
+ return storeImage(name, newTimeStamp, image);
+ }
+ }
+
+ void walCheckpointFull()
+ {
+ try {
+ database.walCheckpointFull();
+ } catch (const Sqlite::StatementIsBusy &) {
+ return walCheckpointFull();
+ }
+ }
+
+private:
+ class Initializer
+ {
+ public:
+ Initializer(DatabaseType &database)
+ {
+ if (!database.isInitialized()) {
+ Sqlite::ExclusiveTransaction transaction{database};
+
+ createImagesTable(database);
+
+ transaction.commit();
+
+ database.setIsInitialized(true);
+
+ database.walCheckpointFull();
+ }
+ }
+
+ void createImagesTable(DatabaseType &database)
+ {
+ Sqlite::Table table;
+ table.setUseIfNotExists(true);
+ table.setName("images");
+ table.addColumn("id", Sqlite::ColumnType::Integer, {Sqlite::PrimaryKey{}});
+ table.addColumn("name", Sqlite::ColumnType::Text, {Sqlite::NotNull{}, Sqlite::Unique{}});
+ table.addColumn("mtime", Sqlite::ColumnType::Integer);
+ table.addColumn("image", Sqlite::ColumnType::Blob);
+ table.addColumn("icon", Sqlite::ColumnType::Blob);
+
+ table.initialize(database);
+ }
+ };
+
+ std::unique_ptr<QBuffer> createImageBuffer(const QImage &image)
+ {
+ auto buffer = std::make_unique<QBuffer>();
+ buffer->open(QIODevice::WriteOnly);
+ QImageWriter writer{buffer.get(), "PNG"};
+ writer.write(image);
+
+ return buffer;
+ }
+
+public:
+ DatabaseType &database;
+ Initializer initializer{database};
+ Sqlite::ImmediateNonThrowingDestructorTransaction transaction{database};
+ mutable ReadStatement selectImageStatement{
+ "SELECT image FROM images WHERE name=?1 AND mtime >= ?2", database};
+ mutable ReadStatement selectIconStatement{
+ "SELECT icon FROM images WHERE name=?1 AND mtime >= ?2", database};
+ WriteStatement upsertImageStatement{
+ "INSERT INTO images(name, mtime, image, icon) VALUES (?1, ?2, ?3, ?4) ON "
+ "CONFLICT(name) DO UPDATE SET mtime=excluded.mtime, image=excluded.image, "
+ "icon=excluded.icon",
+ database};
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
new file mode 100644
index 0000000000..97ace6efe6
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/imagecachestorageinterface.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 <QImage>
+
+#include <sqlitetimestamp.h>
+#include <utils/smallstringview.h>
+
+namespace QmlDesigner {
+namespace Internal {
+class ImageCacheStorageEntry
+{
+ public:
+ QImage image;
+ bool hasEntry = false;
+};
+
+} // namespace Internal
+
+class ImageCacheStorageInterface
+{
+public:
+ using Entry = Internal::ImageCacheStorageEntry;
+
+ virtual Entry fetchImage(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0;
+ virtual Entry fetchIcon(Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp) const = 0;
+ virtual void storeImage(Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image) = 0;
+ virtual void walCheckpointFull() = 0;
+
+protected:
+ ~ImageCacheStorageInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp
new file mode 100644
index 0000000000..99573f175f
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.cpp
@@ -0,0 +1,38 @@
+/****************************************************************************
+**
+** 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 "timestampprovider.h"
+
+#include <QDateTime>
+#include <QFileInfo>
+
+namespace QmlDesigner {
+
+Sqlite::TimeStamp TimeStampProvider::timeStamp(Utils::SmallStringView name) const
+{
+ return QFileInfo{QString{name}}.lastModified().toSecsSinceEpoch();
+}
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h
new file mode 100644
index 0000000000..8acc5fcb58
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/timestampprovider.h
@@ -0,0 +1,39 @@
+/****************************************************************************
+**
+** 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 "timestampproviderinterface.h"
+
+namespace QmlDesigner {
+
+class TimeStampProvider : public TimeStampProviderInterface
+{
+public:
+ Sqlite::TimeStamp timeStamp(Utils::SmallStringView name) const override;
+};
+
+} // namespace QmlDesigner
+
diff --git a/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h b/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h
new file mode 100644
index 0000000000..33cffc9b49
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/imagecache/timestampproviderinterface.h
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** 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 <sqlitetimestamp.h>
+#include <utils/smallstringview.h>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class TimeStampProviderInterface
+{
+public:
+ virtual Sqlite::TimeStamp timeStamp(Utils::SmallStringView name) const = 0;
+
+protected:
+ ~TimeStampProviderInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/abstractview.h b/src/plugins/qmldesigner/designercore/include/abstractview.h
index 12cb457571..6b0276c8b1 100644
--- a/src/plugins/qmldesigner/designercore/include/abstractview.h
+++ b/src/plugins/qmldesigner/designercore/include/abstractview.h
@@ -147,6 +147,7 @@ public:
void setSelectedModelNodes(const QList<ModelNode> &selectedNodeList);
void setSelectedModelNode(const ModelNode &modelNode);
+
void selectModelNode(const ModelNode &node);
void deselectModelNode(const ModelNode &node);
void clearSelectedModelNodes();
diff --git a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h
index e20566724b..640ff367bc 100644
--- a/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h
+++ b/src/plugins/qmldesigner/designercore/include/basetexteditmodifier.h
@@ -49,6 +49,9 @@ public:
bool renameId(const QString &oldId, const QString &newId) override;
bool moveToComponent(int nodeOffset) override;
QStringList autoComplete(QTextDocument *textDocument, int position, bool explicitComplete) override;
+
+private:
+ TextEditor::TextEditorWidget *m_textEdit;
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h b/src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h
new file mode 100644
index 0000000000..c0ba644fdd
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/include/filesystemfacadeinterface.h
@@ -0,0 +1,37 @@
+/****************************************************************************
+**
+** 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
+
+namespace QmlDesigner {
+
+class FileSystemFacadeInterface
+{
+public:
+protected:
+ ~FileSystemFacadeInterface() = default;
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/imagecache.h b/src/plugins/qmldesigner/designercore/include/imagecache.h
new file mode 100644
index 0000000000..4ac360c2a5
--- /dev/null
+++ b/src/plugins/qmldesigner/designercore/include/imagecache.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** 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 <utils/smallstring.h>
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+
+#include <QImage>
+
+namespace QmlDesigner {
+
+class TimeStampProviderInterface;
+class ImageCacheStorageInterface;
+class ImageCacheGeneratorInterface;
+
+class ImageCache
+{
+public:
+ using CaptureCallback = std::function<void(const QImage &)>;
+ using AbortCallback = std::function<void()>;
+
+ ~ImageCache();
+
+ ImageCache(ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider);
+
+ void requestImage(Utils::PathString name,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback);
+ void requestIcon(Utils::PathString name,
+ CaptureCallback captureCallback,
+ AbortCallback abortCallback);
+
+ void clean();
+
+private:
+ enum class RequestType { Image, Icon };
+ struct Entry
+ {
+ Entry() = default;
+ Entry(Utils::PathString name,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback,
+ RequestType requestType)
+ : name{std::move(name)}
+ , captureCallback{std::move(captureCallback)}
+ , abortCallback{std::move(abortCallback)}
+ , requestType{requestType}
+ {}
+
+ Utils::PathString name;
+ CaptureCallback captureCallback;
+ AbortCallback abortCallback;
+ RequestType requestType = RequestType::Image;
+ };
+
+ std::tuple<bool, Entry> getEntry();
+ void addEntry(Utils::PathString &&name,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback,
+ RequestType requestType);
+ void clearEntries();
+ void waitForEntries();
+ void stopThread();
+ bool isRunning();
+ static void request(Utils::SmallStringView name,
+ ImageCache::RequestType requestType,
+ ImageCache::CaptureCallback captureCallback,
+ ImageCache::AbortCallback abortCallback,
+ ImageCacheStorageInterface &storage,
+ ImageCacheGeneratorInterface &generator,
+ TimeStampProviderInterface &timeStampProvider);
+
+private:
+ std::vector<Entry> m_entries;
+ mutable std::mutex m_mutex;
+ std::condition_variable m_condition;
+ std::thread m_backgroundThread;
+ ImageCacheStorageInterface &m_storage;
+ ImageCacheGeneratorInterface &m_generator;
+ TimeStampProviderInterface &m_timeStampProvider;
+ bool m_finishing{false};
+};
+
+} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/include/model.h b/src/plugins/qmldesigner/designercore/include/model.h
index 4d97eb8694..20611736e9 100644
--- a/src/plugins/qmldesigner/designercore/include/model.h
+++ b/src/plugins/qmldesigner/designercore/include/model.h
@@ -43,6 +43,7 @@ namespace QmlDesigner {
namespace Internal {
class ModelPrivate;
class WriteLocker;
+class NodeMetaInfoPrivate;
} //Internal
class AnchorLine;
@@ -68,6 +69,7 @@ class QMLDESIGNERCORE_EXPORT Model : public QObject
friend class QmlDesigner::AbstractView;
friend class Internal::ModelPrivate;
friend class Internal::WriteLocker;
+ friend class QmlDesigner::Internal::NodeMetaInfoPrivate;
Q_OBJECT
@@ -118,6 +120,8 @@ public:
QList<ModelNode> selectedNodes(AbstractView *view) const;
+ void clearMetaInfoCache();
+
protected:
Model();
diff --git a/src/plugins/qmldesigner/designercore/include/modelnode.h b/src/plugins/qmldesigner/designercore/include/modelnode.h
index 3197e746ab..19bed05a25 100644
--- a/src/plugins/qmldesigner/designercore/include/modelnode.h
+++ b/src/plugins/qmldesigner/designercore/include/modelnode.h
@@ -65,7 +65,9 @@ QMLDESIGNERCORE_EXPORT QList<Internal::InternalNodePointer> toInternalNodeList(c
using PropertyListType = QList<QPair<PropertyName, QVariant> >;
-class QMLDESIGNERCORE_EXPORT ModelNode
+static const PropertyName lockedProperty = {("locked")};
+
+class QMLDESIGNERCORE_EXPORT ModelNode
{
friend QMLDESIGNERCORE_EXPORT bool operator ==(const ModelNode &firstNode, const ModelNode &secondNode);
friend QMLDESIGNERCORE_EXPORT bool operator !=(const ModelNode &firstNode, const ModelNode &secondNode);
@@ -216,6 +218,11 @@ public:
void setGlobalStatus(const GlobalAnnotationStatus &status);
void removeGlobalStatus();
+ bool locked() const;
+ void setLocked(bool value);
+
+ static bool isThisOrAncestorLocked(const ModelNode &node);
+
qint32 internalId() const;
void setNodeSource(const QString&);
@@ -241,6 +248,8 @@ public:
private: // functions
Internal::InternalNodePointer internalNode() const;
+ void removeLocked();
+ bool hasLocked() const;
private: // variables
Internal::InternalNodePointer m_internalNode;
diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
index a570a0e690..0e86ad11ee 100644
--- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
@@ -146,6 +146,11 @@ public:
QVariant previewImageDataForGenericNode(const ModelNode &modelNode, const ModelNode &renderNode);
QVariant previewImageDataForImageNode(const ModelNode &modelNode);
+ void setCrashCallback(std::function<void()> crashCallback)
+ {
+ m_crashCallback = std::move(crashCallback);
+ }
+
protected:
void timerEvent(QTimerEvent *event) override;
@@ -231,6 +236,7 @@ private:
// key: fileUrl value: (key: instance qml id, value: related tool states)
QHash<QUrl, QHash<QString, QVariantMap>> m_edit3DToolStates;
+ std::function<void()> m_crashCallback{[this] { handleCrash(); }};
};
} // namespace ProxyNodeInstanceView
diff --git a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
index b5e2e7626c..869d327f5f 100644
--- a/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
+++ b/src/plugins/qmldesigner/designercore/include/nodemetainfo.h
@@ -107,8 +107,6 @@ public:
QString importDirectoryPath() const;
- static void clearCache();
-
private:
QSharedPointer<Internal::NodeMetaInfoPrivate> m_privateData;
};
diff --git a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h
index 500bffd6fd..c18b4d8cfb 100644
--- a/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h
+++ b/src/plugins/qmldesigner/designercore/include/plaintexteditmodifier.h
@@ -47,6 +47,7 @@ private:
public:
PlainTextEditModifier(QPlainTextEdit *textEdit);
+ PlainTextEditModifier(QTextDocument *document, const QTextCursor &textCursor);
~PlainTextEditModifier() override;
QTextDocument *textDocument() const override;
@@ -76,20 +77,17 @@ public:
bool moveToComponent(int /* nodeOffset */) override
{ return false; }
-protected:
- QPlainTextEdit *plainTextEdit() const
- { return m_textEdit; }
-
private:
void textEditChanged();
void runRewriting(Utils::ChangeSet *writer);
private:
- Utils::ChangeSet *m_changeSet;
- QPlainTextEdit *m_textEdit;
- bool m_changeSignalsEnabled;
- bool m_pendingChangeSignal;
- bool m_ongoingTextChange;
+ Utils::ChangeSet *m_changeSet = nullptr;
+ QTextDocument *m_textDocument;
+ QTextCursor m_textCursor;
+ bool m_changeSignalsEnabled{true};
+ bool m_pendingChangeSignal{false};
+ bool m_ongoingTextChange{false};
};
class QMLDESIGNERCORE_EXPORT NotIndentingTextEditModifier: public PlainTextEditModifier
@@ -99,6 +97,10 @@ public:
: PlainTextEditModifier(textEdit)
{}
+ NotIndentingTextEditModifier(QTextDocument *document, const QTextCursor &textCursor)
+ : PlainTextEditModifier{document, textCursor}
+ {}
+
void indent(int /*offset*/, int /*length*/) override
{}
void indentLines(int /*offset*/, int /*length*/) override
diff --git a/src/plugins/qmldesigner/designercore/include/rewriterview.h b/src/plugins/qmldesigner/designercore/include/rewriterview.h
index 0601da3acf..5eae2f221f 100644
--- a/src/plugins/qmldesigner/designercore/include/rewriterview.h
+++ b/src/plugins/qmldesigner/designercore/include/rewriterview.h
@@ -190,7 +190,6 @@ protected: // functions
private: //variables
ModelNode nodeAtTextCursorPositionHelper(const ModelNode &root, int cursorPosition) const;
void setupCanonicalHashes() const;
- void handleLibraryInfoUpdate();
TextModifier *m_textModifier = nullptr;
int transactionLevel = 0;
@@ -211,7 +210,6 @@ private: //variables
std::function<void(bool)> m_setWidgetStatusCallback;
bool m_hasIncompleteTypeInformation = false;
bool m_restoringAuxData = false;
- bool m_modelAttachPending = false;
mutable QHash<int, ModelNode> m_canonicalIntModelNode;
mutable QHash<ModelNode, int> m_canonicalModelNodeInt;
diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
index b7a2cc282e..76d641fe30 100644
--- a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
@@ -33,11 +33,12 @@
namespace QmlDesigner {
-void BaseConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void BaseConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &,
- ProjectExplorer::Target *)
+ ProjectExplorer::Target *,
+ AbstractView *view)
{
- m_nodeInstanceServerProxy = nodeInstanceServerProxy;
+ m_nodeInstanceServer = nodeInstanceServer;
m_isActive = true;
}
@@ -47,7 +48,14 @@ void BaseConnectionManager::shutDown()
writeCommand(QVariant::fromValue(EndPuppetCommand()));
- m_nodeInstanceServerProxy = nullptr;
+ m_nodeInstanceServer = nullptr;
+}
+
+void BaseConnectionManager::setCrashCallback(std::function<void()> callback)
+{
+ std::lock_guard<std::mutex> lock{m_callbackMutex};
+
+ m_crashCallback = std::move(callback);
}
bool BaseConnectionManager::isActive() const
@@ -85,7 +93,7 @@ void BaseConnectionManager::dispatchCommand(const QVariant &command, Connection
if (!isActive())
return;
- m_nodeInstanceServerProxy->dispatchCommand(command);
+ m_nodeInstanceServer->dispatchCommand(command);
}
void BaseConnectionManager::readDataStream(Connection &connection)
@@ -123,5 +131,12 @@ void BaseConnectionManager::readDataStream(Connection &connection)
for (const QVariant &command : commandList)
dispatchCommand(command, connection);
}
+
+void BaseConnectionManager::callCrashCallback()
+{
+ std::lock_guard<std::mutex> lock{m_callbackMutex};
+
+ m_crashCallback();
+}
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h
index 83a41a2bd8..fca035682f 100644
--- a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.h
@@ -29,6 +29,8 @@
#include <QProcess>
+#include <mutex>
+
QT_BEGIN_NAMESPACE
class QLocalSocket;
QT_END_NAMESPACE
@@ -40,7 +42,6 @@ class Target;
namespace QmlDesigner {
class AbstractView;
-class NodeInstanceServerProxy;
class QMLDESIGNERCORE_EXPORT BaseConnectionManager : public QObject, public ConnectionManagerInterface
{
@@ -49,11 +50,14 @@ class QMLDESIGNERCORE_EXPORT BaseConnectionManager : public QObject, public Conn
public:
BaseConnectionManager() = default;
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
void shutDown() override;
+ void setCrashCallback(std::function<void()> callback) override;
+
bool isActive() const;
protected:
@@ -61,15 +65,19 @@ protected:
virtual void showCannotConnectToPuppetWarningAndSwitchToEditMode();
using ConnectionManagerInterface::processFinished;
void processFinished();
- void writeCommandToIODevice(const QVariant &command,
- QIODevice *ioDevice,
- unsigned int commandCounter);
+ static void writeCommandToIODevice(const QVariant &command,
+ QIODevice *ioDevice,
+ unsigned int commandCounter);
void readDataStream(Connection &connection);
- NodeInstanceServerProxy *nodeInstanceServerProxy() const { return m_nodeInstanceServerProxy; }
+ NodeInstanceServerInterface *nodeInstanceServer() const { return m_nodeInstanceServer; }
+
+ void callCrashCallback();
private:
- NodeInstanceServerProxy *m_nodeInstanceServerProxy{};
+ std::mutex m_callbackMutex;
+ std::function<void()> m_crashCallback;
+ NodeInstanceServerInterface *m_nodeInstanceServer{};
bool m_isActive = false;
};
diff --git a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
index adf395d874..90f4226217 100644
--- a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.cpp
@@ -34,11 +34,12 @@
namespace QmlDesigner {
-void CapturingConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void CapturingConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
{
- InteractiveConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target);
+ InteractiveConnectionManager::setUp(nodeInstanceServer, qrcMappingString, target, view);
int indexOfCapturePuppetStream = QCoreApplication::arguments().indexOf(
"-capture-puppet-stream");
@@ -72,7 +73,7 @@ void CapturingConnectionManager::writeCommand(const QVariant &command)
if (m_captureFileForTest.isWritable()) {
qDebug() << "command name: " << QMetaType::typeName(command.userType());
- writeCommandToIODevice(command, &m_captureFileForTest, m_writeCommandCounter);
+ writeCommandToIODevice(command, &m_captureFileForTest, writeCommandCounter());
qDebug() << "\tcatpure file offset: " << m_captureFileForTest.pos();
}
}
diff --git a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h
index 1bedef440b..e13d2e254a 100644
--- a/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/capturingconnectionmanager.h
@@ -32,9 +32,10 @@ namespace QmlDesigner {
class QMLDESIGNERCORE_EXPORT CapturingConnectionManager : public InteractiveConnectionManager
{
public:
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
void processFinished(int exitCode, QProcess::ExitStatus exitStatus) override;
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
index fa8528579d..77ea8706bf 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
@@ -46,19 +46,19 @@ ConnectionManager::ConnectionManager() = default;
ConnectionManager::~ConnectionManager() = default;
-void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void ConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServerProxy,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
{
- BaseConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target);
+ BaseConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target, view);
m_localServer = std::make_unique<QLocalServer>();
QString socketToken(QUuid::createUuid().toString());
m_localServer->listen(socketToken);
m_localServer->setMaxPendingConnections(3);
- NodeInstanceView *nodeInstanceView = nodeInstanceServerProxy->nodeInstanceView();
- PuppetCreator puppetCreator(target, nodeInstanceView->model());
+ PuppetCreator puppetCreator(target, view->model());
puppetCreator.setQrcMappingString(qrcMappingString);
puppetCreator.createQml2PuppetExecutableIfMissing();
@@ -67,7 +67,6 @@ void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
connection.qmlPuppetProcess = puppetCreator.createPuppetProcess(
connection.mode,
socketToken,
- nodeInstanceView,
[&] { printProcessOutput(connection.qmlPuppetProcess.get(), connection.name); },
[&](int exitCode, QProcess::ExitStatus exitStatus) {
processFinished(exitCode, exitStatus);
@@ -90,7 +89,7 @@ void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
if (connectedToPuppet) {
connection.socket.reset(m_localServer->nextPendingConnection());
- QObject::connect(connection.socket.get(), &QIODevice::readyRead, [&] {
+ QObject::connect(connection.socket.get(), &QIODevice::readyRead, this, [&] {
readDataStream(connection);
});
} else {
@@ -101,11 +100,6 @@ void ConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
}
m_localServer->close();
-
- connect(this,
- &ConnectionManager::processCrashed,
- nodeInstanceServerProxy,
- &NodeInstanceServerProxy::processCrashed);
}
void ConnectionManager::shutDown()
@@ -143,7 +137,7 @@ void ConnectionManager::processFinished(int exitCode, QProcess::ExitStatus exitS
closeSocketsAndKillProcesses();
if (exitStatus == QProcess::CrashExit)
- emit processCrashed();
+ callCrashCallback();
}
void ConnectionManager::closeSocketsAndKillProcesses()
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
index 3e8ef26744..c3c2c34afb 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
@@ -48,19 +48,20 @@ public:
~ConnectionManager() override;
enum PuppetStreamType { FirstPuppetStream, SecondPuppetStream, ThirdPuppetStream };
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServerProxy,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
void shutDown() override;
void writeCommand(const QVariant &command) override;
-signals:
- void processCrashed();
-
protected:
using BaseConnectionManager::processFinished;
void processFinished(int exitCode, QProcess::ExitStatus exitStatus) override;
+ std::vector<Connection> &connections() { return m_connections; }
+
+ quint32 &writeCommandCounter() { return m_writeCommandCounter; }
private:
void printProcessOutput(QProcess *process, const QString &connectionName);
@@ -69,7 +70,6 @@ private:
private:
std::unique_ptr<QLocalServer> m_localServer;
-protected:
std::vector<Connection> m_connections;
quint32 m_writeCommandCounter = 0;
};
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
index 92d7449bc0..2fc75c61c2 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
@@ -38,7 +38,8 @@ class Target;
namespace QmlDesigner {
-class NodeInstanceServerProxy;
+class NodeInstanceServerInterface;
+class AbstractView;
class QMLDESIGNERCORE_EXPORT ConnectionManagerInterface
{
@@ -65,12 +66,15 @@ public:
virtual ~ConnectionManagerInterface();
- virtual void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ virtual void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
= 0;
virtual void shutDown() = 0;
+ virtual void setCrashCallback(std::function<void()> callback) = 0;
+
virtual void writeCommand(const QVariant &command) = 0;
protected:
diff --git a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
index 6da44603df..cd9b5fc5cf 100644
--- a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.cpp
@@ -38,20 +38,21 @@ namespace QmlDesigner {
InteractiveConnectionManager::InteractiveConnectionManager()
{
- m_connections.emplace_back("Editor", "editormode");
- m_connections.emplace_back("Render", "rendermode");
- m_connections.emplace_back("Preview", "previewmode");
+ connections().emplace_back("Editor", "editormode");
+ connections().emplace_back("Render", "rendermode");
+ connections().emplace_back("Preview", "previewmode");
}
-void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+void InteractiveConnectionManager::setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target)
+ ProjectExplorer::Target *target,
+ AbstractView *view)
{
- ConnectionManager::setUp(nodeInstanceServerProxy, qrcMappingString, target);
+ ConnectionManager::setUp(nodeInstanceServer, qrcMappingString, target, view);
DesignerSettings settings = QmlDesignerPlugin::instance()->settings();
int timeOutTime = settings.value(DesignerSettingsKey::PUPPET_KILL_TIMEOUT).toInt();
- for (Connection &connection : m_connections)
+ for (Connection &connection : connections())
connection.timer->setInterval(timeOutTime);
if (QmlDesignerPlugin::instance()
@@ -59,7 +60,7 @@ void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceSe
.value(DesignerSettingsKey::DEBUG_PUPPET)
.toString()
.isEmpty()) {
- for (Connection &connection : m_connections) {
+ for (Connection &connection : connections()) {
QObject::connect(connection.timer.get(), &QTimer::timeout, [&]() {
puppetTimeout(connection);
});
@@ -67,6 +68,12 @@ void InteractiveConnectionManager::setUp(NodeInstanceServerProxy *nodeInstanceSe
}
}
+void InteractiveConnectionManager::shutDown()
+{
+ m_view = {};
+ ConnectionManager::shutDown();
+}
+
void InteractiveConnectionManager::showCannotConnectToPuppetWarningAndSwitchToEditMode()
{
Core::AsynchronousMessageBox::warning(
@@ -75,8 +82,8 @@ void InteractiveConnectionManager::showCannotConnectToPuppetWarningAndSwitchToEd
"Switching to another kit might help."));
QmlDesignerPlugin::instance()->switchToTextModeDeferred();
- nodeInstanceServerProxy()->nodeInstanceView()->emitDocumentMessage(
- tr("Cannot Connect to QML Emulation Layer (QML Puppet)"));
+ if (m_view)
+ m_view->emitDocumentMessage(tr("Cannot Connect to QML Emulation Layer (QML Puppet)"));
}
void InteractiveConnectionManager::dispatchCommand(const QVariant &command, Connection &connection)
diff --git a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h
index 1946620a43..03be103ad6 100644
--- a/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/interactiveconnectionmanager.h
@@ -34,9 +34,12 @@ class InteractiveConnectionManager : public ConnectionManager
public:
InteractiveConnectionManager();
- void setUp(NodeInstanceServerProxy *nodeInstanceServerProxy,
+ void setUp(NodeInstanceServerInterface *nodeInstanceServer,
const QString &qrcMappingString,
- ProjectExplorer::Target *target) override;
+ ProjectExplorer::Target *target,
+ AbstractView *view) override;
+
+ void shutDown() override;
void showCannotConnectToPuppetWarningAndSwitchToEditMode() override;
@@ -46,6 +49,9 @@ protected:
private:
void puppetTimeout(Connection &connection);
void puppetAlive(Connection &connection);
+
+private:
+ AbstractView *m_view{};
};
} // namespace QmlDesigner
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
index 173adc1b00..e04d725b38 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
@@ -100,7 +100,7 @@ NodeInstanceServerProxy::NodeInstanceServerProxy(NodeInstanceView *nodeInstanceV
if (instanceViewBenchmark().isInfoEnabled())
m_benchmarkTimer.start();
- m_connectionManager.setUp(this, qrcMappingString(), target);
+ m_connectionManager.setUp(this, qrcMappingString(), target, nodeInstanceView);
qCInfo(instanceViewBenchmark) << "puppets setup:" << m_benchmarkTimer.elapsed();
}
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h
index 0177bd6a14..e4edeb6725 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.h
@@ -84,6 +84,7 @@ public:
void requestModelNodePreviewImage(const RequestModelNodePreviewImageCommand &command) override;
void changeLanguage(const ChangeLanguageCommand &command) override;
void changePreviewImageSize(const ChangePreviewImageSizeCommand &command) override;
+ void dispatchCommand(const QVariant &command) override;
NodeInstanceView *nodeInstanceView() const { return m_nodeInstanceView; }
@@ -91,12 +92,8 @@ public:
protected:
void writeCommand(const QVariant &command);
- void dispatchCommand(const QVariant &command);
NodeInstanceClientInterface *nodeInstanceClient() const;
-signals:
- void processCrashed();
-
private:
NodeInstanceView *m_nodeInstanceView{};
QElapsedTimer m_benchmarkTimer;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index 2f85a9e1a4..3f0b1f8a5f 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -196,10 +196,7 @@ void NodeInstanceView::modelAttached(Model *model)
AbstractView::modelAttached(model);
m_nodeInstanceServer = createNodeInstanceServerProxy();
m_lastCrashTime.start();
- connect(m_nodeInstanceServer.get(),
- &NodeInstanceServerProxy::processCrashed,
- this,
- &NodeInstanceView::handleCrash);
+ m_connectionManager.setCrashCallback(m_crashCallback);
if (!isSkippedRootNode(rootModelNode())) {
m_nodeInstanceServer->createScene(createCreateSceneCommand());
@@ -215,6 +212,8 @@ void NodeInstanceView::modelAttached(Model *model)
void NodeInstanceView::modelAboutToBeDetached(Model * model)
{
+ m_connectionManager.setCrashCallback({});
+
removeAllInstanceNodeRelationships();
if (m_nodeInstanceServer) {
m_nodeInstanceServer->clearScene(createClearSceneCommand());
@@ -281,11 +280,6 @@ void NodeInstanceView::restartProcess()
m_nodeInstanceServer.reset();
m_nodeInstanceServer = createNodeInstanceServerProxy();
- connect(m_nodeInstanceServer.get(),
- &NodeInstanceServerProxy::processCrashed,
- this,
- &NodeInstanceView::handleCrash);
-
if (!isSkippedRootNode(rootModelNode())) {
m_nodeInstanceServer->createScene(createCreateSceneCommand());
m_nodeInstanceServer->changeSelection(
@@ -534,11 +528,11 @@ void NodeInstanceView::auxiliaryDataChanged(const ModelNode &node,
const PropertyName &name,
const QVariant &value)
{
- if (((node.isRootNode() && (name == "width" || name == "height")) || name == "invisible")
+ if (((node.isRootNode() && (name == "width" || name == "height")) || name == "invisible" || name == "locked")
|| name.endsWith(PropertyName("@NodeInstance"))) {
if (hasInstanceForModelNode(node)) {
NodeInstance instance = instanceForModelNode(node);
- if (value.isValid() || name == "invisible") {
+ if (value.isValid() || name == "invisible" || name == "locked") {
PropertyValueContainer container{instance.instanceId(), name, value, TypeName()};
m_nodeInstanceServer->changeAuxiliaryValues({{container}});
} else {
@@ -1496,7 +1490,15 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
if (hasModelNodeForInternalId(container.instanceId()) && !image.isNull()) {
auto node = modelNodeForInternalId(container.instanceId());
if (node.isValid()) {
- image.setDevicePixelRatio(2.);
+#ifndef QMLDESIGNER_TEST
+ const double ratio = QmlDesignerPlugin::formEditorDevicePixelRatio();
+#else
+ const double ratio = 1;
+#endif
+ const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio;
+ if (image.height() != dim || image.width() != dim)
+ image = image.scaled(dim, dim, Qt::KeepAspectRatio);
+ image.setDevicePixelRatio(ratio);
updatePreviewImageForNode(node, image);
}
}
@@ -1540,12 +1542,15 @@ void NodeInstanceView::requestModelNodePreviewImage(const ModelNode &node, const
} else if (node.isComponent()) {
componentPath = node.metaInfo().componentFileName();
}
+#ifndef QMLDESIGNER_TEST
+ const double ratio = QmlDesignerPlugin::formEditorDevicePixelRatio();
+#else
+ const double ratio = 1;
+#endif
+ const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio;
m_nodeInstanceServer->requestModelNodePreviewImage(
- RequestModelNodePreviewImageCommand(
- instance.instanceId(),
- QSize(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS,
- Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS),
- componentPath, renderItemId));
+ RequestModelNodePreviewImageCommand(instance.instanceId(), QSize(dim, dim),
+ componentPath, renderItemId));
}
}
}
@@ -1587,6 +1592,11 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
ModelNodePreviewImageData imageData;
imageData.id = modelNode.id();
imageData.type = QString::fromLatin1(modelNode.type());
+#ifndef QMLDESIGNER_TEST
+ const double ratio = QmlDesignerPlugin::formEditorDevicePixelRatio();
+#else
+ const double ratio = 1;
+#endif
if (imageSource.isEmpty() && modelNode.isSubclassOf("QtQuick3D.Texture")) {
// Texture node may have sourceItem instead
@@ -1601,11 +1611,10 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
return previewImageDataForGenericNode(modelNode, boundNode);
} else {
QmlItemNode itemNode(boundNode);
- imageData.pixmap = itemNode.instanceRenderPixmap().scaled(
- Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2,
- Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2,
- Qt::KeepAspectRatio);
- imageData.pixmap.setDevicePixelRatio(2.);
+ const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio;
+ imageData.pixmap = itemNode.instanceRenderPixmap().scaled(dim, dim, Qt::KeepAspectRatio);
+ imageData.pixmap.setDevicePixelRatio(ratio);
+
}
imageData.info = QObject::tr("Source item: %1").arg(boundNode.id());
}
@@ -1629,10 +1638,9 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
QPixmap originalPixmap;
originalPixmap.load(imageSource);
if (!originalPixmap.isNull()) {
- imageData.pixmap = originalPixmap.scaled(Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2,
- Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * 2,
- Qt::KeepAspectRatio);
- imageData.pixmap.setDevicePixelRatio(2.);
+ const int dim = Constants::MODELNODE_PREVIEW_IMAGE_DIMENSIONS * ratio;
+ imageData.pixmap = originalPixmap.scaled(dim, dim, Qt::KeepAspectRatio);
+ imageData.pixmap.setDevicePixelRatio(ratio);
double imgSize = double(imageFi.size());
static QStringList units({QObject::tr("B"), QObject::tr("KB"), QObject::tr("MB"), QObject::tr("GB")});
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
index 5f691bc5a0..df609f4cb0 100644
--- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp
@@ -181,7 +181,6 @@ PuppetCreator::PuppetCreator(ProjectExplorer::Target *target, const Model *model
QProcessUniquePointer PuppetCreator::createPuppetProcess(
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions) const
@@ -190,7 +189,6 @@ QProcessUniquePointer PuppetCreator::createPuppetProcess(
qmlPuppetDirectory(m_availablePuppetType),
puppetMode,
socketToken,
- handlerObject,
processOutputCallback,
processFinishCallback,
customOptions);
@@ -201,7 +199,6 @@ QProcessUniquePointer PuppetCreator::puppetProcess(
const QString &workingDirectory,
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions) const
@@ -216,7 +213,6 @@ QProcessUniquePointer PuppetCreator::puppetProcess(
&QProcess::kill);
QObject::connect(puppetProcess.get(),
static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
- handlerObject,
processFinishCallback);
#ifndef QMLDESIGNER_TEST
@@ -227,7 +223,7 @@ QProcessUniquePointer PuppetCreator::puppetProcess(
#endif
if (forwardOutput == puppetMode || forwardOutput == "all") {
puppetProcess->setProcessChannelMode(QProcess::MergedChannels);
- QObject::connect(puppetProcess.get(), &QProcess::readyRead, handlerObject, processOutputCallback);
+ QObject::connect(puppetProcess.get(), &QProcess::readyRead, processOutputCallback);
}
puppetProcess->setWorkingDirectory(workingDirectory);
diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
index 69c66688fe..e001b9d0c1 100644
--- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
+++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.h
@@ -58,7 +58,6 @@ public:
QProcessUniquePointer createPuppetProcess(
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions = {}) const;
@@ -89,7 +88,6 @@ protected:
const QString &workingDirectory,
const QString &puppetMode,
const QString &socketToken,
- QObject *handlerObject,
std::function<void()> processOutputCallback,
std::function<void(int, QProcess::ExitStatus)> processFinishCallback,
const QStringList &customOptions) const;
diff --git a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
index afd2aaa9a5..e6a61800e6 100644
--- a/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
+++ b/src/plugins/qmldesigner/designercore/metainfo/nodemetainfo.cpp
@@ -25,6 +25,7 @@
#include "nodemetainfo.h"
#include "model.h"
+#include "model/model_p.h"
#include "metainfo.h"
#include <enumeration.h>
@@ -620,8 +621,6 @@ public:
QSet<QByteArray> &prototypeCachePositives();
QSet<QByteArray> &prototypeCacheNegatives();
- static void clearCache();
-
private:
NodeMetaInfoPrivate(Model *model, TypeName type, int maj = -1, int min = -1);
@@ -657,13 +656,10 @@ private:
const Document *document() const;
QPointer<Model> m_model;
- static QHash<TypeName, Pointer> m_nodeMetaInfoCache;
const ObjectValue *m_objectValue = nullptr;
bool m_propertiesSetup = false;
};
-QHash<TypeName, NodeMetaInfoPrivate::Pointer> NodeMetaInfoPrivate::m_nodeMetaInfoCache;
-
bool NodeMetaInfoPrivate::isFileComponent() const
{
return m_isFileComponent;
@@ -705,11 +701,6 @@ QSet<QByteArray> &NodeMetaInfoPrivate::prototypeCacheNegatives()
return m_prototypeCacheNegatives;
}
-void NodeMetaInfoPrivate::clearCache()
-{
- m_nodeMetaInfoCache.clear();
-}
-
PropertyName NodeMetaInfoPrivate::defaultPropertyName() const
{
if (!m_defaultPropertyName.isEmpty())
@@ -724,17 +715,12 @@ static inline TypeName stringIdentifier( const TypeName &type, int maj, int min)
NodeMetaInfoPrivate::Pointer NodeMetaInfoPrivate::create(Model *model, const TypeName &type, int major, int minor)
{
- if (m_nodeMetaInfoCache.contains(stringIdentifier(type, major, minor))) {
- const Pointer &info = m_nodeMetaInfoCache.value(stringIdentifier(type, major, minor));
- if (info->model() == model)
- return info;
- else
- m_nodeMetaInfoCache.clear();
- }
+ if (model->d->m_nodeMetaInfoCache.contains(stringIdentifier(type, major, minor)))
+ return model->d->m_nodeMetaInfoCache.value(stringIdentifier(type, major, minor));
Pointer newData(new NodeMetaInfoPrivate(model, type, major, minor));
if (newData->isValid())
- m_nodeMetaInfoCache.insert(stringIdentifier(type, major, minor), newData);
+ model->d->m_nodeMetaInfoCache.insert(stringIdentifier(type, major, minor), newData);
return newData;
}
@@ -1671,11 +1657,6 @@ bool NodeMetaInfo::isQmlItem() const
|| isSubclassOf("QtQml.QtObject");
}
-void NodeMetaInfo::clearCache()
-{
- Internal::NodeMetaInfoPrivate::clearCache();
-}
-
bool NodeMetaInfo::isLayoutable() const
{
if (isSubclassOf("<cpp>.QDeclarativeBasePositioner"))
diff --git a/src/plugins/qmldesigner/designercore/model/abstractview.cpp b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
index d7d512334d..6a1a8d4897 100644
--- a/src/plugins/qmldesigner/designercore/model/abstractview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/abstractview.cpp
@@ -35,6 +35,7 @@
#ifndef QMLDESIGNER_TEST
#include <qmldesignerplugin.h>
#include <viewmanager.h>
+#include <nodeabstractproperty.h>
#endif
#include <coreplugin/helpmanager.h>
@@ -397,7 +398,7 @@ QList<ModelNode> AbstractView::toModelNodeList(const QList<Internal::InternalNod
QList<ModelNode> toModelNodeList(const QList<Internal::InternalNode::Pointer> &nodeList, AbstractView *view)
{
QList<ModelNode> newNodeList;
- foreach (const Internal::InternalNode::Pointer &node, nodeList)
+ for (const Internal::InternalNode::Pointer &node : nodeList)
newNodeList.append(ModelNode(node, view->model(), view));
return newNodeList;
@@ -406,7 +407,7 @@ QList<ModelNode> toModelNodeList(const QList<Internal::InternalNode::Pointer> &n
QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode> &nodeList)
{
QList<Internal::InternalNode::Pointer> newNodeList;
- foreach (const ModelNode &node, nodeList)
+ for (const ModelNode &node : nodeList)
newNodeList.append(node.internalNode());
return newNodeList;
@@ -414,15 +415,26 @@ QList<Internal::InternalNode::Pointer> toInternalNodeList(const QList<ModelNode>
/*!
Sets the list of nodes to the actual selected nodes specified by
- \a selectedNodeList.
+ \a selectedNodeList if the node or its ancestors are not locked.
*/
void AbstractView::setSelectedModelNodes(const QList<ModelNode> &selectedNodeList)
{
- model()->d->setSelectedNodes(toInternalNodeList(selectedNodeList));
+ QList<ModelNode> unlockedNodes;
+
+ for (const auto &modelNode : selectedNodeList) {
+ if (!ModelNode::isThisOrAncestorLocked(modelNode))
+ unlockedNodes.push_back(modelNode);
+ }
+
+ model()->d->setSelectedNodes(toInternalNodeList(unlockedNodes));
}
void AbstractView::setSelectedModelNode(const ModelNode &modelNode)
{
+ if (ModelNode::isThisOrAncestorLocked(modelNode)) {
+ clearSelectedModelNodes();
+ return;
+ }
setSelectedModelNodes({modelNode});
}
diff --git a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
index 03239faedc..8113e62846 100644
--- a/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
+++ b/src/plugins/qmldesigner/designercore/model/basetexteditmodifier.cpp
@@ -38,8 +38,9 @@
using namespace QmlDesigner;
-BaseTextEditModifier::BaseTextEditModifier(TextEditor::TextEditorWidget *textEdit):
- PlainTextEditModifier(textEdit)
+BaseTextEditModifier::BaseTextEditModifier(TextEditor::TextEditorWidget *textEdit)
+ : PlainTextEditModifier(textEdit)
+ , m_textEdit{textEdit}
{
}
@@ -47,21 +48,20 @@ void BaseTextEditModifier::indentLines(int startLine, int endLine)
{
if (startLine < 0)
return;
- auto baseTextEditorWidget = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit());
- if (!baseTextEditorWidget)
+
+ if (!m_textEdit)
return;
- QTextDocument *textDocument = plainTextEdit()->document();
- TextEditor::TextDocument *baseTextEditorDocument = baseTextEditorWidget->textDocument();
+ TextEditor::TextDocument *baseTextEditorDocument = m_textEdit->textDocument();
TextEditor::TabSettings tabSettings = baseTextEditorDocument->tabSettings();
- QTextCursor tc(textDocument);
+ QTextCursor tc(textDocument());
tc.beginEditBlock();
for (int i = startLine; i <= endLine; i++) {
- QTextBlock start = textDocument->findBlockByNumber(i);
+ QTextBlock start = textDocument()->findBlockByNumber(i);
if (start.isValid()) {
- QmlJSEditor::Internal::Indenter indenter(textDocument);
+ QmlJSEditor::Internal::Indenter indenter(textDocument());
indenter.indentBlock(start, QChar::Null, tabSettings);
}
}
@@ -82,22 +82,23 @@ void BaseTextEditModifier::indent(int offset, int length)
int BaseTextEditModifier::indentDepth() const
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit()))
- return bte->textDocument()->tabSettings().m_indentSize;
+ if (m_textEdit)
+ return m_textEdit->textDocument()->tabSettings().m_indentSize;
else
return 0;
}
bool BaseTextEditModifier::renameId(const QString &oldId, const QString &newId)
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit())) {
- if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(bte->textDocument())) {
+ if (m_textEdit) {
+ if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(
+ m_textEdit->textDocument())) {
Utils::ChangeSet changeSet;
foreach (const QmlJS::SourceLocation &loc,
document->semanticInfo().idLocations.value(oldId)) {
changeSet.replace(loc.begin(), loc.end(), newId);
}
- QTextCursor tc = bte->textCursor();
+ QTextCursor tc = textCursor();
changeSet.apply(&tc);
return true;
}
@@ -120,10 +121,9 @@ static QmlJS::AST::UiObjectDefinition *getObjectDefinition(const QList<QmlJS::AS
bool BaseTextEditModifier::moveToComponent(int nodeOffset)
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit())) {
- if (auto document
- = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(bte->textDocument())) {
-
+ if (m_textEdit) {
+ if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(
+ m_textEdit->textDocument())) {
auto qualifiedId = QmlJS::AST::cast<QmlJS::AST::UiQualifiedId *>(document->semanticInfo().astNodeAt(nodeOffset));
QList<QmlJS::AST::Node *> path = document->semanticInfo().rangePath(nodeOffset);
QmlJS::AST::UiObjectDefinition *object = getObjectDefinition(path, qualifiedId);
@@ -140,9 +140,9 @@ bool BaseTextEditModifier::moveToComponent(int nodeOffset)
QStringList BaseTextEditModifier::autoComplete(QTextDocument *textDocument, int position, bool explicitComplete)
{
- if (auto bte = qobject_cast<TextEditor::TextEditorWidget*>(plainTextEdit()))
- if (auto document
- = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(bte->textDocument()))
+ if (m_textEdit)
+ if (auto document = qobject_cast<QmlJSEditor::QmlJSEditorDocument *>(
+ m_textEdit->textDocument()))
return QmlJSEditor::qmlJSAutoComplete(textDocument,
position,
document->filePath(),
diff --git a/src/plugins/qmldesigner/designercore/model/model.cpp b/src/plugins/qmldesigner/designercore/model/model.cpp
index f31aaeecdc..aed9ae788c 100644
--- a/src/plugins/qmldesigner/designercore/model/model.cpp
+++ b/src/plugins/qmldesigner/designercore/model/model.cpp
@@ -161,7 +161,7 @@ void ModelPrivate::notifyImportsChanged(const QList<Import> &addedImports, const
resetModel = true;
}
- NodeMetaInfo::clearCache();
+ m_nodeMetaInfoCache.clear();
if (nodeInstanceView())
nodeInstanceView()->importsChanged(addedImports, removedImports);
@@ -2080,6 +2080,11 @@ QList<ModelNode> Model::selectedNodes(AbstractView *view) const
return d->toModelNodeList(d->selectedNodes(), view);
}
+void Model::clearMetaInfoCache()
+{
+ d->m_nodeMetaInfoCache.clear();
+}
+
/*!
\brief Returns the URL against which relative URLs within the model should be resolved.
\return The base URL.
diff --git a/src/plugins/qmldesigner/designercore/model/model_p.h b/src/plugins/qmldesigner/designercore/model/model_p.h
index 5a8dced512..4874959759 100644
--- a/src/plugins/qmldesigner/designercore/model/model_p.h
+++ b/src/plugins/qmldesigner/designercore/model/model_p.h
@@ -85,6 +85,7 @@ class ModelPrivate : public QObject {
friend class QmlDesigner::Model;
friend class QmlDesigner::Internal::WriteLocker;
+ friend class QmlDesigner::Internal::NodeMetaInfoPrivate;
public:
ModelPrivate(Model *model);
@@ -268,6 +269,7 @@ private:
QPointer<NodeInstanceView> m_nodeInstanceView;
QPointer<TextModifier> m_textModifier;
QPointer<Model> m_metaInfoProxyModel;
+ QHash<TypeName, QSharedPointer<NodeMetaInfoPrivate>> m_nodeMetaInfoCache;
bool m_writeLock;
qint32 m_internalIdCounter;
};
diff --git a/src/plugins/qmldesigner/designercore/model/modelnode.cpp b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
index 5c1ffce639..688dae2d07 100644
--- a/src/plugins/qmldesigner/designercore/model/modelnode.cpp
+++ b/src/plugins/qmldesigner/designercore/model/modelnode.cpp
@@ -1228,6 +1228,53 @@ void ModelNode::removeGlobalStatus()
}
}
+bool ModelNode::locked() const
+{
+ if (hasLocked())
+ return auxiliaryData(lockedProperty).toBool();
+
+ return false;
+}
+
+bool ModelNode::hasLocked() const
+{
+ return hasAuxiliaryData(lockedProperty);
+}
+
+void ModelNode::setLocked(bool value)
+{
+ setAuxiliaryData(lockedProperty, value);
+
+ if (value) {
+ // Remove newly locked node and all its descendants from potential selection
+ for (ModelNode node : allSubModelNodesAndThisNode()) {
+ node.deselectNode();
+ node.removeAuxiliaryData("timeline_expanded");
+ node.removeAuxiliaryData("transition_expanded");
+ }
+ }
+}
+
+void ModelNode::removeLocked()
+{
+ if (hasLocked())
+ removeAuxiliaryData(lockedProperty);
+}
+
+bool ModelNode::isThisOrAncestorLocked(const ModelNode &node)
+{
+ if (!node.isValid())
+ return false;
+
+ if (node.locked())
+ return true;
+
+ if (node.isRootNode() || !node.hasParentProperty())
+ return false;
+
+ return isThisOrAncestorLocked(node.parentProperty().parentModelNode());
+}
+
void ModelNode::setScriptFunctions(const QStringList &scriptFunctionList)
{
model()->d->setScriptFunctions(internalNode(), scriptFunctionList);
diff --git a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp
index f87dc73a2c..782ff3794b 100644
--- a/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp
+++ b/src/plugins/qmldesigner/designercore/model/plaintexteditmodifier.cpp
@@ -35,19 +35,17 @@
using namespace Utils;
using namespace QmlDesigner;
-PlainTextEditModifier::PlainTextEditModifier(QPlainTextEdit *textEdit):
- m_changeSet(nullptr),
- m_textEdit(textEdit),
- m_changeSignalsEnabled(true),
- m_pendingChangeSignal(false),
- m_ongoingTextChange(false)
+PlainTextEditModifier::PlainTextEditModifier(QPlainTextEdit *textEdit)
+ : PlainTextEditModifier(textEdit->document(), textEdit->textCursor())
{
- Q_ASSERT(textEdit);
-
- connect(m_textEdit, &QPlainTextEdit::textChanged,
- this, &PlainTextEditModifier::textEditChanged);
+ connect(textEdit, &QPlainTextEdit::textChanged, this, &PlainTextEditModifier::textEditChanged);
}
+PlainTextEditModifier::PlainTextEditModifier(QTextDocument *document, const QTextCursor &textCursor)
+ : m_textDocument{document}
+ , m_textCursor{textCursor}
+{}
+
PlainTextEditModifier::~PlainTextEditModifier() = default;
void PlainTextEditModifier::replace(int offset, int length, const QString &replacement)
@@ -158,17 +156,17 @@ void PlainTextEditModifier::runRewriting(ChangeSet *changeSet)
QTextDocument *PlainTextEditModifier::textDocument() const
{
- return m_textEdit->document();
+ return m_textDocument;
}
QString PlainTextEditModifier::text() const
{
- return m_textEdit->toPlainText();
+ return m_textDocument->toPlainText();
}
QTextCursor PlainTextEditModifier::textCursor() const
{
- return m_textEdit->textCursor();
+ return m_textCursor;
}
void PlainTextEditModifier::deactivateChangeSignals()
diff --git a/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp b/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp
index 106341043d..2b12f03b2b 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriteaction.cpp
@@ -181,8 +181,10 @@ bool ChangePropertyRewriteAction::execute(QmlRefactoring &refactoring, ModelNode
{
if (m_sheduledInHierarchy) {
const int nodeLocation = positionStore.nodeOffset(m_property.parentModelNode());
- if (nodeLocation < 0)
+ if (nodeLocation < 0) {
+ qWarning() << "*** ChangePropertyRewriteAction::execute ignored. Invalid node location";
return true;
+ }
bool result = false;
if (m_property.isDefaultProperty()) {
diff --git a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
index f7a1254035..86b561548e 100644
--- a/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
+++ b/src/plugins/qmldesigner/designercore/model/rewriterview.cpp
@@ -72,12 +72,7 @@ RewriterView::RewriterView(DifferenceHandling differenceHandling, QObject *paren
m_textToModelMerger(new Internal::TextToModelMerger(this))
{
m_amendTimer.setSingleShot(true);
- m_amendTimer.setInterval(400);
connect(&m_amendTimer, &QTimer::timeout, this, &RewriterView::amendQmlText);
-
- QmlJS::ModelManagerInterface *modelManager = QmlJS::ModelManagerInterface::instance();
- connect(modelManager, &QmlJS::ModelManagerInterface::libraryInfoUpdated,
- this, &RewriterView::handleLibraryInfoUpdate, Qt::QueuedConnection);
}
RewriterView::~RewriterView() = default;
@@ -94,8 +89,6 @@ Internal::TextToModelMerger *RewriterView::textToModelMerger() const
void RewriterView::modelAttached(Model *model)
{
- m_modelAttachPending = false;
-
if (model && model->textModifier())
setTextModifier(model->textModifier());
@@ -109,12 +102,10 @@ void RewriterView::modelAttached(Model *model)
if (!(m_errors.isEmpty() && m_warnings.isEmpty()))
notifyErrorsAndWarnings(m_errors);
- if (hasIncompleteTypeInformation()) {
- m_modelAttachPending = true;
+ if (hasIncompleteTypeInformation())
QTimer::singleShot(1000, this, [this, model](){
modelAttached(model);
});
- }
}
void RewriterView::modelAboutToBeDetached(Model * /*model*/)
@@ -812,13 +803,6 @@ void RewriterView::setupCanonicalHashes() const
}
}
-void RewriterView::handleLibraryInfoUpdate()
-{
- // Trigger dummy amend to reload document when library info changes
- if (isAttached() && !m_modelAttachPending)
- m_amendTimer.start();
-}
-
ModelNode RewriterView::nodeAtTextCursorPosition(int cursorPosition) const
{
return nodeAtTextCursorPositionHelper(rootModelNode(), cursorPosition);
@@ -1021,7 +1005,7 @@ void RewriterView::qmlTextChanged()
auto &viewManager = QmlDesignerPlugin::instance()->viewManager();
if (viewManager.usesRewriterView(this)) {
QmlDesignerPlugin::instance()->viewManager().disableWidgets();
- m_amendTimer.start();
+ m_amendTimer.start(400);
}
#else
/*Keep test synchronous*/
diff --git a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
index 61f8d5e0c3..708d120ba5 100644
--- a/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
+++ b/src/plugins/qmldesigner/designercore/model/texttomodelmerger.cpp
@@ -989,7 +989,7 @@ bool TextToModelMerger::load(const QString &data, DifferenceHandler &differenceH
m_rewriterView->setIncompleteTypeInformation(false);
// maybe the project environment (kit, ...) changed, so we need to clean old caches
- NodeMetaInfo::clearCache();
+ m_rewriterView->model()->clearMetaInfoCache();
try {
Snapshot snapshot = m_rewriterView->textModifier()->qmljsSnapshot();
@@ -1119,8 +1119,15 @@ void TextToModelMerger::syncNode(ModelNode &modelNode,
differenceHandler.typeDiffers(isRootNode, modelNode, typeName,
majorVersion, minorVersion,
astNode, context);
- if (!isRootNode)
+
+ if (!modelNode.isValid())
+ return;
+
+ if (!isRootNode && modelNode.majorVersion() != -1 && modelNode.minorVersion() != -1) {
+ qWarning() << "Preempting Node sync. Type differs" << modelNode <<
+ modelNode.majorVersion() << modelNode.minorVersion();
return; // the difference handler will create a new node, so we're done.
+ }
}
if (isComponentType(typeName) || isImplicitComponent)
diff --git a/src/plugins/qmldesigner/qmldesigner_dependencies.pri b/src/plugins/qmldesigner/qmldesigner_dependencies.pri
index 321d2c2b07..473692aa5d 100644
--- a/src/plugins/qmldesigner/qmldesigner_dependencies.pri
+++ b/src/plugins/qmldesigner/qmldesigner_dependencies.pri
@@ -3,7 +3,8 @@ QTC_LIB_DEPENDS += \
utils \
qmljs \
qmleditorwidgets \
- advanceddockingsystem
+ advanceddockingsystem \
+ sqlite
QTC_PLUGIN_DEPENDS += \
coreplugin \
texteditor \
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.pro b/src/plugins/qmldesigner/qmldesignerplugin.pro
index 6590dcb046..8f34f42d0a 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.pro
+++ b/src/plugins/qmldesigner/qmldesignerplugin.pro
@@ -35,6 +35,7 @@ include(components/annotationeditor/annotationeditor.pri)
include(components/richtexteditor/richtexteditor.pri)
include(components/transitioneditor/transitioneditor.pri)
include(components/listmodeleditor/listmodeleditor.pri)
+include(components/previewtooltip/previewtooltipbackend.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 ea57599bd0..8dc0933903 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.qbs
+++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs
@@ -27,6 +27,7 @@ Project {
Depends { name: "LanguageUtils" }
Depends { name: "QtSupport" }
Depends { name: "app_version_header" }
+ Depends { name: "Sqlite" }
cpp.defines: base.concat([
"DESIGNER_CORE_LIBRARY",
@@ -411,6 +412,21 @@ Project {
"pluginmanager/widgetpluginmanager.h",
"pluginmanager/widgetpluginpath.cpp",
"pluginmanager/widgetpluginpath.h",
+ "include/imagecache.h",
+ "imagecache/imagecachecollector.cpp",
+ "imagecache/imagecachecollector.h",
+ "imagecache/imagecache.cpp",
+ "imagecache/imagecachecollectorinterface.h",
+ "imagecache/imagecacheconnectionmanager.cpp",
+ "imagecache/imagecacheconnectionmanager.h",
+ "imagecache/imagecachegeneratorinterface.h",
+ "imagecache/imagecachegenerator.cpp",
+ "imagecache/imagecachegenerator.h",
+ "imagecache/imagecachestorageinterface.h",
+ "imagecache/imagecachestorage.h",
+ "imagecache/timestampproviderinterface.h",
+ "imagecache/timestampprovider.h",
+ "imagecache/timestampprovider.cpp",
]
}
@@ -446,6 +462,8 @@ Project {
"componentcore/modelnodecontextmenu_helper.h",
"componentcore/modelnodeoperations.cpp",
"componentcore/modelnodeoperations.h",
+ "componentcore/navigation2d.cpp",
+ "componentcore/navigation2d.h",
"componentcore/selectioncontext.cpp",
"componentcore/selectioncontext.h",
"componentcore/qmldesignericonprovider.cpp",
@@ -600,6 +618,8 @@ Project {
"itemlibrary/itemlibrarywidget.h",
"itemlibrary/customfilesystemmodel.cpp",
"itemlibrary/customfilesystemmodel.h",
+ "itemlibrary/itemlibraryiconimageprovider.cpp",
+ "itemlibrary/itemlibraryiconimageprovider.h",
"navigator/iconcheckboxitemdelegate.cpp",
"navigator/iconcheckboxitemdelegate.h",
"navigator/nameitemdelegate.cpp",
@@ -791,6 +811,11 @@ Project {
"pathtool/pathtool.h",
"pathtool/pathtoolview.cpp",
"pathtool/pathtoolview.h",
+ "previewtooltip/previewimagetooltip.cpp",
+ "previewtooltip/previewimagetooltip.h",
+ "previewtooltip/previewimagetooltip.ui",
+ "previewtooltip/previewtooltipbackend.cpp",
+ "previewtooltip/previewtooltipbackend.h",
"richtexteditor/hyperlinkdialog.cpp",
"richtexteditor/hyperlinkdialog.h",
"richtexteditor/hyperlinkdialog.ui",
diff --git a/src/plugins/qmldesigner/qmldesignerunittestfiles.pri b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri
index cd4d52e8d3..b5ae93f2f1 100644
--- a/src/plugins/qmldesigner/qmldesignerunittestfiles.pri
+++ b/src/plugins/qmldesigner/qmldesignerunittestfiles.pri
@@ -1,5 +1,6 @@
INCLUDEPATH += $$PWD
INCLUDEPATH += $$PWD/designercore/include
+INCLUDEPATH += $$PWD/designercore/imagecache
INCLUDEPATH += $$PWD/designercore
INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces
INCLUDEPATH += $$PWD/../../../share/qtcreator/qml/qmlpuppet/types
@@ -30,9 +31,18 @@ SOURCES += \
$$PWD/designercore/model/variantproperty.cpp\
$$PWD/designercore/model/annotation.cpp \
$$PWD/designercore/rewritertransaction.cpp \
- $$PWD/components/listmodeleditor/listmodeleditormodel.cpp
+ $$PWD/components/listmodeleditor/listmodeleditormodel.cpp \
+ $$PWD/designercore/imagecache/imagecache.cpp \
+ $$PWD/designercore/imagecache/imagecachegenerator.cpp
HEADERS += \
+ $$PWD/designercore/imagecache/imagecachecollectorinterface.h \
+ $$PWD/designercore/imagecache/imagecachestorage.h \
+ $$PWD/designercore/imagecache/imagecachegenerator.h \
+ $$PWD/designercore/imagecache/imagecachestorageinterface.h \
+ $$PWD/designercore/imagecache/imagecachegeneratorinterface.h \
+ $$PWD/designercore/imagecache/timestampproviderinterface.h \
+ $$PWD/designercore/include/imagecache.h \
$$PWD/designercore/include/modelnode.h \
$$PWD/designercore/include/model.h \
$$PWD/../../../share/qtcreator/qml/qmlpuppet/interfaces/commondefines.h \
diff --git a/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp b/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp
index d1c7c9f289..086ef82fc9 100644
--- a/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp
+++ b/src/plugins/qmlpreview/qmldebugtranslationwidget.cpp
@@ -38,6 +38,7 @@
#include <utils/outputformatter.h>
#include <utils/utilsicons.h>
#include <utils/fileutils.h>
+#include <utils/qtcolorbutton.h>
#include <extensionsystem/pluginmanager.h>
#include <extensionsystem/pluginspec.h>
@@ -94,6 +95,11 @@ namespace QmlPreview {
QmlDebugTranslationWidget::QmlDebugTranslationWidget(QWidget *parent, TestLanguageGetter languagesGetterMethod)
: QWidget(parent)
, m_testLanguagesGetter(languagesGetterMethod)
+ , m_warningColor(Qt::red)
+ //, m_foundTrColor(Qt::green) // invalid color -> init without the frame
+ , m_lastWarningColor(m_warningColor)
+ , m_lastfoundTrColor(Qt::green)
+
{
auto mainLayout = new QVBoxLayout(this);
@@ -121,12 +127,54 @@ QmlDebugTranslationWidget::QmlDebugTranslationWidget(QWidget *parent, TestLangua
m_selectLanguageLayout = new QHBoxLayout;
mainLayout->addLayout(m_selectLanguageLayout);
+ auto settingsLayout = new QHBoxLayout();
+ mainLayout->addLayout(settingsLayout);
+
auto elideWarningCheckBox = new QCheckBox(tr("Enable elide warning"));
- layout()->addWidget(elideWarningCheckBox);
connect(elideWarningCheckBox, &QCheckBox::stateChanged, [this] (int state) {
m_elideWarning = (state == Qt::Checked);
-
});
+ settingsLayout->addWidget(elideWarningCheckBox);
+
+ auto warningColorCheckbox = new QCheckBox(tr("select Warning color: "));
+ settingsLayout->addWidget(warningColorCheckbox);
+ auto warningColorButton = new Utils::QtColorButton();
+ connect(warningColorCheckbox, &QCheckBox::stateChanged, [warningColorButton, this] (int state) {
+ if (state == Qt::Checked) {
+ warningColorButton->setColor(m_lastWarningColor);
+ warningColorButton->setEnabled(true);
+ } else {
+ m_lastWarningColor = warningColorButton->color();
+ warningColorButton->setColor({});
+ warningColorButton->setEnabled(false);
+ }
+ });
+ connect(warningColorButton, &Utils::QtColorButton::colorChanged, [this](const QColor &color) {
+ m_warningColor = color;
+ });
+ warningColorCheckbox->setCheckState(Qt::Checked);
+ settingsLayout->addWidget(warningColorButton);
+
+ auto foundTrColorCheckbox = new QCheckBox(tr("select found 'tr' color: "));
+ settingsLayout->addWidget(foundTrColorCheckbox);
+ auto foundTrColorButton = new Utils::QtColorButton();
+ foundTrColorButton->setDisabled(true);
+ connect(foundTrColorCheckbox, &QCheckBox::stateChanged, [foundTrColorButton, this] (int state) {
+ if (state == Qt::Checked) {
+ foundTrColorButton->setColor(m_lastfoundTrColor);
+ foundTrColorButton->setEnabled(true);
+ } else {
+ m_lastfoundTrColor = foundTrColorButton->color();
+ foundTrColorButton->setColor({});
+ foundTrColorButton->setEnabled(false);
+ }
+ });
+ connect(foundTrColorButton, &Utils::QtColorButton::colorChanged, [this](const QColor &color) {
+ m_foundTrColor = color;
+ });
+ settingsLayout->addWidget(foundTrColorButton);
+
+ settingsLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding));
auto controlLayout = new QHBoxLayout;
mainLayout->addLayout(controlLayout);
@@ -232,6 +280,16 @@ void QmlDebugTranslationWidget::updateStartupProjectTranslations()
updateCurrentTranslations(ProjectExplorer::SessionManager::startupProject());
}
+QColor QmlDebugTranslationWidget::warningColor()
+{
+ return m_warningColor;
+}
+
+QColor QmlDebugTranslationWidget::foundTrColor()
+{
+ return m_foundTrColor;
+}
+
void QmlDebugTranslationWidget::updateCurrentTranslations(ProjectExplorer::Project *project)
{
m_testLanguages.clear();
diff --git a/src/plugins/qmlpreview/qmldebugtranslationwidget.h b/src/plugins/qmlpreview/qmldebugtranslationwidget.h
index 7ea0760ac0..de0c7ba362 100644
--- a/src/plugins/qmlpreview/qmldebugtranslationwidget.h
+++ b/src/plugins/qmlpreview/qmldebugtranslationwidget.h
@@ -63,6 +63,9 @@ public:
void setCurrentFile(const Utils::FilePath &filepath);
void setFiles(const Utils::FilePaths &filePathes);
void updateStartupProjectTranslations();
+
+ QColor warningColor();
+ QColor foundTrColor();
private:
void updateCurrentEditor(const Core::IEditor *editor);
void updateCurrentTranslations(ProjectExplorer::Project *project);
@@ -98,6 +101,10 @@ private:
QHBoxLayout *m_selectLanguageLayout;
TestLanguageGetter m_testLanguagesGetter;
+ QColor m_warningColor;
+ QColor m_foundTrColor;
+ QColor m_lastWarningColor;
+ QColor m_lastfoundTrColor;
};
} // namespace QmlPreview
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 0fdd28182c..516b82ac72 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -4745,7 +4745,8 @@ void TextEditorWidgetPrivate::setupSelections(const PaintEventData &data,
o.start = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn());
o.length = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn()) - o.start;
}
- if (data.textCursor.hasSelection() && data.textCursor == range.cursor) {
+ if (data.textCursor.hasSelection() && data.textCursor == range.cursor
+ && data.textCursor.anchor() == range.cursor.anchor()) {
const QTextCharFormat selectionFormat = data.fontSettings.toTextCharFormat(C_SELECTION);
if (selectionFormat.background().style() != Qt::NoBrush)
o.format.setBackground(selectionFormat.background());
diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp
index c74ff4faf0..9288d9e887 100644
--- a/src/shared/proparser/qmakebuiltins.cpp
+++ b/src/shared/proparser/qmakebuiltins.cpp
@@ -931,8 +931,10 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
else if (!m_tmp2.compare(QLatin1String("lines"), Qt::CaseInsensitive))
lines = true;
}
- int exitCode;
- QByteArray bytes = getCommandOutput(args.at(0).toQString(), &exitCode);
+ int exitCode = 0;
+ QByteArray bytes;
+ if (m_option->runSystemFunction)
+ bytes = getCommandOutput(args.at(0).toQString(), &exitCode);
if (args.count() > 2 && !args.at(2).isEmpty()) {
m_valuemapStack.top()[args.at(2).toKey()] =
ProStringList(ProString(QString::number(exitCode)));
@@ -1778,6 +1780,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
evalError(fL1S("system(exec) requires one argument."));
return ReturnFalse;
}
+ if (!m_option->runSystemFunction)
+ return ReturnTrue;
if (m_cumulative) // Anything else would be insanity
return ReturnFalse;
#ifndef QT_BOOTSTRAPPED
diff --git a/src/shared/proparser/qmakeglobals.h b/src/shared/proparser/qmakeglobals.h
index 96e2299214..c8efbb2252 100644
--- a/src/shared/proparser/qmakeglobals.h
+++ b/src/shared/proparser/qmakeglobals.h
@@ -110,6 +110,7 @@ public:
QString qmakespec, xqmakespec;
QString user_template, user_template_prefix;
QString extra_cmds[4];
+ bool runSystemFunction = false;
#ifdef PROEVALUATOR_DEBUG
int debugLevel;
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index 3d2e53a48c..85809020d8 100644
--- a/src/tools/qml2puppet/CMakeLists.txt
+++ b/src/tools/qml2puppet/CMakeLists.txt
@@ -174,6 +174,7 @@ extend_qtc_executable(qml2puppet
qt5capturepreviewnodeinstanceserver.cpp qt5capturepreviewnodeinstanceserver.h
nodeinstanceserverdispatcher.cpp nodeinstanceserverdispatcher.h
capturenodeinstanceserverdispatcher.cpp capturenodeinstanceserverdispatcher.h
+ qt5captureimagenodeinstanceserver.cpp qt5captureimagenodeinstanceserver.h
)
extend_qtc_executable(qml2puppet
diff --git a/src/tools/qml2puppet/qml2puppet.qbs b/src/tools/qml2puppet/qml2puppet.qbs
index f38a6c8215..742c20406e 100644
--- a/src/tools/qml2puppet/qml2puppet.qbs
+++ b/src/tools/qml2puppet/qml2puppet.qbs
@@ -224,6 +224,8 @@ QtcTool {
"instances/servernodeinstance.h",
"instances/qt5capturepreviewnodeinstanceserver.cpp",
"instances/qt5capturepreviewnodeinstanceserver.h",
+ "instances/qt5captureimagenodeinstanceserver.cpp",
+ "instances/qt5captureimagenodeinstanceserver.h",
"instances/nodeinstanceserverdispatcher.cpp",
"instances/nodeinstanceserverdispatcher.h",
"instances/capturenodeinstanceserverdispatcher.cpp",
diff --git a/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h
index 50d0e22a2d..50dcf529b6 100644
--- a/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/tests/unit/mockup/qmldesigner/designercore/include/nodeinstanceview.h
@@ -28,6 +28,10 @@
#include "qmldesignercorelib_global.h"
#include "abstractview.h"
+namespace ProjectExplorer {
+class Target;
+}
+
namespace QmlDesigner {
class NodeInstanceView : public AbstractView
@@ -88,6 +92,8 @@ public:
void requestModelNodePreviewImage(const ModelNode &node) {}
void sendToken(const QString &token, int number, const QVector<ModelNode> &nodeVector) {}
+ void setTarget(ProjectExplorer::Target *newTarget) {}
+ void setCrashCallback(std::function<void()>) {}
};
} // namespace QmlDesigner
diff --git a/tests/unit/unittest/CMakeLists.txt b/tests/unit/unittest/CMakeLists.txt
index ff0c1abaad..13741ae478 100644
--- a/tests/unit/unittest/CMakeLists.txt
+++ b/tests/unit/unittest/CMakeLists.txt
@@ -174,6 +174,19 @@ add_qtc_test(unittest GTEST
sqlstatementbuilder-test.cpp
createtablesqlstatementbuilder-test.cpp
sqlitevalue-test.cpp
+ imagecache-test.cpp
+ imagecachegenerator-test.cpp
+ imagecachestorage-test.cpp
+ sqlitedatabasemock.h
+ sqlitereadstatementmock.cpp sqlitereadstatementmock.h
+ sqlitestatementmock.h
+ sqlitetransactionbackendmock.h
+ sqlitewritestatementmock.cpp sqlitewritestatementmock.h
+ notification.h
+ mocktimestampprovider.h
+ imagecachecollectormock.h
+ mockimagecachegenerator.h
+ mockimagecachestorage.h
)
function(extend_qtc_test_with_target_sources target)
@@ -335,6 +348,7 @@ extend_qtc_test(unittest
"${QmlDesignerDir}"
"${QmlDesignerDir}/designercore"
"${QmlDesignerDir}/designercore/include"
+ "${QmlDesignerDir}/designercore/imagecache"
"${QmlDesignerDir}/../../../share/qtcreator/qml/qmlpuppet/interfaces"
"${QmlDesignerDir}/../../../share/qtcreator/qml/qmlpuppet/types"
DEFINES
@@ -382,6 +396,13 @@ extend_qtc_test(unittest
model/signalhandlerproperty.cpp include/signalhandlerproperty.h
model/variantproperty.cpp include/variantproperty.h
rewritertransaction.cpp rewritertransaction.h
+ imagecache/imagecache.cpp include/imagecache.h
+ imagecache/imagecachecollectorinterface.h
+ imagecache/imagecachegenerator.cpp imagecache/imagecachegenerator.h
+ imagecache/imagecachegeneratorinterface.h
+ imagecache/imagecachestorage.h
+ imagecache/imagecachestorageinterface.h
+ imagecache/timestampproviderinterface.h
include/qmldesignercorelib_global.h
diff --git a/tests/unit/unittest/gmock_dependency.pri b/tests/unit/unittest/gmock_dependency.pri
index 1af7b95f09..614c6379d3 100644
--- a/tests/unit/unittest/gmock_dependency.pri
+++ b/tests/unit/unittest/gmock_dependency.pri
@@ -14,10 +14,8 @@ defineTest(setGoogleTestDirectories) {
}
isEmpty(GOOGLETEST_DIR) {
- exists($$PWD/../../../../googletest) {
- setGoogleTestDirectories($$PWD/../../../../googletest)
- } else: exists($$PWD/../../../../../googletest) {
- setGoogleTestDirectories($$PWD/../../../../../googletest)
+ exists($$PWD/3rdparty/googletest) {
+ setGoogleTestDirectories($$PWD/3rdparty/googletest)
} else: linux {
GTEST_INCLUDE_DIR = /usr/include/gtest
GMOCK_INCLUDE_DIR = /usr/include/gmock
diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h
index 99514fe9b4..4419c8236d 100644
--- a/tests/unit/unittest/google-using-declarations.h
+++ b/tests/unit/unittest/google-using-declarations.h
@@ -35,12 +35,16 @@ using testing::An;
using testing::AnyNumber;
using testing::AnyOf;
using testing::Assign;
+using testing::AtLeast;
+using testing::AtMost;
+using testing::Between;
using testing::ByMove;
using testing::ByRef;
using testing::ContainerEq;
using testing::Contains;
using testing::ElementsAre;
using testing::Eq;
+using testing::Exactly;
using testing::Field;
using testing::Ge;
using testing::Gt;
diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp
index 8d84ad4d2e..c9c871475d 100644
--- a/tests/unit/unittest/gtest-creator-printing.cpp
+++ b/tests/unit/unittest/gtest-creator-printing.cpp
@@ -70,6 +70,7 @@
#include <usedmacro.h>
#include <utils/link.h>
#include <variantproperty.h>
+#include <qmldesigner/designercore/imagecache/imagecachestorageinterface.h>
#include <sqlite3ext.h>
@@ -1468,6 +1469,15 @@ std::ostream &operator<<(std::ostream &out, const VariantProperty &property)
return out << "(" << property.parentModelNode() << ", " << property.name() << ", "
<< property.value() << ")";
}
+
+namespace Internal {
+std::ostream &operator<<(std::ostream &out, const ImageCacheStorageEntry &entry)
+{
+ return out << "(" << entry.image << ", " << entry.hasEntry << ")";
+}
+
+} // namespace Internal
+
} // namespace QmlDesigner
void setFilePathCache(ClangBackEnd::FilePathCaching *cache)
diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h
index 565479be03..cdef782578 100644
--- a/tests/unit/unittest/gtest-creator-printing.h
+++ b/tests/unit/unittest/gtest-creator-printing.h
@@ -356,6 +356,13 @@ class VariantProperty;
std::ostream &operator<<(std::ostream &out, const ModelNode &node);
std::ostream &operator<<(std::ostream &out, const VariantProperty &property);
+
+namespace Internal {
+class ImageCacheStorageEntry;
+
+std::ostream &operator<<(std::ostream &out, const ImageCacheStorageEntry &entry);
+
+} // namespace Internal
} // 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 c097fd0b4c..445ebf7f51 100644
--- a/tests/unit/unittest/gtest-qt-printing.cpp
+++ b/tests/unit/unittest/gtest-qt-printing.cpp
@@ -85,6 +85,11 @@ std::ostream &operator<<(std::ostream &out, const QTextCharFormat &format)
return out;
}
+std::ostream &operator<<(std::ostream &out, const QImage &image)
+{
+ return out << "(" << image.width() << ", " << image.height() << ", " << image.format() << ")";
+}
+
void PrintTo(const QString &text, std::ostream *os)
{
*os << text;
diff --git a/tests/unit/unittest/gtest-qt-printing.h b/tests/unit/unittest/gtest-qt-printing.h
index ebaeb2c785..54db4ee105 100644
--- a/tests/unit/unittest/gtest-qt-printing.h
+++ b/tests/unit/unittest/gtest-qt-printing.h
@@ -34,11 +34,13 @@ QT_BEGIN_NAMESPACE
class QVariant;
class QString;
class QTextCharFormat;
+class QImage;
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);
+std::ostream &operator<<(std::ostream &out, const QImage &image);
void PrintTo(const QString &text, std::ostream *os);
void PrintTo(const QVariant &variant, std::ostream *os);
diff --git a/tests/unit/unittest/imagecache-test.cpp b/tests/unit/unittest/imagecache-test.cpp
new file mode 100644
index 0000000000..4219335774
--- /dev/null
+++ b/tests/unit/unittest/imagecache-test.cpp
@@ -0,0 +1,326 @@
+/****************************************************************************
+**
+** 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 "googletest.h"
+
+#include "mockimagecachegenerator.h"
+#include "mockimagecachestorage.h"
+#include "mocktimestampprovider.h"
+#include "notification.h"
+
+#include <imagecache.h>
+
+namespace {
+
+class ImageCache : public testing::Test
+{
+protected:
+ Notification notification;
+ Notification waitInThread;
+ NiceMock<MockImageCacheStorage> mockStorage;
+ NiceMock<MockImageCacheGenerator> mockGenerator;
+ NiceMock<MockTimeStampProvider> mockTimeStampProvider;
+ QmlDesigner::ImageCache cache{mockStorage, mockGenerator, mockTimeStampProvider};
+ NiceMock<MockFunction<void()>> mockAbortCallback;
+ NiceMock<MockFunction<void(const QImage &image)>> mockCaptureCallback;
+ QImage image1{10, 10, QImage::Format_ARGB32};
+};
+
+TEST_F(ImageCache, RequestImageFetchesImageFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
+ });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageFetchesImageFromStorageWithTimeStamp)
+{
+ EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillRepeatedly(Return(Sqlite::TimeStamp{123}));
+ EXPECT_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, false};
+ });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsCaptureCallbackWithImageFromStorage)
+{
+ ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsAbortCallbackWithoutImage)
+{
+ ON_CALL(mockStorage, fetchImage(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, true}));
+
+ EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageRequestImageFromGenerator)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}), _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&callback, auto) { notification.notify(); });
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsCaptureCallbackWithImageFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&callback, auto) {
+ callback(QImage{image1});
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestImageCallsAbortCallbackFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&, auto &&abortCallback) {
+ abortCallback();
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockAbortCallback, Call());
+
+ cache.requestImage("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconFetchesIconFromStorage)
+{
+ EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{{}, false};
+ });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconFetchesIconFromStorageWithTimeStamp)
+{
+ EXPECT_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillRepeatedly(Return(Sqlite::TimeStamp{123}));
+ EXPECT_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123})))
+ .WillRepeatedly([&](Utils::SmallStringView, auto) {
+ notification.notify();
+ return QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, false};
+ });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsCaptureCallbackWithImageFromStorage)
+{
+ ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{image1, true}));
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1))).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsAbortCallbackWithoutIcon)
+{
+ ON_CALL(mockStorage, fetchIcon(Eq("/path/to/Component.qml"), _))
+ .WillByDefault(Return(QmlDesigner::ImageCacheStorageInterface::Entry{QImage{}, true}));
+
+ EXPECT_CALL(mockAbortCallback, Call()).WillRepeatedly([&] { notification.notify(); });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconRequestImageFromGenerator)
+{
+ ON_CALL(mockTimeStampProvider, timeStamp(Eq("/path/to/Component.qml")))
+ .WillByDefault(Return(Sqlite::TimeStamp{123}));
+
+ EXPECT_CALL(mockGenerator,
+ generateImage(Eq("/path/to/Component.qml"), Eq(Sqlite::TimeStamp{123}), _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&callback, auto) { notification.notify(); });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsCaptureCallbackWithImageFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&callback, auto) {
+ callback(QImage{image1});
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockCaptureCallback, Call(Eq(image1)));
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, RequestIconCallsAbortCallbackFromGenerator)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillByDefault([&](auto &&, auto, auto &&, auto &&abortCallback) {
+ abortCallback();
+ notification.notify();
+ });
+
+ EXPECT_CALL(mockAbortCallback, Call());
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCache, CleanRemovesEntries)
+{
+ EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component1.qml"), _, _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&mockCaptureCallback, auto &&) {
+ mockCaptureCallback(QImage{});
+ waitInThread.wait();
+ });
+ EXPECT_CALL(mockGenerator, generateImage(_, _, _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&mockCaptureCallback, auto &&) {
+ mockCaptureCallback(QImage{});
+ });
+ cache.requestIcon("/path/to/Component1.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+
+ EXPECT_CALL(mockCaptureCallback, Call(_)).Times(AtMost(1));
+
+ cache.requestIcon("/path/to/Component3.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ cache.clean();
+ waitInThread.notify();
+}
+
+TEST_F(ImageCache, CleanCallsAbort)
+{
+ ON_CALL(mockGenerator, generateImage(Eq("/path/to/Component1.qml"), _, _, _))
+ .WillByDefault(
+ [&](auto &&, auto, auto &&mockCaptureCallback, auto &&) { waitInThread.wait(); });
+ cache.requestIcon("/path/to/Component1.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ cache.requestIcon("/path/to/Component2.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+
+ EXPECT_CALL(mockAbortCallback, Call()).Times(AtLeast(2));
+
+ cache.requestIcon("/path/to/Component3.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ cache.clean();
+ waitInThread.notify();
+}
+
+TEST_F(ImageCache, CleanCallsGeneratorClean)
+{
+ EXPECT_CALL(mockGenerator, clean()).Times(AtLeast(1));
+
+ cache.clean();
+}
+
+TEST_F(ImageCache, AfterCleanNewJobsWorks)
+{
+ cache.clean();
+
+ EXPECT_CALL(mockGenerator, generateImage(Eq("/path/to/Component.qml"), _, _, _))
+ .WillRepeatedly([&](auto &&, auto, auto &&, auto &&) { notification.notify(); });
+
+ cache.requestIcon("/path/to/Component.qml",
+ mockCaptureCallback.AsStdFunction(),
+ mockAbortCallback.AsStdFunction());
+ notification.wait();
+}
+
+} // namespace
diff --git a/tests/unit/unittest/imagecachecollectormock.h b/tests/unit/unittest/imagecachecollectormock.h
new file mode 100644
index 0000000000..93520c418d
--- /dev/null
+++ b/tests/unit/unittest/imagecachecollectormock.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+**
+** 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 <imagecachecollectorinterface.h>
+
+class ImageCacheCollectorMock : public QmlDesigner::ImageCacheCollectorInterface
+{
+public:
+ MOCK_METHOD(void,
+ start,
+ (Utils::SmallStringView filePath,
+ ImageCacheCollectorInterface::CaptureCallback captureCallback,
+ ImageCacheCollectorInterface::AbortCallback abortCallback),
+ (override));
+};
diff --git a/tests/unit/unittest/imagecachegenerator-test.cpp b/tests/unit/unittest/imagecachegenerator-test.cpp
new file mode 100644
index 0000000000..f152bd83ad
--- /dev/null
+++ b/tests/unit/unittest/imagecachegenerator-test.cpp
@@ -0,0 +1,240 @@
+/****************************************************************************
+**
+** 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 "googletest.h"
+
+#include "imagecachecollectormock.h"
+#include "mockimagecachestorage.h"
+#include "notification.h"
+
+#include <imagecachegenerator.h>
+
+#include <mutex>
+
+namespace {
+
+class ImageCacheGenerator : public testing::Test
+{
+protected:
+ template<typename Callable, typename... Arguments>
+ static void executeAsync(Callable &&call, Arguments... arguments)
+ {
+ std::thread thread(
+ [](Callable &&call, Arguments... arguments) {
+ call(std::forward<Arguments>(arguments)...);
+ },
+ std::forward<Callable>(call),
+ std::forward<Arguments>(arguments)...);
+ thread.detach();
+ }
+
+protected:
+ Notification waitInThread;
+ Notification notification;
+ QImage image1{10, 10, QImage::Format_ARGB32};
+ NiceMock<MockFunction<void(const QImage &)>> imageCallbackMock;
+ NiceMock<MockFunction<void()>> abortCallbackMock;
+ NiceMock<ImageCacheCollectorMock> collectorMock;
+ NiceMock<MockImageCacheStorage> storageMock;
+ QmlDesigner::ImageCacheGenerator generator{collectorMock, storageMock};
+};
+
+TEST_F(ImageCacheGenerator, CallsCollectorWithCaptureCallback)
+{
+ EXPECT_CALL(collectorMock, start(Eq("name"), _, _))
+ .WillRepeatedly([&](auto, auto captureCallback, auto) { captureCallback(QImage{image1}); });
+ EXPECT_CALL(imageCallbackMock, Call(_)).WillRepeatedly([&](const QImage &) {
+ notification.notify();
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, CallsCollectorOnlyIfNotProcessing)
+{
+ EXPECT_CALL(collectorMock, start(Eq("name"), _, _)).WillRepeatedly([&](auto, auto, auto) {
+ notification.notify();
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait(2);
+}
+
+TEST_F(ImageCacheGenerator, ProcessTaskAfterFirstFinished)
+{
+ ON_CALL(imageCallbackMock, Call(_)).WillByDefault([&](const QImage &) { notification.notify(); });
+
+ EXPECT_CALL(collectorMock, start(Eq("name"), _, _)).WillOnce([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+ EXPECT_CALL(collectorMock, start(Eq("name2"), _, _)).WillOnce([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name2", {}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait(2);
+}
+
+TEST_F(ImageCacheGenerator, DontCrashAtDestructingGenerator)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+
+ generator.generateImage("name", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name2", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name3", {}, imageCallbackMock.AsStdFunction(), {});
+ generator.generateImage("name4", {}, imageCallbackMock.AsStdFunction(), {});
+}
+
+TEST_F(ImageCacheGenerator, StoreImage)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(image1)))
+ .WillRepeatedly([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage("name", {11}, imageCallbackMock.AsStdFunction(), {});
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, StoreNullImage)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{});
+ });
+
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{})))
+ .WillRepeatedly([&](auto, auto, auto) { notification.notify(); });
+
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, AbortCallback)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{image1});
+ });
+ ON_CALL(collectorMock, start(Eq("name2"), _, _)).WillByDefault([&](auto, auto, auto abortCallback) {
+ abortCallback();
+ });
+
+ EXPECT_CALL(imageCallbackMock, Call(_)).WillOnce([&](const QImage &) { notification.notify(); });
+ EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
+
+ generator.generateImage("name",
+ {},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ generator.generateImage("name2",
+ {},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait(2);
+}
+
+TEST_F(ImageCacheGenerator, StoreNullImageForAbortCallback)
+{
+ ON_CALL(collectorMock, start(_, _, _)).WillByDefault([&](auto, auto, auto abortCallback) {
+ abortCallback();
+ notification.notify();
+ });
+
+ EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
+ EXPECT_CALL(storageMock, storeImage(Eq("name"), Eq(Sqlite::TimeStamp{11}), Eq(QImage{})));
+
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, AbortForEmptyImage)
+{
+ NiceMock<MockFunction<void()>> abortCallbackMock;
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback(QImage{});
+ });
+
+ EXPECT_CALL(abortCallbackMock, Call()).WillOnce([&]() { notification.notify(); });
+
+ generator.generateImage("name",
+ {},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, CallWalCheckpointFullIfQueueIsEmpty)
+{
+ ON_CALL(collectorMock, start(Eq("name"), _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback({});
+ });
+
+ EXPECT_CALL(storageMock, walCheckpointFull()).WillRepeatedly([&]() { notification.notify(); });
+
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ generator.generateImage("name2",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ notification.wait();
+}
+
+TEST_F(ImageCacheGenerator, Clean)
+{
+ ON_CALL(collectorMock, start(_, _, _)).WillByDefault([&](auto, auto captureCallback, auto) {
+ captureCallback({});
+ waitInThread.wait();
+ });
+ generator.generateImage("name",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+ generator.generateImage("name2",
+ {11},
+ imageCallbackMock.AsStdFunction(),
+ abortCallbackMock.AsStdFunction());
+
+ EXPECT_CALL(imageCallbackMock, Call(_)).Times(0);
+
+ generator.clean();
+ waitInThread.notify();
+}
+
+} // namespace
diff --git a/tests/unit/unittest/imagecachestorage-test.cpp b/tests/unit/unittest/imagecachestorage-test.cpp
new file mode 100644
index 0000000000..aaf30526b6
--- /dev/null
+++ b/tests/unit/unittest/imagecachestorage-test.cpp
@@ -0,0 +1,334 @@
+/****************************************************************************
+**
+** 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 "googletest.h"
+
+#include "sqlitedatabasemock.h"
+
+#include <imagecachestorage.h>
+#include <sqlitedatabase.h>
+
+namespace {
+
+MATCHER_P2(IsEntry,
+ image,
+ hasEntry,
+ std::string(negation ? "is't" : "is")
+ + PrintToString(QmlDesigner::ImageCacheStorageInterface::Entry{image, hasEntry}))
+{
+ const QmlDesigner::ImageCacheStorageInterface::Entry &entry = arg;
+ return entry.image == image && entry.hasEntry == hasEntry;
+}
+
+class ImageCacheStorageTest : public testing::Test
+{
+protected:
+ QImage createImage()
+ {
+ QImage image{150, 150, QImage::Format_ARGB32};
+ image.fill(QColor{128, 64, 0, 11});
+ image.setPixelColor(75, 75, QColor{1, 255, 33, 196});
+
+ return image;
+ }
+
+protected:
+ using ReadStatement = QmlDesigner::ImageCacheStorage<SqliteDatabaseMock>::ReadStatement;
+ using WriteStatement = QmlDesigner::ImageCacheStorage<SqliteDatabaseMock>::WriteStatement;
+
+ NiceMock<SqliteDatabaseMock> databaseMock;
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+ ReadStatement &selectImageStatement = storage.selectImageStatement;
+ ReadStatement &selectIconStatement = storage.selectIconStatement;
+ WriteStatement &upsertImageStatement = storage.upsertImageStatement;
+ QImage image1{createImage()};
+};
+
+TEST_F(ImageCacheStorageTest, Initialize)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, exclusiveBegin());
+ EXPECT_CALL(databaseMock,
+ execute(Eq("CREATE TABLE IF NOT EXISTS images(id INTEGER PRIMARY KEY, name TEXT "
+ "NOT NULL UNIQUE, mtime INTEGER, image BLOB, icon BLOB)")));
+ EXPECT_CALL(databaseMock, commit());
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(databaseMock, prepare(Eq(selectImageStatement.sqlStatement)));
+ EXPECT_CALL(databaseMock, prepare(Eq(selectIconStatement.sqlStatement)));
+ EXPECT_CALL(databaseMock, prepare(Eq(upsertImageStatement.sqlStatement)));
+ EXPECT_CALL(databaseMock, commit());
+
+ QmlDesigner::ImageCacheStorage<SqliteDatabaseMock> storage{databaseMock};
+}
+
+TEST_F(ImageCacheStorageTest, FetchImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchImage("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchImageCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)))
+ .WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, rollback());
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectImageStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchImage("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchIconCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectIconStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchIcon("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, FetchIconCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectIconStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)))
+ .WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, rollback());
+ EXPECT_CALL(databaseMock, deferredBegin());
+ EXPECT_CALL(selectIconStatement,
+ valueReturnBlob(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123)));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.fetchIcon("/path/to/component", {123});
+}
+
+TEST_F(ImageCacheStorageTest, StoreImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(upsertImageStatement,
+ write(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123),
+ A<Sqlite::BlobView>(),
+ A<Sqlite::BlobView>()));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.storeImage("/path/to/component", {123}, image1);
+}
+
+TEST_F(ImageCacheStorageTest, StoreEmptyImageCalls)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(upsertImageStatement,
+ write(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123),
+ A<Sqlite::NullValue>(),
+ A<Sqlite::NullValue>()));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.storeImage("/path/to/component", {123}, QImage{});
+}
+
+TEST_F(ImageCacheStorageTest, StoreImageCallsIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, immediateBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, immediateBegin());
+ EXPECT_CALL(upsertImageStatement,
+ write(TypedEq<Utils::SmallStringView>("/path/to/component"),
+ TypedEq<long long>(123),
+ A<Sqlite::NullValue>(),
+ A<Sqlite::NullValue>()));
+ EXPECT_CALL(databaseMock, commit());
+
+ storage.storeImage("/path/to/component", {123}, QImage{});
+}
+
+TEST_F(ImageCacheStorageTest, CallWalCheckointFull)
+{
+ EXPECT_CALL(databaseMock, walCheckpointFull());
+
+ storage.walCheckpointFull();
+}
+
+TEST_F(ImageCacheStorageTest, CallWalCheckointFullIsBusy)
+{
+ InSequence s;
+
+ EXPECT_CALL(databaseMock, walCheckpointFull()).WillOnce(Throw(Sqlite::StatementIsBusy("busy")));
+ EXPECT_CALL(databaseMock, walCheckpointFull());
+
+ storage.walCheckpointFull();
+}
+
+class ImageCacheStorageSlowTest : public testing::Test
+{
+protected:
+ QImage createImage()
+ {
+ QImage image{150, 150, QImage::Format_ARGB32};
+ image.fill(QColor{128, 64, 0, 11});
+ image.setPixelColor(1, 1, QColor{1, 255, 33, 196});
+
+ return image;
+ }
+
+protected:
+ Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory};
+ QmlDesigner::ImageCacheStorage<Sqlite::Database> storage{database};
+ QImage image1{createImage()};
+ QImage image2{10, 10, QImage::Format_ARGB32};
+ QImage icon1{image1.scaled(96, 96)};
+};
+
+TEST_F(ImageCacheStorageSlowTest, StoreImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(image1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreEmptyImageAfterEntry)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ storage.storeImage("/path/to/component", {123}, QImage{});
+
+ ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(QImage{}, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, StoreEmptyEntry)
+{
+ storage.storeImage("/path/to/component", {123}, QImage{});
+
+ ASSERT_THAT(storage.fetchImage("/path/to/component", {123}), IsEntry(QImage{}, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNonExistingImageIsEmpty)
+{
+ auto image = storage.fetchImage("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchSameTimeImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchImage("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(image1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchImage("/path/to/component", {124});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNewerImage)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchImage("/path/to/component", {122});
+
+ ASSERT_THAT(image, IsEntry(image1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNonExistingIconIsEmpty)
+{
+ auto image = storage.fetchIcon("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchSameTimeIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchIcon("/path/to/component", {123});
+
+ ASSERT_THAT(image, IsEntry(icon1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DoNotFetchOlderIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchIcon("/path/to/component", {124});
+
+ ASSERT_THAT(image, IsEntry(QImage{}, false));
+}
+
+TEST_F(ImageCacheStorageSlowTest, FetchNewerIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image1);
+
+ auto image = storage.fetchIcon("/path/to/component", {122});
+
+ ASSERT_THAT(image, IsEntry(icon1, true));
+}
+
+TEST_F(ImageCacheStorageSlowTest, DontScaleSmallerIcon)
+{
+ storage.storeImage("/path/to/component", {123}, image2);
+
+ auto image = storage.fetchImage("/path/to/component", {122});
+
+ ASSERT_THAT(image, IsEntry(image2, true));
+}
+
+} // namespace
diff --git a/tests/unit/unittest/mockimagecachegenerator.h b/tests/unit/unittest/mockimagecachegenerator.h
new file mode 100644
index 0000000000..ffe8d9c709
--- /dev/null
+++ b/tests/unit/unittest/mockimagecachegenerator.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+**
+** 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 <imagecachegeneratorinterface.h>
+
+class MockImageCacheGenerator : public QmlDesigner::ImageCacheGeneratorInterface
+{
+public:
+ MOCK_METHOD(void,
+ generateImage,
+ (Utils::SmallStringView name,
+ Sqlite::TimeStamp timeStamp,
+ CaptureCallback &&captureCallback,
+ AbortCallback &&abortCallback),
+ (override));
+ MOCK_METHOD(void, clean, (), (override));
+};
diff --git a/tests/unit/unittest/mockimagecachestorage.h b/tests/unit/unittest/mockimagecachestorage.h
new file mode 100644
index 0000000000..add2cfaa96
--- /dev/null
+++ b/tests/unit/unittest/mockimagecachestorage.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 <imagecachestorageinterface.h>
+
+class MockImageCacheStorage : public QmlDesigner::ImageCacheStorageInterface
+{
+public:
+ MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::Entry,
+ fetchImage,
+ (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
+ (const, override));
+
+ MOCK_METHOD(QmlDesigner::ImageCacheStorageInterface::Entry,
+ fetchIcon,
+ (Utils::SmallStringView name, Sqlite::TimeStamp minimumTimeStamp),
+ (const, override));
+
+ MOCK_METHOD(void,
+ storeImage,
+ (Utils::SmallStringView name, Sqlite::TimeStamp newTimeStamp, const QImage &image),
+ (override));
+ MOCK_METHOD(void, walCheckpointFull, (), (override));
+};
diff --git a/tests/unit/unittest/mocktimestampprovider.h b/tests/unit/unittest/mocktimestampprovider.h
new file mode 100644
index 0000000000..0adad4c030
--- /dev/null
+++ b/tests/unit/unittest/mocktimestampprovider.h
@@ -0,0 +1,36 @@
+/****************************************************************************
+**
+** 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 <timestampproviderinterface.h>
+
+class MockTimeStampProvider : public QmlDesigner::TimeStampProviderInterface
+{
+public:
+ MOCK_METHOD(Sqlite::TimeStamp, timeStamp, (Utils::SmallStringView name), (const, override));
+};
diff --git a/tests/unit/unittest/notification.h b/tests/unit/unittest/notification.h
new file mode 100644
index 0000000000..a27d9acb76
--- /dev/null
+++ b/tests/unit/unittest/notification.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.
+**
+****************************************************************************/
+
+#include "googletest.h"
+
+#include <condition_variable>
+#include <mutex>
+
+class Notification
+{
+public:
+ void wait(int count = 1)
+ {
+ std::unique_lock<std::mutex> lock{m_mutex};
+ m_waitCount += count;
+ if (m_waitCount > 0)
+ m_condition.wait(lock, [&] { return m_waitCount <= 0; });
+ }
+
+ void notify()
+ {
+ {
+ std::unique_lock<std::mutex> lock{m_mutex};
+ --m_waitCount;
+ }
+
+ m_condition.notify_all();
+ }
+
+private:
+ std::mutex m_mutex;
+ std::condition_variable m_condition;
+ int m_waitCount = 0;
+};
diff --git a/tests/unit/unittest/sqlitedatabasemock.h b/tests/unit/unittest/sqlitedatabasemock.h
new file mode 100644
index 0000000000..8c1179e424
--- /dev/null
+++ b/tests/unit/unittest/sqlitedatabasemock.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "sqlitereadstatementmock.h"
+#include "sqlitetransactionbackendmock.h"
+#include "sqlitewritestatementmock.h"
+
+#include <sqlitedatabaseinterface.h>
+#include <sqlitetable.h>
+#include <sqlitetransaction.h>
+
+#include <utils/smallstringview.h>
+
+class SqliteDatabaseMock : public SqliteTransactionBackendMock, public Sqlite::DatabaseInterface
+{
+public:
+ using ReadStatement = NiceMock<SqliteReadStatementMock>;
+ using WriteStatement = NiceMock<SqliteWriteStatementMock>;
+
+ MOCK_METHOD(void, prepare, (Utils::SmallStringView sqlStatement), ());
+
+ MOCK_METHOD(void, execute, (Utils::SmallStringView sqlStatement), ());
+
+ MOCK_METHOD(int64_t, lastInsertedRowId, (), (const));
+
+ MOCK_METHOD(void, setLastInsertedRowId, (int64_t), (const));
+
+ MOCK_METHOD(bool, isInitialized, (), (const));
+
+ MOCK_METHOD(void, setIsInitialized, (bool), ());
+
+ MOCK_METHOD(void, walCheckpointFull, (), (override));
+
+ MOCK_METHOD(void,
+ setUpdateHook,
+ (void *object,
+ void (*)(void *object, int, char const *database, char const *, long long rowId)),
+ (override));
+
+ MOCK_METHOD(void, resetUpdateHook, (), (override));
+
+ MOCK_METHOD(void, applyAndUpdateSessions, (), (override));
+
+ MOCK_METHOD(void, setAttachedTables, (const Utils::SmallStringVector &tables), (override));
+};
+
diff --git a/tests/unit/unittest/sqlitereadstatementmock.cpp b/tests/unit/unittest/sqlitereadstatementmock.cpp
new file mode 100644
index 0000000000..e3e22d4e16
--- /dev/null
+++ b/tests/unit/unittest/sqlitereadstatementmock.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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 "sqlitereadstatementmock.h"
+
+#include "sqlitedatabasemock.h"
+
+SqliteReadStatementMock::SqliteReadStatementMock(Utils::SmallStringView sqlStatement,
+ SqliteDatabaseMock &databaseMock)
+ : sqlStatement(sqlStatement)
+{
+ databaseMock.prepare(sqlStatement);
+}
+
+template<>
+std::vector<Utils::SmallString> SqliteReadStatementMock::values<Utils::SmallString>(std::size_t reserveSize)
+{
+ return valuesReturnStringVector(reserveSize);
+}
+
+template<>
+std::vector<long long> SqliteReadStatementMock::values<long long>(std::size_t reserveSize)
+{
+ return valuesReturnRowIds(reserveSize);
+}
+
+template<>
+Utils::optional<long long> SqliteReadStatementMock::value<long long>()
+{
+ return valueReturnLongLong();
+}
+
+template<>
+Utils::optional<Sqlite::ByteArrayBlob> SqliteReadStatementMock::value<Sqlite::ByteArrayBlob>(
+ const Utils::SmallStringView &name, const long long &blob)
+{
+ return valueReturnBlob(name, blob);
+}
diff --git a/tests/unit/unittest/sqlitereadstatementmock.h b/tests/unit/unittest/sqlitereadstatementmock.h
new file mode 100644
index 0000000000..f74cce1e8e
--- /dev/null
+++ b/tests/unit/unittest/sqlitereadstatementmock.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 <sqliteblob.h>
+#include <utils/optional.h>
+#include <utils/smallstring.h>
+
+#include <QImage>
+
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+class SqliteDatabaseMock;
+
+class SqliteReadStatementMock
+{
+public:
+ SqliteReadStatementMock() = default;
+ SqliteReadStatementMock(Utils::SmallStringView sqlStatement, SqliteDatabaseMock &databaseMock);
+
+ MOCK_METHOD(std::vector<Utils::SmallString>, valuesReturnStringVector, (std::size_t), ());
+
+ MOCK_METHOD(std::vector<long long>, valuesReturnRowIds, (std::size_t), ());
+ MOCK_METHOD(Utils::optional<long long>, valueReturnLongLong, (), ());
+ MOCK_METHOD(Utils::optional<Sqlite::ByteArrayBlob>,
+ valueReturnBlob,
+ (Utils::SmallStringView, long long),
+ ());
+
+ template<typename ResultType, int ResultTypeCount = 1, typename... QueryType>
+ std::vector<ResultType> values(std::size_t reserveSize, const QueryType &... queryValues);
+
+ template <typename ResultType,
+ int ResultTypeCount = 1,
+ typename... QueryType>
+ std::vector<ResultType> values(std::size_t reserveSize);
+
+ template <typename ResultType,
+ int ResultTypeCount = 1,
+ template <typename...> class QueryContainerType,
+ typename QueryElementType>
+ std::vector<ResultType> values(std::size_t reserveSize,
+ const QueryContainerType<QueryElementType> &queryValues);
+
+ template <typename ResultType,
+ int ResultTypeCount = 1,
+ typename... QueryTypes>
+ Utils::optional<ResultType> value(const QueryTypes&... queryValues);
+
+public:
+ Utils::SmallString sqlStatement;
+};
+
+template<>
+std::vector<Utils::SmallString> SqliteReadStatementMock::values<Utils::SmallString>(
+ std::size_t reserveSize);
+
+template<>
+std::vector<long long> SqliteReadStatementMock::values<long long>(std::size_t reserveSize);
+
+template<>
+Utils::optional<long long> SqliteReadStatementMock::value<long long>();
+
+template<>
+Utils::optional<Sqlite::ByteArrayBlob> SqliteReadStatementMock::value<Sqlite::ByteArrayBlob>(
+ const Utils::SmallStringView &name, const long long &blob);
diff --git a/tests/unit/unittest/sqlitestatement-test.cpp b/tests/unit/unittest/sqlitestatement-test.cpp
index 341679a903..19c897f7cb 100644
--- a/tests/unit/unittest/sqlitestatement-test.cpp
+++ b/tests/unit/unittest/sqlitestatement-test.cpp
@@ -27,6 +27,7 @@
#include "mocksqlitestatement.h"
#include "sqliteteststatement.h"
+#include <sqliteblob.h>
#include <sqlitedatabase.h>
#include <sqlitereadstatement.h>
#include <sqlitereadwritestatement.h>
@@ -38,14 +39,6 @@
#include <vector>
-namespace Sqlite {
-bool operator==(Utils::span<const byte> first, Utils::span<const byte> second)
-{
- return first.size() == second.size()
- && std::memcmp(first.data(), second.data(), first.size()) == 0;
-}
-} // namespace Sqlite
-
namespace {
using Sqlite::Database;
@@ -270,7 +263,7 @@ TEST_F(SqliteStatement, BindBlob)
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
const unsigned char chars[] = "aaafdfdlll";
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(chars);
- Utils::span<const Sqlite::byte> bytes{bytePointer, sizeof(chars) - 1};
+ Sqlite::BlobView bytes{bytePointer, sizeof(chars) - 1};
statement.bind(1, bytes);
statement.next();
@@ -281,7 +274,7 @@ TEST_F(SqliteStatement, BindBlob)
TEST_F(SqliteStatement, BindEmptyBlob)
{
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
- Utils::span<const Sqlite::byte> bytes;
+ Sqlite::BlobView bytes;
statement.bind(1, bytes);
statement.next();
@@ -341,7 +334,7 @@ TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundValu
TEST_F(SqliteStatement, BindIndexIsToLargeIsThrowingBindingIndexIsOutOfBoundBlob)
{
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
- Utils::span<const Sqlite::byte> bytes;
+ Sqlite::BlobView bytes;
ASSERT_THROW(statement.bind(2, bytes), Sqlite::BindingIndexIsOutOfRange);
}
@@ -408,34 +401,25 @@ TEST_F(SqliteStatement, WriteEmptyBlobs)
{
SqliteTestStatement statement("WITH T(blob) AS (VALUES (?)) SELECT blob FROM T", database);
- Utils::span<const Sqlite::byte> bytes;
+ Sqlite::BlobView bytes;
statement.write(bytes);
ASSERT_THAT(statement.fetchBlobValue(0), IsEmpty());
}
-class Blob
-{
-public:
- Blob(Utils::span<const Sqlite::byte> bytes)
- : bytes(bytes.begin(), bytes.end())
- {}
-
- std::vector<Sqlite::byte> bytes;
-};
-
TEST_F(SqliteStatement, WriteBlobs)
{
SqliteTestStatement statement("INSERT INTO test VALUES ('blob', 40, ?)", database);
SqliteTestStatement readStatement("SELECT value FROM test WHERE name = 'blob'", database);
const unsigned char chars[] = "aaafdfdlll";
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(chars);
- Utils::span<const Sqlite::byte> bytes{bytePointer, sizeof(chars) - 1};
+ Sqlite::BlobView bytes{bytePointer, sizeof(chars) - 1};
statement.write(bytes);
- ASSERT_THAT(readStatement.template value<Blob>(), Optional(Field(&Blob::bytes, Eq(bytes))));
+ ASSERT_THAT(readStatement.template value<Sqlite::Blob>(),
+ Optional(Field(&Sqlite::Blob::bytes, Eq(bytes))));
}
TEST_F(SqliteStatement, CannotWriteToClosedDatabase)
@@ -624,38 +608,38 @@ TEST_F(SqliteStatement, GetBlobValues)
ReadStatement statement("SELECT value FROM test WHERE name='blob'", database);
const int value = 0xDDCCBBAA;
auto bytePointer = reinterpret_cast<const Sqlite::byte *>(&value);
- Utils::span<const Sqlite::byte> bytes{bytePointer, 4};
+ Sqlite::BlobView bytes{bytePointer, 4};
- auto values = statement.values<Blob>(1);
+ auto values = statement.values<Sqlite::Blob>(1);
- ASSERT_THAT(values, ElementsAre(Field(&Blob::bytes, Eq(bytes))));
+ ASSERT_THAT(values, ElementsAre(Field(&Sqlite::Blob::bytes, Eq(bytes))));
}
TEST_F(SqliteStatement, GetEmptyBlobValueForInteger)
{
ReadStatement statement("SELECT value FROM test WHERE name='poo'", database);
- auto value = statement.value<Blob>();
+ auto value = statement.value<Sqlite::Blob>();
- ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
+ ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty())));
}
TEST_F(SqliteStatement, GetEmptyBlobValueForFloat)
{
ReadStatement statement("SELECT number FROM test WHERE name='foo'", database);
- auto value = statement.value<Blob>();
+ auto value = statement.value<Sqlite::Blob>();
- ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
+ ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty())));
}
TEST_F(SqliteStatement, GetEmptyBlobValueForText)
{
ReadStatement statement("SELECT number FROM test WHERE name='bar'", database);
- auto value = statement.value<Blob>();
+ auto value = statement.value<Sqlite::Blob>();
- ASSERT_THAT(value, Optional(Field(&Blob::bytes, IsEmpty())));
+ ASSERT_THAT(value, Optional(Field(&Sqlite::Blob::bytes, IsEmpty())));
}
TEST_F(SqliteStatement, GetOptionalSingleValueAndMultipleQueryValue)
diff --git a/tests/unit/unittest/sqlitestatementmock.h b/tests/unit/unittest/sqlitestatementmock.h
new file mode 100644
index 0000000000..ad8a73e0a5
--- /dev/null
+++ b/tests/unit/unittest/sqlitestatementmock.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** 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 <sqlitebasestatement.h>
+
+class BaseSqliteStatementMock
+{
+public:
+ MOCK_METHOD(bool, next, ());
+ MOCK_METHOD(void, step, ());
+ MOCK_METHOD(void, reset, ());
+
+ MOCK_METHOD(int, fetchIntValue, (int), (const));
+ MOCK_METHOD(long, fetchLongValue, (int), (const));
+ MOCK_METHOD(long long, fetchLongLongValue, (int), (const));
+ MOCK_METHOD(double, fetchDoubleValue, (int), (const));
+ MOCK_METHOD(Utils::SmallString, fetchSmallStringValue, (int), (const));
+ MOCK_METHOD(Utils::PathString, fetchPathStringValue, (int), (const));
+
+ template<typename Type>
+ Type fetchValue(int column) const;
+
+ MOCK_METHOD(void, bind, (int, int), ());
+ MOCK_METHOD(void, bind, (int, long long), ());
+ MOCK_METHOD(void, bind, (int, double), ());
+ MOCK_METHOD(void, bind, (int, Utils::SmallStringView), ());
+ MOCK_METHOD(void, bind, (int, long) );
+ MOCK_METHOD(int, bindingIndexForName, (Utils::SmallStringView name), (const));
+
+ MOCK_METHOD(void, prepare, (Utils::SmallStringView sqlStatement));
+};
+
+template<>
+int BaseSqliteStatementMock::fetchValue<int>(int column) const
+{
+ return fetchIntValue(column);
+}
+
+template<>
+long BaseSqliteStatementMock::fetchValue<long>(int column) const
+{
+ return fetchLongValue(column);
+}
+
+template<>
+long long BaseSqliteStatementMock::fetchValue<long long>(int column) const
+{
+ return fetchLongLongValue(column);
+}
+
+template<>
+double BaseSqliteStatementMock::fetchValue<double>(int column) const
+{
+ return fetchDoubleValue(column);
+}
+
+template<>
+Utils::SmallString BaseSqliteStatementMock::fetchValue<Utils::SmallString>(int column) const
+{
+ return fetchSmallStringValue(column);
+}
+
+template<>
+Utils::PathString BaseSqliteStatementMock::fetchValue<Utils::PathString>(int column) const
+{
+ return fetchPathStringValue(column);
+}
+
+class SqliteStatementMock : public Sqlite::StatementImplementation<NiceMock<BaseSqliteStatementMock>>
+{
+public:
+ explicit SqliteStatementMock()
+ : Sqlite::StatementImplementation<NiceMock<BaseSqliteStatementMock>>()
+ {}
+
+
+protected:
+ void checkIsWritableStatement();
+};
diff --git a/tests/unit/unittest/sqlitetransactionbackendmock.h b/tests/unit/unittest/sqlitetransactionbackendmock.h
new file mode 100644
index 0000000000..7aad4f1f6a
--- /dev/null
+++ b/tests/unit/unittest/sqlitetransactionbackendmock.h
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** 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 <sqlitetransaction.h>
+
+class SqliteTransactionBackendMock : public Sqlite::TransactionInterface
+{
+public:
+ MOCK_METHOD(void, deferredBegin, (), (override));
+ MOCK_METHOD(void, immediateBegin, (), (override));
+ MOCK_METHOD(void, exclusiveBegin, (), (override));
+ MOCK_METHOD(void, commit, (), (override));
+ MOCK_METHOD(void, rollback, (), (override));
+ MOCK_METHOD(void, lock, (), (override));
+ MOCK_METHOD(void, unlock, (), (override));
+ MOCK_METHOD(void, immediateSessionBegin, (), (override));
+ MOCK_METHOD(void, sessionCommit, (), (override));
+ MOCK_METHOD(void, sessionRollback, (), (override));
+};
diff --git a/tests/unit/unittest/sqlitewritestatementmock.cpp b/tests/unit/unittest/sqlitewritestatementmock.cpp
new file mode 100644
index 0000000000..a3612833b2
--- /dev/null
+++ b/tests/unit/unittest/sqlitewritestatementmock.cpp
@@ -0,0 +1,35 @@
+/****************************************************************************
+**
+** 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 "sqlitewritestatementmock.h"
+
+#include "sqlitedatabasemock.h"
+
+SqliteWriteStatementMock::SqliteWriteStatementMock(Utils::SmallStringView sqlStatement,
+ SqliteDatabaseMock &database)
+ : sqlStatement(sqlStatement)
+{
+ database.prepare(sqlStatement);
+}
diff --git a/tests/unit/unittest/sqlitewritestatementmock.h b/tests/unit/unittest/sqlitewritestatementmock.h
new file mode 100644
index 0000000000..3a8d159329
--- /dev/null
+++ b/tests/unit/unittest/sqlitewritestatementmock.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** 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 <sqliteblob.h>
+#include <sqlitevalue.h>
+
+class SqliteDatabaseMock;
+
+class SqliteWriteStatementMock
+{
+public:
+ SqliteWriteStatementMock() = default;
+ SqliteWriteStatementMock(Utils::SmallStringView sqlStatement, SqliteDatabaseMock &database);
+
+ MOCK_METHOD(void, execute, (), ());
+
+ MOCK_METHOD(void, write, (Utils::SmallStringView), ());
+ MOCK_METHOD(void, write, (long long), ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView), ());
+ MOCK_METHOD(void, write, (long long, Utils::SmallStringView), ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView, Utils::SmallStringView, Utils::SmallStringView),
+ ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView, long long), ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, Utils::SmallStringView, double), ());
+ MOCK_METHOD(void, write, (long long, Utils::SmallStringView, Utils::SmallStringView), ());
+ MOCK_METHOD(void, write, (long long, Utils::SmallStringView, const Sqlite::Value &), ());
+ MOCK_METHOD(void, write, (Utils::SmallStringView, long long, Sqlite::BlobView, Sqlite::BlobView), ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView, long long, Sqlite::NullValue, Sqlite::NullValue),
+ ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView),
+ ());
+ MOCK_METHOD(void,
+ write,
+ (long long, Utils::SmallStringView, Utils::SmallStringView, Utils::SmallStringView),
+ ());
+ MOCK_METHOD(void, write, (long long, long long, Utils::SmallStringView, Utils::SmallStringView), ());
+ MOCK_METHOD(void,
+ write,
+ (Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView,
+ Utils::SmallStringView),
+ ());
+
+ MOCK_METHOD(void, write, (void *, long long), ());
+
+ Utils::SmallString sqlStatement;
+};
diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro
index 967463bbdc..6200a24d7f 100644
--- a/tests/unit/unittest/unittest.pro
+++ b/tests/unit/unittest/unittest.pro
@@ -66,6 +66,9 @@ SOURCES += \
filepathview-test.cpp \
gtest-creator-printing.cpp \
gtest-qt-printing.cpp \
+ imagecache-test.cpp \
+ imagecachegenerator-test.cpp \
+ imagecachestorage-test.cpp \
lastchangedrowid-test.cpp \
lineprefixer-test.cpp \
listmodeleditor-test.cpp \
@@ -134,7 +137,9 @@ SOURCES += \
sqlitestatement-test.cpp \
sqlitetable-test.cpp \
sqlstatementbuilder-test.cpp \
- createtablesqlstatementbuilder-test.cpp
+ createtablesqlstatementbuilder-test.cpp \
+ sqlitereadstatementmock.cpp \
+ sqlitewritestatementmock.cpp
!isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += matchingtext-test.cpp
@@ -240,12 +245,15 @@ HEADERS += \
gtest-llvm-printing.h \
gtest-qt-printing.h \
gtest-std-printing.h \
+ imagecachecollectormock.h \
mimedatabase-utilities.h \
mockclangcodemodelclient.h \
mockclangcodemodelserver.h \
mockclangpathwatcher.h \
mockclangpathwatchernotifier.h \
mockfilesystem.h \
+ mockimagecachegenerator.h \
+ mockimagecachestorage.h \
mocklistmodeleditorview.h \
mockpchcreator.h \
mockpchmanagerclient.h \
@@ -258,6 +266,8 @@ HEADERS += \
mocksearchhandle.h \
mocksearchresult.h \
mocksyntaxhighligher.h \
+ mocktimestampprovider.h \
+ notification.h \
processevents-utilities.h \
sourcerangecontainer-matcher.h \
spydummy.h \
@@ -301,7 +311,12 @@ HEADERS += \
mockpchtaskgenerator.h \
../mockup/qmldesigner/designercore/include/nodeinstanceview.h \
../mockup/qmldesigner/designercore/include/rewriterview.h \
- ../mockup/qmldesigner/designercore/include/itemlibraryitem.h
+ ../mockup/qmldesigner/designercore/include/itemlibraryitem.h\
+ sqlitedatabasemock.h \
+ sqlitereadstatementmock.h \
+ sqlitestatementmock.h \
+ sqlitetransactionbackendmock.h \
+ sqlitewritestatementmock.h
!isEmpty(LIBCLANG_LIBS) {
diff --git a/tests/unit/unittest/unittest.qbs b/tests/unit/unittest/unittest.qbs
index 5ceb723b22..ac7eb08b82 100644
--- a/tests/unit/unittest/unittest.qbs
+++ b/tests/unit/unittest/unittest.qbs
@@ -333,6 +333,19 @@ Project {
"unittests-main.cpp",
"usedmacrofilter-test.cpp",
"utf8-test.cpp",
+ "imagecache-test.cpp",
+ "imagecachegenerator-test.cpp",
+ "imagecachestorage-test.cpp",
+ "sqlitedatabasemock.h",
+ "sqlitereadstatementmock.h",
+ "sqlitestatementmock.h",
+ "sqlitetransactionbackendmock.h",
+ "sqlitewritestatementmock.h",
+ "notification.h",
+ "mocktimestampprovider.h",
+ "imagecachecollectormock.h",
+ "mockimagecachegenerator.h",
+ "mockimagecachestorage.h",
]
Group {