From 6ab70135797ce4354502cf1444a22c9c95787a6f Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 14 Sep 2020 09:03:12 +0200 Subject: [PATCH 01/21] AutoTest: Fix summary for boost Do not take skipped tests into account when computing passes out of the available information. These are handled separately and may lead to a negative passes amount. Change-Id: I47bb1f054468dc5374d0d4e7fdeb3ad87efd0dff Reviewed-by: David Schulz --- src/plugins/autotest/boost/boosttestoutputreader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/plugins/autotest/boost/boosttestoutputreader.cpp b/src/plugins/autotest/boost/boosttestoutputreader.cpp index 2fe0a1c9283..84cd3e45a08 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.cpp +++ b/src/plugins/autotest/boost/boosttestoutputreader.cpp @@ -206,8 +206,6 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) static QRegularExpression finish("^\\*{3} (\\d+) failure(s are| is) detected in the " "test module \"(.*)\"$"); - static QRegularExpression errDetect("^\\*{3} Errors where detected in the " - "test module \"(.*}\"; see standard output for details"); QString noErrors("*** No errors detected"); const QString line = removeCommandlineColors(QString::fromUtf8(outputLine)); @@ -340,8 +338,7 @@ void BoostTestOutputReader::processOutputLine(const QByteArray &outputLine) BoostTestResult *result = new BoostTestResult(id(), m_projectFile, QString()); int failed = match.captured(1).toInt(); QString txt = tr("%1 failures detected in %2.").arg(failed).arg(match.captured(3)); - int passed = (m_testCaseCount != -1) - ? m_testCaseCount - failed - m_summary[ResultType::Skip] : -1; + int passed = (m_testCaseCount != -1) ? m_testCaseCount - failed : -1; if (m_testCaseCount != -1) txt.append(' ').append(tr("%1 tests passed.").arg(passed)); result->setDescription(txt); From 226982b7b026fea5f8e8cf36da2e544de882b6e2 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 11 Sep 2020 15:52:53 +0200 Subject: [PATCH 02/21] OutputFormatter: Fix flushing a pending line feed ... for the edge case of a lone carriage return occurring in the output. Amends b0cad9e9c7. Also adds missing adaptations to the unit test. Change-Id: I219b24b8fb41bb0fcea9f677cd79286d03b130c2 Reviewed-by: Christian Stenger --- src/libs/utils/outputformatter.cpp | 2 +- src/plugins/coreplugin/outputwindow.cpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp index 49cb31d3e50..eb6e974dff2 100644 --- a/src/libs/utils/outputformatter.cpp +++ b/src/libs/utils/outputformatter.cpp @@ -429,6 +429,7 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format) { if (!plainTextEdit()) return; + flushTrailingNewline(); int startPos = 0; int crPos = -1; while ((crPos = text.indexOf('\r', startPos)) >= 0) { @@ -437,7 +438,6 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format) d->cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor); startPos = crPos + 1; } - flushTrailingNewline(); if (startPos < text.count()) d->cursor.insertText(text.mid(startPos), format); } diff --git a/src/plugins/coreplugin/outputwindow.cpp b/src/plugins/coreplugin/outputwindow.cpp index 9d58bae06a9..80fb566698b 100644 --- a/src/plugins/coreplugin/outputwindow.cpp +++ b/src/plugins/coreplugin/outputwindow.cpp @@ -590,7 +590,7 @@ class TestFormatterA : public OutputLineParser private: Result handleLine(const QString &text, OutputFormat) override { - static const QString replacement = "handled by A\n"; + static const QString replacement = "handled by A"; if (m_handling) { if (text.startsWith("A")) { m_handling = false; @@ -615,7 +615,7 @@ private: Result handleLine(const QString &text, OutputFormat) override { if (text.startsWith("B")) - return {Status::Done, {}, QString("handled by B\n")}; + return {Status::Done, {}, QString("handled by B")}; return Status::NotHandled; } }; @@ -656,6 +656,7 @@ void Internal::CorePlugin::testOutputFormatter() formatter.setLineParsers({new TestFormatterB, new TestFormatterA}); formatter.appendMessage(input.left(i), StdOutFormat); formatter.appendMessage(input.mid(i), StdOutFormat); + formatter.flush(); QCOMPARE(textEdit.toPlainText(), output); } } From d14be7493f8ed963840d198315ef46b4641427bd Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 8 Sep 2020 15:48:52 +0200 Subject: [PATCH 03/21] ClangBackEnd: Support highlighting of alias templates Fixes: QTCREATORBUG-24552 Change-Id: I80d2b16114234cf896173cd4104e6a5f12009f69 Reviewed-by: Christian Stenger --- src/tools/clangbackend/source/tokeninfo.cpp | 1 + tests/unit/unittest/data/highlightingmarks.cpp | 4 ++++ tests/unit/unittest/tokenprocessor-test.cpp | 7 +++++++ tests/unit/unittest/unittest.pro | 10 +++++----- tests/unit/unittest/unittest.qbs | 6 +++--- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/tools/clangbackend/source/tokeninfo.cpp b/src/tools/clangbackend/source/tokeninfo.cpp index de40a954e16..47b8188986c 100644 --- a/src/tools/clangbackend/source/tokeninfo.cpp +++ b/src/tools/clangbackend/source/tokeninfo.cpp @@ -317,6 +317,7 @@ void TokenInfo::typeKind(const Cursor &cursor) m_types.mixinHighlightingTypes.push_back(HighlightingType::Namespace); return; case CXCursor_TypeAliasDecl: + case CXCursor_TypeAliasTemplateDecl: m_types.mixinHighlightingTypes.push_back(HighlightingType::TypeAlias); return; case CXCursor_TypedefDecl: diff --git a/tests/unit/unittest/data/highlightingmarks.cpp b/tests/unit/unittest/data/highlightingmarks.cpp index a703e89a11b..2476cabfb22 100644 --- a/tests/unit/unittest/data/highlightingmarks.cpp +++ b/tests/unit/unittest/data/highlightingmarks.cpp @@ -695,3 +695,7 @@ protected: private: static int privateValue; }; + +template struct S { }; +template using spec = S; +spec<2> s; diff --git a/tests/unit/unittest/tokenprocessor-test.cpp b/tests/unit/unittest/tokenprocessor-test.cpp index 285c6d3446f..786b980f222 100644 --- a/tests/unit/unittest/tokenprocessor-test.cpp +++ b/tests/unit/unittest/tokenprocessor-test.cpp @@ -1722,6 +1722,13 @@ TEST_F(TokenProcessor, StaticPrivateMember) ASSERT_THAT(container.extraInfo.accessSpecifier, ClangBackEnd::AccessSpecifier::Private); } +TEST_F(TokenProcessor, TemplateAlias) +{ + const auto infos = translationUnit.tokenInfosInRange(sourceRange(701, 8)); + + ASSERT_THAT(infos[0], HasTwoTypes(HighlightingType::Type, HighlightingType::TypeAlias)); +} + Data *TokenProcessor::d; void TokenProcessor::SetUpTestCase() diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 3917237d0bf..4b3254a4d20 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -174,12 +174,14 @@ SOURCES += \ diagnosticset-test.cpp \ diagnostic-test.cpp \ fixit-test.cpp \ + gtest-clang-printing.cpp \ highlightingresultreporter-test.cpp \ senddocumenttracker-test.cpp \ skippedsourceranges-test.cpp \ sourcelocation-test.cpp \ sourcerange-test.cpp \ token-test.cpp \ + tokenprocessor-test.cpp \ translationunitupdater-test.cpp \ unsavedfiles-test.cpp \ unsavedfile-test.cpp \ @@ -201,7 +203,6 @@ SOURCES += \ clangqueryprojectfindfilter-test.cpp \ clangquery-test.cpp \ clangreferencescollector-test.cpp \ - gtest-clang-printing.cpp \ pchcreator-test.cpp \ refactoringclientserverinprocess-test.cpp \ refactoringclient-test.cpp \ @@ -212,8 +213,7 @@ SOURCES += \ symbolscollector-test.cpp \ testclangtool.cpp \ usedmacrocollector-test.cpp \ - builddependencycollector-test.cpp \ - tokenprocessor-test.cpp + builddependencycollector-test.cpp !isEmpty(QTC_UNITTEST_BUILD_CPP_PARSER):SOURCES += refactoringengine-test.cpp @@ -311,14 +311,14 @@ HEADERS += \ clangasyncjob-base.h \ clangcompareoperators.h \ diagnosticcontainer-matcher.h \ + gtest-clang-printing.h } !isEmpty(LIBTOOLING_LIBS) { HEADERS += \ - gtest-clang-printing.h \ mockrefactoringclient.h \ mockrefactoringserver.h \ - testclangtool.h \ + testclangtool.h } OTHER_FILES += $$files(data/*) $$files(data/include/*) diff --git a/tests/unit/unittest/unittest.qbs b/tests/unit/unittest/unittest.qbs index 43cfc86eedb..4e5a7993e42 100644 --- a/tests/unit/unittest/unittest.qbs +++ b/tests/unit/unittest/unittest.qbs @@ -323,6 +323,8 @@ CppApplication { "diagnosticcontainer-matcher.h", "diagnosticset-test.cpp", "fixit-test.cpp", + "gtest-clang-printing.cpp", + "gtest-clang-printing.h", "highlightingresultreporter-test.cpp", "readexporteddiagnostics-test.cpp", "senddocumenttracker-test.cpp", @@ -330,6 +332,7 @@ CppApplication { "sourcelocation-test.cpp", "sourcerange-test.cpp", "token-test.cpp", + "tokenprocessor-test.cpp", "translationunitupdater-test.cpp", "unsavedfile-test.cpp", "unsavedfiles-test.cpp", @@ -347,8 +350,6 @@ CppApplication { "clangquerygatherer-test.cpp", "clangqueryprojectfindfilter-test.cpp", "clangreferencescollector-test.cpp", - "gtest-clang-printing.cpp", - "gtest-clang-printing.h", "gtest-llvm-printing.cpp", "mockrefactoringclient.h", "mockrefactoringserver.h", @@ -363,7 +364,6 @@ CppApplication { "symbolscollector-test.cpp", "testclangtool.cpp", "testclangtool.h", - "tokenprocessor-test.cpp", "usedmacrocollector-test.cpp", ] } From f641434ec15996d566ad06d9f19f52edb4b3e46c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 11 Sep 2020 07:00:30 +0200 Subject: [PATCH 04/21] QmlJSCheck: Fix crash Task-number: QDS-2766 Change-Id: I4b0e81b8104ac35a286a318e87e95ea022ed66f6 Reviewed-by: Marco Bubke --- src/libs/qmljs/qmljscheck.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index 92e1c1e59cf..375cb9b4526 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1838,7 +1838,10 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id) const bool isListElementScope = (!m_typeStack.isEmpty() && m_typeStack.last() == "ListElement"); - if (!value && !isListElementScope) { + if (isListElementScope) + return nullptr; + + if (!value) { addMessage(ErrInvalidPropertyName, id->identifierToken, propertyName); return nullptr; } From 64a37ec38a21b31d946a2184d8af18fe0b4f4ad4 Mon Sep 17 00:00:00 2001 From: Michael Winkelmann Date: Mon, 14 Sep 2020 12:29:21 +0200 Subject: [PATCH 05/21] QmlPuppet: Return color property Change-Id: Icc25fb0408aae50f67633688f221194ab6f77cd3 Reviewed-by: Marco Bubke --- .../instances/qt5capturepreviewnodeinstanceserver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp index 46461581f83..c70ef76afef 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/qt5capturepreviewnodeinstanceserver.cpp @@ -66,7 +66,10 @@ CapturedDataCommand::StateData collectStateData(ServerNodeInstance rootNodeInsta nodeData.sceneTransform = instance.sceneTransform(); auto textProperty = instance.property("text"); if (!textProperty.isNull() && instance.holdsGraphical()) - nodeData.properties.emplace_back(QString{"text"}, textProperty.toString()); + nodeData.properties.emplace_back(QString{"text"}, textProperty); + auto colorProperty = instance.property("color"); + if (!colorProperty.isNull()) + nodeData.properties.emplace_back(QString{"color"}, colorProperty); stateData.nodeData.push_back(std::move(nodeData)); } From 5c76ca32ee3303af196cfcc6c9aa0b5f51279513 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 14 Sep 2020 14:59:39 +0200 Subject: [PATCH 06/21] cdbext: add option to skip cdb detection Since a few windows sdk versions are the headers and libs of the debugger part of the sdk installation. Add an option to sdkip the cdb detection and fall back to use these headers and libs that are already configured in INCLUDE and LIBS of the vcvars.bat environment. Change-Id: If1eeecdbae34523ddbcc1a22d8640df7fa46bf6a Reviewed-by: Eike Ziller --- src/libs/libs.pro | 15 ++++++--- src/libs/qtcreatorcdbext/cdb_detect.pri | 3 +- src/libs/qtcreatorcdbext/qtcreatorcdbext.pro | 35 ++++++++++++-------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/libs/libs.pro b/src/libs/libs.pro index 013376911fa..b6f7518ae91 100644 --- a/src/libs/libs.pro +++ b/src/libs/libs.pro @@ -67,13 +67,18 @@ win32:SUBDIRS += utils/process_ctrlc_stub.pro # Windows: Compile Qt Creator CDB extension if Debugging tools can be detected. win32: isEmpty(QTC_SKIP_CDBEXT) { - include(qtcreatorcdbext/cdb_detect.pri) - !isEmpty(CDB_PATH): exists($$CDB_PATH) { + CDB_NO_AUTODETECT = $$(CDB_NO_AUTODETECT) #use the debugger header and lib defined in INCLUDE and LIB env var + !isEmpty(CDB_NO_AUTODETECT) { SUBDIRS += qtcreatorcdbext } else { - message("Compiling Qt Creator without a CDB extension.") - message("If CDB is installed in a none default path define a CDB_PATH") - message("environment variable pointing to your CDB installation.") + include(qtcreatorcdbext/cdb_detect.pri) + !isEmpty(CDB_PATH): exists($$CDB_PATH) { + SUBDIRS += qtcreatorcdbext + } else { + message("Compiling Qt Creator without a CDB extension.") + message("If CDB is installed in a none default path define a CDB_PATH") + message("environment variable pointing to your CDB installation.") + } } } diff --git a/src/libs/qtcreatorcdbext/cdb_detect.pri b/src/libs/qtcreatorcdbext/cdb_detect.pri index ae09e258d4d..7f866d1d6cd 100644 --- a/src/libs/qtcreatorcdbext/cdb_detect.pri +++ b/src/libs/qtcreatorcdbext/cdb_detect.pri @@ -2,7 +2,8 @@ # in case MS VS compilers are used. CDB_PATH="" -msvc { +CDB_NO_AUTODETECT = $$(CDB_NO_AUTODETECT) #use the debugger header and lib defined in INCLUDE and LIB env var +isEmpty(CDB_NO_AUTODETECT): msvc { CDB_PATH="$$(CDB_PATH)" isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk" !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk" diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro b/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro index 774e889b2ae..ff7d16bc472 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro @@ -36,39 +36,46 @@ contains(ENV_TARGET_ARCH, .*64$) { DIRNAME=$${BASENAME}64 CDB_PLATFORM=amd64 - exists($$CDB_PATH/lib/amd64) { - LIBS+= -L$$CDB_PATH/lib/amd64 -ldbgeng - } else { - LIBS+= -L$$CDB_PATH/lib/x64 -ldbgeng + !isEmpty(CDB_PATH) { + exists($$CDB_PATH/lib/amd64) { + LIBS+= -L$$CDB_PATH/lib/amd64 + } else { + LIBS+= -L$$CDB_PATH/lib/x64 + } } } else:isEmpty(ENV_TARGET_ARCH):contains(ENV_LIBPATH, ^.*amd64.*$) { DIRNAME=$${BASENAME}64 CDB_PLATFORM=amd64 - exists($$CDB_PATH/lib/amd64) { - LIBS+= -L$$CDB_PATH/lib/amd64 -ldbgeng - } else { - LIBS+= -L$$CDB_PATH/lib/x64 -ldbgeng + + !isEmpty(CDB_PATH) { + exists($$CDB_PATH/lib/amd64) { + LIBS+= -L$$CDB_PATH/lib/amd64 + } else { + LIBS+= -L$$CDB_PATH/lib/x64 + } } } else { DIRNAME=$${BASENAME}32 CDB_PLATFORM=i386 - exists($$CDB_PATH/lib/i386}) { - LIBS+= -L$$CDB_PATH/lib/i386 -ldbgeng - } else { - LIBS+= -L$$CDB_PATH/lib/x86 -ldbgeng + !isEmpty(CDB_PATH) { + exists($$CDB_PATH/lib/i386}) { + LIBS+= -L$$CDB_PATH/lib/i386 + } else { + LIBS+= -L$$CDB_PATH/lib/x86 + } } } -LIBS+=-luser32 +LIBS+=-ldbgeng -luser32 DESTDIR=$$IDE_BUILD_TREE/lib/$${DIRNAME} TARGET = $$BASENAME message("Compiling Qt Creator CDB extension $$TARGET $$DESTDIR for $$CDB_PLATFORM using $$CDB_PATH") -INCLUDEPATH += $$CDB_PATH/inc +!isEmpty(CDB_PATH): INCLUDEPATH += $$CDB_PATH/inc CONFIG -= qt QT -= gui From 26fc085658e8b6c44d1db7ac2eeb19fc3d290420 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 15 Sep 2020 09:02:26 +0200 Subject: [PATCH 07/21] Add 4.13.1 change log Change-Id: I8e0b8f6d002c0251268aff1aeddd3470cfdee140 Reviewed-by: Leena Miettinen --- dist/changes-4.13.1.md | 108 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 dist/changes-4.13.1.md diff --git a/dist/changes-4.13.1.md b/dist/changes-4.13.1.md new file mode 100644 index 00000000000..597c20850c9 --- /dev/null +++ b/dist/changes-4.13.1.md @@ -0,0 +1,108 @@ +Qt Creator 4.13.1 +================= + +Qt Creator version 4.13.1 contains bug fixes. + +The most important changes are listed in this document. For a complete +list of changes, see the Git log for the Qt Creator sources that +you can check out from the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/v4.13.0..v4.13.1 + +Editing +------- + +* Fixed whitespace cleaning (QTCREATORBUG-24565) +* Fixed selection color (QTCREATORBUG-24479) + +### C++ + +* Fixed crash with adjacent raw string literals (QTCREATORBUG-24577) + +### QML + +* Fixed wrong diagnostics for `ListElement` (QDS-2602) + +### Language Client + +* Fixed performance issue + +Projects +-------- + +* Fixed parsing of QTest application output (QTCREATORBUG-24560) +* Fixed visibility of output in output panes (QTCREATORBUG-24411) + +### qmake + +* Fixed handling of unset environment variables (QTCREATORBUG-21729) + +### CMake + +* Fixed removal of CMake tools + +### Qbs + +* Fixed install step + +Debugging +--------- + +### GDB + +* Fixed disabling of environment variables + +Analyzer +-------- + +### Clang + +* Updated Clazy to version 1.7 + +Qt Quick Designer +----------------- + +* Improved composition of custom materials (QDS-2657) +* Fixed available items for MCU (QDS-2681, QDS-2512) +* Fixed visual artifacts when changing states +* Fixed rich text editor styling +* Fixed layout issues in state editor (QDS-2623, QDS-2615) +* Fixed support for `.hrd` images (QDS-2128) + +Platforms +--------- + +### Android + +* Fixed service handling in manifest editor (QTCREATORBUG-24557) + +### macOS + +* Fixed Clazy (QTCREATORBUG-24567) + +Credits for these changes go to: +-------------------------------- +Aleksei German +André Pönitz +Andy Shaw +Christian Kandeler +Christian Stenger +David Schulz +Dominik Holland +Eike Ziller +Henning Gruendl +Johanna Vanhatapio +Kai Köhne +Knud Dollereder +Leena Miettinen +Mahmoud Badri +Marco Bubke +Michael Winkelmann +Miikka Heikkinen +Orgad Shaneh +Sergey Belyashov +Thomas Hartmann +Venugopal Shivashankar +Vikas Pachdha +Ville Voutilainen From bc4187727cf65e25d839bd833ed320cfbe0ced62 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 15 Sep 2020 09:54:41 +0200 Subject: [PATCH 08/21] cdbext: remove cdbdetection completely All required header and libs are inside the Windows 10 SDK, which is required to build with Visual Studio 2017. Change-Id: I68ee7457041653d89e5f5c9e258f9780a417dbe4 Reviewed-by: Eike Ziller --- src/libs/libs.pro | 15 +------ src/libs/qtcreatorcdbext/CMakeLists.txt | 25 +---------- src/libs/qtcreatorcdbext/cdb_detect.pri | 26 ----------- src/libs/qtcreatorcdbext/qtcreatorcdbext.pro | 34 +------------- src/libs/qtcreatorcdbext/qtcreatorcdbext.qbs | 47 ++------------------ 5 files changed, 7 insertions(+), 140 deletions(-) delete mode 100644 src/libs/qtcreatorcdbext/cdb_detect.pri diff --git a/src/libs/libs.pro b/src/libs/libs.pro index b6f7518ae91..9675dca0495 100644 --- a/src/libs/libs.pro +++ b/src/libs/libs.pro @@ -65,21 +65,8 @@ isEmpty(KSYNTAXHIGHLIGHTING_LIB_DIR) { win32:SUBDIRS += utils/process_ctrlc_stub.pro -# Windows: Compile Qt Creator CDB extension if Debugging tools can be detected. win32: isEmpty(QTC_SKIP_CDBEXT) { - CDB_NO_AUTODETECT = $$(CDB_NO_AUTODETECT) #use the debugger header and lib defined in INCLUDE and LIB env var - !isEmpty(CDB_NO_AUTODETECT) { - SUBDIRS += qtcreatorcdbext - } else { - include(qtcreatorcdbext/cdb_detect.pri) - !isEmpty(CDB_PATH): exists($$CDB_PATH) { - SUBDIRS += qtcreatorcdbext - } else { - message("Compiling Qt Creator without a CDB extension.") - message("If CDB is installed in a none default path define a CDB_PATH") - message("environment variable pointing to your CDB installation.") - } - } + SUBDIRS += qtcreatorcdbext } QMAKE_EXTRA_TARGETS += deployqt # dummy diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt index 329a228afc6..394405f37fb 100644 --- a/src/libs/qtcreatorcdbext/CMakeLists.txt +++ b/src/libs/qtcreatorcdbext/CMakeLists.txt @@ -2,30 +2,7 @@ if (NOT WIN32 OR NOT MSVC) return() endif() -find_path(WDbgExtsPath wdbgexts.h - HINTS - "$ENV{CDB_PATH}" - "$ENV{ProgramFiles}/Debugging Tools For Windows/sdk" - "$ENV{ProgramFiles}/Debugging Tools For Windows (x86)/sdk" - "$ENV{ProgramFiles}/Debugging Tools For Windows (x64)/sdk" - "$ENV{ProgramFiles}/Debugging Tools For Windows 64-bit/sdk" - "$ENV{ProgramW6432}/Debugging Tools For Windows (x86)/sdk" - "$ENV{ProgramW6432}/Debugging Tools For Windows (x64)/sdk" - "$ENV{ProgramW6432}/Debugging Tools For Windows 64-bit/sdk" - "$ENV{ProgramFiles}/Windows Kits/8.0/Debuggers" - "$ENV{ProgramFiles}/Windows Kits/8.1/Debuggers" - "$ENV{ProgramFiles}/Windows Kits/10/Debuggers" - "$ENV{ProgramFiles\(x86\)}/Windows Kits/8.0/Debuggers/inc" - "$ENV{ProgramFiles\(x86\)}/Windows Kits/8.1/Debuggers/inc" - "$ENV{ProgramFiles\(x86\)}/Windows Kits/10/Debuggers/inc" -) - -if (NOT WDbgExtsPath) - message(WARNING "wdbgexts.h not found. Removing qtcreatorcdbext from build.") - return() -endif() - -find_library(DbgEngLib dbgeng HINTS ${WDbgExtsPath}) +find_library(DbgEngLib dbgeng) set(ArchSuffix 32) if (CMAKE_SIZEOF_VOID_P EQUAL 8) diff --git a/src/libs/qtcreatorcdbext/cdb_detect.pri b/src/libs/qtcreatorcdbext/cdb_detect.pri deleted file mode 100644 index 7f866d1d6cd..00000000000 --- a/src/libs/qtcreatorcdbext/cdb_detect.pri +++ /dev/null @@ -1,26 +0,0 @@ -# Detect presence of "Debugging Tools For Windows" -# in case MS VS compilers are used. - -CDB_PATH="" -CDB_NO_AUTODETECT = $$(CDB_NO_AUTODETECT) #use the debugger header and lib defined in INCLUDE and LIB env var -isEmpty(CDB_NO_AUTODETECT): msvc { - CDB_PATH="$$(CDB_PATH)" - isEmpty(CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows/sdk" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x86)/sdk" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows (x64)/sdk" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramFiles)/Debugging Tools For Windows 64-bit/sdk" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramW6432)/Debugging Tools For Windows (x86)/sdk" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramW6432)/Debugging Tools For Windows (x64)/sdk" - !exists($$CDB_PATH):CDB_PATH="$$(ProgramW6432)/Debugging Tools For Windows 64-bit/sdk" -# Starting from Windows SDK 8, the headers and libs are under 'ProgramFiles (x86)'. -# The libraries are under 'ProgramFiles'as well, so, check for existence of 'inc'. -# 32bit qmake: - !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles)/Windows Kits/8.0/Debuggers" - !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles)/Windows Kits/8.1/Debuggers" - !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles)/Windows Kits/10/Debuggers" -# 64bit qmake: - !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles) (x86)/Windows Kits/8.0/Debuggers" - !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles) (x86)/Windows Kits/8.1/Debuggers" - !exists($$CDB_PATH/inc):CDB_PATH="$$(ProgramFiles) (x86)/Windows Kits/10/Debuggers" - !exists($$CDB_PATH/inc):CDB_PATH="" -} diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro b/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro index ff7d16bc472..98961887b94 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.pro @@ -2,7 +2,6 @@ TEMPLATE = lib include(../../../qtcreator.pri) -include(cdb_detect.pri) isEmpty(QTC_KEEP_CDBEXT_DEFAULT_CONFIG): CONFIG += release @@ -30,42 +29,13 @@ DEF_FILE=$$PWD/qtcreatorcdbext.def ENV_TARGET_ARCH=$$(VSCMD_ARG_TGT_ARCH) isEmpty(ENV_TARGET_ARCH):ENV_TARGET_ARCH = $$(Platform) -ENV_LIBPATH=$$(LIBPATH) contains(ENV_TARGET_ARCH, .*64$) { DIRNAME=$${BASENAME}64 CDB_PLATFORM=amd64 - - !isEmpty(CDB_PATH) { - exists($$CDB_PATH/lib/amd64) { - LIBS+= -L$$CDB_PATH/lib/amd64 - } else { - LIBS+= -L$$CDB_PATH/lib/x64 - } - } -} else:isEmpty(ENV_TARGET_ARCH):contains(ENV_LIBPATH, ^.*amd64.*$) { - DIRNAME=$${BASENAME}64 - CDB_PLATFORM=amd64 - - - !isEmpty(CDB_PATH) { - exists($$CDB_PATH/lib/amd64) { - LIBS+= -L$$CDB_PATH/lib/amd64 - } else { - LIBS+= -L$$CDB_PATH/lib/x64 - } - } } else { DIRNAME=$${BASENAME}32 CDB_PLATFORM=i386 - - !isEmpty(CDB_PATH) { - exists($$CDB_PATH/lib/i386}) { - LIBS+= -L$$CDB_PATH/lib/i386 - } else { - LIBS+= -L$$CDB_PATH/lib/x86 - } - } } LIBS+=-ldbgeng -luser32 @@ -73,9 +43,7 @@ LIBS+=-ldbgeng -luser32 DESTDIR=$$IDE_BUILD_TREE/lib/$${DIRNAME} TARGET = $$BASENAME -message("Compiling Qt Creator CDB extension $$TARGET $$DESTDIR for $$CDB_PLATFORM using $$CDB_PATH") - -!isEmpty(CDB_PATH): INCLUDEPATH += $$CDB_PATH/inc +message("Compiling Qt Creator CDB extension $$TARGET $$DESTDIR for $$CDB_PLATFORM") CONFIG -= qt QT -= gui diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.qbs b/src/libs/qtcreatorcdbext/qtcreatorcdbext.qbs index 2243c217586..3f446e2916f 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.qbs +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.qbs @@ -6,47 +6,9 @@ import qbs.Process import qbs.Utilities QtcLibrary { - condition: qbs.toolchain.contains("msvc") && cdbPath + condition: qbs.toolchain.contains("msvc") name: "qtcreatorcdbext" targetName: name - property string cdbPath: { - var paths = [ - Environment.getEnv("CDB_PATH"), - Environment.getEnv("ProgramFiles") + "/Debugging Tools For Windows/sdk", - Environment.getEnv("ProgramFiles") + "/Debugging Tools For Windows (x86)/sdk", - Environment.getEnv("ProgramFiles") + "/Debugging Tools For Windows (x64)/sdk", - Environment.getEnv("ProgramFiles") + "/Debugging Tools For Windows 64-bit/sdk", - Environment.getEnv("ProgramW6432") + "/Debugging Tools For Windows (x86)/sdk", - Environment.getEnv("ProgramW6432") + "/Debugging Tools For Windows (x64)/sdk", - Environment.getEnv("ProgramW6432") + "/Debugging Tools For Windows 64-bit/sdk", - Environment.getEnv("ProgramFiles") + "/Windows Kits/8.0/Debuggers", - Environment.getEnv("ProgramFiles") + "/Windows Kits/8.1/Debuggers", - Environment.getEnv("ProgramFiles") + "/Windows Kits/10/Debuggers", - Environment.getEnv("ProgramFiles(x86)") + "/Windows Kits/8.0/Debuggers/inc", - Environment.getEnv("ProgramFiles(x86)") + "/Windows Kits/8.1/Debuggers/inc", - Environment.getEnv("ProgramFiles(x86)") + "/Windows Kits/10/Debuggers/inc" - ]; - var c = paths.length; - for (var i = 0; i < c; ++i) { - if (File.exists(paths[i])) { - // The inc subdir is just used for detection. See qtcreatorcdbext.pro. - return paths[i].endsWith("/inc") ? paths[i].substr(0, paths[i].length - 4) - : paths[i]; - } - } - return undefined; - } - property string cdbLibPath: { - var paths = qbs.architecture.contains("x86_64") ? ["x64", "amd64"] : ["x86", "i386"]; - var c = paths.length; - for (var i = 0; i < c; ++i) { - var libPath = FileInfo.joinPaths(cdbPath, "lib", paths[i]); - if (File.exists(libPath)) { - return libPath; - } - } - return undefined; - } property string pythonInstallDir: Environment.getEnv("PYTHON_INSTALL_DIR") @@ -127,13 +89,12 @@ QtcLibrary { cpp.defines: ["WITH_PYTHON=1"] } cpp.includePaths: { - var paths = [FileInfo.joinPaths(cdbPath, "inc")]; if (pythonDllProbe.found) - paths.push(FileInfo.joinPaths(pythonInstallDir, "include")); - return paths; + return [ FileInfo.joinPaths(pythonInstallDir, "include") ]; + return [ ]; } cpp.dynamicLibraries: { - var libs = [ "user32.lib", FileInfo.joinPaths(cdbLibPath, "dbgeng.lib") ]; + var libs = [ "user32.lib", "dbgeng.lib" ]; if (pythonDllProbe.found) libs.push(FileInfo.joinPaths(pythonInstallDir, "libs", pythonDllProbe.fileNamePrefix + ".lib")); From a50f1baf5dee2c42f9e44d3c42acd834e4f0d81e Mon Sep 17 00:00:00 2001 From: hjk Date: Mon, 7 Sep 2020 14:00:09 +0200 Subject: [PATCH 09/21] Debugger: Change LLDB value creation hack ... to something that works with LLDB 12. Task-number: QTCREATORBUG-24596 Change-Id: Ib2f8255d45a02fa3d2c737864994397f587e9b15 Reviewed-by: Christian Stenger Reviewed-by: Eike Ziller --- share/qtcreator/debugger/lldbbridge.py | 23 ++++++++++++++--------- tests/auto/debugger/tst_dumpers.cpp | 1 + 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 31d180898a9..13ad3d3e9ed 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -121,6 +121,8 @@ class Dumper(DumperBase): self.process = None self.target = None + self.fakeAddress_ = None + self.fakeLAddress_ = None self.eventState = lldb.eStateInvalid self.executable_ = None @@ -258,10 +260,15 @@ class Dumper(DumperBase): return align def listMembers(self, value, nativeType): - #DumperBase.warn("ADDR: 0x%x" % self.fakeAddress) - fakeAddress = self.fakeAddress if value.laddress is None else value.laddress - sbaddr = lldb.SBAddress(fakeAddress, self.target) - fakeValue = self.target.CreateValueFromAddress('x', sbaddr, nativeType) + #DumperBase.warn("ADDR: 0x%x" % self.fakeAddress_) + if value.laddress: + fakeAddress = lldb.SBAddress(value.laddress, self.target) + fakeLAddress = value.laddress + else: + fakeAddress = self.fakeAddress_ + fakeLAddress = self.fakeLAddress_ + + fakeValue = self.target.CreateValueFromAddress('x', fakeAddress, nativeType) fakeValue.SetPreferSyntheticValue(False) baseNames = {} @@ -304,7 +311,7 @@ class Dumper(DumperBase): fieldName = '#%s' % anonNumber fakeMember = fakeValue.GetChildAtIndex(i) fakeMemberAddress = fakeMember.GetLoadAddress() - offset = fakeMemberAddress - fakeAddress + offset = fakeMemberAddress - fakeLAddress yield self.Field(self, name=fieldName, type=self.fromNativeType(nativeFieldType), bitsize=fieldBitsize, bitpos=8 * offset) @@ -1228,10 +1235,6 @@ class Dumper(DumperBase): if not self.partialVariable: self.resetPerStepCaches() - anyModule = self.target.GetModuleAtIndex(0) - anySymbol = anyModule.GetSymbolAtIndex(0) - self.fakeAddress = int(anySymbol.GetStartAddress()) - frame = self.currentFrame() if frame is None: self.reportResult('error="No frame"', args) @@ -1968,6 +1971,8 @@ class Tester(Dumper): if line != 0: self.report = savedReport self.process.SetSelectedThread(stoppedThread) + self.fakeAddress_ = frame.GetPC() + self.fakeLAddress_ = frame.GetPCAddress() self.fetchVariables(args) #self.describeLocation(frame) self.report('@NS@%s@' % self.qtNamespace()) diff --git a/tests/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 3646f9eaac4..86b08d7f265 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -1215,6 +1215,7 @@ void tst_Dumpers::initTestCase() m_debuggerVersion = ba.toInt(); if (!m_debuggerVersion) { if (output.startsWith("lldb version")) { + output = output.split('\n')[0]; // drop clang/llvm version int pos1 = output.indexOf('.', 13); int major = output.mid(13, pos1++ - 13).toInt(); int pos2 = output.indexOf(' ', pos1); From f3bd07efd171da8a153d0fa298d6930b82c39f8f Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 10 Sep 2020 10:39:56 +0200 Subject: [PATCH 10/21] QmakeProjectManager: Restore incremental re-parsing This was broken in commit 37aecdd112, where we overlooked that a special type of IDocument was used that triggers a re-parse of only the affected part of the project tree. As a result, all changes to a .pri or .pro file would trigger a re-parse of the entire project. Fixes: QTCREATORBUG-24572 Change-Id: I480cff4e53cf86a17e1eaac0eb9b32901bc87051 Reviewed-by: hjk --- src/plugins/projectexplorer/project.cpp | 27 ++++++++++-- src/plugins/projectexplorer/project.h | 9 +++- .../qmakeprojectmanager/qmakeparsernodes.cpp | 35 --------------- .../qmakeprojectmanager/qmakeproject.cpp | 44 ++++++++++++++++++- 4 files changed, 73 insertions(+), 42 deletions(-) diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 03e24ee3af9..7f32733e1aa 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -64,7 +64,6 @@ #include #include -#include /*! \class ProjectExplorer::Project @@ -357,7 +356,8 @@ void Project::setNeedsInitialExpansion(bool needsExpansion) d->m_needsInitialExpansion = needsExpansion; } -void Project::setExtraProjectFiles(const QSet &projectDocumentPaths) +void Project::setExtraProjectFiles(const QSet &projectDocumentPaths, + const DocGenerator docGenerator) { QSet uniqueNewFiles = projectDocumentPaths; uniqueNewFiles.remove(projectFilePath()); // Make sure to never add the main project file! @@ -372,8 +372,14 @@ void Project::setExtraProjectFiles(const QSet &projectDocumentP return toRemove.contains(d->filePath()); }); for (const Utils::FilePath &p : toAdd) { - d->m_extraProjectDocuments.emplace_back( - std::make_unique(d->m_document->mimeType(), p, this)); + if (docGenerator) { + std::unique_ptr doc = docGenerator(p); + QTC_ASSERT(doc, continue); + d->m_extraProjectDocuments.push_back(std::move(doc)); + } else { + d->m_extraProjectDocuments.emplace_back(std::make_unique( + d->m_document->mimeType(), p, this)); + } } } @@ -802,6 +808,19 @@ bool Project::isKnownFile(const Utils::FilePath &filename) const &element, nodeLessThan); } +const Node *Project::nodeForFilePath(const Utils::FilePath &filePath, + const Project::NodeMatcher &extraMatcher) +{ + const FileNode dummy(filePath, FileType::Unknown); + const auto range = std::equal_range(d->m_sortedNodeList.cbegin(), d->m_sortedNodeList.cend(), + &dummy, &nodeLessThan); + for (auto it = range.first; it != range.second; ++it) { + if ((*it)->filePath() == filePath && (!extraMatcher || extraMatcher(*it))) + return *it; + } + return nullptr; +} + void Project::setProjectLanguages(Core::Context language) { if (d->m_projectLanguages == language) diff --git a/src/plugins/projectexplorer/project.h b/src/plugins/projectexplorer/project.h index 8d12e4c15ca..42fd4282c5c 100644 --- a/src/plugins/projectexplorer/project.h +++ b/src/plugins/projectexplorer/project.h @@ -39,6 +39,7 @@ #include #include +#include namespace Core { class Context; } namespace Utils { @@ -123,6 +124,8 @@ public: Utils::FilePaths files(const NodeMatcher &matcher) const; bool isKnownFile(const Utils::FilePath &filename) const; + const Node *nodeForFilePath(const Utils::FilePath &filePath, + const NodeMatcher &extraMatcher = {}); virtual QVariantMap toMap() const; @@ -159,9 +162,11 @@ public: void setRootProjectNode(std::unique_ptr &&root); - // Set project files that will be watched and trigger the same callback + // Set project files that will be watched and by default trigger the same callback // as the main project file. - void setExtraProjectFiles(const QSet &projectDocumentPaths); + using DocGenerator = std::function(const Utils::FilePath &)>; + void setExtraProjectFiles(const QSet &projectDocumentPaths, + const DocGenerator docGenerator = {}); void setDisplayName(const QString &name); void setProjectLanguage(Utils::Id id, bool enabled); diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index b694d2255c6..6f16d26d29d 100644 --- a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp @@ -63,41 +63,6 @@ using namespace QmakeProjectManager::Internal; using namespace QMakeInternal; using namespace Utils; -namespace { - -class QmakePriFileDocument : public Core::IDocument -{ -public: - QmakePriFileDocument(QmakePriFile *qmakePriFile, const Utils::FilePath &filePath) : - IDocument(nullptr), m_priFile(qmakePriFile) - { - setId("Qmake.PriFile"); - setMimeType(QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE)); - setFilePath(filePath); - } - - ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override - { - Q_UNUSED(state) - Q_UNUSED(type) - return BehaviorSilent; - } - bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override - { - Q_UNUSED(errorString) - Q_UNUSED(flag) - if (type == TypePermissions) - return true; - m_priFile->scheduleUpdate(); - return true; - } - -private: - QmakePriFile *m_priFile; -}; - -} // namespace - namespace QmakeProjectManager { static Q_LOGGING_CATEGORY(qmakeParse, "qtc.qmake.parsing", QtWarningMsg); diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index 7a5eb09c4d4..28707f26bb5 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -33,6 +33,7 @@ #include "qmakeprojectmanagerconstants.h" #include "qmakestep.h" +#include #include #include #include @@ -98,6 +99,38 @@ static Q_LOGGING_CATEGORY(qmakeBuildSystemLog, "qtc.qmake.buildsystem", QtWarnin << msg; \ } +class QmakePriFileDocument : public Core::IDocument +{ +public: + QmakePriFileDocument(QmakePriFile *qmakePriFile, const Utils::FilePath &filePath) : + IDocument(nullptr), m_priFile(qmakePriFile) + { + setId("Qmake.PriFile"); + setMimeType(QLatin1String(QmakeProjectManager::Constants::PROFILE_MIMETYPE)); + setFilePath(filePath); + Core::DocumentManager::addDocument(this); + } + + ReloadBehavior reloadBehavior(ChangeTrigger state, ChangeType type) const override + { + Q_UNUSED(state) + Q_UNUSED(type) + return BehaviorSilent; + } + bool reload(QString *errorString, ReloadFlag flag, ChangeType type) override + { + Q_UNUSED(errorString) + Q_UNUSED(flag) + if (type == TypePermissions) + return true; + m_priFile->scheduleUpdate(); + return true; + } + +private: + QmakePriFile *m_priFile; +}; + /// Watches folders for QmakePriFile nodes /// use one file system watcher to watch all folders /// such minimizing system ressouce usage @@ -270,8 +303,17 @@ void QmakeBuildSystem::updateDocuments() QSet projectDocuments; project()->rootProjectNode()->forEachProjectNode([&projectDocuments](const ProjectNode *n) { projectDocuments.insert(n->filePath()); + + }); + project()->setExtraProjectFiles(projectDocuments, [p = project()](const FilePath &fp) + -> std::unique_ptr { + const Node * const n = p->nodeForFilePath(fp, [](const Node *n) { + return dynamic_cast(n); }); + QTC_ASSERT(n, return std::make_unique()); + QmakePriFile * const priFile = static_cast(n)->priFile(); + QTC_ASSERT(priFile, return std::make_unique()); + return std::make_unique(priFile, fp); }); - project()->setExtraProjectFiles(projectDocuments); } void QmakeBuildSystem::updateCppCodeModel() From 71ca4f4402ea0682a778d2bbd39bf0d3db00a96d Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 15 Sep 2020 10:23:22 +0200 Subject: [PATCH 11/21] Doc: Fix broken external links Change-Id: Iaf3502a8d940a5cb2eb45ee7451e834a6d4b756e Reviewed-by: Christian Kandeler Reviewed-by: Paul Wicking --- .../src/debugger/creator-only/creator-debugger.qdoc | 8 ++++---- .../projects/creator-only/creator-projects-compilers.qdoc | 2 +- .../creator-projects-settings-build-qbs.qdocinc | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc index 143349d5558..66be3ccef4c 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger.qdoc @@ -935,7 +935,7 @@ \li \c value of type \c Value, wrapping either a \l{https://sourceware.org/gdb/onlinedocs/gdb/Values-From-Inferior.html} {gdb.Value} or an - \l{http://lldb.llvm.org/cpp_reference/html/classlldb_1_1SBValue.html} + \l{https://lldb.llvm.org/cpp_reference/classlldb_1_1SBValue.html} {lldb.SBValue}. \endlist @@ -1440,9 +1440,9 @@ \uicontrol {Debugger Log} view. Browse the contents of the pane on the right hand side to find out what went wrong. Always attach the contents of the pane to debugger-related questions to the \QC - mailing list (qt-creator@trolltech.com) or paste them to - \l{http://creator.pastebin.com}{creator.pastebin.com} before asking - questions in the IRC (on the #qt-creator channel at FreeNode). + mailing list (qt-creator@qt-project.org) or paste them to a + \l{Pasting and Fetching Code Snippets}{code pasting service} before + asking questions in the IRC (on the #qt-creator channel at FreeNode). \endlist diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc index e3e022cbca0..357d41ff739 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-compilers.qdoc @@ -94,7 +94,7 @@ compilers from the various KEIL development environments. \note Currently supported architectures are \c 8051 and \c ARM. - \li \l{https://sdcc.sourceforge.net}{SDCC} is a retargetable, optimizing + \li \l{http://sdcc.sourceforge.net}{SDCC} is a retargetable, optimizing C bare-metal compiler for various architectures. \note Currently supported architectures are \c 8051 and \c STM8. diff --git a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdocinc b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdocinc index c77409ed196..54e2f3c03e5 100644 --- a/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdocinc +++ b/doc/qtcreator/src/projects/creator-only/creator-projects-settings-build-qbs.qdocinc @@ -60,8 +60,7 @@ \li Select \uicontrol {Force probes} to force re-execution of the configure scripts of - \l{https://doc.qt.io/qbs/probe-item.html}{Probes}. - \li In the \uicontrol Flags field: + \l{https://doc.qt.io/qbs/qbsprobes-qmlmodule.html}{Probes}. \endlist From 7ca297cb25c9828bd3193fa2bc44c9177640ae1d Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 15 Sep 2020 11:17:29 +0200 Subject: [PATCH 12/21] CppEditor: Fix test code Change-Id: I01073f481ebdae64c30971afd8fb332ee92e2a1e Reviewed-by: Christian Kandeler --- src/plugins/cppeditor/cppdoxygen_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/cppeditor/cppdoxygen_test.cpp b/src/plugins/cppeditor/cppdoxygen_test.cpp index 639e0d5b8ae..97d2fb26b92 100644 --- a/src/plugins/cppeditor/cppdoxygen_test.cpp +++ b/src/plugins/cppeditor/cppdoxygen_test.cpp @@ -204,11 +204,11 @@ void DoxygenTest::testBasic_data() QTest::newRow("cpp_styleA_indented_preserve_mixed_indention_continuation") << _( "\t bool preventFolding;\n" - "\t /// \brief a|\n" + "\t /// \\brief a|\n" "\t int a;\n" ) << _( "\t bool preventFolding;\n" - "\t /// \brief a\n" + "\t /// \\brief a\n" "\t /// \n" "\t int a;\n" ); From 745beb76348f3589632d6cd19dc47865e404ee77 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 15 Sep 2020 14:36:26 +0200 Subject: [PATCH 13/21] AutoTest: Fix possible nullptr access If the process of the test executable cannot be started for whatever reason we will not have an active run configuration or output reader. Change-Id: I50146a2c8228ad39574e007ae2659465122ad20b Reviewed-by: David Schulz --- src/plugins/autotest/testrunner.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 56973b01df5..b553f28f82d 100644 --- a/src/plugins/autotest/testrunner.cpp +++ b/src/plugins/autotest/testrunner.cpp @@ -268,7 +268,7 @@ void TestRunner::cancelCurrent(TestRunner::CancelReason reason) void TestRunner::onProcessFinished() { - if (m_executingTests && QTC_GUARD(m_currentConfig)) { + if (m_executingTests && m_currentConfig) { QTC_CHECK(m_fakeFutureInterface); m_fakeFutureInterface->setProgressValue(m_fakeFutureInterface->progressValue() + m_currentConfig->testCaseCount()); @@ -286,13 +286,15 @@ void TestRunner::onProcessFinished() } } } - const int disabled = m_currentOutputReader->disabledTests(); - if (disabled > 0) - emit hadDisabledTests(disabled); - if (m_currentOutputReader->hasSummary()) - emit reportSummary(m_currentOutputReader->id(), m_currentOutputReader->summary()); + if (m_currentOutputReader) { + const int disabled = m_currentOutputReader->disabledTests(); + if (disabled > 0) + emit hadDisabledTests(disabled); + if (m_currentOutputReader->hasSummary()) + emit reportSummary(m_currentOutputReader->id(), m_currentOutputReader->summary()); - m_currentOutputReader->resetCommandlineColor(); + m_currentOutputReader->resetCommandlineColor(); + } resetInternalPointers(); if (!m_fakeFutureInterface) { From 1da2c1f7666ced45d6eea1cd0adfdc68a828e3a0 Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Mon, 14 Sep 2020 10:11:14 +0200 Subject: [PATCH 14/21] QmlDesigner: Change RichTextEditor icon Change-Id: I5a294e625f4ac61d15511369d7e765e05447a269 Reviewed-by: Brook Cronin Reviewed-by: Thomas Hartmann --- .../imports/HelperWidgets/StandardTextSection.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml index b4c07db2652..4ff60966f87 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml @@ -57,7 +57,7 @@ Section { StudioControls.AbstractButton { id: richTextEditorButton - buttonIcon: StudioTheme.Constants.textAlignTop + buttonIcon: StudioTheme.Constants.edit onClicked: { richTextDialogLoader.show() } From e371eafd4bde6f83ca7c9dd1373e41017851fe42 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 15 Sep 2020 13:33:37 +0200 Subject: [PATCH 15/21] Editor: Calculate annotation color based on editor color scheme Amends 9182d4eda75033bbbebf50e87e518adc33d499f6 Fixes: QTCREATORBUG-24644 Change-Id: Id34bfbc4ea3d1102835c7c1cf3afa1580dff01d8 Reviewed-by: Alessandro Portale --- src/plugins/texteditor/textmark.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index 47e5f54f761..dff6b86d63c 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -24,6 +24,8 @@ ****************************************************************************/ #include "textmark.h" + +#include "fontsettings.h" #include "textdocument.h" #include "texteditor.h" #include "texteditorplugin.h" @@ -139,8 +141,10 @@ void TextMark::paintAnnotation(QPainter &painter, QRectF *annotationRect, const QColor &markColor = m_color.has_value() ? Utils::creatorTheme()->color(m_color.value()).toHsl() : painter.pen().color(); + + const FontSettings &fontSettings = m_baseTextDocument->fontSettings(); const AnnotationColors &colors = AnnotationColors::getAnnotationColors( - markColor, painter.background().color()); + markColor, fontSettings.toTextCharFormat(C_TEXT).background().color()); painter.save(); QLinearGradient grad(rects.fadeInRect.topLeft() - contentOffset, From af88afe943fef260f59c530313d2cef8247ca601 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 16 Sep 2020 06:09:22 +0200 Subject: [PATCH 16/21] Debugger: Activate split that contains the breakpoint being hit Do not change the current editor after hitting a breakpoint if we already have an editor open in another split, but switch the focus to that split. Task-number: QTCREATORBUG-24646 Change-Id: Id85cecdfb522807bafa097e578509853326a2e31 Reviewed-by: hjk --- src/plugins/debugger/debuggerengine.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 3b0a06c2b99..c7540e8ae1d 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -1088,10 +1088,12 @@ void DebuggerEngine::gotoLocation(const Location &loc) const QString file = loc.fileName().toString(); const int line = loc.lineNumber(); bool newEditor = false; - IEditor *editor = EditorManager::openEditor( - file, Id(), - EditorManager::IgnoreNavigationHistory | EditorManager::DoNotSwitchToDesignMode, - &newEditor); + IEditor *editor = EditorManager::openEditor(file, + Id(), + EditorManager::IgnoreNavigationHistory + | EditorManager::DoNotSwitchToDesignMode + | EditorManager::SwitchSplitIfAlreadyVisible, + &newEditor); QTC_ASSERT(editor, return); // Unreadable file? editor->gotoLine(line, 0, !boolSetting(StationaryEditorWhileStepping)); From 7c8847333b6e0dab509e1e06006d133ff773c4bf Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 1 Sep 2020 16:36:56 +0200 Subject: [PATCH 17/21] QmlDesigner: Go into components Task-nubmer: QDS-2696 Change-Id: I2f0ee71deea593da08fa4a754d783b53867473ed Reviewed-by: Michael Winkelmann Reviewed-by: Leena Miettinen Reviewed-by: Thomas Hartmann --- .../componentcore/designeractionmanager.cpp | 62 ++++++++++++++++--- .../componentcore/modelnodeoperations.cpp | 4 +- .../componentcore/modelnodeoperations.h | 2 +- .../listmodeleditor/listmodeleditormodel.cpp | 14 +++-- .../listmodeleditor/listmodeleditormodel.h | 5 +- src/plugins/qmldesigner/documentmanager.cpp | 7 ++- src/plugins/qmldesigner/documentmanager.h | 2 +- .../unit/unittest/google-using-declarations.h | 2 + tests/unit/unittest/listmodeleditor-test.cpp | 57 ++++++++++++++--- 9 files changed, 127 insertions(+), 28 deletions(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index d9cc1521816..5c961eb8aef 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -26,14 +26,15 @@ #include "designeractionmanager.h" #include "changestyleaction.h" -#include "modelnodecontextmenu_helper.h" -#include -#include -#include -#include -#include #include "designeractionmanagerview.h" +#include "modelnodecontextmenu_helper.h" #include "qmldesignerconstants.h" +#include "rewritingexception.h" +#include +#include +#include +#include +#include #include @@ -44,8 +45,6 @@ #include #include -#include -#include #include #include @@ -53,6 +52,12 @@ #include #include +#include +#include +#include + +#include + namespace QmlDesigner { static inline QString captionForModelNode(const ModelNode &modelNode) @@ -339,6 +344,12 @@ public: } }; +class DocumentError : public std::exception +{ +public: + const char *what() const override { return "Current document contains errors."; } +}; + class EditListModelAction final : public ModelNodeContextMenuAction { public: @@ -384,6 +395,24 @@ public: return view->createModelNode(elementMetaInfo.typeName(), elementMetaInfo.majorVersion(), elementMetaInfo.minorVersion()); + }, + [&](const ModelNode &node) { + bool isNowInComponent = ModelNodeOperations::goIntoComponent( + node); + + Model *currentModel = QmlDesignerPlugin::instance() + ->currentDesignDocument() + ->currentModel(); + + if (currentModel->rewriterView() + && currentModel->rewriterView()->inErrorState()) { + throw DocumentError{}; + } + + if (isNowInComponent) + return view->rootModelNode(); + + return node; }}; model.setListView(targetNode); @@ -391,7 +420,22 @@ public: ListModelEditorDialog dialog{Core::ICore::mainWindow()}; dialog.setModel(&model); - dialog.exec(); + try { + dialog.exec(); + } catch (const DocumentError &) { + QMessageBox::warning( + Core::ICore::mainWindow(), + QCoreApplication::translate("DesignerActionManager", "Document has errors"), + QCoreApplication::translate("DesignerActionManager", + "The document which contains the list model " + "contains errors. So we cannot edit it.")); + } catch (const RewritingException &) { + QMessageBox::warning( + Core::ICore::mainWindow(), + QCoreApplication::translate("DesignerActionManager", "Document cannot be written"), + QCoreApplication::translate("DesignerActionManager", + "An error occurred during a write attemp.")); + } } }; diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 7453807a835..0fbaca46795 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -127,9 +127,9 @@ static void setUpperLeftPostionToNode(const ModelNode &layoutNode, const QList &createModelCallback) + const std::function &createModelCallback, + const std::function &goIntoComponentCallback) { if (listViewNode.hasProperty("model")) { - if (listViewNode.hasBindingProperty("model")) - return listViewNode.bindingProperty("model").resolveToModelNode(); - else if (listViewNode.hasNodeProperty("model")) - return listViewNode.nodeProperty("model").modelNode(); + if (listViewNode.hasBindingProperty("model")) { + return goIntoComponentCallback(listViewNode.bindingProperty("model").resolveToModelNode()); + } else if (listViewNode.hasNodeProperty("model")) { + return goIntoComponentCallback(listViewNode.nodeProperty("model").modelNode()); + } } ModelNode newModel = createModelCallback(); @@ -251,7 +253,7 @@ void ListModelEditorModel::setListModel(ModelNode node) void ListModelEditorModel::setListView(ModelNode listView) { - setListModel(listModelNode(listView, m_createModelCallback)); + setListModel(listModelNode(listView, m_createModelCallback, m_goIntoComponentCallback)); } void ListModelEditorModel::addRow() diff --git a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h index 97bd9c18d8b..5d3b87bbd71 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.h @@ -39,9 +39,11 @@ class ListModelEditorModel : public QStandardItemModel public: ListModelEditorModel(std::function createModelCallback, - std::function createElementCallback) + std::function createElementCallback, + std::function goIntoComponentCallback) : m_createModelCallback(std::move(createModelCallback)) , m_createElementCallback(std::move(createElementCallback)) + , m_goIntoComponentCallback(std::move(goIntoComponentCallback)) {} void setListModel(ModelNode node); @@ -76,6 +78,7 @@ private: QList m_propertyNames; std::function m_createModelCallback; std::function m_createElementCallback; + std::function m_goIntoComponentCallback; }; } // namespace QmlDesigner diff --git a/src/plugins/qmldesigner/documentmanager.cpp b/src/plugins/qmldesigner/documentmanager.cpp index 4550d8a4ea4..109b6577b4f 100644 --- a/src/plugins/qmldesigner/documentmanager.cpp +++ b/src/plugins/qmldesigner/documentmanager.cpp @@ -271,7 +271,7 @@ void DocumentManager::removeEditors(const QList &editors) delete m_designDocumentHash.take(editor).data(); } -void DocumentManager::goIntoComponent(const ModelNode &modelNode) +bool DocumentManager::goIntoComponent(const ModelNode &modelNode) { if (modelNode.isValid() && modelNode.isComponent() && designDocument()) { QmlDesignerPlugin::instance()->viewManager().setComponentNode(modelNode); @@ -286,9 +286,14 @@ void DocumentManager::goIntoComponent(const ModelNode &modelNode) openComponentSourcePropertyOfLoader(modelNode); else openInlineComponent(modelNode); + ModelNode rootModelNode = designDocument()->rewriterView()->rootModelNode(); applyProperties(rootModelNode, oldProperties); + + return true; } + + return false; } bool DocumentManager::createFile(const QString &filePath, const QString &contents) diff --git a/src/plugins/qmldesigner/documentmanager.h b/src/plugins/qmldesigner/documentmanager.h index bb3b13c9acc..8cd67eca9f6 100644 --- a/src/plugins/qmldesigner/documentmanager.h +++ b/src/plugins/qmldesigner/documentmanager.h @@ -51,7 +51,7 @@ public: void removeEditors(const QList &editors); - static void goIntoComponent(const ModelNode &modelNode); + static bool goIntoComponent(const ModelNode &modelNode); static bool createFile(const QString &filePath, const QString &contents); static void addFileToVersionControl(const QString &directoryPath, const QString &newFilePath); diff --git a/tests/unit/unittest/google-using-declarations.h b/tests/unit/unittest/google-using-declarations.h index de524834039..99514fe9b4a 100644 --- a/tests/unit/unittest/google-using-declarations.h +++ b/tests/unit/unittest/google-using-declarations.h @@ -63,6 +63,7 @@ using testing::Pair; using testing::PrintToString; using testing::Property; using testing::Return; +using testing::ReturnArg; using testing::ReturnRef; using testing::SafeMatcherCast; using testing::SaveArg; @@ -74,3 +75,4 @@ using testing::Throw; using testing::TypedEq; using testing::UnorderedElementsAre; using testing::VariantWith; +using testing::WithArg; diff --git a/tests/unit/unittest/listmodeleditor-test.cpp b/tests/unit/unittest/listmodeleditor-test.cpp index bdd5e9c0700..1bfa00646a1 100644 --- a/tests/unit/unittest/listmodeleditor-test.cpp +++ b/tests/unit/unittest/listmodeleditor-test.cpp @@ -99,17 +99,31 @@ public: listViewNode = mockView.createModelNode("QtQuick.ListView", 2, 15); listModelNode = mockView.createModelNode("QtQml.Models.ListModel", 2, 15); mockView.rootModelNode().defaultNodeListProperty().reparentHere(listModelNode); - element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}}); - element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}}); - element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}}); + element1 = createElement({{"name", "foo"}, {"value", 1}, {"value2", 42}}, + mockView, + listModelNode); + element2 = createElement({{"value", 4}, {"name", "bar"}, {"image", "pic.png"}}, + mockView, + listModelNode); + element3 = createElement({{"image", "pic.png"}, {"name", "poo"}, {"value", 111}}, + mockView, + listModelNode); + + componentModel->attachView(&mockComponentView); + + componentElement = createElement({{"name", "com"}, {"value", 11}, {"value2", 55}}, + mockComponentView, + mockComponentView.rootModelNode()); + + ON_CALL(mockGoIntoComponent, Call(_)).WillByDefault([](ModelNode node) { return node; }); } using Entry = std::pair; - ModelNode createElement(std::initializer_list entries) + ModelNode createElement(std::initializer_list entries, AbstractView &view, ModelNode listModel) { - auto element = mockView.createModelNode("QtQml.Models/ListElement", 2, 15); - listModelNode.defaultNodeListProperty().reparentHere(element); + auto element = view.createModelNode("QtQml.Models/ListElement", 2, 15); + listModel.defaultNodeListProperty().reparentHere(element); for (const auto &entry : entries) { element.variantProperty(entry.first).setValue(entry.second); @@ -184,17 +198,23 @@ public: } protected: + MockFunction mockGoIntoComponent; std::unique_ptr designerModel{QmlDesigner::Model::create("QtQuick.Item", 1, 1)}; NiceMock mockView; QmlDesigner::ListModelEditorModel model{ [&] { return mockView.createModelNode("QtQml.Models.ListModel", 2, 15); }, - [&] { return mockView.createModelNode("QtQml.Models.ListElement", 2, 15); }}; + [&] { return mockView.createModelNode("QtQml.Models.ListElement", 2, 15); }, + mockGoIntoComponent.AsStdFunction()}; ModelNode listViewNode; ModelNode listModelNode; ModelNode emptyListModelNode; ModelNode element1; ModelNode element2; ModelNode element3; + std::unique_ptr componentModel{ + QmlDesigner::Model::create("QtQml.Models.ListModel", 1, 1)}; + NiceMock mockComponentView; + ModelNode componentElement; }; TEST_F(ListModelEditor, CreatePropertyNameSet) @@ -1376,4 +1396,27 @@ TEST_F(ListModelEditor, AddFalseAsStringProperties) IsVariantProperty("value", 111)))); } +TEST_F(ListModelEditor, GoIntoComponentForBinding) +{ + EXPECT_CALL(mockGoIntoComponent, Call(Eq(listModelNode))) + .WillRepeatedly(Return(mockComponentView.rootModelNode())); + listModelNode.setIdWithoutRefactoring("listModel"); + listViewNode.bindingProperty("model").setExpression("listModel"); + + model.setListView(listViewNode); + + ASSERT_THAT(displayValues(), ElementsAre(ElementsAre("com", 11, 55))); +} + +TEST_F(ListModelEditor, GoIntoComponentForModelNode) +{ + EXPECT_CALL(mockGoIntoComponent, Call(Eq(listModelNode))) + .WillRepeatedly(Return(mockComponentView.rootModelNode())); + listViewNode.nodeProperty("model").reparentHere(listModelNode); + + model.setListView(listViewNode); + + ASSERT_THAT(displayValues(), ElementsAre(ElementsAre("com", 11, 55))); +} + } // namespace From a489fc18eb89fbb3fd8f80a92ec91abbde64b4a9 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Thu, 10 Sep 2020 16:40:22 +0200 Subject: [PATCH 18/21] Doc: Add LiteHTML license and related licenses to acknowledgments Fixes: QTCREATORBUG-24619 Change-Id: I8d28bf8a579e8885b430cd7df292f53363990ffa Reviewed-by: Kai Koehne Reviewed-by: Eike Ziller --- README.md | 68 +++++++++++++- .../overview/creator-acknowledgements.qdoc | 93 +++++++++++++++++-- 2 files changed, 151 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 725744339c6..cbebc279564 100644 --- a/README.md +++ b/README.md @@ -584,7 +584,6 @@ SQLite (https://www.sqlite.org) is in the Public Domain. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - \endcode The class contains the UTF-8 Decoder from Bjoern Hoehrmann which is licensed under the MIT License (see above). Copyright © 2008-2009 Björn @@ -593,3 +592,70 @@ SQLite (https://www.sqlite.org) is in the Public Domain. The class contains a slightly modified version of the Grisu2 algorithm from Florian Loitsch which is licensed under the MIT License (see above). Copyright © 2009 Florian Loitsch + +### litehtml + + The litehtml HTML/CSS rendering engine is used as a help viewer backend + to display help files. + + The sources can be found in: + * QtCreator/src/plugins/help/qlitehtml + * https://github.com/litehtml + + Copyright (c) 2013, Yuri Kobets (tordex) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +### gumbo + + The litehtml HTML/CSS rendering engine uses the gumbo parser. + + Copyright 2010, 2011 Google + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +### gumbo/utf8.c + + The litehtml HTML/CSS rendering engine uses gumbo/utf8.c parser. + + Copyright (c) 2008-2009 Bjoern Hoehrmann + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. diff --git a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc index faf769614ba..41bc076f7a7 100644 --- a/doc/qtcreator/src/overview/creator-acknowledgements.qdoc +++ b/doc/qtcreator/src/overview/creator-acknowledgements.qdoc @@ -84,7 +84,7 @@ as well as a ready-to-use QSyntaxHighlighter sub-class. Distributed under the: - \code + \badcode MIT License Permission is hereby granted, free of charge, to any person obtaining @@ -213,7 +213,7 @@ \l{https://spdx.org/licenses/BSD-3-Clause.html} {BSD 3-clause "New" or "Revised" License} - \code + \badcode Copyright (C) 2002-2013 The ANGLE Project Authors. All rights reserved. @@ -261,7 +261,7 @@ BSD 2-clause "Simplified" License. - \code + \badcode Copyright (C) 2012 Apple Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -294,7 +294,7 @@ The sources can be found in \c qtbase/src/3rdparty/angle/src/third_party/murmurhash. - \code + \badcode MurmurHash3 was written by Austin Appleby, and is placed in the public domain. The author hereby disclaims copyright to this source code. \endcode @@ -311,7 +311,7 @@ BSD 2-clause "Simplified" License. - \code + \badcode Copyright (C) 2009 Apple Inc. All Rights Reserved. Redistribution and use in source and binary forms, with or without @@ -346,7 +346,7 @@ BSD 3-clause "New" or "Revised" License. - \code + \badcode Copyright 2013 The Chromium Authors. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -401,7 +401,7 @@ the OpenSSL License and the original SSLeay license apply to the toolkit. See below for the actual license texts. - \code + \badcode OpenSSL License ==================================================================== @@ -527,7 +527,7 @@ \l{https://spdx.org/licenses/MIT.html}{MIT License}: - \code + \badcode Copyright (C) 1999-2007 Brian Paul All Rights Reserved. Permission is hereby granted, free of charge, to any person obtaining a @@ -561,7 +561,7 @@ Copyright (C) 2013-2019 Niels Lohmann - \code + \badcode Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the @@ -589,5 +589,80 @@ from Florian Loitsch which is licensed under the MIT License (see above). Copyright (C) 2009 Florian Loitsch + \li \b litehtml + + The litehtml HTML/CSS rendering engine is used as a help viewer backend + to display help files. + + The sources can be found in: + \list + \li \c QtCreator/src/plugins/help/qlitehtml + \li \l https://github.com/litehtml + \endlist + + Copyright (c) 2013, Yuri Kobets (tordex) + + \badcode + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + \endcode + + \li \b gumbo + + The litehtml HTML/CSS rendering engine uses the gumbo parser. + + Copyright 2010, 2011 Google + + \badcode + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + \endcode + + \li \b {gumbo/utf8.c} + + The litehtml HTML/CSS rendering engine uses gumbo/utf8.c parser. + + Copyright (c) 2008-2009 Bjoern Hoehrmann + + \badcode + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + \endcode + \endlist */ From 513d1e7e02c1cc48daa74522f837aa0c0e2c716b Mon Sep 17 00:00:00 2001 From: Henning Gruendl Date: Tue, 18 Aug 2020 17:52:44 +0200 Subject: [PATCH 19/21] QmlDesigner: Improve connection editor dialog * Add ComboBoxes to connection editor dialog * Add type toggle to dialog * Add slots and properties to ComboBoxes * Parse connection expression and fill ComboBoxes Task-number: QDS-2498 Task-number: QDS-2495 Task-number: QDS-2496 Change-Id: I2cca6d4c85d1508e54d4ad8863056f22ad777ae6 Reviewed-by: Thomas Hartmann --- src/plugins/qmldesigner/CMakeLists.txt | 3 + .../bindingeditor/abstracteditordialog.cpp | 183 +++++ .../bindingeditor/abstracteditordialog.h | 93 +++ .../components/bindingeditor/actioneditor.cpp | 183 ++++- .../components/bindingeditor/actioneditor.h | 18 +- .../bindingeditor/actioneditordialog.cpp | 656 ++++++++++++++++++ .../bindingeditor/actioneditordialog.h | 155 +++++ .../bindingeditor/bindingeditor.cpp | 42 +- .../bindingeditor/bindingeditor.pri | 6 + .../bindingeditor/bindingeditordialog.cpp | 150 +--- .../bindingeditor/bindingeditordialog.h | 48 +- .../bindingeditor/connectionvisitor.cpp | 112 +++ .../bindingeditor/connectionvisitor.h | 66 ++ .../connectioneditor/connectionviewwidget.cpp | 36 +- .../connectioneditor/connectionviewwidget.h | 2 +- src/plugins/qmldesigner/qmldesignerplugin.qbs | 6 + 16 files changed, 1526 insertions(+), 233 deletions(-) create mode 100644 src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp create mode 100644 src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h create mode 100644 src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp create mode 100644 src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h create mode 100644 src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.cpp create mode 100644 src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.h diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index 6c0e25c37ad..0938acb5b50 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -586,8 +586,11 @@ extend_qtc_plugin(QmlDesigner SOURCES_PREFIX components/bindingeditor SOURCES bindingeditor.cpp bindingeditor.h actioneditor.cpp actioneditor.h + abstracteditordialog.cpp abstracteditordialog.h + actioneditordialog.cpp actioneditordialog.h bindingeditordialog.cpp bindingeditordialog.h bindingeditorwidget.cpp bindingeditorwidget.h + connectionvisitor.cpp connectionvisitor.h ) extend_qtc_plugin(QmlDesigner diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp new file mode 100644 index 00000000000..872a004fcc8 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** 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 "abstracteditordialog.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace QmlDesigner { + +AbstractEditorDialog::AbstractEditorDialog(QWidget *parent, const QString &title) + : QDialog(parent) + , m_titleString(title) +{ + setWindowFlag(Qt::Tool, true); + setWindowTitle(defaultTitle()); + setModal(false); + + setupJSEditor(); + setupUIComponents(); + + QObject::connect(m_buttonBox, &QDialogButtonBox::accepted, + this, &AbstractEditorDialog::accepted); + QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, + this, &AbstractEditorDialog::rejected); + QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked, + this, &AbstractEditorDialog::accepted); + QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged, + this, &AbstractEditorDialog::textChanged); +} + +AbstractEditorDialog::~AbstractEditorDialog() +{ + delete m_editor; // m_editorWidget is handled by basetexteditor destructor + delete m_buttonBox; + delete m_comboBoxLayout; + delete m_verticalLayout; +} + +void AbstractEditorDialog::showWidget() +{ + this->show(); + this->raise(); + m_editorWidget->setFocus(); +} + +void AbstractEditorDialog::showWidget(int x, int y) +{ + showWidget(); + move(QPoint(x, y)); +} + +QString AbstractEditorDialog::editorValue() const +{ + if (!m_editorWidget) + return {}; + + return m_editorWidget->document()->toPlainText(); +} + +void AbstractEditorDialog::setEditorValue(const QString &text) +{ + if (m_editorWidget) + m_editorWidget->document()->setPlainText(text); +} + +void AbstractEditorDialog::unregisterAutoCompletion() +{ + if (m_editorWidget) + m_editorWidget->unregisterAutoCompletion(); +} + +QString AbstractEditorDialog::defaultTitle() const +{ + return m_titleString; +} + +void AbstractEditorDialog::setupJSEditor() +{ + static BindingEditorFactory f; + m_editor = qobject_cast(f.createEditor()); + m_editorWidget = qobject_cast(m_editor->editorWidget()); + + Core::Context context = m_editor->context(); + context.prepend(BINDINGEDITOR_CONTEXT_ID); + m_editorWidget->m_context->setContext(context); + + auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor(); + + m_editorWidget->qmljsdocument = qobject_cast( + qmlDesignerEditor->widget())->qmlJsEditorDocument(); + + m_editorWidget->setLineNumbersVisible(false); + m_editorWidget->setMarksVisible(false); + m_editorWidget->setCodeFoldingSupported(false); + m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); + m_editorWidget->setTabChangesFocus(true); +} + +void AbstractEditorDialog::setupUIComponents() +{ + m_verticalLayout = new QVBoxLayout(this); + + m_comboBoxLayout = new QHBoxLayout; + + m_editorWidget->setParent(this); + m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); + m_editorWidget->show(); + + m_buttonBox = new QDialogButtonBox(this); + m_buttonBox->setOrientation(Qt::Horizontal); + m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + m_verticalLayout->addLayout(m_comboBoxLayout); + m_verticalLayout->addWidget(m_editorWidget); + m_verticalLayout->addWidget(m_buttonBox); + + this->resize(660, 240); +} + +bool AbstractEditorDialog::isNumeric(const TypeName &type) +{ + static QList numericTypes = {"double", "int", "real"}; + return numericTypes.contains(type); +} + +bool AbstractEditorDialog::isColor(const TypeName &type) +{ + static QList colorTypes = {"QColor", "color"}; + return colorTypes.contains(type); +} + +bool AbstractEditorDialog::isVariant(const TypeName &type) +{ + static QList variantTypes = {"alias", "unknown", "variant", "var"}; + return variantTypes.contains(type); +} + +void AbstractEditorDialog::textChanged() +{ + if (m_lock) + return; + + m_lock = true; + adjustProperties(); + m_lock = false; +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h new file mode 100644 index 00000000000..ed8cdd0a136 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/abstracteditordialog.h @@ -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. +** +****************************************************************************/ + +#ifndef ABSTRACTEDITORDIALOG_H +#define ABSTRACTEDITORDIALOG_H + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QVBoxLayout; +class QHBoxLayout; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class AbstractEditorDialog : public QDialog +{ + Q_OBJECT + +public: + AbstractEditorDialog(QWidget *parent = nullptr, const QString &title = tr("Untitled Editor")); + ~AbstractEditorDialog() override; + + void showWidget(); + void showWidget(int x, int y); + + QString editorValue() const; + void setEditorValue(const QString &text); + + virtual void adjustProperties()= 0; + + void unregisterAutoCompletion(); + + QString defaultTitle() const; + + BindingEditorWidget *bindingEditorWidget() const + { + return m_editorWidget; + } + +protected: + void setupJSEditor(); + void setupUIComponents(); + + static bool isNumeric(const TypeName &type); + static bool isColor(const TypeName &type); + static bool isVariant(const TypeName &type); + +public slots: + void textChanged(); + +protected: + TextEditor::BaseTextEditor *m_editor = nullptr; + BindingEditorWidget *m_editorWidget = nullptr; + QVBoxLayout *m_verticalLayout = nullptr; + QDialogButtonBox *m_buttonBox = nullptr; + QHBoxLayout *m_comboBoxLayout = nullptr; + bool m_lock = false; + QString m_titleString; + + const QString undefinedString = {"[Undefined]"}; +}; + +} + +#endif //ABSTRACTEDITORDIALOG_H diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp index ac863778162..486e33640d3 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,14 @@ #include #include +#include +#include + +#include +#include + +static Q_LOGGING_CATEGORY(ceLog, "qtc.qmldesigner.connectioneditor", QtWarningMsg) + namespace QmlDesigner { static ActionEditor *s_lastActionEditor = nullptr; @@ -58,15 +67,14 @@ void ActionEditor::prepareDialog() { if (s_lastActionEditor) s_lastActionEditor->hideWidget(); + s_lastActionEditor = this; - m_dialog = new BindingEditorDialog(Core::ICore::dialogParent(), - BindingEditorDialog::DialogType::ActionDialog); + m_dialog = new ActionEditorDialog(Core::ICore::dialogParent()); - - QObject::connect(m_dialog, &BindingEditorDialog::accepted, + QObject::connect(m_dialog, &AbstractEditorDialog::accepted, this, &ActionEditor::accepted); - QObject::connect(m_dialog, &BindingEditorDialog::rejected, + QObject::connect(m_dialog, &AbstractEditorDialog::rejected, this, &ActionEditor::rejected); m_dialog->setAttribute(Qt::WA_DeleteOnClose); @@ -88,14 +96,14 @@ void ActionEditor::hideWidget() { if (s_lastActionEditor == this) s_lastActionEditor = nullptr; - if (m_dialog) - { - m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override + + if (m_dialog) { + m_dialog->unregisterAutoCompletion(); // we have to do it separately, otherwise we have an autocompletion action override m_dialog->close(); } } -QString ActionEditor::bindingValue() const +QString ActionEditor::connectionValue() const { if (!m_dialog) return {}; @@ -103,7 +111,7 @@ QString ActionEditor::bindingValue() const return m_dialog->editorValue(); } -void ActionEditor::setBindingValue(const QString &text) +void ActionEditor::setConnectionValue(const QString &text) { if (m_dialog) m_dialog->setEditorValue(text); @@ -129,11 +137,160 @@ void ActionEditor::setModelIndex(const QModelIndex &index) m_index = index; } +void ActionEditor::setModelNode(const ModelNode &modelNode) +{ + if (modelNode.isValid()) + m_modelNode = modelNode; +} + +bool isLiteral(QmlJS::AST::Node *ast) +{ + if (QmlJS::AST::cast(ast) + || QmlJS::AST::cast(ast) + || QmlJS::AST::cast(ast) + || QmlJS::AST::cast(ast)) + return true; + else + return false; +} + +void ActionEditor::prepareConnections() +{ + if (!m_modelNode.isValid()) + return; + + BindingEditorWidget *bindingEditorWidget = m_dialog->bindingEditorWidget(); + + if (!bindingEditorWidget) { + qCInfo(ceLog) << Q_FUNC_INFO << "BindingEditorWidget is missing!"; + return; + } + + if (!bindingEditorWidget->qmlJsEditorDocument()) { + qCInfo(ceLog) << Q_FUNC_INFO << "QmlJsEditorDocument is missing!"; + return; + } + + // Prepare objects for analysing slots + const QmlJSTools::SemanticInfo &semanticInfo = bindingEditorWidget->qmljsdocument->semanticInfo(); + const QList path = semanticInfo.rangePath(0); + const QmlJS::ContextPtr &context = semanticInfo.context; + const QmlJS::ScopeChain &scopeChain = semanticInfo.scopeChain(path); + + static QList typeWhiteList({"string", + "real", "int", "double", + "bool", + "QColor", "color", + "QtQuick.Item", "QQuickItem"}); + + static QList methodBlackList({"toString", "destroy"}); + + QList connections; + QList singletons; + QStringList states; + + const QList allNodes = m_modelNode.view()->allModelNodes(); + for (const auto &modelNode : allNodes) { + // Skip nodes without specified id + if (!modelNode.hasId()) + continue; + + ActionEditorDialog::ConnectionOption connection(modelNode.id()); + + for (const auto &propertyName : modelNode.metaInfo().propertyNames()) { + if (!typeWhiteList.contains(modelNode.metaInfo().propertyTypeName(propertyName))) + continue; + + const QString name = QString::fromUtf8(propertyName); + TypeName type = modelNode.metaInfo().propertyTypeName(propertyName); + if (type.contains(".")) + type.remove(0, 6); + + connection.properties.append(ActionEditorDialog::PropertyOption(name, type)); + } + + for (const VariantProperty &variantProperty : modelNode.variantProperties()) { + if (variantProperty.isValid()) { + if (variantProperty.isDynamic()) { + if (!typeWhiteList.contains(variantProperty.dynamicTypeName())) + continue; + + const QString name = QString::fromUtf8(variantProperty.name()); + TypeName type = variantProperty.dynamicTypeName(); + if (type.contains(".")) + type.remove(0, 6); + + connection.properties.append(ActionEditorDialog::PropertyOption(name, type)); + } + } + } + + for (const auto &slotName : modelNode.metaInfo().slotNames()) { + QmlJS::Document::MutablePtr newDoc = QmlJS::Document::create( + QLatin1String(""), QmlJS::Dialect::JavaScript); + newDoc->setSource(QLatin1String(slotName)); + newDoc->parseExpression(); + + QmlJS::AST::ExpressionNode *expression = newDoc->expression(); + + if (expression && !isLiteral(expression)) { + QmlJS::ValueOwner *interp = context->valueOwner(); + const QmlJS::Value *value = interp->convertToObject(scopeChain.evaluate(expression)); + + if (const QmlJS::FunctionValue *f = value->asFunctionValue()) { + // Only add slots with zero arguments + if (f->namedArgumentCount() == 0 && !methodBlackList.contains(slotName)) + connection.methods.append(QString::fromUtf8(slotName)); + } + } + } + + connection.methods.removeDuplicates(); + connections.append(connection); + } + + // Singletons + if (RewriterView *rv = m_modelNode.view()->rewriterView()) { + for (const QmlTypeData &data : rv->getQMLTypes()) { + if (!data.typeName.isEmpty()) { + NodeMetaInfo metaInfo = m_modelNode.view()->model()->metaInfo(data.typeName.toUtf8()); + if (metaInfo.isValid()) { + ActionEditorDialog::SingletonOption singelton; + for (const PropertyName &propertyName : metaInfo.propertyNames()) { + TypeName type = metaInfo.propertyTypeName(propertyName); + + if (!typeWhiteList.contains(type)) + continue; + + const QString name = QString::fromUtf8(propertyName); + if (type.contains(".")) + type.remove(0, 6); + + singelton.properties.append(ActionEditorDialog::PropertyOption(name, type)); + } + + if (!singelton.properties.isEmpty()) { + singelton.item = data.typeName; + singletons.append(singelton); + } + } + } + } + } + + // States + for (const QmlModelState &state : QmlItemNode(m_modelNode).states().allStates()) + states.append(state.name()); + + + if (!connections.isEmpty() && !m_dialog.isNull()) + m_dialog->setAllConnections(connections, singletons, states); +} + void ActionEditor::updateWindowName() { - if (!m_dialog.isNull()) - { - m_dialog->setWindowTitle(tr("Connection Editor")); + if (!m_dialog.isNull()) { + m_dialog->setWindowTitle(m_dialog->defaultTitle()); m_dialog->raise(); } } diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h index 7c53dffb603..c0356e81c42 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditor.h @@ -26,7 +26,7 @@ #ifndef ACTIONEDITOR_H #define ACTIONEDITOR_H -#include +#include #include #include @@ -40,7 +40,7 @@ class ActionEditor : public QObject { Q_OBJECT - Q_PROPERTY(QString text READ bindingValue WRITE setBindingValue) + Q_PROPERTY(QString text READ connectionValue WRITE setConnectionValue) public: ActionEditor(QObject *parent = nullptr); @@ -52,14 +52,18 @@ public: Q_INVOKABLE void showWidget(int x, int y); Q_INVOKABLE void hideWidget(); - QString bindingValue() const; - void setBindingValue(const QString &text); + QString connectionValue() const; + void setConnectionValue(const QString &text); bool hasModelIndex() const; void resetModelIndex(); QModelIndex modelIndex() const; void setModelIndex(const QModelIndex &index); + void setModelNode(const ModelNode &modelNode); + + void prepareConnections(); + Q_INVOKABLE void updateWindowName(); signals: @@ -67,14 +71,12 @@ signals: void rejected(); private: - QVariant backendValue() const; - QVariant modelNodeBackend() const; - QVariant stateModelNode() const; void prepareDialog(); private: - QPointer m_dialog; + QPointer m_dialog; QModelIndex m_index; + QmlDesigner::ModelNode m_modelNode; }; } diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp new file mode 100644 index 00000000000..bcd99de7168 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.cpp @@ -0,0 +1,656 @@ +/**************************************************************************** +** +** 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 "actioneditordialog.h" + +#include "connectionvisitor.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static Q_LOGGING_CATEGORY(ceLog, "qtc.qmldesigner.connectioneditor", QtWarningMsg) + +namespace QmlDesigner { + +ActionEditorDialog::ActionEditorDialog(QWidget *parent) + : AbstractEditorDialog(parent, tr("Connection Editor")) +{ + setupUIComponents(); + + QObject::connect(m_comboBoxType, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::Type); }); + + // Action connections + QObject::connect(m_actionTargetItem, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetItem); }); + QObject::connect(m_actionMethod, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetProperty); }); + + // Assignment connections + QObject::connect(m_assignmentTargetItem, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetItem); }); + QObject::connect(m_assignmentTargetProperty, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::TargetProperty); }); + QObject::connect(m_assignmentSourceItem, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::SourceItem); }); + QObject::connect(m_assignmentSourceProperty, QOverload::of(&QComboBox::activated), + [this] (int idx) { this->updateComboBoxes(idx, ComboBox::SourceProperty); }); +} + +ActionEditorDialog::~ActionEditorDialog() +{ +} + +void ActionEditorDialog::adjustProperties() +{ + // Analyze the current connection editor statement/expression + const auto qmlJSDocument = bindingEditorWidget()->qmlJsEditorDocument(); + auto doc = QmlJS::Document::create(QLatin1String(""), QmlJS::Dialect::JavaScript); + doc->setSource(qmlJSDocument->plainText()); + bool parseResult = doc->parseExpression(); + + if (!parseResult) { + qCInfo(ceLog) << Q_FUNC_INFO << "Couldn't parse the expression!"; + return; + } + + auto astNode = doc->ast(); + if (!astNode) { + qCInfo(ceLog) << Q_FUNC_INFO << "There was no AST::Node in the document!"; + return; + } + + ConnectionVisitor qmlVisitor; + QmlJS::AST::Node::accept(astNode, &qmlVisitor); + + const auto expression = qmlVisitor.expression(); + + if (expression.isEmpty()) { + // Set all ComboBoxes to [Undefined], add connections to target item ComboBox + fillAndSetTargetItem(undefinedString); + fillAndSetTargetProperty(undefinedString); + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(undefinedString); + return; + } + + bool typeDone = false; + bool targetDone = false; + + for (int i = 0; i < expression.count(); ++i) { + switch (expression[i].first) { + + case QmlJS::AST::Node::Kind::Kind_CallExpression: + { + setType(ConnectionType::Action); + typeDone = true; + } + break; + + case QmlJS::AST::Node::Kind::Kind_BinaryExpression: + { + setType(ConnectionType::Assignment); + typeDone = true; + } + break; + + case QmlJS::AST::Node::Kind::Kind_FieldMemberExpression: + { + QString fieldMember = expression[i].second; + ++i;// Increment index to get IdentifierExpression or next FieldMemberExpression + while (expression[i].first == QmlJS::AST::Node::Kind::Kind_FieldMemberExpression) { + fieldMember.prepend(expression[i].second + "."); + ++i; // Increment index to get IdentifierExpression + } + + if (targetDone && m_comboBoxType->currentIndex() != ConnectionType::Action) { + fillAndSetSourceItem(expression[i].second); + fillAndSetSourceProperty(fieldMember); + } else { + if (typeDone) { + fillAndSetTargetItem(expression[i].second); + fillAndSetTargetProperty(fieldMember); + } else { // e.g. 'element.width' + // In this case Assignment is more likley + setType(ConnectionType::Assignment); + fillAndSetTargetItem(expression[i].second); + fillAndSetTargetProperty(fieldMember); + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(undefinedString); + } + targetDone = true; + } + + } + break; + + case QmlJS::AST::Node::Kind::Kind_TrueLiteral: + case QmlJS::AST::Node::Kind::Kind_FalseLiteral: + case QmlJS::AST::Node::Kind::Kind_NumericLiteral: + case QmlJS::AST::Node::Kind::Kind_StringLiteral: + { + if (targetDone) { + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(expression[i].second, expression[i].first); + } else { + // In this case Assignment is more likley + setType(ConnectionType::Assignment); + fillAndSetTargetItem(undefinedString); + fillAndSetTargetProperty(undefinedString); + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(expression[i].second, expression[i].first); + } + } + break; + + case QmlJS::AST::Node::Kind::Kind_IdentifierExpression: + { + if (typeDone) { + if (m_comboBoxType->currentIndex() == ConnectionType::Assignment) { // e.g. 'element = rectangle + if (targetDone) { + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(undefinedString); + } else { + fillAndSetTargetItem(expression[i].second); + fillAndSetTargetProperty(undefinedString); + targetDone = true; + } + } else { // e.g. 'print("blabla")' + fillAndSetTargetItem(undefinedString); + fillAndSetTargetProperty(undefinedString); + targetDone = true; + } + } else { // e.g. 'element' + // In this case Assignment is more likley + setType(ConnectionType::Assignment); + fillAndSetTargetItem(expression[i].second); + fillAndSetTargetProperty(undefinedString); + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(undefinedString); + } + } + break; + + default: + { + fillAndSetTargetItem(undefinedString); + fillAndSetTargetProperty(undefinedString); + fillAndSetSourceItem(undefinedString); + fillAndSetSourceProperty(undefinedString); + } + break; + } + } +} + +void ActionEditorDialog::setAllConnections(const QList &connections, + const QList &singletons, + const QStringList &states) +{ + m_lock = true; + + m_connections = connections; + m_singletons = singletons; + m_states = states; + adjustProperties(); + + m_lock = false; +} + +void ActionEditorDialog::updateComboBoxes(int index, ComboBox type) +{ + Q_UNUSED(index) + + const int currentType = m_comboBoxType->currentIndex(); + const int currentStack = m_stackedLayout->currentIndex(); + bool typeChanged = false; + + if (type == ComboBox::Type) { + if (currentType != currentStack) + typeChanged = true; + else + return; // Prevent rebuild of expression if type didn't change + } + + if (typeChanged) { + m_stackedLayout->setCurrentIndex(currentType); + if (currentStack == ConnectionType::Action) { + // Previous type was Action + const auto targetItem = m_actionTargetItem->currentText(); + fillAndSetTargetItem(targetItem, true); + fillAndSetTargetProperty(QString(), true); + fillAndSetSourceItem(QString(), true); + fillAndSetSourceProperty(QString(), QmlJS::AST::Node::Kind::Kind_Undefined, true); + } else { + // Previous type was Assignment + const auto targetItem = m_assignmentTargetItem->currentText(); + fillAndSetTargetItem(targetItem, true); + fillAndSetTargetProperty(QString(), true); + } + } else { + if (currentType == ConnectionType::Action) { + // Prevent rebuild of expression if undefinedString item was selected + switch (type) { + case ComboBox::TargetItem: + if (m_actionTargetItem->currentText() == undefinedString) + return; + break; + case ComboBox::TargetProperty: + if (m_actionMethod->currentText() == undefinedString) + return; + break; + default: + break; + } + + fillAndSetTargetItem(m_actionTargetItem->currentText()); + fillAndSetTargetProperty(m_actionMethod->currentText(), true); + } else { // ConnectionType::Assignment + const auto targetItem = m_assignmentTargetItem->currentText(); + const auto targetProperty = m_assignmentTargetProperty->currentText(); + const auto sourceItem = m_assignmentSourceItem->currentText(); + const auto sourceProperty = m_assignmentSourceProperty->currentText(); + + // Prevent rebuild of expression if undefinedString item was selected + switch (type) { + case ComboBox::TargetItem: + if (targetItem == undefinedString) + return; + break; + case ComboBox::TargetProperty: + if (targetProperty == undefinedString) + return; + break; + case ComboBox::SourceItem: + if (sourceItem == undefinedString) + return; + break; + case ComboBox::SourceProperty: + if (sourceProperty == undefinedString) + return; + break; + default: + break; + } + + fillAndSetTargetItem(targetItem, true); + fillAndSetTargetProperty(targetProperty, true); + + const auto sourcePropertyType = m_assignmentSourceProperty->currentData().value(); + + if (type == ComboBox::SourceItem) { + fillAndSetSourceItem(sourceItem, true); + + if (sourcePropertyType == specificItem) + fillAndSetSourceProperty(QString(), + QmlJS::AST::Node::Kind::Kind_Undefined, + true); + else + fillAndSetSourceProperty(sourceProperty, + QmlJS::AST::Node::Kind::Kind_Undefined, + true); + } else if (type == ComboBox::SourceProperty) { + if (sourcePropertyType == specificItem) { + fillAndSetSourceItem(QString(), false); + fillAndSetSourceProperty(sourceProperty, + QmlJS::AST::Node::Kind::Kind_StringLiteral, + false); + } else { + fillAndSetSourceProperty(sourceProperty); + } + } else { + if (sourcePropertyType == specificItem) { + fillAndSetSourceItem(QString(), false); + fillAndSetSourceProperty(sourceProperty, + QmlJS::AST::Node::Kind::Kind_StringLiteral, + false); + } else { + fillAndSetSourceItem(sourceItem, true); + fillAndSetSourceProperty(sourceProperty, + QmlJS::AST::Node::Kind::Kind_Undefined, + true); + } + } + } + } + + // Compose expression + QString value; + if (currentType == ConnectionType::Action) { + const auto targetItem = m_actionTargetItem->currentText(); + const auto method = m_actionMethod->currentText(); + + if (targetItem != undefinedString && method != undefinedString){ + value = targetItem + "." + method + "()"; + } else if (targetItem != undefinedString && method == undefinedString) { + value = targetItem; + } + } else { // ConnectionType::Assignment + const auto targetItem = m_assignmentTargetItem->currentText(); + const auto targetProperty = m_assignmentTargetProperty->currentText(); + const auto sourceItem = m_assignmentSourceItem->currentText(); + const auto sourceProperty = m_assignmentSourceProperty->currentText(); + + QString lhs; + + if (targetItem != undefinedString && targetProperty != undefinedString) { + lhs = targetItem + "." + targetProperty; + } else if (targetItem != undefinedString && targetProperty == undefinedString) { + lhs = targetItem; + } + + QString rhs; + + if (sourceItem != undefinedString && sourceProperty != undefinedString) { + rhs = sourceItem + "." + sourceProperty; + } else if (sourceItem != undefinedString && sourceProperty == undefinedString) { + rhs = sourceItem; + } else if (sourceItem == undefinedString && sourceProperty != undefinedString) { + const QString data = m_assignmentTargetProperty->currentData().toString(); + if (data == "string") { + rhs = "\"" + sourceProperty + "\""; + } else { + rhs = sourceProperty; + } + } + + if (!lhs.isEmpty() && !rhs.isEmpty()) { + value = lhs + " = " + rhs; + } else { + value = lhs + rhs; + } + } + + { + const QSignalBlocker blocker(m_editorWidget); + setEditorValue(value); + } +} + +void ActionEditorDialog::setupUIComponents() +{ + m_comboBoxType = new QComboBox(this); + + QMetaEnum metaEnum = QMetaEnum::fromType(); + for (int i = 0; i != metaEnum.keyCount(); ++i) { + const char *key = QMetaEnum::fromType().valueToKey(i); + m_comboBoxType->addItem(QString::fromLatin1(key)); + } + + m_comboBoxLayout->addWidget(m_comboBoxType); + + m_stackedLayout = new QStackedLayout(); + + m_actionLayout = new QHBoxLayout(); + m_assignmentLayout = new QHBoxLayout(); + + m_actionPlaceholder = new QWidget(this); + m_actionPlaceholder->setLayout(m_actionLayout); + + m_assignmentPlaceholder = new QWidget(this); + m_assignmentPlaceholder->setLayout(m_assignmentLayout); + + // Setup action ComboBoxes + m_actionTargetItem = new QComboBox(this); + m_actionMethod = new QComboBox(this); + m_actionLayout->addWidget(m_actionTargetItem); + m_actionLayout->addWidget(m_actionMethod); + + // Setup assignment ComboBoxes + m_assignmentTargetItem = new QComboBox(this); + m_assignmentTargetProperty = new QComboBox(this); + m_assignmentSourceItem = new QComboBox(this); + m_assignmentSourceProperty = new QComboBox(this); + m_assignmentLayout->addWidget(m_assignmentTargetItem); + m_assignmentLayout->addWidget(m_assignmentTargetProperty); + m_assignmentLayout->addWidget(m_assignmentSourceItem); + m_assignmentLayout->addWidget(m_assignmentSourceProperty); + + m_stackedLayout->addWidget(m_actionPlaceholder); + m_stackedLayout->addWidget(m_assignmentPlaceholder); + + m_comboBoxLayout->addItem(m_stackedLayout); + + this->resize(720, 240); +} + +void ActionEditorDialog::setType(ConnectionType type) +{ + m_comboBoxType->setCurrentIndex(type); + m_stackedLayout->setCurrentIndex(type); +} + +void ActionEditorDialog::fillAndSetTargetItem(const QString &value, bool useDefault) +{ + if (m_comboBoxType->currentIndex() == ConnectionType::Action) { + m_actionTargetItem->clear(); + for (const auto &connection : m_connections) { + if (!connection.methods.isEmpty()) + m_actionTargetItem->addItem(connection.item); + } + + if (m_actionTargetItem->findText(value) != -1) { + m_actionTargetItem->setCurrentText(value); + } else { + if (useDefault && m_actionTargetItem->count()) + m_actionTargetItem->setCurrentIndex(0); + else + insertAndSetUndefined(m_actionTargetItem); + } + } else { // ConnectionType::Assignment + m_assignmentTargetItem->clear(); + for (const auto &connection : m_connections) { + if (!connection.properties.isEmpty()) + m_assignmentTargetItem->addItem(connection.item); + } + + if (m_assignmentTargetItem->findText(value) != -1) { + m_assignmentTargetItem->setCurrentText(value); + } else { + if (useDefault && m_actionTargetItem->count()) + m_actionTargetItem->setCurrentIndex(0); + else + insertAndSetUndefined(m_assignmentTargetItem); + } + } +} + +void ActionEditorDialog::fillAndSetTargetProperty(const QString &value, bool useDefault) +{ + if (m_comboBoxType->currentIndex() == ConnectionType::Action) { + m_actionMethod->clear(); + const QString targetItem = m_actionTargetItem->currentText(); + const int idx = m_connections.indexOf(targetItem); + + if (idx == -1) { + insertAndSetUndefined(m_actionMethod); + } else { + m_actionMethod->addItems(m_connections[idx].methods); + + if (m_actionMethod->findText(value) != -1) { + m_actionMethod->setCurrentText(value); + } else { + if (useDefault && m_actionMethod->count()) + m_actionMethod->setCurrentIndex(0); + else + insertAndSetUndefined(m_actionMethod); + } + } + } else { // ConnectionType::Assignment + m_assignmentTargetProperty->clear(); + const QString targetItem = m_assignmentTargetItem->currentText(); + const int idx = m_connections.indexOf(targetItem); + + if (idx == -1) { + insertAndSetUndefined(m_assignmentTargetProperty); + } else { + for (const auto &property : m_connections[idx].properties) + m_assignmentTargetProperty->addItem(property.name, property.type); + + if (m_assignmentTargetProperty->findText(value) != -1) { + m_assignmentTargetProperty->setCurrentText(value); + } else { + if (useDefault && m_assignmentTargetProperty->count()) + m_assignmentTargetProperty->setCurrentIndex(0); + else + insertAndSetUndefined(m_assignmentTargetProperty); + } + } + } +} + +void ActionEditorDialog::fillAndSetSourceItem(const QString &value, bool useDefault) +{ + m_assignmentSourceItem->clear(); + const TypeName targetPropertyType = m_assignmentTargetProperty->currentData().value(); + + if (!targetPropertyType.isEmpty()) { + for (const ConnectionOption &connection : m_connections) { + if (!connection.containsType(targetPropertyType)) + continue; + + m_assignmentSourceItem->addItem(connection.item); + } + + // Add Constants + for (const SingletonOption &singleton : m_singletons) { + if (!singleton.containsType(targetPropertyType)) + continue; + + m_assignmentSourceItem->addItem(singleton.item, singletonItem); + } + } + + if (m_assignmentSourceItem->findText(value) != -1) { + m_assignmentSourceItem->setCurrentText(value); + } else { + if (useDefault && m_assignmentSourceItem->count()) + m_assignmentSourceItem->setCurrentIndex(0); + else + insertAndSetUndefined(m_assignmentSourceItem); + } +} + +void ActionEditorDialog::fillAndSetSourceProperty(const QString &value, + QmlJS::AST::Node::Kind kind, + bool useDefault) +{ + m_assignmentSourceProperty->clear(); + const TypeName targetPropertyType = m_assignmentTargetProperty->currentData().value(); + const QString targetProperty = m_assignmentTargetProperty->currentText(); + + if (kind != QmlJS::AST::Node::Kind::Kind_Undefined) { + if (targetPropertyType == "bool") { + m_assignmentSourceProperty->addItem("true", specificItem); + m_assignmentSourceProperty->addItem("false", specificItem); + + if (m_assignmentSourceProperty->findText(value) != -1) + m_assignmentSourceProperty->setCurrentText(value); + else + insertAndSetUndefined(m_assignmentSourceProperty); + } else if (targetProperty == "state") { + for (const auto &state : m_states) + m_assignmentSourceProperty->addItem(state, specificItem); + + if (m_assignmentSourceProperty->findText(value) != -1) + m_assignmentSourceProperty->setCurrentText(value); + else + insertAndSetUndefined(m_assignmentSourceProperty); + } else { + m_assignmentSourceProperty->insertItem(0, value, specificItem); + m_assignmentSourceProperty->setCurrentIndex(0); + } + + } else { + const TypeName sourceItemType = m_assignmentSourceItem->currentData().value(); + const QString sourceItem = m_assignmentSourceItem->currentText(); + // We need to distinguish between singleton (Constants) and standard item + const int idx = (sourceItemType == singletonItem) ? m_singletons.indexOf(sourceItem) + : m_connections.indexOf(sourceItem); + + if (idx == -1) { + insertAndSetUndefined(m_assignmentSourceProperty); + } else { + int specificsEnd = -1; + // Add type specific items + if (targetPropertyType == "bool") { + m_assignmentSourceProperty->addItem("true", specificItem); + m_assignmentSourceProperty->addItem("false", specificItem); + specificsEnd = 2; + } else if (targetProperty == "state") { + for (const auto &state : m_states) + m_assignmentSourceProperty->addItem(state, specificItem); + + specificsEnd = m_states.count(); + } + + if (specificsEnd != -1) + m_assignmentSourceProperty->insertSeparator(specificsEnd); + + if (sourceItemType == singletonItem) { + for (const auto &property : m_singletons[idx].properties) { + if (targetPropertyType.isEmpty() // TODO isEmpty correct?! + || property.type == targetPropertyType + || (isNumeric(property.type) && isNumeric(targetPropertyType))) + m_assignmentSourceProperty->addItem(property.name, property.type); + } + } else { + for (const auto &property : m_connections[idx].properties) { + if (targetPropertyType.isEmpty() // TODO isEmpty correct?! + || property.type == targetPropertyType + || (isNumeric(property.type) && isNumeric(targetPropertyType))) + m_assignmentSourceProperty->addItem(property.name, property.type); + } + } + + if (m_assignmentSourceProperty->findText(value) != -1 && !value.isEmpty()) { + m_assignmentSourceProperty->setCurrentText(value); + } else { + if (useDefault && m_assignmentSourceProperty->count()) + m_assignmentSourceProperty->setCurrentIndex(specificsEnd + 1); + else + insertAndSetUndefined(m_assignmentSourceProperty); + } + } + } +} + +void ActionEditorDialog::insertAndSetUndefined(QComboBox *comboBox) +{ + comboBox->insertItem(0, undefinedString); + comboBox->setCurrentIndex(0); +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h new file mode 100644 index 00000000000..e19c7fe657b --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/actioneditordialog.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** 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. +** +****************************************************************************/ + +#ifndef ACTIONEDITORDIALOG_H +#define ACTIONEDITORDIALOG_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QComboBox; +QT_END_NAMESPACE + +namespace QmlDesigner { + +class ActionEditorDialog : public AbstractEditorDialog +{ + Q_OBJECT + +public: + enum ConnectionType { Action, Assignment }; + Q_ENUM(ConnectionType) + + enum ComboBox { Type, TargetItem, TargetProperty, SourceItem, SourceProperty }; + Q_ENUM(ComboBox) + + class PropertyOption + { + public: + PropertyOption() {} + PropertyOption(const QString &n, const TypeName &t) + : name(n) + , type(t) + {} + + bool operator==(const QString &value) const { return value == name; } + bool operator==(const PropertyOption &value) const { return value.name == name; } + + QString name; + TypeName type; + }; + + class SingletonOption + { + public: + SingletonOption() {} + SingletonOption(const QString &value) { item = value; } + + bool containsType(const TypeName &t) const + { + for (const auto &p : properties) { + if (t == p.type || (isNumeric(t) && isNumeric(p.type))) + return true; + } + + return false; + } + + bool operator==(const QString &value) const { return value == item; } + bool operator==(const SingletonOption &value) const { return value.item == item; } + + QString item; + QList properties; + }; + + class ConnectionOption : public SingletonOption + { + public: + ConnectionOption() {} + ConnectionOption(const QString &value) : SingletonOption(value) {} + + QStringList methods; + }; + + + ActionEditorDialog(QWidget *parent = nullptr); + ~ActionEditorDialog() override; + + void adjustProperties() override; + + void setAllConnections(const QList &connections, + const QList &singeltons, + const QStringList &states); + + void updateComboBoxes(int idx, ComboBox type); + +private: + void setupUIComponents(); + + void setType(ConnectionType type); + + void fillAndSetTargetItem(const QString &value, bool useDefault = false); + void fillAndSetTargetProperty(const QString &value, bool useDefault = false); + + void fillAndSetSourceItem(const QString &value, bool useDefault = false); + void fillAndSetSourceProperty(const QString &value, + QmlJS::AST::Node::Kind kind = QmlJS::AST::Node::Kind::Kind_Undefined, + bool useDefault = false); + + void insertAndSetUndefined(QComboBox *comboBox); + +private: + QComboBox *m_comboBoxType = nullptr; + + QStackedLayout *m_stackedLayout = nullptr; + + QWidget *m_actionPlaceholder = nullptr; + QWidget *m_assignmentPlaceholder = nullptr; + + QHBoxLayout *m_actionLayout = nullptr; + QHBoxLayout *m_assignmentLayout = nullptr; + + QComboBox *m_actionTargetItem = nullptr; + QComboBox *m_actionMethod = nullptr; + + QComboBox *m_assignmentTargetItem = nullptr; + QComboBox *m_assignmentTargetProperty = nullptr; + QComboBox *m_assignmentSourceItem = nullptr; + QComboBox *m_assignmentSourceProperty = nullptr; // Value + + QList m_connections; + QList m_singletons; + QStringList m_states; + + const TypeName specificItem = {"specific"}; + const TypeName singletonItem = {"singleton"}; +}; + +} + +#endif //ACTIONEDITORDIALOG_H diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp index d2bdd3cbb80..5d7f8281d04 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -60,14 +61,14 @@ void BindingEditor::prepareDialog() { if (s_lastBindingEditor) s_lastBindingEditor->hideWidget(); + s_lastBindingEditor = this; m_dialog = new BindingEditorDialog(Core::ICore::dialogParent()); - - QObject::connect(m_dialog, &BindingEditorDialog::accepted, + QObject::connect(m_dialog, &AbstractEditorDialog::accepted, this, &BindingEditor::accepted); - QObject::connect(m_dialog, &BindingEditorDialog::rejected, + QObject::connect(m_dialog, &AbstractEditorDialog::rejected, this, &BindingEditor::rejected); m_dialog->setAttribute(Qt::WA_DeleteOnClose); @@ -89,8 +90,8 @@ void BindingEditor::hideWidget() { if (s_lastBindingEditor == this) s_lastBindingEditor = nullptr; - if (m_dialog) - { + + if (m_dialog) { m_dialog->unregisterAutoCompletion(); //we have to do it separately, otherwise we have an autocompletion action override m_dialog->close(); } @@ -118,8 +119,7 @@ void BindingEditor::setBackendValue(const QVariant &backendValue) const PropertyEditorValue *propertyEditorValue = qobject_cast(backendValueObj); const ModelNode node = propertyEditorValue->modelNode(); - if (node.isValid()) - { + if (node.isValid()) { m_backendValueTypeName = node.metaInfo().propertyTypeName(propertyEditorValue->name()); if (m_backendValueTypeName == "alias" || m_backendValueTypeName == "unknown") @@ -141,9 +141,8 @@ void BindingEditor::setModelNodeBackend(const QVariant &modelNodeBackend) const auto backendObjectCasted = qobject_cast(modelNodeBackendObject); - if (backendObjectCasted) { + if (backendObjectCasted) m_modelNode = backendObjectCasted->qmlObjectNode().modelNode(); - } emit modelNodeBackendChanged(); } @@ -151,8 +150,7 @@ void BindingEditor::setModelNodeBackend(const QVariant &modelNodeBackend) void BindingEditor::setStateModelNode(const QVariant &stateModelNode) { - if (stateModelNode.isValid()) - { + if (stateModelNode.isValid()) { m_stateModelNode = stateModelNode; m_modelNode = m_stateModelNode.value(); @@ -188,21 +186,21 @@ void BindingEditor::prepareBindings() const QList variantTypes = {"alias", "unknown", "variant", "var"}; const QList numericTypes = {"double", "real", "int"}; const QList colorTypes = {"QColor", "color"}; - auto isNumeric = [&numericTypes](TypeName compareType) { return numericTypes.contains(compareType); }; - auto isColor = [&colorTypes](TypeName compareType) { return colorTypes.contains(compareType); }; + auto isVariant = [&variantTypes](const TypeName &compareType) { return variantTypes.contains(compareType); }; + auto isNumeric = [&numericTypes](const TypeName &compareType) { return numericTypes.contains(compareType); }; + auto isColor = [&colorTypes](const TypeName &compareType) { return colorTypes.contains(compareType); }; - const bool skipTypeFiltering = variantTypes.contains(m_backendValueTypeName); + const bool skipTypeFiltering = isVariant(m_backendValueTypeName); const bool targetTypeIsNumeric = isNumeric(m_backendValueTypeName); for (const auto &objnode : allNodes) { BindingEditorDialog::BindingOption binding; - for (const auto &propertyName : objnode.metaInfo().propertyNames()) - { + for (const auto &propertyName : objnode.metaInfo().propertyNames()) { TypeName propertyTypeName = objnode.metaInfo().propertyTypeName(propertyName); if (skipTypeFiltering || (m_backendValueTypeName == propertyTypeName) - || variantTypes.contains(propertyTypeName) + || isVariant(propertyTypeName) || (targetTypeIsNumeric && isNumeric(propertyTypeName))) { binding.properties.append(QString::fromUtf8(propertyName)); } @@ -215,7 +213,7 @@ void BindingEditor::prepareBindings() const TypeName dynamicTypeName = bindingProperty.dynamicTypeName(); if (skipTypeFiltering || (dynamicTypeName == m_backendValueTypeName) - || variantTypes.contains(dynamicTypeName) + || isVariant(dynamicTypeName) || (targetTypeIsNumeric && isNumeric(dynamicTypeName))) { binding.properties.append(QString::fromUtf8(bindingProperty.name())); } @@ -228,7 +226,7 @@ void BindingEditor::prepareBindings() const TypeName dynamicTypeName = variantProperty.dynamicTypeName(); if (skipTypeFiltering || (dynamicTypeName == m_backendValueTypeName) - || variantTypes.contains(dynamicTypeName) + || isVariant(dynamicTypeName) || (targetTypeIsNumeric && isNumeric(dynamicTypeName))) { binding.properties.append(QString::fromUtf8(variantProperty.name())); } @@ -243,7 +241,7 @@ void BindingEditor::prepareBindings() } //singletons: - if (RewriterView* rv = m_modelNode.view()->rewriterView()) { + if (RewriterView *rv = m_modelNode.view()->rewriterView()) { for (const QmlTypeData &data : rv->getQMLTypes()) { if (!data.typeName.isEmpty()) { NodeMetaInfo metaInfo = m_modelNode.view()->model()->metaInfo(data.typeName.toUtf8()); @@ -256,7 +254,7 @@ void BindingEditor::prepareBindings() if (skipTypeFiltering || (m_backendValueTypeName == propertyTypeName) - || (variantTypes.contains(propertyTypeName)) + || (isVariant(propertyTypeName)) || (targetTypeIsNumeric && isNumeric(propertyTypeName)) || (isColor(m_backendValueTypeName) && isColor(propertyTypeName))) { binding.properties.append(QString::fromUtf8(propertyName)); @@ -281,9 +279,7 @@ void BindingEditor::prepareBindings() void BindingEditor::updateWindowName() { if (!m_dialog.isNull() && !m_backendValueTypeName.isEmpty()) - { m_dialog->setWindowTitle(m_dialog->defaultTitle() + " [" + m_backendValueTypeName + "]"); - } } QVariant BindingEditor::backendValue() const diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri index 925cea35398..ff2920ffabc 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditor.pri @@ -1,9 +1,15 @@ HEADERS += $$PWD/bindingeditor.h HEADERS += $$PWD/actioneditor.h +HEADERS += $$PWD/abstracteditordialog.h +HEADERS += $$PWD/actioneditordialog.h HEADERS += $$PWD/bindingeditordialog.h HEADERS += $$PWD/bindingeditorwidget.h +HEADERS += $$PWD/connectionvisitor.h SOURCES += $$PWD/bindingeditor.cpp SOURCES += $$PWD/actioneditor.cpp +SOURCES += $$PWD/abstracteditordialog.cpp +SOURCES += $$PWD/actioneditordialog.cpp SOURCES += $$PWD/bindingeditordialog.cpp SOURCES += $$PWD/bindingeditorwidget.cpp +SOURCES += $$PWD/connectionvisitor.cpp diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp index 9d2b1cb9e0c..b74d805f084 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -41,80 +41,19 @@ namespace QmlDesigner { -BindingEditorDialog::BindingEditorDialog(QWidget *parent, DialogType type) - : QDialog(parent) - , m_dialogType(type) +BindingEditorDialog::BindingEditorDialog(QWidget *parent) + : AbstractEditorDialog(parent, tr("Binding Editor")) { - setWindowFlag(Qt::Tool, true); - setWindowTitle(defaultTitle()); - setModal(false); - - setupJSEditor(); setupUIComponents(); - QObject::connect(m_buttonBox, &QDialogButtonBox::accepted, - this, &BindingEditorDialog::accepted); - QObject::connect(m_buttonBox, &QDialogButtonBox::rejected, - this, &BindingEditorDialog::rejected); - QObject::connect(m_editorWidget, &BindingEditorWidget::returnKeyClicked, - this, &BindingEditorDialog::accepted); - - if (m_dialogType == DialogType::BindingDialog) { - QObject::connect(m_comboBoxItem, QOverload::of(&QComboBox::currentIndexChanged), - this, &BindingEditorDialog::itemIDChanged); - QObject::connect(m_comboBoxProperty, QOverload::of(&QComboBox::currentIndexChanged), - this, &BindingEditorDialog::propertyIDChanged); - QObject::connect(m_editorWidget, &QPlainTextEdit::textChanged, - this, &BindingEditorDialog::textChanged); - } + QObject::connect(m_comboBoxItem, QOverload::of(&QComboBox::currentIndexChanged), + this, &BindingEditorDialog::itemIDChanged); + QObject::connect(m_comboBoxProperty, QOverload::of(&QComboBox::currentIndexChanged), + this, &BindingEditorDialog::propertyIDChanged); } BindingEditorDialog::~BindingEditorDialog() { - delete m_editor; //m_editorWidget is handled by basetexteditor destructor - delete m_buttonBox; - delete m_comboBoxItem; - delete m_comboBoxProperty; - delete m_comboBoxLayout; - delete m_verticalLayout; -} - -void BindingEditorDialog::showWidget() -{ - this->show(); - this->raise(); - m_editorWidget->setFocus(); -} - -void BindingEditorDialog::showWidget(int x, int y) -{ - showWidget(); - move(QPoint(x, y)); -} - -QString BindingEditorDialog::editorValue() const -{ - if (!m_editorWidget) - return {}; - - return m_editorWidget->document()->toPlainText(); -} - -void BindingEditorDialog::setEditorValue(const QString &text) -{ - if (m_editorWidget) - m_editorWidget->document()->setPlainText(text); -} - -void BindingEditorDialog::setAllBindings(QList bindings) -{ - m_lock = true; - - m_bindings = bindings; - setupComboBoxes(); - adjustProperties(); - - m_lock = false; } void BindingEditorDialog::adjustProperties() @@ -155,69 +94,26 @@ void BindingEditorDialog::adjustProperties() m_comboBoxProperty->setCurrentText(property); } -void BindingEditorDialog::unregisterAutoCompletion() +void BindingEditorDialog::setAllBindings(QList bindings) { - if (m_editorWidget) - m_editorWidget->unregisterAutoCompletion(); -} + m_lock = true; -QString BindingEditorDialog::defaultTitle() const -{ - return titleString; -} + m_bindings = bindings; + setupComboBoxes(); + adjustProperties(); -void BindingEditorDialog::setupJSEditor() -{ - static BindingEditorFactory f; - m_editor = qobject_cast(f.createEditor()); - m_editorWidget = qobject_cast(m_editor->editorWidget()); - - Core::Context context = m_editor->context(); - context.prepend(BINDINGEDITOR_CONTEXT_ID); - m_editorWidget->m_context->setContext(context); - - auto qmlDesignerEditor = QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor(); - - m_editorWidget->qmljsdocument = qobject_cast( - qmlDesignerEditor->widget())->qmlJsEditorDocument(); - - - m_editorWidget->setLineNumbersVisible(false); - m_editorWidget->setMarksVisible(false); - m_editorWidget->setCodeFoldingSupported(false); - m_editorWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_editorWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); - m_editorWidget->setTabChangesFocus(true); + m_lock = false; } void BindingEditorDialog::setupUIComponents() { - m_verticalLayout = new QVBoxLayout(this); + m_comboBoxItem = new QComboBox(this); + m_comboBoxProperty = new QComboBox(this); - if (m_dialogType == DialogType::BindingDialog) { - m_comboBoxLayout = new QHBoxLayout; - m_comboBoxItem = new QComboBox(this); - m_comboBoxProperty = new QComboBox(this); - } + m_comboBoxLayout->addWidget(m_comboBoxItem); + m_comboBoxLayout->addWidget(m_comboBoxProperty); - m_editorWidget->setParent(this); - m_editorWidget->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); - m_editorWidget->show(); - - m_buttonBox = new QDialogButtonBox(this); - m_buttonBox->setOrientation(Qt::Horizontal); - m_buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); - - if (m_dialogType == DialogType::BindingDialog) { - m_comboBoxLayout->addWidget(m_comboBoxItem); - m_comboBoxLayout->addWidget(m_comboBoxProperty); - m_verticalLayout->addLayout(m_comboBoxLayout); - } - m_verticalLayout->addWidget(m_editorWidget); - m_verticalLayout->addWidget(m_buttonBox); - - this->resize(660, 240); + //this->resize(660, 240); } void BindingEditorDialog::setupComboBoxes() @@ -260,14 +156,4 @@ void BindingEditorDialog::propertyIDChanged(int propertyID) m_comboBoxProperty->removeItem(undefinedProperty); } -void BindingEditorDialog::textChanged() -{ - if (m_lock) - return; - - m_lock = true; - adjustProperties(); - m_lock = false; -} - } // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h index fdcdca07624..0c37abaa3a5 100644 --- a/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h +++ b/src/plugins/qmldesigner/components/bindingeditor/bindingeditordialog.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2019 The Qt Company Ltd. +** Copyright (C) 2020 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -26,21 +26,15 @@ #ifndef BINDINGEDITORDIALOG_H #define BINDINGEDITORDIALOG_H -#include -#include - -#include +#include QT_BEGIN_NAMESPACE -class QDialogButtonBox; -class QVBoxLayout; -class QHBoxLayout; class QComboBox; QT_END_NAMESPACE namespace QmlDesigner { -class BindingEditorDialog : public QDialog +class BindingEditorDialog : public AbstractEditorDialog { Q_OBJECT @@ -57,52 +51,26 @@ public: QStringList properties; }; - enum DialogType { - Unknown = 0, - BindingDialog = 1, - ActionDialog = 2 - }; - -public: - BindingEditorDialog(QWidget *parent = nullptr, DialogType type = DialogType::BindingDialog); + BindingEditorDialog(QWidget *parent = nullptr); ~BindingEditorDialog() override; - void showWidget(); - void showWidget(int x, int y); + void adjustProperties() override; - QString editorValue() const; - void setEditorValue(const QString &text); - - void setAllBindings(QList bindings); - void adjustProperties(); - - void unregisterAutoCompletion(); - - QString defaultTitle() const; + void setAllBindings(QList bindings); private: - void setupJSEditor(); void setupUIComponents(); void setupComboBoxes(); public slots: void itemIDChanged(int); void propertyIDChanged(int); - void textChanged(); private: - DialogType m_dialogType = DialogType::BindingDialog; - TextEditor::BaseTextEditor *m_editor = nullptr; - BindingEditorWidget *m_editorWidget = nullptr; - QVBoxLayout *m_verticalLayout = nullptr; - QDialogButtonBox *m_buttonBox = nullptr; - QHBoxLayout *m_comboBoxLayout = nullptr; QComboBox *m_comboBoxItem = nullptr; QComboBox *m_comboBoxProperty = nullptr; - QList m_bindings; - bool m_lock = false; - const QString undefinedString = {"[Undefined]"}; - const QString titleString = {tr("Binding Editor")}; + + QList m_bindings; }; } diff --git a/src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.cpp b/src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.cpp new file mode 100644 index 00000000000..ddf328978f0 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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 "connectionvisitor.h" + +namespace QmlDesigner { + +ConnectionVisitor::ConnectionVisitor() +{ +} + +bool ConnectionVisitor::visit(QmlJS::AST::StringLiteral *ast) +{ + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_StringLiteral, + ast->value.toString())); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::NumericLiteral *ast) +{ + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_NumericLiteral, + QString::number(ast->value))); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::TrueLiteral *ast) +{ + Q_UNUSED(ast) + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_TrueLiteral, QString("true"))); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::FalseLiteral *ast) +{ + Q_UNUSED(ast) + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_FalseLiteral, QString("false"))); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::BinaryExpression *ast) +{ + Q_UNUSED(ast) + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_BinaryExpression, + QString())); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::CallExpression *ast) +{ + Q_UNUSED(ast) + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_CallExpression, + QString())); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::ArgumentList *ast) +{ + Q_UNUSED(ast) + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_ArgumentList, + QString())); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::FunctionExpression *ast) +{ + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_FunctionExpression, + ast->name.toString())); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::FieldMemberExpression *ast) +{ + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_FieldMemberExpression, + ast->name.toString())); + return true; +} + +bool ConnectionVisitor::visit(QmlJS::AST::IdentifierExpression *ast) +{ + m_expression.append(qMakePair(QmlJS::AST::Node::Kind::Kind_IdentifierExpression, + ast->name.toString())); + return true; +} + +void ConnectionVisitor::throwRecursionDepthError() +{ + qWarning("Warning: Hit maximum recursion depth while visiting AST in ConnectionVisitor"); +} + +} // QmlDesigner namespace diff --git a/src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.h b/src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.h new file mode 100644 index 00000000000..abcebcdb8e0 --- /dev/null +++ b/src/plugins/qmldesigner/components/bindingeditor/connectionvisitor.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. +** +****************************************************************************/ + +#ifndef CONNECTIONVISITOR_H +#define CONNECTIONVISITOR_H + +#include +#include +#include + +namespace QmlDesigner { + +class ConnectionVisitor : public QmlJS::AST::Visitor +{ +public: + explicit ConnectionVisitor(); + + bool visit(QmlJS::AST::StringLiteral *ast) override; + bool visit(QmlJS::AST::NumericLiteral *ast) override; + bool visit(QmlJS::AST::TrueLiteral *ast) override; + bool visit(QmlJS::AST::FalseLiteral *ast) override; + + bool visit(QmlJS::AST::BinaryExpression *ast) override; + bool visit(QmlJS::AST::CallExpression *ast) override; + + bool visit(QmlJS::AST::ArgumentList *ast) override; + bool visit(QmlJS::AST::FunctionExpression *ast) override; // unused + + bool visit(QmlJS::AST::FieldMemberExpression *ast) override; + bool visit(QmlJS::AST::IdentifierExpression *ast) override; + + void throwRecursionDepthError() override; + + const QList> &expression() const { + return m_expression; + } + +private: + QList> m_expression; +}; + +} + +#endif //CONNECTIONVISITOR_H diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp index 25ea5814a90..c650be42210 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.cpp @@ -60,7 +60,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : QFrame(parent), ui(new Ui::ConnectionViewWidget) { - m_connectonEditor = new QmlDesigner::ActionEditor(this); + m_connectionEditor = new QmlDesigner::ActionEditor(this); m_bindingEditor = new QmlDesigner::BindingEditor(this); m_dynamicEditor = new QmlDesigner::BindingEditor(this); @@ -111,7 +111,7 @@ ConnectionViewWidget::ConnectionViewWidget(QWidget *parent) : ConnectionViewWidget::~ConnectionViewWidget() { - delete m_connectonEditor; + delete m_connectionEditor; delete m_bindingEditor; delete m_dynamicEditor; delete ui; @@ -161,10 +161,14 @@ void ConnectionViewWidget::contextMenuEvent(QContextMenuEvent *event) menu.addAction(tr("Open Connection Editor"), [&]() { if (index.isValid()) { - m_connectonEditor->showWidget(); - m_connectonEditor->setBindingValue(index.data().toString()); - m_connectonEditor->setModelIndex(index); - m_connectonEditor->updateWindowName(); + auto *connectionModel = qobject_cast(targetView->model()); + ModelNode node = connectionModel->connectionView()->rootModelNode(); + m_connectionEditor->showWidget(); + m_connectionEditor->setConnectionValue(index.data().toString()); + m_connectionEditor->setModelIndex(index); + m_connectionEditor->setModelNode(node); + m_connectionEditor->prepareConnections(); + m_connectionEditor->updateWindowName(); } }); @@ -455,29 +459,29 @@ void ConnectionViewWidget::addButtonClicked() void ConnectionViewWidget::editorForConnection() { - QObject::connect(m_connectonEditor, &QmlDesigner::ActionEditor::accepted, + QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::accepted, [&]() { - if (m_connectonEditor->hasModelIndex()) { + if (m_connectionEditor->hasModelIndex()) { ConnectionModel *connectionModel = qobject_cast(ui->connectionView->model()); if (connectionModel->connectionView()->isWidgetEnabled() - && (connectionModel->rowCount() > m_connectonEditor->modelIndex().row())) { + && (connectionModel->rowCount() > m_connectionEditor->modelIndex().row())) { connectionModel->connectionView() ->executeInTransaction("ConnectionView::setSignal", [this, connectionModel]() { SignalHandlerProperty signalHandler = connectionModel->signalHandlerPropertyForRow( - m_connectonEditor->modelIndex().row()); - signalHandler.setSource(m_connectonEditor->bindingValue()); + m_connectionEditor->modelIndex().row()); + signalHandler.setSource(m_connectionEditor->connectionValue()); }); } - m_connectonEditor->resetModelIndex(); + m_connectionEditor->resetModelIndex(); } - m_connectonEditor->hideWidget(); + m_connectionEditor->hideWidget(); }); - QObject::connect(m_connectonEditor, &QmlDesigner::ActionEditor::rejected, + QObject::connect(m_connectionEditor, &QmlDesigner::ActionEditor::rejected, [&]() { - m_connectonEditor->resetModelIndex(); - m_connectonEditor->hideWidget(); + m_connectionEditor->resetModelIndex(); + m_connectionEditor->hideWidget(); }); } diff --git a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h index 68319e16b5c..dc12d535646 100644 --- a/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h +++ b/src/plugins/qmldesigner/components/connectioneditor/connectionviewwidget.h @@ -107,7 +107,7 @@ private: private: Ui::ConnectionViewWidget *ui; - QmlDesigner::ActionEditor *m_connectonEditor; //editor for connections in connection view + QmlDesigner::ActionEditor *m_connectionEditor; //editor for connections in connection view QmlDesigner::BindingEditor *m_bindingEditor; //editor for properties in binding view QmlDesigner::BindingEditor *m_dynamicEditor; //editor for properties in dynamic view diff --git a/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 17c6b4282ad..4678d7d27a1 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -693,10 +693,16 @@ Project { "bindingeditor/bindingeditor.h", "bindingeditor/actioneditor.cpp", "bindingeditor/actioneditor.h", + "bindingeditor/abstracteditordialog.cpp", + "bindingeditor/abstracteditordialog.h", + "bindingeditor/actioneditordialog.cpp", + "bindingeditor/actioneditordialog.h", "bindingeditor/bindingeditordialog.cpp", "bindingeditor/bindingeditordialog.h", "bindingeditor/bindingeditorwidget.cpp", "bindingeditor/bindingeditorwidget.h", + "bindingeditor/connectionvisitor.cpp", + "bindingeditor/connectionvisitor.h", "colortool/colortool.cpp", "colortool/colortool.h", "connectioneditor/addnewbackenddialog.h", From df5e211d86050835b44af2584e6d050917f66fd7 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 16 Sep 2020 13:43:38 +0200 Subject: [PATCH 20/21] QmlDesigner: Fix compilation Change-Id: Ia69deafa77ca234c9f80be10d19fcf582ab5e906 Reviewed-by: Cristian Adam --- .../components/componentcore/designeractionmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 5c961eb8aef..b1aaeb3fe77 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -347,7 +347,7 @@ public: class DocumentError : public std::exception { public: - const char *what() const override { return "Current document contains errors."; } + const char *what() const noexcept override { return "Current document contains errors."; } }; class EditListModelAction final : public ModelNodeContextMenuAction From 364288b79cfb27f4bea9a7a41b785660fb8d0dde Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 16 Sep 2020 12:02:49 +0200 Subject: [PATCH 21/21] Python: Fix loading working directory Fixes: QTCREATORBUG-24440 Change-Id: I27a543f764e285acf678427cc909d2a95e5cb322 Reviewed-by: Christian Stenger --- src/plugins/python/pythonrunconfiguration.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index 4d9b12cdc8e..6ec33ca87f6 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -281,6 +281,7 @@ PythonRunConfiguration::PythonRunConfiguration(Target *target, Utils::Id id) const QString script = bti.targetFilePath.toUserOutput(); setDefaultDisplayName(tr("Run %1").arg(script)); scriptAspect->setValue(script); + aspect()->setDefaultWorkingDirectory(bti.targetFilePath.parentDir()); }); connect(target, &Target::buildSystemUpdated, this, &RunConfiguration::update); @@ -298,9 +299,6 @@ void PythonRunConfiguration::updateLanguageServer() PyLSConfigureAssistant::instance()->openDocumentWithPython(python, document); } } - - aspect()->setDefaultWorkingDirectory( - Utils::FilePath::fromString(mainScript()).parentDir()); } bool PythonRunConfiguration::supportsDebugger() const