diff --git a/README.md b/README.md index 91fa767e034..9aeb8119f8e 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/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 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/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 */ 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 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/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)); } diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/StandardTextSection.qml index e590a565a24..c15f1268113 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() } diff --git a/src/libs/libs.pro b/src/libs/libs.pro index 013376911fa..9675dca0495 100644 --- a/src/libs/libs.pro +++ b/src/libs/libs.pro @@ -65,16 +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) { - 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/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; } 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 ae09e258d4d..00000000000 --- a/src/libs/qtcreatorcdbext/cdb_detect.pri +++ /dev/null @@ -1,25 +0,0 @@ -# Detect presence of "Debugging Tools For Windows" -# in case MS VS compilers are used. - -CDB_PATH="" -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 774e889b2ae..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,45 +29,21 @@ 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 - - exists($$CDB_PATH/lib/amd64) { - LIBS+= -L$$CDB_PATH/lib/amd64 -ldbgeng - } else { - LIBS+= -L$$CDB_PATH/lib/x64 -ldbgeng - } -} 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 - } } 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 - } } -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 +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")); 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/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); diff --git a/src/plugins/autotest/testrunner.cpp b/src/plugins/autotest/testrunner.cpp index 4581b91bd53..37fc75f464f 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) { 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); } } diff --git a/src/plugins/cppeditor/cppdoxygen_test.cpp b/src/plugins/cppeditor/cppdoxygen_test.cpp index 59aad20d072..9fc6f1575c1 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" ); 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)); 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/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index bed2621763a..209d0906b2e 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 diff --git a/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp b/src/plugins/qmakeprojectmanager/qmakeparsernodes.cpp index 6a18b68f8f0..126ef39bcb5 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 c421e7983ae..7d22796d55d 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 @@ -97,6 +98,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 @@ -266,8 +299,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() diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt index cf5ee686bfa..01809f21524 100644 --- a/src/plugins/qmldesigner/CMakeLists.txt +++ b/src/plugins/qmldesigner/CMakeLists.txt @@ -589,8 +589,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/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 89151b7ea67..254d43f4fd1 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) @@ -387,6 +392,12 @@ public: } }; +class DocumentError : public std::exception +{ +public: + const char *what() const noexcept override { return "Current document contains errors."; } +}; + class EditListModelAction final : public ModelNodeContextMenuAction { public: @@ -432,6 +443,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); @@ -439,7 +468,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 a78ac045ac5..cf22a9bbcf1 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 QListshowWidget(); - 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/components/listmodeleditor/listmodeleditormodel.cpp b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp index 62b8b780881..e6e10a017c1 100644 --- a/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp +++ b/src/plugins/qmldesigner/components/listmodeleditor/listmodeleditormodel.cpp @@ -199,13 +199,15 @@ void renameProperties(const QStandardItemModel *model, } ModelNode listModelNode(const ModelNode &listViewNode, - const std::function &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/src/plugins/qmldesigner/qmldesignerplugin.qbs b/src/plugins/qmldesigner/qmldesignerplugin.qbs index 5c7ec4efc67..f95f853c7d6 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.qbs +++ b/src/plugins/qmldesigner/qmldesignerplugin.qbs @@ -698,10 +698,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", 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, 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/auto/debugger/tst_dumpers.cpp b/tests/auto/debugger/tst_dumpers.cpp index 593419ecc62..4849508430e 100644 --- a/tests/auto/debugger/tst_dumpers.cpp +++ b/tests/auto/debugger/tst_dumpers.cpp @@ -1226,6 +1226,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); 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/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 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 e79342561a1..967463bbdc3 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -173,12 +173,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 \ @@ -200,7 +202,6 @@ SOURCES += \ clangqueryprojectfindfilter-test.cpp \ clangquery-test.cpp \ clangreferencescollector-test.cpp \ - gtest-clang-printing.cpp \ pchcreator-test.cpp \ refactoringclientserverinprocess-test.cpp \ refactoringclient-test.cpp \ @@ -211,8 +212,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 @@ -310,14 +310,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 c2e21f767fa..cf2f475540b 100644 --- a/tests/unit/unittest/unittest.qbs +++ b/tests/unit/unittest/unittest.qbs @@ -324,6 +324,8 @@ QtcProduct { "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", @@ -331,6 +333,7 @@ QtcProduct { "sourcelocation-test.cpp", "sourcerange-test.cpp", "token-test.cpp", + "tokenprocessor-test.cpp", "translationunitupdater-test.cpp", "unsavedfile-test.cpp", "unsavedfiles-test.cpp", @@ -348,8 +351,6 @@ QtcProduct { "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", @@ -364,7 +365,6 @@ QtcProduct { "symbolscollector-test.cpp", "testclangtool.cpp", "testclangtool.h", - "tokenprocessor-test.cpp", "usedmacrocollector-test.cpp", ] }