diff --git a/dist/changelog/changes-13.0.0.md b/dist/changelog/changes-13.0.0.md index b8aa6aa1160..6ca281668ff 100644 --- a/dist/changelog/changes-13.0.0.md +++ b/dist/changelog/changes-13.0.0.md @@ -39,6 +39,8 @@ General for searching in `Files in File System` * Added `Copy to Clipboard` to the `About Qt Creator` dialog ([QTCREATORBUG-29886](https://bugreports.qt.io/browse/QTCREATORBUG-29886)) +* Fixed issues with the window actions + ([QTCREATORBUG-30381](https://bugreports.qt.io/browse/QTCREATORBUG-30381)) Editing ------- @@ -78,6 +80,8 @@ Editing * Clangd * Fixed that `Follow Symbol Under Cursor` only worked for exact matches ([QTCREATORBUG-29814](https://bugreports.qt.io/browse/QTCREATORBUG-29814)) + * Fixed the version check for remote `clangd` executables + ([QTCREATORBUG-30374](https://bugreports.qt.io/browse/QTCREATORBUG-30374)) ### QML @@ -133,6 +137,8 @@ Projects ([QTCREATORBUG-29530](https://bugreports.qt.io/browse/QTCREATORBUG-29530)) * Added a file wizard for Qt translation (`.ts`) files ([QTCREATORBUG-29775](https://bugreports.qt.io/browse/QTCREATORBUG-29775)) +* Added an optional warning for special characters in build directories + ([QTCREATORBUG-20834](https://bugreports.qt.io/browse/QTCREATORBUG-20834)) * Improved the environment settings by making the changes explicit in a separate, text-based editor * Increased the maximum width of the target selector @@ -184,6 +190,9 @@ Debugging ### C++ * Added a pretty printer for `std::tuple` +* Improved the display of size information for the pretty printer of + `QByteArray` + ([QTCREATORBUG-30065](https://bugreports.qt.io/browse/QTCREATORBUG-30065)) * Fixed that breakpoints were not hit while the message dialog about missing debug information was shown ([QTCREATORBUG-30168](https://bugreports.qt.io/browse/QTCREATORBUG-30168)) @@ -270,6 +279,7 @@ Andre Hartmann André Pönitz Andreas Loth Artem Sokolovskii +Assam Boudjelthia Brook Cronin Burak Hancerli Christian Kandeler @@ -306,6 +316,7 @@ Robert Löhning Sami Shalayel Samuel Jose Raposo Vieira Mira Samuel Mira +Semih Yavuz Serg Kryvonos Shrief Gabr Sivert Krøvel diff --git a/doc/qtcreator/images/qtcreator-git-add-branch.webp b/doc/qtcreator/images/qtcreator-git-add-branch.webp new file mode 100644 index 00000000000..19782ad4fe9 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-git-add-branch.webp differ diff --git a/doc/qtcreator/images/qtcreator-git-branches.webp b/doc/qtcreator/images/qtcreator-git-branches.webp new file mode 100644 index 00000000000..a2b343461d1 Binary files /dev/null and b/doc/qtcreator/images/qtcreator-git-branches.webp differ diff --git a/doc/qtcreator/images/qtcreator-vcs-gitbranch.png b/doc/qtcreator/images/qtcreator-vcs-gitbranch.png deleted file mode 100644 index f6a5586eac2..00000000000 Binary files a/doc/qtcreator/images/qtcreator-vcs-gitbranch.png and /dev/null differ diff --git a/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc b/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc index 53780c96a5d..66386d72c0a 100644 --- a/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc +++ b/doc/qtcreator/src/debugger/creator-only/creator-debugger-setup.qdoc @@ -99,7 +99,7 @@ \section2 Supported GDB Versions Use GDB 7.5, or later, with the Python scripting extension and Python version - 3.3, or later. + 3.5, or later. For remote debugging using GDB and GDB server, the minimum supported version of GDB server on the target \l{glossary-device}{device} is 7.0. @@ -121,35 +121,26 @@ On Linux, the minimum supported version is LLDB 3.8. - \omit - - \section2 GDB Adapter Modes - - [Advanced Topic] + \section2 GDB Run Modes The GDB native debugger used internally by the debugger plugin runs in - different adapter modes to cope with the variety of supported platforms and - environments. All GDB adapters inherit from AbstractGdbAdapter: + different modes to cope with the variety of supported platforms and + environments: \list - \li PlainGdbAdapter debugs locally started GUI processes. It is - physically split into parts that are relevant only when Python is - available, parts relevant only when Python is not available, and - mixed code. + \li Plain mode debugs locally started processes that do not need console input. - \li TermGdbAdapter debugs locally started processes that need a console. + \li Terminal mode debugs locally started processes that need a console. - \li AttachGdbAdapter debugs local processes started outside \QC. + \li Attach mode debugs local processes started outside \QC. - \li CoreGdbAdapter debugs core files generated from crashes. + \li Core mode debugs core files generated from crashes. - \li RemoteGdbAdapter interacts with the GDB server running on Linux. + \li Remote mode interacts with the GDB server running on Linux. \endlist - \endomit - \section1 Installing Native Debuggers The following sections describe installing native debuggers. diff --git a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc index 80c50359f69..a89ca3c7ef8 100644 --- a/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc +++ b/doc/qtcreator/src/editors/creator-only/creator-language-server.qdoc @@ -238,6 +238,10 @@ \image qtcreator-qml-js-editing.webp {QML/JS Editing preferences} + When using \c qmlls from Qt 6.7 or later, it is recommended + to set \l{QT_QML_GENERATE_QMLLS_INI} to \c{ON} in + \uicontrol {Initial Configuration}. + \sa {Manage Language Servers}{How To: Manage Language Servers}, {Enabling and Disabling Messages}, {Language Servers} */ diff --git a/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc b/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc index 0f09a773d47..dac80681896 100644 --- a/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc +++ b/doc/qtcreator/src/user-interface/creator-only/creator-reference-terminal-view.qdoc @@ -15,9 +15,8 @@ application or the \uicontrol {Open Terminal} button to open a terminal, it opens as an output view. - To open the terminal in a separate window, select \preferences > - \uicontrol Terminal, and deselet the \uicontrol {Use internal terminal} - check box. + To open the terminal in a separate window, go to \preferences > + \uicontrol Terminal, and clear \uicontrol {Use internal terminal}. On Linux and \macos, you can set the terminal to open by selecting \preferences > \uicontrol Environment > \uicontrol System. @@ -39,7 +38,8 @@ . \li To select a word in a terminal, double-click it. To select the whole line, - triple-click it. + triple-click it. To select all text, select \uicontrol {Select All} + in the context menu or press \key {Ctrl+A}. \li To open links in a browser, files in the editor, or folders in the \l Projects view, hover the mouse over them, and press \key Ctrl. diff --git a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc index 9f5f79f3910..0b0cbb0ee20 100644 --- a/doc/qtcreator/src/vcs/creator-vcs-git.qdoc +++ b/doc/qtcreator/src/vcs/creator-vcs-git.qdoc @@ -309,10 +309,11 @@ \section2 Working with Branches To work with Git branches, select \uicontrol {Branches}. The - \uicontrol {Git Branches} sidebar view shows the checked out - branch in bold and underlined in the list of branches. + \uicontrol {Git Branches} view shows a list of branches, as well + as the differences between your local branches and their origin. + The branch you checked out is shown in bold and underlined. - \image qtcreator-vcs-gitbranch.png {Git Branches sidebar view} + \image qtcreator-git-branches.webp {Git Branches view} Old entries and tags are filtered out of the list of branches by default. To include them, select \inlineimage icons/filtericon.png @@ -332,6 +333,20 @@ To refresh the list of branches, select \inlineimage icons/reload_gray.png (\uicontrol Refresh). + \section3 Adding Branches + + To create a new tracking or non-tracking branch, select + \inlineimage icons/plus.png. + + \image qtcreator-git-add-branch.webp {Add Branch dialog} + + To check out the branch when creating it, select + \uicontrol {Checkout new branch}. + + To track the selected branch, select \uicontrol {Track local branch}. + + \section3 Managing Branches + The context menu for a branch has the following functions: \table diff --git a/share/qtcreator/debugger/cdbbridge.py b/share/qtcreator/debugger/cdbbridge.py index d323714ba44..b5fc683cbae 100644 --- a/share/qtcreator/debugger/cdbbridge.py +++ b/share/qtcreator/debugger/cdbbridge.py @@ -11,7 +11,7 @@ from utils import TypeCode sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))) -from dumper import DumperBase, SubItem +from dumper import DumperBase, SubItem, Children, DisplayFormat, UnnamedSubItem class FakeVoidType(cdbext.Type): @@ -84,10 +84,9 @@ class Dumper(DumperBase): self.check(isinstance(nativeValue, cdbext.Value)) val = self.Value(self) val.name = nativeValue.name() - val._type = self.fromNativeType(nativeValue.type()) # There is no cdb api for the size of bitfields. # Workaround this issue by parsing the native debugger text for integral types. - if val._type.code == TypeCode.Integral: + if nativeValue.type().code() == TypeCode.Integral: try: integerString = nativeValue.nativeDebuggerValue() except UnicodeDecodeError: @@ -106,16 +105,18 @@ class Dumper(DumperBase): base = 16 else: base = 10 - signed = not val._type.name.startswith('unsigned') + signed = not nativeValue.type().name().startswith('unsigned') try: - val.ldata = int(integerString, base).to_bytes(val._type.size(), + val.ldata = int(integerString, base).to_bytes((nativeValue.type().bitsize() +7) // 8, byteorder='little', signed=signed) except: # read raw memory in case the integerString can not be interpreted pass - if val._type.code == TypeCode.Enum: + if nativeValue.type().code() == TypeCode.Enum: val.ldisplay = self.enumValue(nativeValue) - val.isBaseClass = val.name == val._type.name + elif not nativeValue.type().resolved and nativeValue.type().code() == TypeCode.Struct and not nativeValue.hasChildren(): + val.ldisplay = self.enumValue(nativeValue) + val.isBaseClass = val.name == nativeValue.type().name() val.nativeValue = nativeValue val.laddress = nativeValue.address() val.lbitsize = nativeValue.bitsize() @@ -136,6 +137,9 @@ class Dumper(DumperBase): for f in nativeType.fields()]) return typeId + def nativeValueType(self, nativeValue): + return self.fromNativeType(nativeValue.type()) + def fromNativeType(self, nativeType): self.check(isinstance(nativeType, cdbext.Type)) typeId = self.nativeTypeId(nativeType) @@ -150,51 +154,66 @@ class Dumper(DumperBase): if nativeType.name().startswith(''): code = TypeCode.Function elif nativeType.targetName() != nativeType.name(): - targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId()) - if targetType is not None and targetType is not nativeType: - return self.createPointerType(targetType) + return self.createPointerType(nativeType.targetName()) if code == TypeCode.Array: # cdb reports virtual function tables as arrays those ar handled separetly by # the DumperBase. Declare those types as structs prevents a lookup to a # none existing type if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith(' 0: namespace = name[:namespaceIndex + 2] + self.qtNamespace = lambda: namespace self.qtCustomEventFunc = self.parseAndEvaluate( '%s!%sQObject::customEvent' % (self.qtCoreModuleName(), namespace)).address() @@ -498,7 +518,7 @@ class Dumper(DumperBase): else: val = self.Value(self) val.laddress = value.pointer() - val._type = value.type.dereference() + val._type = DumperBase.Type(self, value.type.targetName) val.nativeValue = value.nativeValue return val @@ -519,3 +539,424 @@ class Dumper(DumperBase): def symbolAddress(self, symbolName): res = self.nativeParseAndEvaluate(symbolName) return None if res is None else res.address() + + def putItemX(self, value): + #DumperBase.warn('PUT ITEM: %s' % value.stringify()) + + typeobj = value.type # unqualified() + typeName = typeobj.name + + self.addToCache(typeobj) # Fill type cache + + if not value.lIsInScope: + self.putSpecialValue('optimizedout') + #self.putType(typeobj) + #self.putSpecialValue('outofscope') + self.putNumChild(0) + return + + if not isinstance(value, self.Value): + raise RuntimeError('WRONG TYPE IN putItem: %s' % type(self.Value)) + + # Try on possibly typedefed type first. + if self.tryPutPrettyItem(typeName, value): + if typeobj.code == TypeCode.Pointer: + self.putOriginalAddress(value.address()) + else: + self.putAddress(value.address()) + return + + if typeobj.code == TypeCode.Pointer: + self.putFormattedPointer(value) + return + + self.putAddress(value.address()) + if value.lbitsize is not None: + self.putField('size', value.lbitsize // 8) + + if typeobj.code == TypeCode.Function: + #DumperBase.warn('FUNCTION VALUE: %s' % value) + self.putType(typeobj) + self.putSymbolValue(value.pointer()) + self.putNumChild(0) + return + + if typeobj.code == TypeCode.Enum: + #DumperBase.warn('ENUM VALUE: %s' % value.stringify()) + self.putType(typeobj.name) + self.putValue(value.display()) + self.putNumChild(0) + return + + if typeobj.code == TypeCode.Array: + #DumperBase.warn('ARRAY VALUE: %s' % value) + self.putCStyleArray(value) + return + + if typeobj.code == TypeCode.Integral: + #DumperBase.warn('INTEGER: %s %s' % (value.name, value)) + val = value.value() + self.putNumChild(0) + self.putValue(val) + self.putType(typeName) + return + + if typeobj.code == TypeCode.Float: + #DumperBase.warn('FLOAT VALUE: %s' % value) + self.putValue(value.value()) + self.putNumChild(0) + self.putType(typeobj.name) + return + + if typeobj.code in (TypeCode.Reference, TypeCode.RValueReference): + #DumperBase.warn('REFERENCE VALUE: %s' % value) + val = value.dereference() + if val.laddress != 0: + self.putItem(val) + else: + self.putSpecialValue('nullreference') + self.putBetterType(typeName) + return + + if typeobj.code == TypeCode.Complex: + self.putType(typeobj) + self.putValue(value.display()) + self.putNumChild(0) + return + + self.putType(typeName) + + if value.summary is not None and self.useFancy: + self.putValue(self.hexencode(value.summary), 'utf8:1:0') + self.putNumChild(0) + return + + self.putExpandable() + self.putEmptyValue() + #DumperBase.warn('STRUCT GUTS: %s ADDRESS: 0x%x ' % (value.name, value.address())) + if self.showQObjectNames: + #with self.timer(self.currentIName): + self.putQObjectNameValue(value) + if self.isExpanded(): + self.putField('sortable', 1) + with Children(self): + baseIndex = 0 + for item in self.listValueChildren(value): + if item.name.startswith('__vfptr'): + with SubItem(self, '[vptr]'): + # int (**)(void) + self.putType(' ') + self.putSortGroup(20) + self.putValue(item.name) + n = 100 + if self.isExpanded(): + with Children(self): + n = self.putVTableChildren(item, n) + self.putNumChild(n) + continue + + if item.isBaseClass: + baseIndex += 1 + # We cannot use nativeField.name as part of the iname as + # it might contain spaces and other strange characters. + with UnnamedSubItem(self, "@%d" % baseIndex): + self.putField('iname', self.currentIName) + self.putField('name', '[%s]' % item.name) + self.putSortGroup(1000 - baseIndex) + self.putAddress(item.address()) + self.putItem(item) + continue + + + with SubItem(self, item.name): + self.putItem(item) + if self.showQObjectNames: + self.tryPutQObjectGuts(value) + + + def putFormattedPointerX(self, value: DumperBase.Value): + self.putOriginalAddress(value.address()) + pointer = value.pointer() + self.putAddress(pointer) + if pointer == 0: + self.putType(value.type) + self.putValue('0x0') + return + + typeName = value.type.name + + try: + self.readRawMemory(pointer, 1) + except: + # Failure to dereference a pointer should at least + # show the value of a pointer. + #DumperBase.warn('BAD POINTER: %s' % value) + self.putValue('0x%x' % pointer) + self.putType(typeName) + return + + if self.currentIName.endswith('.this'): + self.putDerefedPointer(value) + return + + displayFormat = self.currentItemFormat(value.type.name) + + if value.type.targetName == 'void': + #DumperBase.warn('VOID POINTER: %s' % displayFormat) + self.putType(typeName) + self.putSymbolValue(pointer) + return + + if displayFormat == DisplayFormat.Raw: + # Explicitly requested bald pointer. + #DumperBase.warn('RAW') + self.putType(typeName) + self.putValue('0x%x' % pointer) + self.putExpandable() + if self.currentIName in self.expandedINames: + with Children(self): + with SubItem(self, '*'): + self.putItem(value.dereference()) + return + + limit = self.displayStringLimit + if displayFormat in (DisplayFormat.SeparateLatin1String, DisplayFormat.SeparateUtf8String): + limit = 1000000 + if self.tryPutSimpleFormattedPointer(pointer, typeName, + value.type.targetName, displayFormat, limit): + self.putExpandable() + return + + if DisplayFormat.Array10 <= displayFormat and displayFormat <= DisplayFormat.Array10000: + n = (10, 100, 1000, 10000)[displayFormat - DisplayFormat.Array10] + self.putType(typeName) + self.putItemCount(n) + self.putArrayData(value.pointer(), n, value.type.targetName) + return + + #DumperBase.warn('AUTODEREF: %s' % self.autoDerefPointers) + #DumperBase.warn('INAME: %s' % self.currentIName) + if self.autoDerefPointers: + # Generic pointer type with AutomaticFormat, but never dereference char types: + if value.type.targetName not in ( + 'char', + 'signed char', + 'int8_t', + 'qint8', + 'unsigned char', + 'uint8_t', + 'quint8', + 'wchar_t', + 'CHAR', + 'WCHAR', + 'char8_t', + 'char16_t', + 'char32_t' + ): + self.putDerefedPointer(value) + return + + #DumperBase.warn('GENERIC PLAIN POINTER: %s' % value.type) + #DumperBase.warn('ADDR PLAIN POINTER: 0x%x' % value.laddress) + self.putType(typeName) + self.putSymbolValue(pointer) + self.putExpandable() + if self.currentIName in self.expandedINames: + with Children(self): + with SubItem(self, '*'): + self.putItem(value.dereference()) + + + def putCStyleArray(self, value): + arrayType = value.type + innerType = arrayType.ltarget + address = value.address() + if address: + self.putValue('@0x%x' % address, priority=-1) + else: + self.putEmptyValue() + self.putType(arrayType) + + displayFormat = self.currentItemFormat() + arrayByteSize = arrayType.size() + n = self.arrayItemCountFromTypeName(value.type.name, 100) + + p = value.address() + if displayFormat != DisplayFormat.Raw and p: + if innerType.name in ( + 'char', + 'int8_t', + 'qint8', + 'wchar_t', + 'unsigned char', + 'uint8_t', + 'quint8', + 'signed char', + 'CHAR', + 'WCHAR', + 'char8_t', + 'char16_t', + 'char32_t' + ): + self.putCharArrayHelper(p, n, innerType, self.currentItemFormat(), + makeExpandable=False) + else: + self.tryPutSimpleFormattedPointer(p, arrayType, innerType, + displayFormat, arrayByteSize) + self.putNumChild(n) + + if self.isExpanded(): + if n > 100: + addrStep = innerType.size() + with Children(self, n, innerType, addrBase=address, addrStep=addrStep): + for i in self.childRange(): + self.putSubItem(i, self.createValue(address + i * addrStep, innerType)) + else: + with Children(self): + n = 0 + for item in self.listValueChildren(value): + with SubItem(self, n): + n += 1 + self.putItem(item) + + + def putArrayData(self, base, n, innerType, childNumChild=None): + self.checkIntType(base) + self.checkIntType(n) + addrBase = base + innerType = self.createType(innerType) + innerSize = innerType.size() + self.putNumChild(n) + #DumperBase.warn('ADDRESS: 0x%x INNERSIZE: %s INNERTYPE: %s' % (addrBase, innerSize, innerType)) + enc = innerType.simpleEncoding() + maxNumChild = self.maxArrayCount() + if enc: + self.put('childtype="%s",' % innerType.name) + self.put('addrbase="0x%x",' % addrBase) + self.put('addrstep="0x%x",' % innerSize) + self.put('arrayencoding="%s",' % enc) + self.put('endian="%s",' % self.packCode) + if n > maxNumChild: + self.put('childrenelided="%s",' % n) + n = maxNumChild + self.put('arraydata="') + self.put(self.readMemory(addrBase, n * innerSize)) + self.put('",') + else: + with Children(self, n, innerType, childNumChild, maxNumChild, + addrBase=addrBase, addrStep=innerSize): + for i in self.childRange(): + self.putSubItem(i, self.createValue(addrBase + i * innerSize, innerType)) + + def tryPutSimpleFormattedPointer(self, ptr, typeName, innerType, displayFormat, limit): + if isinstance(innerType, self.Type): + innerType = innerType.name + if displayFormat == DisplayFormat.Automatic: + if innerType in ('char', 'signed char', 'unsigned char', 'uint8_t', 'CHAR'): + # Use UTF-8 as default for char *. + self.putType(typeName) + (length, shown, data) = self.readToFirstZero(ptr, 1, limit) + self.putValue(data, 'utf8', length=length) + if self.isExpanded(): + self.putArrayData(ptr, shown, innerType) + return True + + if innerType in ('wchar_t', 'WCHAR'): + self.putType(typeName) + charSize = self.lookupType('wchar_t').size() + (length, data) = self.encodeCArray(ptr, charSize, limit) + if charSize == 2: + self.putValue(data, 'utf16', length=length) + else: + self.putValue(data, 'ucs4', length=length) + return True + + if displayFormat == DisplayFormat.Latin1String: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 1, limit) + self.putValue(data, 'latin1', length=length) + return True + + if displayFormat == DisplayFormat.SeparateLatin1String: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 1, limit) + self.putValue(data, 'latin1', length=length) + self.putDisplay('latin1:separate', data) + return True + + if displayFormat == DisplayFormat.Utf8String: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 1, limit) + self.putValue(data, 'utf8', length=length) + return True + + if displayFormat == DisplayFormat.SeparateUtf8String: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 1, limit) + self.putValue(data, 'utf8', length=length) + self.putDisplay('utf8:separate', data) + return True + + if displayFormat == DisplayFormat.Local8BitString: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 1, limit) + self.putValue(data, 'local8bit', length=length) + return True + + if displayFormat == DisplayFormat.Utf16String: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 2, limit) + self.putValue(data, 'utf16', length=length) + return True + + if displayFormat == DisplayFormat.Ucs4String: + self.putType(typeName) + (length, data) = self.encodeCArray(ptr, 4, limit) + self.putValue(data, 'ucs4', length=length) + return True + + return False + + def putDerefedPointer(self, value): + derefValue = value.dereference() + savedCurrentChildType = self.currentChildType + self.currentChildType = value.type.targetName + self.putType(value.type.targetName) + derefValue.name = '*' + derefValue.autoDerefCount = value.autoDerefCount + 1 + + if derefValue.type.code == TypeCode.Pointer: + self.putField('autoderefcount', '{}'.format(derefValue.autoDerefCount)) + + self.putItem(derefValue) + self.currentChildType = savedCurrentChildType + + def extractPointer(self, value): + code = 'I' if self.ptrSize() == 4 else 'Q' + return self.extractSomething(value, code, 8 * self.ptrSize()) + + def createValue(self, datish, typish): + if self.isInt(datish): # Used as address. + return self.createValueFromAddressAndType(datish, typish) + if isinstance(datish, bytes): + val = self.Value(self) + if isinstance(typish, self.Type): + val._type = typish + else: + val._type = self.Type(self, typish) + #DumperBase.warn('CREATING %s WITH DATA %s' % (val.type.name, self.hexencode(datish))) + val.ldata = datish + val.check() + return val + raise RuntimeError('EXPECTING ADDRESS OR BYTES, GOT %s' % type(datish)) + + def createValueFromAddressAndType(self, address, typish): + val = self.Value(self) + if isinstance(typish, self.Type): + val._type = typish + else: + val._type = self.Type(self, typish) + val.laddress = address + if self.useDynamicType: + val._type = val.type.dynamicType(address) + return val diff --git a/share/qtcreator/debugger/dumper.py b/share/qtcreator/debugger/dumper.py index 2fa7d31da50..836b1860cb2 100644 --- a/share/qtcreator/debugger/dumper.py +++ b/share/qtcreator/debugger/dumper.py @@ -3041,7 +3041,7 @@ class DumperBase(): or self.type.name.startswith('unsigned ') \ or self.type.name.find(' unsigned ') != -1 if bitsize is None: - bitsize = self.type.bitsize() + bitsize = self.type.lbitsize return self.extractInteger(bitsize, unsigned) def floatingPoint(self): @@ -3512,26 +3512,40 @@ class DumperBase(): tdata.moduleName = self.moduleName return tdata + @property + def bitsize(self): + if callable(self.lbitsize): + self.lbitsize = self.lbitsize() + return self.lbitsize + class Type(): def __init__(self, dumper, typeId): - self.typeId = typeId + self.typeId = typeId.replace('@', dumper.qtNamespace()) self.dumper = dumper - self.tdata = dumper.typeData.get(typeId, None) - if self.tdata is None: - #DumperBase.warn('USING : %s' % self.typeId) - self.dumper.lookupType(self.typeId) - self.tdata = self.dumper.typeData.get(self.typeId) + self.initialized = False def __str__(self): #return self.typeId return self.stringify() + @property + def tdata(self): + if not self.initialized: + self.initialized = True + self.data = self.dumper.typeData.get(self.typeId, None) + if self.data is None: + #DumperBase.warn('USING : %s' % self.typeId) + self.dumper.lookupType(self.typeId) + self.data = self.dumper.typeData.get(self.typeId) + return self.data + + def setTdata(self, tdata): + self.initialized = True + self.data = tdata + @property def name(self): - tdata = self.dumper.typeData.get(self.typeId) - if tdata is None: - return self.typeId - return tdata.name + return self.typeId if self.tdata is None else self.tdata.name @property def code(self): @@ -3539,7 +3553,7 @@ class DumperBase(): @property def lbitsize(self): - return self.tdata.lbitsize + return self.tdata.bitsize @property def lbitpos(self): @@ -3547,15 +3561,25 @@ class DumperBase(): @property def ltarget(self): + if isinstance(self.tdata.ltarget, str): + self.tdata.ltarget = self.dumper.createType(self.tdata.ltarget) return self.tdata.ltarget + @property + def targetName(self): + if self.tdata.ltarget is None: + return '' + return self.tdata.ltarget if isinstance(self.tdata.ltarget, str) else self.tdata.ltarget.name + @property def moduleName(self): + if callable(self.tdata.moduleName): + self.tdata.moduleName = self.tdata.moduleName() return self.tdata.moduleName def stringify(self): return 'Type(name="%s",bsize=%s,code=%s)' \ - % (self.tdata.name, self.tdata.lbitsize, self.tdata.code) + % (self.tdata.name, self.lbitsize, self.tdata.code) def __getitem__(self, index): if self.dumper.isInt(index): @@ -3659,7 +3683,7 @@ class DumperBase(): def alignment(self): if self.tdata.code == TypeCode.Typedef: - return self.tdata.ltarget.alignment() + return self.ltarget.alignment() if self.tdata.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum): if self.tdata.name in ('double', 'long long', 'unsigned long long'): # Crude approximation. @@ -3678,7 +3702,7 @@ class DumperBase(): return self.dumper.createPointerType(self) def target(self): - return self.tdata.ltarget + return self.ltarget def stripTypedefs(self): if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef: @@ -3687,7 +3711,7 @@ class DumperBase(): return self.ltarget def size(self): - bs = self.bitsize() + bs = self.lbitsize if bs % 8 != 0: DumperBase.warn('ODD SIZE: %s' % self) return (7 + bs) >> 3 @@ -3797,12 +3821,12 @@ class DumperBase(): return val def createPointerType(self, targetType): - if not isinstance(targetType, self.Type): - raise RuntimeError('Expected type in createPointerType(), got %s' + if not isinstance(targetType, (str, self.Type)): + raise RuntimeError('Expected type or str in createPointerType(), got %s' % type(targetType)) - typeId = targetType.typeId + ' *' + typeId = (targetType if isinstance(targetType, str) else targetType.typeId) + ' *' tdata = self.TypeData(self, typeId) - tdata.name = targetType.name + '*' + tdata.name = (targetType if isinstance(targetType, str) else targetType.name) + '*' tdata.lbitsize = 8 * self.ptrSize() tdata.code = TypeCode.Pointer tdata.ltarget = targetType @@ -3927,7 +3951,7 @@ class DumperBase(): tdata = self.typeData.get(typish, None) if tdata is not None: if tdata.lbitsize is not None: - if tdata.lbitsize > 0: + if callable(tdata.lbitsize) or tdata.lbitsize > 0: return self.Type(self, typish) knownType = self.lookupType(typish) @@ -3944,7 +3968,7 @@ class DumperBase(): if typish.endswith('*'): tdata.code = TypeCode.Pointer tdata.lbitsize = 8 * self.ptrSize() - tdata.ltarget = self.createType(typish[:-1].strip()) + tdata.ltarget = typish[:-1].strip() typeobj = self.Type(self, tdata.typeId) #DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify()) diff --git a/share/qtcreator/debugger/lldbbridge.py b/share/qtcreator/debugger/lldbbridge.py index 7558bcdfa47..7809a6a3dcb 100644 --- a/share/qtcreator/debugger/lldbbridge.py +++ b/share/qtcreator/debugger/lldbbridge.py @@ -419,7 +419,7 @@ class Dumper(DumperBase): targetTypeName = typeName[0:pos1].strip() #DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName) targetType = self.fromNativeType(nativeTargetType) - targetType.tdata = targetType.tdata.copy() + targetType.setTdata(targetType.tdata.copy()) targetType.tdata.name = targetTypeName return self.createArrayType(targetType, count) if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x @@ -1583,7 +1583,8 @@ class Dumper(DumperBase): result += ',ignorecount="%d"' % loc.GetIgnoreCount() result += ',file="%s"' % toCString(lineEntry.GetFileSpec()) result += ',line="%d"' % lineEntry.GetLine() - result += ',addr="%s"},' % addr.GetFileAddress() + result += ',addr="%s"' % addr.GetLoadAddress(self.target) + result += ',faddr="%s"},' % addr.GetFileAddress() result += ']' if lineEntry is not None: result += ',file="%s"' % toCString(lineEntry.GetFileSpec()) diff --git a/share/qtcreator/themes/dark.creatortheme b/share/qtcreator/themes/dark.creatortheme index a7dcedd104a..c2846fa0940 100644 --- a/share/qtcreator/themes/dark.creatortheme +++ b/share/qtcreator/themes/dark.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=dark.figmatokens ThemeName=Dark PreferredStyles= DefaultTextEditorColorScheme=dark.xml @@ -406,39 +407,6 @@ Debugger_WatchItem_ValueChanged=ffff6666 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - dark mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff8f8f8 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ff1f1f1f -Token_Background_Muted=ff262626 -Token_Background_Subtle=ff2e2e2e -Token_Foreground_Default=ff5a5a5a -Token_Foreground_Muted=ff3e3e3e -Token_Foreground_Subtle=ff303030 -Token_Text_Default=fff8f8f8 -Token_Text_Muted=ffaeaeae -Token_Text_Subtle=ff595959 -Token_Stroke_Strong=ffeeeeee -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ff3a3a3a -Token_Notification_Alert=ffc98014 -Token_Notification_Success=ff1f9b5d -Token_Notification_Neutral=ff016876 -Token_Notification_Danger=ffb22245 - -Welcome_TextColor=text -Welcome_ForegroundPrimaryColor=ffa3a3a3 -Welcome_ForegroundSecondaryColor=ff808080 -Welcome_BackgroundPrimaryColor=normalBackground -Welcome_BackgroundSecondaryColor=shadowBackground -Welcome_HoverColor=ff404040 -Welcome_AccentColor=ff57d658 -Welcome_LinkColor=ff67e668 -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=text Timeline_BackgroundColor1=normalBackground Timeline_BackgroundColor2=ff444444 diff --git a/share/qtcreator/themes/dark.figmatokens b/share/qtcreator/themes/dark.figmatokens new file mode 100644 index 00000000000..fdd98e80c10 --- /dev/null +++ b/share/qtcreator/themes/dark.figmatokens @@ -0,0 +1,31 @@ +; Qt Creator Color Tokens - dark mode + +[Colors] + +Token_Basic_Black=ff131313 +Token_Basic_White=fff8f8f8 + +Token_Accent_Default=ff23b26a +Token_Accent_Muted=ff1f9b5d +Token_Accent_Subtle=ff1a8550 + +Token_Background_Default=ff1f1f1f +Token_Background_Muted=ff262626 +Token_Background_Subtle=ff2e2e2e + +Token_Foreground_Default=ff5a5a5a +Token_Foreground_Muted=ff3e3e3e +Token_Foreground_Subtle=ff303030 + +Token_Text_Default=fff8f8f8 +Token_Text_Muted=ffaeaeae +Token_Text_Subtle=ff595959 + +Token_Stroke_Strong=ffeeeeee +Token_Stroke_Muted=ff727272 +Token_Stroke_Subtle=ff3a3a3a + +Token_Notification_Alert=ffc98014 +Token_Notification_Success=ff1f9b5d +Token_Notification_Neutral=ff016876 +Token_Notification_Danger=ffb22245 diff --git a/share/qtcreator/themes/default.creatortheme b/share/qtcreator/themes/default.creatortheme index 2dee7ab2469..f2c4c7f7a43 100644 --- a/share/qtcreator/themes/default.creatortheme +++ b/share/qtcreator/themes/default.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=light.figmatokens ThemeName=Classic PreferredStyles= @@ -398,39 +399,6 @@ Debugger_WatchItem_ValueChanged=ffc80000 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - light mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff2f2f2 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ffe3e3e3 -Token_Background_Muted=ffeeeeee -Token_Background_Subtle=fffbfbfb -Token_Foreground_Default=ffcdcdcd -Token_Foreground_Muted=ffd5d5d5 -Token_Foreground_Subtle=ffdddddd -Token_Text_Default=ff393939 -Token_Text_Muted=ff7c7c7c -Token_Text_Subtle=ffbebebe -Token_Stroke_Strong=ff464646 -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ffcdcdcd -Token_Notification_Alert=ffeb991f -Token_Notification_Success=ff23b26a -Token_Notification_Neutral=ff0e7887 -Token_Notification_Danger=ffdc1343 - -Welcome_TextColor=ff000000 -Welcome_ForegroundPrimaryColor=shadowBackground -Welcome_ForegroundSecondaryColor=ff939393 -Welcome_BackgroundPrimaryColor=fffafafa -Welcome_BackgroundSecondaryColor=ffffffff -Welcome_HoverColor=ffefefef -Welcome_AccentColor=ff45ce55 -Welcome_LinkColor=ff20a020 -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=darkText Timeline_BackgroundColor1=ffffffff Timeline_BackgroundColor2=fff6f6f6 diff --git a/share/qtcreator/themes/design-light.creatortheme b/share/qtcreator/themes/design-light.creatortheme index 8931b620424..41a9f07ba84 100644 --- a/share/qtcreator/themes/design-light.creatortheme +++ b/share/qtcreator/themes/design-light.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=light.figmatokens ThemeName=Design Light PreferredStyles= @@ -410,39 +411,6 @@ Debugger_WatchItem_ValueChanged=ffbf0303 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - light mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff2f2f2 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ffe3e3e3 -Token_Background_Muted=ffeeeeee -Token_Background_Subtle=fffbfbfb -Token_Foreground_Default=ffcdcdcd -Token_Foreground_Muted=ffd5d5d5 -Token_Foreground_Subtle=ffdddddd -Token_Text_Default=ff393939 -Token_Text_Muted=ff7c7c7c -Token_Text_Subtle=ffbebebe -Token_Stroke_Strong=ff464646 -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ffcdcdcd -Token_Notification_Alert=ffeb991f -Token_Notification_Success=ff23b26a -Token_Notification_Neutral=ff0e7887 -Token_Notification_Danger=ffdc1343 - -Welcome_TextColor=ff000000 -Welcome_ForegroundPrimaryColor=ff404040 -Welcome_ForegroundSecondaryColor=ff727272 -Welcome_BackgroundPrimaryColor=ffeaeaea -Welcome_BackgroundSecondaryColor=ffefefef -Welcome_HoverColor=ffe1e1e1 -Welcome_AccentColor=ff25709a -Welcome_LinkColor=ff104090 -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=text Timeline_BackgroundColor1=normalBackground Timeline_BackgroundColor2=fff6f6f6 diff --git a/share/qtcreator/themes/design.creatortheme b/share/qtcreator/themes/design.creatortheme index e29240a3cf6..11022bd7e5d 100644 --- a/share/qtcreator/themes/design.creatortheme +++ b/share/qtcreator/themes/design.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=dark.figmatokens ThemeName=Design Dark PreferredStyles= DefaultTextEditorColorScheme=creator-dark.xml @@ -414,39 +415,6 @@ Debugger_WatchItem_ValueChanged=ffff6666 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - dark mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff8f8f8 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ff1f1f1f -Token_Background_Muted=ff262626 -Token_Background_Subtle=ff2e2e2e -Token_Foreground_Default=ff5a5a5a -Token_Foreground_Muted=ff3e3e3e -Token_Foreground_Subtle=ff303030 -Token_Text_Default=fff8f8f8 -Token_Text_Muted=ffaeaeae -Token_Text_Subtle=ff595959 -Token_Stroke_Strong=ffeeeeee -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ff3a3a3a -Token_Notification_Alert=ffc98014 -Token_Notification_Success=ff1f9b5d -Token_Notification_Neutral=ff016876 -Token_Notification_Danger=ffb22245 - -Welcome_TextColor=text -Welcome_ForegroundPrimaryColor=ffa3a3a3 -Welcome_ForegroundSecondaryColor=ff808080 -Welcome_BackgroundPrimaryColor=ff242424 -Welcome_BackgroundSecondaryColor=ff1c1c1c -Welcome_HoverColor=ff2b2a2a -Welcome_AccentColor=ff3f8ccc -Welcome_LinkColor=ff5fafef -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=text Timeline_BackgroundColor1=normalBackground Timeline_BackgroundColor2=ff444444 diff --git a/share/qtcreator/themes/flat-dark.creatortheme b/share/qtcreator/themes/flat-dark.creatortheme index 7e94645b743..9737e9cd53e 100644 --- a/share/qtcreator/themes/flat-dark.creatortheme +++ b/share/qtcreator/themes/flat-dark.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=dark.figmatokens ThemeName=Flat Dark PreferredStyles= DefaultTextEditorColorScheme=creator-dark.xml @@ -410,39 +411,6 @@ Debugger_WatchItem_ValueChanged=ffff6666 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - dark mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff8f8f8 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ff1f1f1f -Token_Background_Muted=ff262626 -Token_Background_Subtle=ff2e2e2e -Token_Foreground_Default=ff5a5a5a -Token_Foreground_Muted=ff3e3e3e -Token_Foreground_Subtle=ff303030 -Token_Text_Default=fff8f8f8 -Token_Text_Muted=ffaeaeae -Token_Text_Subtle=ff595959 -Token_Stroke_Strong=ffeeeeee -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ff3a3a3a -Token_Notification_Alert=ffc98014 -Token_Notification_Success=ff1f9b5d -Token_Notification_Neutral=ff016876 -Token_Notification_Danger=ffb22245 - -Welcome_TextColor=text -Welcome_ForegroundPrimaryColor=ff999999 -Welcome_ForegroundSecondaryColor=ff808080 -Welcome_BackgroundPrimaryColor=normalBackground -Welcome_BackgroundSecondaryColor=ff242628 -Welcome_HoverColor=ff404243 -Welcome_AccentColor=ff36c148 -Welcome_LinkColor=ff5fcf4f -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=text Timeline_BackgroundColor1=normalBackground Timeline_BackgroundColor2=ff444444 diff --git a/share/qtcreator/themes/flat-light.creatortheme b/share/qtcreator/themes/flat-light.creatortheme index eddf38971d2..1ff8a7a3ff4 100644 --- a/share/qtcreator/themes/flat-light.creatortheme +++ b/share/qtcreator/themes/flat-light.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=light.figmatokens ThemeName=Flat Light PreferredStyles= @@ -407,39 +408,6 @@ Debugger_WatchItem_ValueChanged=ffbf0303 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - light mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff2f2f2 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ffe3e3e3 -Token_Background_Muted=ffeeeeee -Token_Background_Subtle=fffbfbfb -Token_Foreground_Default=ffcdcdcd -Token_Foreground_Muted=ffd5d5d5 -Token_Foreground_Subtle=ffdddddd -Token_Text_Default=ff393939 -Token_Text_Muted=ff7c7c7c -Token_Text_Subtle=ffbebebe -Token_Stroke_Strong=ff464646 -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ffcdcdcd -Token_Notification_Alert=ffeb991f -Token_Notification_Success=ff23b26a -Token_Notification_Neutral=ff0e7887 -Token_Notification_Danger=ffdc1343 - -Welcome_TextColor=ff000000 -Welcome_ForegroundPrimaryColor=ff232323 -Welcome_ForegroundSecondaryColor=ff939393 -Welcome_BackgroundPrimaryColor=fffafafa -Welcome_BackgroundSecondaryColor=ffffffff -Welcome_HoverColor=ffefefef -Welcome_AccentColor=ff45ce55 -Welcome_LinkColor=ff20a020 -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=text Timeline_BackgroundColor1=normalBackground Timeline_BackgroundColor2=fff6f6f6 diff --git a/share/qtcreator/themes/flat.creatortheme b/share/qtcreator/themes/flat.creatortheme index 08697334f40..f479077889e 100644 --- a/share/qtcreator/themes/flat.creatortheme +++ b/share/qtcreator/themes/flat.creatortheme @@ -1,4 +1,5 @@ [General] +Includes=light.figmatokens ThemeName=Flat PreferredStyles= @@ -405,39 +406,6 @@ Debugger_WatchItem_ValueChanged=ffbf0303 Debugger_Breakpoint_TextMarkColor=ffff4040 -; Qt Creator Color Tokens - light mode -Token_Basic_Black=ff131313 -Token_Basic_White=fff2f2f2 -Token_Accent_Default=ff23b26a -Token_Accent_Muted=ff1f9b5d -Token_Accent_Subtle=ff1a8550 -Token_Background_Default=ffe3e3e3 -Token_Background_Muted=ffeeeeee -Token_Background_Subtle=fffbfbfb -Token_Foreground_Default=ffcdcdcd -Token_Foreground_Muted=ffd5d5d5 -Token_Foreground_Subtle=ffdddddd -Token_Text_Default=ff393939 -Token_Text_Muted=ff7c7c7c -Token_Text_Subtle=ffbebebe -Token_Stroke_Strong=ff464646 -Token_Stroke_Muted=ff727272 -Token_Stroke_Subtle=ffcdcdcd -Token_Notification_Alert=ffeb991f -Token_Notification_Success=ff23b26a -Token_Notification_Neutral=ff0e7887 -Token_Notification_Danger=ffdc1343 - -Welcome_TextColor=ff000000 -Welcome_ForegroundPrimaryColor=shadowBackground -Welcome_ForegroundSecondaryColor=ff939393 -Welcome_BackgroundPrimaryColor=fffafafa -Welcome_BackgroundSecondaryColor=ffffffff -Welcome_HoverColor=ffefefef -Welcome_AccentColor=ff45ce55 -Welcome_LinkColor=ff20a020 -Welcome_DisabledLinkColor=textDisabled - Timeline_TextColor=text Timeline_BackgroundColor1=normalBackground Timeline_BackgroundColor2=fff6f6f6 diff --git a/share/qtcreator/themes/light.figmatokens b/share/qtcreator/themes/light.figmatokens new file mode 100644 index 00000000000..a4d1bfcaa8d --- /dev/null +++ b/share/qtcreator/themes/light.figmatokens @@ -0,0 +1,31 @@ +; Qt Creator Color Tokens - light mode + +[Colors] + +Token_Basic_Black=ff131313 +Token_Basic_White=fff2f2f2 + +Token_Accent_Default=ff23b26a +Token_Accent_Muted=ff1f9b5d +Token_Accent_Subtle=ff1a8550 + +Token_Background_Default=fffcfcfc +Token_Background_Muted=ffefefef +Token_Background_Subtle=ffe7e7e7 + +Token_Foreground_Default=ffcdcdcd +Token_Foreground_Muted=ffd5d5d5 +Token_Foreground_Subtle=ffdddddd + +Token_Text_Default=ff393939 +Token_Text_Muted=ff6a6a6a +Token_Text_Subtle=ffbebebe + +Token_Stroke_Strong=ff464646 +Token_Stroke_Muted=ff727272 +Token_Stroke_Subtle=ffcdcdcd + +Token_Notification_Alert=ffeb991f +Token_Notification_Success=ff23b26a +Token_Notification_Neutral=ff0e7887 +Token_Notification_Danger=ffdc1343 diff --git a/src/libs/qtcreatorcdbext/pytype.cpp b/src/libs/qtcreatorcdbext/pytype.cpp index 0621793b7e0..9345c1d91af 100644 --- a/src/libs/qtcreatorcdbext/pytype.cpp +++ b/src/libs/qtcreatorcdbext/pytype.cpp @@ -121,6 +121,9 @@ static std::string stripPointerType(const std::string &typeNameIn) std::string typeName = typeNameIn; if (typeName.back() == '*') { typeName.pop_back(); + trimBack(typeName); + if (endsWith(typeName, "const")) + typeName = typeName.erase(typeName.size() - 5, 5); } else { const auto arrayPosition = typeName.find_first_of('['); if (arrayPosition != std::string::npos @@ -296,35 +299,19 @@ int PyType::code() const return std::nullopt; }; - if (!resolve()) - return parseTypeName(name()).value_or(TypeCodeUnresolvable); - - if (m_tag < 0) { - if (const std::optional typeCode = parseTypeName(name())) - return *typeCode; - - IDebugSymbolGroup2 *sg = 0; - if (FAILED(ExtensionCommandContext::instance()->symbols()->CreateSymbolGroup2(&sg))) - return TypeCodeStruct; - - const std::string helperValueName = SymbolGroupValue::pointedToSymbolName(0, name(true)); - ULONG index = DEBUG_ANY_ID; - if (SUCCEEDED(sg->AddSymbol(helperValueName.c_str(), &index))) - m_tag = PyValue(index, sg).tag(); - sg->Release(); + if (m_tag >= 0) { + switch (m_tag) { + case SymTagUDT: return TypeCodeStruct; + case SymTagEnum: return TypeCodeEnum; + case SymTagTypedef: return TypeCodeTypedef; + case SymTagFunctionType: return TypeCodeFunction; + case SymTagPointerType: return TypeCodePointer; + case SymTagArrayType: return TypeCodeArray; + case SymTagBaseType: return isIntegralType(name()) ? TypeCodeIntegral : TypeCodeFloat; + default: break; + } } - switch (m_tag) { - case SymTagUDT: return TypeCodeStruct; - case SymTagEnum: return TypeCodeEnum; - case SymTagTypedef: return TypeCodeTypedef; - case SymTagFunctionType: return TypeCodeFunction; - case SymTagPointerType: return TypeCodePointer; - case SymTagArrayType: return TypeCodeArray; - case SymTagBaseType: return isIntegralType(name()) ? TypeCodeIntegral : TypeCodeFloat; - default: break; - } - - return TypeCodeStruct; + return parseTypeName(name()).value_or(TypeCodeStruct); } PyType PyType::target() const @@ -533,6 +520,7 @@ PY_FUNC_RET_OBJECT_LIST(fields, PY_OBJ_NAME) PY_FUNC_RET_STD_STRING(module, PY_OBJ_NAME) PY_FUNC(moduleId, PY_OBJ_NAME, "K") PY_FUNC(arrayElements, PY_OBJ_NAME, "k") +PY_FUNC_RET_BOOL(resolved, PY_OBJ_NAME) PY_FUNC_DECL(templateArguments, PY_OBJ_NAME) { PY_IMPL_GUARD; @@ -568,6 +556,8 @@ static PyMethodDef typeMethods[] = { "Returns the number of elements in an array or 0 for non array types"}, {"templateArguments", PyCFunction(templateArguments), METH_NOARGS, "Returns all template arguments."}, + {"resolved", PyCFunction(resolved), METH_NOARGS, + "Returns whether the type is resolved"}, {NULL} /* Sentinel */ }; diff --git a/src/libs/qtcreatorcdbext/pytype.h b/src/libs/qtcreatorcdbext/pytype.h index 8b05fffe0ad..b71eae1ab02 100644 --- a/src/libs/qtcreatorcdbext/pytype.h +++ b/src/libs/qtcreatorcdbext/pytype.h @@ -29,6 +29,7 @@ public: std::string module() const; ULONG64 moduleId() const; int arrayElements() const; + bool resolved() const { return m_resolved.value_or(false); } struct TemplateArgument { diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index ffb7efc417c..23beeb4b3ff 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -933,7 +933,17 @@ QColor StyleHelper::ensureReadableOn(const QColor &background, const QColor &des return foreground; } -static QStringList brandFontFamilies() +static const QStringList &applicationFontFamilies() +{ + const static QStringList families = [] { + const QLatin1String familyName("Inter"); + // Font is either installed in the system, or was loaded from share/qtcreator/fonts/ + return QFontDatabase::hasFamily(familyName) ? QStringList(familyName) : QStringList(); + }(); + return families; +} + +static const QStringList &brandFontFamilies() { const static QStringList families = []{ const int id = QFontDatabase::addApplicationFont(":/studiofonts/TitilliumWeb-Regular.ttf"); @@ -959,10 +969,14 @@ static const UiFontMetrics& uiFontMetrics(StyleHelper::UiElement element) {StyleHelper::UiElementH5, {14, 16, QFont::DemiBold}}, {StyleHelper::UiElementH6, {12, 14, QFont::DemiBold}}, {StyleHelper::UiElementH6Capital, {12, 14, QFont::DemiBold}}, + {StyleHelper::UiElementBody1, {14, 20, QFont::Light}}, + {StyleHelper::UiElementBody2, {12, 20, QFont::Light}}, + {StyleHelper::UiElementButtonMedium, {12, 16, QFont::Bold}}, + {StyleHelper::UiElementButtonSmall, {10, 12, QFont::Bold}}, {StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}}, {StyleHelper::UiElementCaption, {10, 12, QFont::Normal}}, - {StyleHelper::UIElementIconStandard, {12, 16, QFont::Normal}}, - {StyleHelper::UIElementIconActive, {12, 16, QFont::DemiBold}}, + {StyleHelper::UiElementIconStandard, {12, 16, QFont::Medium}}, + {StyleHelper::UiElementIconActive, {12, 16, QFont::DemiBold}}, }; QTC_ASSERT(metrics.count(element) > 0, return metrics.at(StyleHelper::UiElementCaptionStrong)); return metrics.at(element); @@ -983,8 +997,10 @@ QFont StyleHelper::uiFont(UiElement element) case UiElementH3: case UiElementH6Capital: font.setCapitalization(QFont::AllUppercase); - break; + [[fallthrough]]; default: + if (!applicationFontFamilies().isEmpty()) + font.setFamilies(applicationFontFamilies()); break; } diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h index 4b986c68313..7d434925e66 100644 --- a/src/libs/utils/stylehelper.h +++ b/src/libs/utils/stylehelper.h @@ -42,15 +42,15 @@ constexpr char C_TOOLBAR_ACTIONWIDGET[] = "toolbar_actionWidget"; constexpr char C_QT_SCALE_FACTOR_ROUNDING_POLICY[] = "QT_SCALE_FACTOR_ROUNDING_POLICY"; namespace SpacingTokens { - constexpr int VPaddingXXS = 4; // Top and bottom padding within the component - constexpr int HPaddingXXS = 4; // Left and right padding within the component - constexpr int VGapXXS = 4; // Vertical Space between TEXT LINE within the Component - constexpr int HGapXXS = 4; // Horizontal Space between elements within the Component + constexpr int VPaddingXxs = 4; // Top and bottom padding within the component + constexpr int HPaddingXxs = 4; // Left and right padding within the component + constexpr int VGapXxs = 4; // Vertical Space between TEXT LINE within the Component + constexpr int HGapXxs = 4; // Horizontal Space between elements within the Component - constexpr int VPaddingXS = 8; - constexpr int HPaddingXS = 8; - constexpr int VGapXS = 4; - constexpr int HGapXS = 8; + constexpr int VPaddingXs = 8; + constexpr int HPaddingXs = 8; + constexpr int VGapXs = 4; + constexpr int HGapXs = 8; constexpr int VPaddingS = 8; constexpr int HPaddingS = 16; @@ -62,10 +62,15 @@ namespace SpacingTokens { constexpr int VGapM = 4; constexpr int HGapM = 16; - constexpr int VPaddingL = 12; + constexpr int VPaddingL = 16; constexpr int HPaddingL = 24; constexpr int VGapL = 8; constexpr int HGapL = 16; + + constexpr int ExPaddingGapS = 2; + constexpr int ExPaddingGapM = 6; + constexpr int ExPaddingGapL = 12; + constexpr int ExVPaddingGapXl = 24; } enum ToolbarStyle { @@ -82,10 +87,14 @@ enum UiElement { UiElementH5, UiElementH6, UiElementH6Capital, + UiElementBody1, + UiElementBody2, + UiElementButtonMedium, + UiElementButtonSmall, UiElementCaptionStrong, UiElementCaption, - UIElementIconStandard, - UIElementIconActive, + UiElementIconStandard, + UiElementIconActive, }; // Height of the project explorer navigation bar diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp index 8800c85eb80..9b24078f86a 100644 --- a/src/libs/utils/theme/theme.cpp +++ b/src/libs/utils/theme/theme.cpp @@ -245,7 +245,6 @@ void Theme::readSettingsInternal(QSettings &settings) QMetaEnum e = m.enumerator(m.indexOfEnumerator("Flag")); for (int i = 0, total = e.keyCount(); i < total; ++i) { const QString key = QLatin1String(e.key(i)); - QTC_ASSERT(settings.contains(key), return ); d->flags[i] = settings.value(key).toBool(); } settings.endGroup(); diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h index 2e4d61d943b..65d0ee91eaf 100644 --- a/src/libs/utils/theme/theme.h +++ b/src/libs/utils/theme/theme.h @@ -247,18 +247,6 @@ public: Token_Notification_Neutral, Token_Notification_Danger, - /* Welcome Plugin */ - - Welcome_TextColor, - Welcome_ForegroundPrimaryColor, - Welcome_ForegroundSecondaryColor, - Welcome_BackgroundPrimaryColor, - Welcome_BackgroundSecondaryColor, - Welcome_HoverColor, - Welcome_AccentColor, - Welcome_LinkColor, - Welcome_DisabledLinkColor, - /* Timeline Library */ Timeline_TextColor, Timeline_BackgroundColor1, diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp index 8f64e647764..9dcf2d502ac 100644 --- a/src/plugins/android/androidrunnerworker.cpp +++ b/src/plugins/android/androidrunnerworker.cpp @@ -819,15 +819,13 @@ void AndroidRunnerWorker::removeForwardPort(const QString &port) void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser) { - qint64 pid = pidUser.first; - qint64 user = pidUser.second; // Don't write to m_psProc from a different thread QTC_ASSERT(QThread::currentThread() == thread(), return); qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID - << "to:" << pid; - m_processPID = pid; - m_processUser = user; - if (pid == -1) { + << "to:" << pidUser.first; + m_processPID = pidUser.first; + m_processUser = pidUser.second; + if (m_processPID == -1) { emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.") .arg(m_packageName)); // App died/killed. Reset log, monitor, jdb & gdbserver/lldb-server processes. @@ -852,7 +850,10 @@ void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser) QTC_ASSERT(m_psIsAlive, return); m_psIsAlive->setObjectName("IsAliveProcess"); m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels); - connect(m_psIsAlive.get(), &Process::done, this, [this] { onProcessIdChanged({-1, -1}); }); + connect(m_psIsAlive.get(), &Process::done, this, [this] { + m_psIsAlive.release()->deleteLater(); + onProcessIdChanged({-1, -1}); + }); } } diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 168ae2712e6..f08848049ad 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -13,14 +13,15 @@ #include +#include #include #include #include #include #include #include -#include #include +#include #include #include @@ -203,6 +204,56 @@ enum OpenSslValidation { OpenSslCmakeListsPathExists }; +static expected_str testJavaC(const FilePath &jdkPath) +{ + if (!jdkPath.isReadableDir()) + return make_unexpected(Tr::tr("The selected path does not exist or is not readable.")); + + const QString javacCommand("javac"); + const QString versionParameter("-version"); + constexpr int requiredMajorVersion = 17; + const FilePath bin = jdkPath / "bin" / (javacCommand + QTC_HOST_EXE_SUFFIX); + + if (!bin.isExecutableFile()) + return make_unexpected( + Tr::tr("Could not find \"%1\" in the selected path.") + .arg(bin.toUserOutput())); + + QVersionNumber jdkVersion; + + Process javacProcess; + const CommandLine cmd(bin, {versionParameter}); + javacProcess.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels); + javacProcess.setCommand(cmd); + javacProcess.runBlocking(); + + const QString stdOut = javacProcess.stdOut().trimmed(); + + if (javacProcess.exitCode() != 0) + return make_unexpected( + Tr::tr("The selected path does not contain a valid JDK. (%1 failed: %2)") + .arg(cmd.toUserOutput()) + .arg(stdOut)); + + // We expect "javac " where is "major.minor.patch" + const QString outputPrefix = javacCommand + " "; + if (!stdOut.startsWith(outputPrefix)) + return make_unexpected(Tr::tr("Unexpected output from \"%1\": %2") + .arg(cmd.toUserOutput()) + .arg(stdOut)); + + jdkVersion = QVersionNumber::fromString(stdOut.mid(outputPrefix.length()).split('\n').first()); + + if (jdkVersion.isNull() || jdkVersion.majorVersion() != requiredMajorVersion) { + return make_unexpected(Tr::tr("Unsupported JDK version (needs to be %1): %2 (parsed: %3)") + .arg(requiredMajorVersion) + .arg(stdOut) + .arg(jdkVersion.toString())); + } + + return {}; +} + AndroidSettingsWidget::AndroidSettingsWidget() { setWindowTitle(Tr::tr("Android Configuration")); @@ -307,6 +358,15 @@ AndroidSettingsWidget::AndroidSettingsWidget() Tr::tr("OpenSSL settings have errors."), openSslDetailsWidget); + m_openJdkLocationPathChooser->setValidationFunction([](const QString &s) { + return Utils::asyncRun([s]() -> expected_str { + expected_str test = testJavaC(FilePath::fromUserInput(s)); + if (!test) + return make_unexpected(test.error()); + return s; + }); + }); + connect(m_openJdkLocationPathChooser, &PathChooser::rawPathChanged, this, &AndroidSettingsWidget::validateJdk); if (androidConfig().openJDKLocation().isEmpty()) @@ -533,10 +593,9 @@ bool AndroidSettingsWidget::isDefaultNdkSelected() const void AndroidSettingsWidget::validateJdk() { androidConfig().setOpenJDKLocation(m_openJdkLocationPathChooser->filePath()); - bool jdkPathExists = androidConfig().openJDKLocation().exists(); - const FilePath bin = androidConfig().openJDKLocation() - .pathAppended("bin/javac" QTC_HOST_EXE_SUFFIX); - m_androidSummary->setPointValid(JavaPathExistsAndWritableRow, jdkPathExists && bin.exists()); + expected_str test = testJavaC(androidConfig().openJDKLocation()); + + m_androidSummary->setPointValid(JavaPathExistsAndWritableRow, test.has_value()); updateUI(); diff --git a/src/plugins/autotest/catch/catchcodeparser.cpp b/src/plugins/autotest/catch/catchcodeparser.cpp index 186dde5ea1b..f9d6b19ed04 100644 --- a/src/plugins/autotest/catch/catchcodeparser.cpp +++ b/src/plugins/autotest/catch/catchcodeparser.cpp @@ -79,14 +79,16 @@ void CatchCodeParser::handleIdentifier() || unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_SIG") { handleParameterizedTestCase(false); } else if (unprefixed == "TEST_CASE_METHOD") { - handleFixtureOrRegisteredTestCase(true); + handleFixtureOrRegisteredTestCase(/*fixture=*/true, /*scenario=*/false); + } else if (unprefixed == "SCENARIO_METHOD") { + handleFixtureOrRegisteredTestCase(/*fixture=*/true, /*scenario=*/true); } else if (unprefixed == "TEMPLATE_TEST_CASE_METHOD_SIG" || unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG" || unprefixed == "TEMPLATE_TEST_CASE_METHOD" || unprefixed == "TEMPLATE_LIST_TEST_CASE_METHOD") { handleParameterizedTestCase(true); } else if (unprefixed == "METHOD_AS_TEST_CASE" || unprefixed == "REGISTER_TEST_CASE") { - handleFixtureOrRegisteredTestCase(false); + handleFixtureOrRegisteredTestCase(/*fixture=*/false, /*scenario=*/false); } } @@ -124,7 +126,7 @@ void CatchCodeParser::handleParameterizedTestCase(bool isFixture) if (!skipCommentsUntil(T_LPAREN)) return; - if (isFixture && !skipFixtureParameter()) + if (isFixture && !skipParameter()) return; CatchTestCodeLocationAndType locationAndType @@ -154,18 +156,13 @@ void CatchCodeParser::handleParameterizedTestCase(bool isFixture) m_testCases.append(locationAndType); } -void CatchCodeParser::handleFixtureOrRegisteredTestCase(bool isFixture) +void CatchCodeParser::handleFixtureOrRegisteredTestCase(bool isFixture, bool isScenario) { if (!skipCommentsUntil(T_LPAREN)) return; - if (isFixture) { - if (!skipFixtureParameter()) + if (!skipParameter()) return; - } else { - if (!skipFunctionParameter()) - return; - } CatchTestCodeLocationAndType locationAndType = locationAndTypeFromToken(m_tokens.at(m_currentIndex)); @@ -183,6 +180,9 @@ void CatchCodeParser::handleFixtureOrRegisteredTestCase(bool isFixture) if (stoppedAt != T_RPAREN) return; + if (isScenario) + testCaseName.prepend("Scenario: "); // use a flag? + locationAndType.m_name = testCaseName; locationAndType.tags = parseTags(tagsString); if (isFixture) @@ -245,19 +245,12 @@ Kind CatchCodeParser::skipUntilCorrespondingRParen() return T_ERROR; } -bool CatchCodeParser::skipFixtureParameter() -{ - if (!skipCommentsUntil(T_IDENTIFIER)) - return false; - return skipCommentsUntil(T_COMMA); -} - -bool CatchCodeParser::skipFunctionParameter() +bool CatchCodeParser::skipParameter() { if (!skipCommentsUntil(T_IDENTIFIER)) return false; if (skipCommentsUntil(T_COLON_COLON)) - return skipFunctionParameter(); + return skipParameter(); return skipCommentsUntil(T_COMMA); } diff --git a/src/plugins/autotest/catch/catchcodeparser.h b/src/plugins/autotest/catch/catchcodeparser.h index 9315b3ccc2c..4bebb16bce8 100644 --- a/src/plugins/autotest/catch/catchcodeparser.h +++ b/src/plugins/autotest/catch/catchcodeparser.h @@ -22,13 +22,12 @@ private: void handleIdentifier(); void handleTestCase(bool isScenario); void handleParameterizedTestCase(bool isFixture); - void handleFixtureOrRegisteredTestCase(bool isFixture); + void handleFixtureOrRegisteredTestCase(bool isFixture, bool isScenario); QString getStringLiteral(CPlusPlus::Kind &stoppedAtKind); bool skipCommentsUntil(CPlusPlus::Kind nextExpectedKind); // moves currentIndex if succeeds CPlusPlus::Kind skipUntilCorrespondingRParen(); // moves currentIndex - bool skipFixtureParameter(); - bool skipFunctionParameter(); + bool skipParameter(); const QByteArray &m_source; const CPlusPlus::LanguageFeatures &m_features; diff --git a/src/plugins/autotest/catch/catchtestparser.cpp b/src/plugins/autotest/catch/catchtestparser.cpp index 96f536a9055..bc2f4e0644e 100644 --- a/src/plugins/autotest/catch/catchtestparser.cpp +++ b/src/plugins/autotest/catch/catchtestparser.cpp @@ -29,7 +29,9 @@ static bool isCatchTestCaseMacro(const QString ¯oName) QStringLiteral("TEMPLATE_TEST_CASE_SIG"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_SIG"), QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD"), - QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD_SIG"), + QStringLiteral("TEST_CASE_METHOD"), + QStringLiteral("SCENARIO_METHOD"), + QStringLiteral("TEMPLATE_TEST_CASE_METHOD_SIG"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_LIST_TEST_CASE_METHOD"), @@ -105,7 +107,7 @@ bool CatchTestParser::processDocument(QPromise &promise, if (!hasCatchNames(doc)) { static const QRegularExpression regex("\\b(CATCH_)?" - "(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|" + "(SCENARIO(_METHOD)?|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|" "TEMPLATE_TEST_CASE(_METHOD)?_SIG|" "TEMPLATE_PRODUCT_TEST_CASE(_METHOD)?_SIG|" "TEMPLATE_LIST_TEST_CASE_METHOD|METHOD_AS_TEST_CASE|" diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp index 1824b316d59..9962a08c621 100644 --- a/src/plugins/autotest/testcodeparser.cpp +++ b/src/plugins/autotest/testcodeparser.cpp @@ -55,6 +55,7 @@ TestCodeParser::TestCodeParser() m_qmlEditorRev.remove(filePath); }); m_reparseTimer.setSingleShot(true); + m_reparseTimer.setInterval(1000); connect(&m_reparseTimer, &QTimer::timeout, this, &TestCodeParser::parsePostponedFiles); connect(&m_taskTreeRunner, &TaskTreeRunner::aboutToStart, this, [this](TaskTree *taskTree) { if (m_withTaskProgress) { @@ -238,27 +239,10 @@ bool TestCodeParser::postponed(const QSet &filePaths) if (filePaths.size() == 1) { if (m_reparseTimerTimedOut) return false; - const FilePath filePath = *filePaths.begin(); - switch (m_postponedFiles.size()) { - case 0: - m_postponedFiles.insert(filePath); - m_reparseTimer.setInterval(1000); - m_reparseTimer.start(); - return true; - case 1: - if (m_postponedFiles.contains(filePath)) { - m_reparseTimer.start(); - return true; - } - Q_FALLTHROUGH(); - default: - m_postponedFiles.insert(filePath); - m_reparseTimer.stop(); - m_reparseTimer.setInterval(0); - m_reparseTimerTimedOut = false; - m_reparseTimer.start(); - return true; - } + + m_postponedFiles.insert(*filePaths.begin()); + m_reparseTimer.start(); + return true; } return false; case PartialParse: @@ -377,7 +361,7 @@ void TestCodeParser::scanForTests(const QSet &filePaths, return true; return cppSnapshot.contains(fn); }); - m_withTaskProgress = filteredFiles.size() > 5; + m_withTaskProgress = isFullParse || filteredFiles.size() > 20; qCDebug(LOG) << "Starting scan of" << filteredFiles.size() << "(" << files.size() << ")" << "files with" << codeParsers.size() << "parsers"; diff --git a/src/plugins/axivion/axivion.qrc b/src/plugins/axivion/axivion.qrc index fa3ad146d37..d1e41e3cdb3 100644 --- a/src/plugins/axivion/axivion.qrc +++ b/src/plugins/axivion/axivion.qrc @@ -2,18 +2,18 @@ images/axivion.png images/axivion@2x.png - images/button-av.png - images/button-av@2x.png - images/button-cl.png - images/button-cl@2x.png - images/button-cy.png - images/button-cy@2x.png - images/button-de.png - images/button-de@2x.png - images/button-mv.png - images/button-mv@2x.png - images/button-sv.png - images/button-sv@2x.png + images/button-AV.png + images/button-AV@2x.png + images/button-CL.png + images/button-CL@2x.png + images/button-CY.png + images/button-CY@2x.png + images/button-DE.png + images/button-DE@2x.png + images/button-MV.png + images/button-MV@2x.png + images/button-SV.png + images/button-SV@2x.png images/sortAsc.png images/sortAsc@2x.png images/sortDesc.png diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp index a13b63dd3a2..70e44057e9f 100644 --- a/src/plugins/axivion/axivionoutputpane.cpp +++ b/src/plugins/axivion/axivionoutputpane.cpp @@ -334,6 +334,8 @@ IssuesWidget::IssuesWidget(QWidget *parent) connect(m_pathGlobFilter, &QLineEdit::textEdited, this, &IssuesWidget::onSearchParameterChanged); m_issuesView = new BaseTreeView(this); + m_issuesView->setFrameShape(QFrame::StyledPanel); // Bring back Qt default + m_issuesView->setFrameShadow(QFrame::Sunken); // Bring back Qt default m_headerView = new IssueHeaderView(this); connect(m_headerView, &IssueHeaderView::sortTriggered, this, &IssuesWidget::onSearchParameterChanged); @@ -562,7 +564,7 @@ void IssuesWidget::updateBasicProjectInfo(std::optional inf int buttonId = 0; for (const Dto::IssueKindInfoDto &kind : issueKinds) { auto button = new QToolButton(this); - button->setIcon(iconForIssue(kind.prefix)); + button->setIcon(iconForIssue(kind.getOptionalPrefixEnum())); button->setToolTip(kind.nicePluralName); button->setCheckable(true); connect(button, &QToolButton::clicked, this, [this, prefix = kind.prefix]{ diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp index c559e2b7d3a..06c0d72aac4 100644 --- a/src/plugins/axivion/axivionplugin.cpp +++ b/src/plugins/axivion/axivionplugin.cpp @@ -63,18 +63,21 @@ using namespace Utils; namespace Axivion::Internal { -QIcon iconForIssue(const QString &prefix) +QIcon iconForIssue(const std::optional &issueKind) { - static QHash prefixToIcon; - auto it = prefixToIcon.find(prefix); + if (!issueKind) + return {}; - if (it == prefixToIcon.end()) { - Icon icon({{FilePath::fromString(":/axivion/images/button-" + prefix.toLower() + ".png"), - Theme::PaletteButtonText}}, - Icon::Tint); - it = prefixToIcon.insert(prefix, icon.icon()); - } - return it.value(); + static QHash prefixToIcon; + + auto it = prefixToIcon.constFind(*issueKind); + if (it != prefixToIcon.constEnd()) + return *it; + + const QLatin1String prefix = Dto::IssueKindMeta::enumToStr(*issueKind); + const Icon icon({{FilePath::fromString(":/axivion/images/button-" + prefix + ".png"), + Theme::PaletteButtonText}}, Icon::Tint); + return prefixToIcon.insert(*issueKind, icon.icon()).value(); } QString anyToSimpleString(const Dto::Any &any) @@ -122,11 +125,30 @@ static QString credentialKey() QString escaped = string; return escaped.replace('\\', "\\\\").replace('@', "\\@"); }; - return escape(settings().server.dashboard) + '@' + escape(settings().server.username); + return escape(settings().server.username) + '@' + escape(settings().server.dashboard); } -static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInfoDto &infoDto) +template +struct GetDtoStorage { + QUrl url; + std::optional credential; + std::optional dtoData; +}; + +template +struct PostDtoStorage +{ + QUrl url; + std::optional credential; + QByteArray csrfToken; + QByteArray writeData; + std::optional dtoData; +}; + +static DashboardInfo toDashboardInfo(const GetDtoStorage &dashboardStorage) +{ + const Dto::DashboardInfoDto &infoDto = *dashboardStorage.dtoData; const QVersionNumber versionNumber = infoDto.dashboardVersionNumber ? QVersionNumber::fromString(*infoDto.dashboardVersionNumber) : QVersionNumber(); @@ -139,7 +161,7 @@ static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInf projectUrls.insert(project.name, project.url); } } - return {source, versionNumber, projects, projectUrls, infoDto.checkCredentialsUrl}; + return {dashboardStorage.url, versionNumber, projects, projectUrls, infoDto.checkCredentialsUrl}; } QString IssueListSearch::toQuery() const @@ -226,7 +248,7 @@ public: const QString markText = issue.description; const QString id = issue.kind + QString::number(issue.id.value_or(-1)); setToolTip(id + '\n' + markText); - setIcon(iconForIssue(issue.kind)); + setIcon(iconForIssue(issue.getOptionalKindEnum())); if (color) setColor(*color); setPriority(TextMark::NormalPriority); @@ -323,18 +345,26 @@ void AxivionPluginPrivate::onStartupProjectChanged(Project *project) static QUrl urlForProject(const QString &projectName) { - return QUrl(settings().server.dashboard).resolved(QString("api/projects/")).resolved(projectName); + if (!dd->m_dashboardInfo) + return {}; + return dd->m_dashboardInfo->source.resolved(QString("api/projects/")).resolved(projectName); } static constexpr int httpStatusCodeOk = 200; constexpr char s_htmlContentType[] = "text/html"; constexpr char s_jsonContentType[] = "application/json"; +static bool isServerAccessEstablished() +{ + return dd->m_serverAccess == ServerAccess::NoAuthorization + || (dd->m_serverAccess == ServerAccess::WithAuthorization && dd->m_apiToken); +} + static Group fetchHtmlRecipe(const QUrl &url, const std::function &handler) { // TODO: Refactor so that it's a common code with fetchDataRecipe(). const auto onQuerySetup = [url](NetworkQuery &query) { - if (dd->m_serverAccess == ServerAccess::Unknown) + if (!isServerAccessEstablished()) return SetupResult::StopWithError; // TODO: start authorizationRecipe()? QNetworkRequest request(url); @@ -367,24 +397,6 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function -struct GetDtoStorage -{ - QUrl url; - std::optional credential; - std::optional dtoData; -}; - -template -struct PostDtoStorage -{ - QUrl url; - std::optional credential; - QByteArray csrfToken; - QByteArray writeData; - std::optional dtoData; -}; - template typename DtoStorageType> static Group dtoRecipe(const Storage> &dtoStorage) { @@ -410,7 +422,8 @@ static Group dtoRecipe(const Storage> &dtoStorage) query.setNetworkAccessManager(&dd->m_networkAccessManager); }; - const auto onNetworkQueryDone = [storage](const NetworkQuery &query, DoneWith doneWith) { + const auto onNetworkQueryDone = [storage, dtoStorage](const NetworkQuery &query, + DoneWith doneWith) { QNetworkReply *reply = query.reply(); const QNetworkReply::NetworkError error = reply->error(); const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -423,6 +436,7 @@ static Group dtoRecipe(const Storage> &dtoStorage) if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk && contentType == s_jsonContentType) { *storage = reply->readAll(); + dtoStorage->url = reply->url(); return DoneResult::Success; } @@ -503,7 +517,7 @@ static Group authorizationRecipe() { const Storage> unauthorizedDashboardStorage; const auto onUnauthorizedGroupSetup = [unauthorizedDashboardStorage] { - if (dd->m_serverAccess != ServerAccess::NoAuthorization) + if (isServerAccessEstablished()) return SetupResult::StopWithSuccess; unauthorizedDashboardStorage->url = QUrl(settings().server.dashboard); @@ -512,8 +526,7 @@ static Group authorizationRecipe() const auto onUnauthorizedGroupDone = [unauthorizedDashboardStorage] { if (unauthorizedDashboardStorage->dtoData) { dd->m_serverAccess = ServerAccess::NoAuthorization; - dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, - *unauthorizedDashboardStorage->dtoData); + dd->m_dashboardInfo = toDashboardInfo(*unauthorizedDashboardStorage); } else { dd->m_serverAccess = ServerAccess::WithAuthorization; } @@ -540,7 +553,7 @@ static Group authorizationRecipe() const Storage passwordStorage; const Storage> dashboardStorage; - const auto onDashboardGroupSetup = [passwordStorage, dashboardStorage] { + const auto onPasswordGroupSetup = [passwordStorage, dashboardStorage] { if (dd->m_apiToken) return SetupResult::StopWithSuccess; @@ -563,8 +576,7 @@ static Group authorizationRecipe() if (!dashboardStorage->dtoData) return SetupResult::StopWithSuccess; - dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, - *dashboardStorage->dtoData); + dd->m_dashboardInfo = toDashboardInfo(*dashboardStorage); const Dto::DashboardInfoDto &dashboardDto = *dashboardStorage->dtoData; if (!dashboardDto.userApiTokenUrl) @@ -572,7 +584,7 @@ static Group authorizationRecipe() apiTokenStorage->credential = dashboardStorage->credential; apiTokenStorage->url - = QUrl(settings().server.dashboard).resolved(*dashboardDto.userApiTokenUrl); + = dd->m_dashboardInfo->source.resolved(*dashboardDto.userApiTokenUrl); apiTokenStorage->csrfToken = dashboardDto.csrfToken.toUtf8(); const Dto::ApiTokenCreationRequestDto requestDto{*passwordStorage, "IdePlugin", apiTokenDescription(), 0}; @@ -598,6 +610,29 @@ static Group authorizationRecipe() return DoneResult::Success; }; + const auto onDashboardGroupSetup = [dashboardStorage] { + if (dd->m_dashboardInfo || dd->m_serverAccess != ServerAccess::WithAuthorization + || !dd->m_apiToken) { + return SetupResult::StopWithSuccess; // Unauthorized access should have collect dashboard before + } + dashboardStorage->credential = "AxToken " + *dd->m_apiToken; + dashboardStorage->url = QUrl(settings().server.dashboard); + return SetupResult::Continue; + }; + const auto onDeleteCredentialSetup = [dashboardStorage](CredentialQuery &credential) { + if (dashboardStorage->dtoData) { + dd->m_dashboardInfo = toDashboardInfo(*dashboardStorage); + return SetupResult::StopWithSuccess; + } + dd->m_apiToken = {}; + MessageManager::writeFlashing(QString("Axivion: %1") + .arg(Tr::tr("The stored ApiToken is not valid anymore, removing it."))); + credential.setOperation(CredentialOperation::Delete); + credential.setService(s_axivionKeychainService); + credential.setKey(credentialKey()); + return SetupResult::Continue; + }; + return { Group { unauthorizedDashboardStorage, @@ -611,7 +646,7 @@ static Group authorizationRecipe() Group { passwordStorage, dashboardStorage, - onGroupSetup(onDashboardGroupSetup), + onGroupSetup(onPasswordGroupSetup), Group { // GET DashboardInfoDto finishAllAndSuccess, dtoRecipe(dashboardStorage) @@ -622,6 +657,13 @@ static Group authorizationRecipe() dtoRecipe(apiTokenStorage), CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error) } + }, + Group { + finishAllAndSuccess, + dashboardStorage, + onGroupSetup(onDashboardGroupSetup), + dtoRecipe(dashboardStorage), + CredentialQueryTask(onDeleteCredentialSetup) } } }; @@ -633,10 +675,11 @@ static Group fetchDataRecipe(const QUrl &url, const std::function> dtoStorage; const auto onDtoSetup = [dtoStorage, url] { - if (!dd->m_apiToken) + if (!isServerAccessEstablished()) return SetupResult::StopWithError; - dtoStorage->credential = "AxToken " + *dd->m_apiToken; + if (dd->m_serverAccess == ServerAccess::WithAuthorization && dd->m_apiToken) + dtoStorage->credential = "AxToken " + *dd->m_apiToken; dtoStorage->url = url; return SetupResult::Continue; }; @@ -661,27 +704,22 @@ Group dashboardInfoRecipe(const DashboardInfoHandler &handler) { const auto onSetup = [handler] { if (dd->m_dashboardInfo) { - if (handler) - handler(*dd->m_dashboardInfo); + handler(*dd->m_dashboardInfo); return SetupResult::StopWithSuccess; } return SetupResult::Continue; }; - const auto onDone = [handler] { - if (handler) - handler(make_unexpected(QString("Error"))); // TODO: Collect error message in the storage. - }; - - const auto resultHandler = [handler](const Dto::DashboardInfoDto &data) { - dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard, data); - if (handler) + const auto onDone = [handler](DoneWith result) { + if (result == DoneWith::Success && dd->m_dashboardInfo) handler(*dd->m_dashboardInfo); + else + handler(make_unexpected(QString("Error"))); // TODO: Collect error message in the storage. }; const Group root { onGroupSetup(onSetup), // Stops if cache exists. - fetchDataRecipe(settings().server.dashboard, resultHandler), - onGroupDone(onDone, CallDoneIf::Error) + authorizationRecipe(), + onGroupDone(onDone) }; return root; } @@ -754,13 +792,13 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName) handleOpenedDocs(); }; - const QUrl url(settings().server.dashboard); - taskTree.setRecipe(fetchDataRecipe(url.resolved(*it), handler)); + taskTree.setRecipe( + fetchDataRecipe(m_dashboardInfo->source.resolved(*it), handler)); return SetupResult::Continue; }; const Group root { - dashboardInfoRecipe(), + authorizationRecipe(), TaskTreeTask(onTaskTreeSetup) }; m_taskTreeRunner.start(root); diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h index c60aad6e9e9..39783a956da 100644 --- a/src/plugins/axivion/axivionplugin.h +++ b/src/plugins/axivion/axivionplugin.h @@ -23,6 +23,8 @@ namespace Utils { class FilePath; } namespace Axivion::Internal { +constexpr int DefaultSearchLimit = 2048; + struct IssueListSearch { QString kind; @@ -33,7 +35,7 @@ struct IssueListSearch QString filter_path; QString sort; int offset = 0; - int limit = 150; + int limit = DefaultSearchLimit; bool computeTotalRowCount = false; QString toQuery() const; @@ -71,7 +73,7 @@ void fetchProjectInfo(const QString &projectName); std::optional projectInfo(); bool handleCertificateIssue(); -QIcon iconForIssue(const QString &prefix); +QIcon iconForIssue(const std::optional &issueKind); QString anyToSimpleString(const Dto::Any &any); void fetchIssueInfo(const QString &id); diff --git a/src/plugins/axivion/dynamiclistmodel.cpp b/src/plugins/axivion/dynamiclistmodel.cpp index 70abe087bdb..7c5262132b8 100644 --- a/src/plugins/axivion/dynamiclistmodel.cpp +++ b/src/plugins/axivion/dynamiclistmodel.cpp @@ -3,6 +3,7 @@ #include "dynamiclistmodel.h" +#include "axivionplugin.h" #include "axiviontr.h" #include @@ -10,8 +11,6 @@ namespace Axivion::Internal { -constexpr int pageSize = 150; - DynamicListModel::DynamicListModel(QObject *parent) : QAbstractItemModel(parent) { @@ -161,7 +160,7 @@ QModelIndex DynamicListModel::indexForItem(const ListItem *item) const void DynamicListModel::onNeedFetch(int row) { m_fetchStart = row; - m_fetchEnd = row + pageSize; + m_fetchEnd = row + DefaultSearchLimit; if (m_fetchStart < 0) return; m_fetchMoreTimer.start(); @@ -171,14 +170,14 @@ void DynamicListModel::fetchNow() { const int old = m_lastFetch; m_lastFetch = m_fetchStart; // we need the "original" fetch request to avoid endless loop - m_lastFetchEnd = m_fetchStart + pageSize; + m_lastFetchEnd = m_fetchStart + DefaultSearchLimit; if (old != -1) { const int diff = old - m_fetchStart; - if (0 < diff && diff < pageSize) { - m_fetchStart = qMax(old - pageSize, 0); - } else if (0 > diff && diff > -pageSize) { - m_fetchStart = old + pageSize; + if (0 < diff && diff < DefaultSearchLimit) { + m_fetchStart = qMax(old - DefaultSearchLimit, 0); + } else if (0 > diff && diff > - DefaultSearchLimit) { + m_fetchStart = old + DefaultSearchLimit; if (m_expectedRowCount && m_fetchStart > *m_expectedRowCount) m_fetchStart = *m_expectedRowCount; } @@ -186,7 +185,7 @@ void DynamicListModel::fetchNow() QTC_CHECK(m_expectedRowCount ? m_fetchStart <= *m_expectedRowCount : m_fetchStart >= m_children.size()); - emit fetchRequested(m_fetchStart, pageSize); + emit fetchRequested(m_fetchStart, DefaultSearchLimit); m_fetchStart = -1; m_fetchEnd = -1; } diff --git a/src/plugins/axivion/images/button-av.png b/src/plugins/axivion/images/button-AV.png similarity index 100% rename from src/plugins/axivion/images/button-av.png rename to src/plugins/axivion/images/button-AV.png diff --git a/src/plugins/axivion/images/button-av@2x.png b/src/plugins/axivion/images/button-AV@2x.png similarity index 100% rename from src/plugins/axivion/images/button-av@2x.png rename to src/plugins/axivion/images/button-AV@2x.png diff --git a/src/plugins/axivion/images/button-cl.png b/src/plugins/axivion/images/button-CL.png similarity index 100% rename from src/plugins/axivion/images/button-cl.png rename to src/plugins/axivion/images/button-CL.png diff --git a/src/plugins/axivion/images/button-cl@2x.png b/src/plugins/axivion/images/button-CL@2x.png similarity index 100% rename from src/plugins/axivion/images/button-cl@2x.png rename to src/plugins/axivion/images/button-CL@2x.png diff --git a/src/plugins/axivion/images/button-cy.png b/src/plugins/axivion/images/button-CY.png similarity index 100% rename from src/plugins/axivion/images/button-cy.png rename to src/plugins/axivion/images/button-CY.png diff --git a/src/plugins/axivion/images/button-cy@2x.png b/src/plugins/axivion/images/button-CY@2x.png similarity index 100% rename from src/plugins/axivion/images/button-cy@2x.png rename to src/plugins/axivion/images/button-CY@2x.png diff --git a/src/plugins/axivion/images/button-de.png b/src/plugins/axivion/images/button-DE.png similarity index 100% rename from src/plugins/axivion/images/button-de.png rename to src/plugins/axivion/images/button-DE.png diff --git a/src/plugins/axivion/images/button-de@2x.png b/src/plugins/axivion/images/button-DE@2x.png similarity index 100% rename from src/plugins/axivion/images/button-de@2x.png rename to src/plugins/axivion/images/button-DE@2x.png diff --git a/src/plugins/axivion/images/button-mv.png b/src/plugins/axivion/images/button-MV.png similarity index 100% rename from src/plugins/axivion/images/button-mv.png rename to src/plugins/axivion/images/button-MV.png diff --git a/src/plugins/axivion/images/button-mv@2x.png b/src/plugins/axivion/images/button-MV@2x.png similarity index 100% rename from src/plugins/axivion/images/button-mv@2x.png rename to src/plugins/axivion/images/button-MV@2x.png diff --git a/src/plugins/axivion/images/button-sv.png b/src/plugins/axivion/images/button-SV.png similarity index 100% rename from src/plugins/axivion/images/button-sv.png rename to src/plugins/axivion/images/button-SV.png diff --git a/src/plugins/axivion/images/button-sv@2x.png b/src/plugins/axivion/images/button-SV@2x.png similarity index 100% rename from src/plugins/axivion/images/button-sv@2x.png rename to src/plugins/axivion/images/button-SV@2x.png diff --git a/src/plugins/axivion/issueheaderview.cpp b/src/plugins/axivion/issueheaderview.cpp index 3f8efd86c6e..ce432eb1265 100644 --- a/src/plugins/axivion/issueheaderview.cpp +++ b/src/plugins/axivion/issueheaderview.cpp @@ -61,8 +61,10 @@ void IssueHeaderView::mousePressEvent(QMouseEvent *event) if (y > 1 && y < height() - 2) { // TODO improve const int pos = position.x(); const int logical = logicalIndexAt(pos); - const int end = sectionViewportPosition(logical) + sectionSize(logical); - const int start = end - ICON_SIZE - 2; + m_lastToggleLogicalPos = logical; + const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this); + const int end = sectionViewportPosition(logical) + sectionSize(logical) - margin; + const int start = end - ICON_SIZE; m_maybeToggleSort = start < pos && end > pos; } } @@ -79,7 +81,8 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event) const QPoint position = event->position().toPoint(); const int y = position.y(); const int logical = logicalIndexAt(position.x()); - if (logical > -1 && logical < m_sortableColumns.size()) { + if (logical == m_lastToggleLogicalPos + && logical > -1 && logical < m_sortableColumns.size()) { if (m_sortableColumns.at(logical)) { // ignore non-sortable if (y < height() / 2) // TODO improve onToggleSort(logical, SortOrder::Ascending); @@ -88,6 +91,7 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event) } } } + m_lastToggleLogicalPos = -1; QHeaderView::mouseReleaseEvent(event); } @@ -118,8 +122,10 @@ QSize IssueHeaderView::sectionSizeFromContents(int logicalIndex) const const QSize oldSize = QHeaderView::sectionSizeFromContents(logicalIndex); const QSize newSize = logicalIndex < m_columnWidths.size() ? QSize(qMax(m_columnWidths.at(logicalIndex), oldSize.width()), oldSize.height()) : oldSize; - // add icon size and margin (2) - return QSize{newSize.width() + ICON_SIZE + 2, qMax(newSize.height(), ICON_SIZE)}; + + const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this); + // add icon size and margin (default resize handle margin + 1) + return QSize{newSize.width() + ICON_SIZE + margin, qMax(newSize.height(), ICON_SIZE)}; } void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const @@ -132,9 +138,10 @@ void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int log if (!m_sortableColumns.at(logicalIndex)) return; + const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this); const QIcon icon = iconForSorted(logicalIndex == m_currentSortIndex ? m_currentSortOrder : SortOrder::None); const int offset = qMax((rect.height() - ICON_SIZE), 0) / 2; - const QRect iconRect(rect.left() + rect.width() - ICON_SIZE - 2, offset, ICON_SIZE, ICON_SIZE); + const QRect iconRect(rect.left() + rect.width() - ICON_SIZE - margin, offset, ICON_SIZE, ICON_SIZE); icon.paint(painter, iconRect); } diff --git a/src/plugins/axivion/issueheaderview.h b/src/plugins/axivion/issueheaderview.h index 1fbbd9b76fd..c1c8230440c 100644 --- a/src/plugins/axivion/issueheaderview.h +++ b/src/plugins/axivion/issueheaderview.h @@ -35,6 +35,7 @@ private: void onToggleSort(int index, SortOrder order); bool m_dragging = false; bool m_maybeToggleSort = false; + int m_lastToggleLogicalPos = -1; int m_currentSortIndex = -1; SortOrder m_currentSortOrder = SortOrder::None; QList m_sortableColumns; diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index 4036d34f6dc..747c30c2a57 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -59,9 +59,14 @@ clang::format::FormatStyle calculateQtcStyle() style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; style.AllowShortLoopsOnASingleLine = false; - style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; style.AlwaysBreakBeforeMultilineStrings = false; +#if LLVM_VERSION_MAJOR >= 19 + style.BreakAfterReturnType = FormatStyle::RTBS_None; + style.BreakTemplateDeclarations = FormatStyle::BTDS_Yes; +#else + style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; +#endif style.BinPackArguments = false; style.BinPackParameters = false; style.BraceWrapping.AfterClass = true; diff --git a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp index f105a9155c7..01c8b6cadb7 100644 --- a/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp +++ b/src/plugins/cmakeprojectmanager/fileapidataextractor.cpp @@ -301,8 +301,24 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t, continue; const FilePath buildDir = relativeLibs ? buildDirectory : currentBuildDir; - FilePath tmp = buildDir.resolvePath(part); + std::optional dllName; + if (buildDir.osType() == OsTypeWindows && (f.role == "libraries")) { + // Skip object libraries on Windows. This case can happen with static qml plugins + if (part.endsWith(".obj") || part.endsWith(".o")) + continue; + + // Only consider dlls, not static libraries + for (const QString &suffix : + {QString(".lib"), QString(".dll.a"), QString(".a")}) { + if (part.endsWith(suffix) && !dllName) + dllName = FilePath::fromUserInput( + part.chopped(suffix.length()).append(".dll")) + .fileName(); + } + } + + FilePath tmp = buildDir.resolvePath(part); if (f.role == "libraries") tmp = tmp.parentDir(); @@ -312,19 +328,20 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t, // "/usr/local/lib" since these are usually in the standard search // paths. There probably are more, but the naming schemes are arbitrary // so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR"). - if (buildDir.osType() == OsTypeWindows - || !isChildOf(tmp, - {"/lib", - "/lib64", - "/usr/lib", - "/usr/lib64", - "/usr/local/lib"})) { + if (buildDir.osType() != OsTypeWindows + && !isChildOf(tmp, + {"/lib", "/lib64", "/usr/lib", "/usr/lib64", "/usr/local/lib"})) librarySeachPaths.append(tmp); + + if (buildDir.osType() == OsTypeWindows && dllName) { + if (tmp.pathAppended(*dllName).exists()) + librarySeachPaths.append(tmp); + // Libraries often have their import libs in ../lib and the // actual dll files in ../bin on windows. Qt is one example of that. if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) { const FilePath path = tmp.parentDir().pathAppended("bin"); - if (path.isDir()) + if (path.isDir() && path.pathAppended(*dllName).exists()) librarySeachPaths.append(path); } } diff --git a/src/plugins/coreplugin/core.qrc b/src/plugins/coreplugin/core.qrc index ff855e3f4b8..239bd946121 100644 --- a/src/plugins/coreplugin/core.qrc +++ b/src/plugins/coreplugin/core.qrc @@ -1,5 +1,9 @@ + images/expandarrow.png + images/expandarrow@2x.png + images/search.png + images/search@2x.png images/settingscategory_core.png images/settingscategory_core@2x.png images/settingscategory_design.png diff --git a/src/plugins/coreplugin/images/expandarrow.png b/src/plugins/coreplugin/images/expandarrow.png new file mode 100644 index 00000000000..c463d0236d1 Binary files /dev/null and b/src/plugins/coreplugin/images/expandarrow.png differ diff --git a/src/plugins/coreplugin/images/expandarrow@2x.png b/src/plugins/coreplugin/images/expandarrow@2x.png new file mode 100644 index 00000000000..9b8341673fe Binary files /dev/null and b/src/plugins/coreplugin/images/expandarrow@2x.png differ diff --git a/src/plugins/coreplugin/images/search.png b/src/plugins/coreplugin/images/search.png new file mode 100644 index 00000000000..398e2fe2bb1 Binary files /dev/null and b/src/plugins/coreplugin/images/search.png differ diff --git a/src/plugins/coreplugin/images/search@2x.png b/src/plugins/coreplugin/images/search@2x.png new file mode 100644 index 00000000000..aaae492449a Binary files /dev/null and b/src/plugins/coreplugin/images/search@2x.png differ diff --git a/src/plugins/coreplugin/iwelcomepage.cpp b/src/plugins/coreplugin/iwelcomepage.cpp index 3eb63d80312..beef33f40d6 100644 --- a/src/plugins/coreplugin/iwelcomepage.cpp +++ b/src/plugins/coreplugin/iwelcomepage.cpp @@ -3,25 +3,8 @@ #include "iwelcomepage.h" -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -using namespace Utils; - namespace Core { -const char WITHACCENTCOLOR_PROPERTY_NAME[] = "_withAccentColor"; - static QList g_welcomePages; const QList IWelcomePage::allWelcomePages() @@ -39,171 +22,4 @@ IWelcomePage::~IWelcomePage() g_welcomePages.removeOne(this); } -QPalette WelcomePageFrame::buttonPalette(bool isActive, bool isCursorInside, bool forText) -{ - QPalette pal; - pal.setBrush(QPalette::Window, {}); - pal.setBrush(QPalette::WindowText, {}); - - Theme *theme = Utils::creatorTheme(); - if (isActive) { - if (forText) { - pal.setColor(QPalette::Window, theme->color(Theme::Welcome_ForegroundPrimaryColor)); - pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_BackgroundPrimaryColor)); - } else { - pal.setColor(QPalette::Window, theme->color(Theme::Welcome_AccentColor)); - pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_AccentColor)); - } - } else { - if (isCursorInside) { - if (forText) { - pal.setColor(QPalette::Window, theme->color(Theme::Welcome_HoverColor)); - pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_TextColor)); - } else { - pal.setColor(QPalette::Window, theme->color(Theme::Welcome_HoverColor)); - pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_ForegroundSecondaryColor)); - } - } else { - if (forText) { - pal.setColor(QPalette::Window, theme->color(Theme::Welcome_ForegroundPrimaryColor)); - pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_TextColor)); - } else { - pal.setColor(QPalette::Window, theme->color(Theme::Welcome_BackgroundPrimaryColor)); - pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_ForegroundSecondaryColor)); - } - } - } - return pal; -} - -WelcomePageFrame::WelcomePageFrame(QWidget *parent) - : QWidget(parent) -{ - setContentsMargins(1, 1, 1, 1); -} - -void WelcomePageFrame::paintEvent(QPaintEvent *event) -{ - QWidget::paintEvent(event); - QPainter p(this); - - qDrawPlainRect(&p, rect(), palette().color(QPalette::WindowText), 1); - - if (property(WITHACCENTCOLOR_PROPERTY_NAME).toBool()) { - const int accentRectWidth = 10; - const QRect accentRect = rect().adjusted(width() - accentRectWidth, 0, 0, 0); - p.fillRect(accentRect, creatorTheme()->color(Theme::Welcome_AccentColor)); - } -} - -class WelcomePageButtonPrivate -{ -public: - explicit WelcomePageButtonPrivate(WelcomePageButton *parent) - : q(parent) {} - bool isActive() const; - void doUpdate(bool cursorInside); - - WelcomePageButton *q; - QHBoxLayout *m_layout = nullptr; - QLabel *m_label = nullptr; - - std::function onClicked; - std::function activeChecker; -}; - -WelcomePageButton::WelcomePageButton(QWidget *parent) - : WelcomePageFrame(parent), d(new WelcomePageButtonPrivate(this)) -{ - setAutoFillBackground(true); - setPalette(buttonPalette(false, false, false)); - setContentsMargins(0, 1, 0, 1); - - d->m_label = new QLabel(this); - d->m_label->setPalette(buttonPalette(false, false, true)); - d->m_label->setAlignment(Qt::AlignCenter); - - d->m_layout = new QHBoxLayout; - d->m_layout->setSpacing(0); - d->m_layout->addWidget(d->m_label); - setSize(SizeLarge); - setLayout(d->m_layout); -} - -WelcomePageButton::~WelcomePageButton() -{ - delete d; -} - -void WelcomePageButton::mousePressEvent(QMouseEvent *) -{ - if (d->onClicked) - d->onClicked(); -} - -void WelcomePageButton::enterEvent(QEnterEvent *) -{ - d->doUpdate(true); -} - -void WelcomePageButton::leaveEvent(QEvent *) -{ - d->doUpdate(false); -} - -bool WelcomePageButtonPrivate::isActive() const -{ - return activeChecker && activeChecker(); -} - -void WelcomePageButtonPrivate::doUpdate(bool cursorInside) -{ - const bool active = isActive(); - q->setPalette(WelcomePageFrame::buttonPalette(active, cursorInside, false)); - const QPalette lpal = WelcomePageFrame::buttonPalette(active, cursorInside, true); - m_label->setPalette(lpal); - q->update(); -} - -void WelcomePageButton::setText(const QString &text) -{ - d->m_label->setText(text); -} - -void WelcomePageButton::setSize(Size size) -{ - const int hMargin = size == SizeSmall ? 12 : 26; - const int vMargin = size == SizeSmall ? 2 : 4; - d->m_layout->setContentsMargins(hMargin, vMargin, hMargin, vMargin); -} - -void WelcomePageButton::setWithAccentColor(bool withAccent) -{ - setProperty(WITHACCENTCOLOR_PROPERTY_NAME, withAccent); -} - -void WelcomePageButton::setActiveChecker(const std::function &value) -{ - d->activeChecker = value; -} - -void WelcomePageButton::recheckActive() -{ - bool isActive = d->isActive(); - d->doUpdate(isActive); -} - -void WelcomePageButton::click() -{ - if (d->onClicked) - d->onClicked(); -} - -void WelcomePageButton::setOnClicked(const std::function &value) -{ - d->onClicked = value; - if (d->isActive()) - click(); -} - } // namespace Core diff --git a/src/plugins/coreplugin/iwelcomepage.h b/src/plugins/coreplugin/iwelcomepage.h index 9a64dd3a701..1f59a791195 100644 --- a/src/plugins/coreplugin/iwelcomepage.h +++ b/src/plugins/coreplugin/iwelcomepage.h @@ -7,13 +7,10 @@ #include -#include #include -#include - QT_BEGIN_NAMESPACE -class QPixmap; +class QWidget; QT_END_NAMESPACE namespace Core { @@ -37,43 +34,4 @@ public: static const QList allWelcomePages(); }; -class WelcomePageButtonPrivate; - -class CORE_EXPORT WelcomePageFrame : public QWidget -{ -public: - WelcomePageFrame(QWidget *parent); - - void paintEvent(QPaintEvent *event) override; - - static QPalette buttonPalette(bool isActive, bool isCursorInside, bool forText); -}; - -class CORE_EXPORT WelcomePageButton : public WelcomePageFrame -{ -public: - enum Size { - SizeSmall, - SizeLarge, - }; - - explicit WelcomePageButton(QWidget *parent = nullptr); - ~WelcomePageButton() override; - - void mousePressEvent(QMouseEvent *) override; - void enterEvent(QEnterEvent *) override; - void leaveEvent(QEvent *) override; - - void setText(const QString &text); - void setSize(enum Size); - void setWithAccentColor(bool withAccent); - void setOnClicked(const std::function &value); - void setActiveChecker(const std::function &value); - void recheckActive(); - void click(); - -private: - WelcomePageButtonPrivate *d; -}; - } // Core diff --git a/src/plugins/coreplugin/loggingviewer.cpp b/src/plugins/coreplugin/loggingviewer.cpp index 5e83c76df3e..d6aad897148 100644 --- a/src/plugins/coreplugin/loggingviewer.cpp +++ b/src/plugins/coreplugin/loggingviewer.cpp @@ -559,10 +559,16 @@ private: const QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz"); - if (rowCount() >= 1000000) // limit log to 1000000 items - destroyItem(itemForIndex(index(0, 0))); + auto append = [this, timestamp, type, category, msg] { + if (rowCount() >= 1000000) // limit log to 1000000 items + destroyItem(itemForIndex(index(0, 0))); + appendItem(LogEntry{timestamp, messageTypeToString(type), category, msg}); + }; - appendItem(LogEntry{timestamp, messageTypeToString(type), category, msg}); + if (QThread::currentThread() != thread()) + QMetaObject::invokeMethod(this, append, Qt::QueuedConnection); + else + append(); } private: diff --git a/src/plugins/coreplugin/session.cpp b/src/plugins/coreplugin/session.cpp index c188f6812c3..921046ffed4 100644 --- a/src/plugins/coreplugin/session.cpp +++ b/src/plugins/coreplugin/session.cpp @@ -267,6 +267,11 @@ QDateTime SessionManager::lastActiveTime(const QString &session) return d->m_lastActiveTimes.value(session); } +int SessionManager::sessionsCount() +{ + return d->m_sessions.count(); +} + FilePath SessionManager::sessionNameToFileName(const QString &session) { return ICore::userResourcePath(session + ".qws"); diff --git a/src/plugins/coreplugin/session.h b/src/plugins/coreplugin/session.h index 962db5172b5..ef51228b018 100644 --- a/src/plugins/coreplugin/session.h +++ b/src/plugins/coreplugin/session.h @@ -34,6 +34,7 @@ public: static QStringList sessions(); static QDateTime sessionDateTime(const QString &session); static QDateTime lastActiveTime(const QString &session); + static int sessionsCount(); static bool createSession(const QString &session); diff --git a/src/plugins/coreplugin/welcomepagehelper.cpp b/src/plugins/coreplugin/welcomepagehelper.cpp index 44fc75270dd..636888d6b47 100644 --- a/src/plugins/coreplugin/welcomepagehelper.cpp +++ b/src/plugins/coreplugin/welcomepagehelper.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -26,36 +27,34 @@ #include + +QT_BEGIN_NAMESPACE +void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); +QT_END_NAMESPACE + using namespace Utils; namespace Core { using namespace WelcomePageHelpers; +using namespace StyleHelper::SpacingTokens; static QColor themeColor(Theme::Color role) { return creatorTheme()->color(role); } -static QFont sizedFont(int size, const QWidget *widget) -{ - QFont f = widget->font(); - f.setPixelSize(size); - return f; -} - namespace WelcomePageHelpers { -QWidget *panelBar(QWidget *parent) +void setBackgroundColor(QWidget *widget, Theme::Color colorRole) { - auto frame = new QWidget(parent); - frame->setAutoFillBackground(true); - frame->setMinimumWidth(WelcomePageHelpers::HSpacing); - QPalette pal; - pal.setBrush(QPalette::Window, {}); - pal.setColor(QPalette::Window, themeColor(Theme::Welcome_BackgroundPrimaryColor)); - frame->setPalette(pal); - return frame; + QPalette palette = creatorTheme()->palette(); + const QPalette::ColorRole role = QPalette::Window; + palette.setBrush(role, {}); + palette.setColor(role, creatorTheme()->color(colorRole)); + widget->setPalette(palette); + widget->setBackgroundRole(role); + widget->setAutoFillBackground(true); } void drawCardBackground(QPainter *painter, const QRectF &rect, @@ -77,30 +76,351 @@ void drawCardBackground(QPainter *painter, const QRectF &rect, painter->restore(); } +QWidget *createRule(Qt::Orientation orientation, QWidget *parent) +{ + auto rule = new QWidget(parent); + if (orientation == Qt::Horizontal) + rule->setFixedHeight(1); + else + rule->setFixedWidth(1); + setBackgroundColor(rule, Theme::Token_Stroke_Subtle); + return rule; +} + } // namespace WelcomePageHelpers -SearchBox::SearchBox(QWidget *parent) - : WelcomePageFrame(parent) +enum WidgetState { + WidgetStateDefault, + WidgetStateChecked, + WidgetStateHovered, +}; + +static const TextFormat &buttonTF(Button::Role role, WidgetState state) { - setAutoFillBackground(true); + using namespace WelcomePageHelpers; + static const TextFormat mediumPrimaryTF + {Theme::Token_Basic_White, StyleHelper::UiElement::UiElementButtonMedium, + Qt::AlignCenter | Qt::TextDontClip}; + static const TextFormat mediumSecondaryTF + {Theme::Token_Text_Default, mediumPrimaryTF.uiElement, mediumPrimaryTF.drawTextFlags}; + static const TextFormat smallPrimaryTF + {mediumPrimaryTF.themeColor, StyleHelper::UiElement::UiElementButtonSmall, + mediumPrimaryTF.drawTextFlags}; + static const TextFormat smallSecondaryTF + {mediumSecondaryTF.themeColor, smallPrimaryTF.uiElement, smallPrimaryTF.drawTextFlags}; + static const TextFormat smallListDefaultTF + {Theme::Token_Text_Default, StyleHelper::UiElement::UiElementIconStandard, + Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip}; + static const TextFormat smallListCheckedTF + {smallListDefaultTF.themeColor, StyleHelper::UiElement::UiElementIconActive, + smallListDefaultTF.drawTextFlags}; + static const TextFormat smallLinkDefaultTF + {Theme::Token_Text_Default, smallListDefaultTF.uiElement, smallListDefaultTF.drawTextFlags}; + static const TextFormat smallLinkHoveredTF + {Theme::Token_Accent_Default, smallListCheckedTF.uiElement, + smallLinkDefaultTF.drawTextFlags}; - m_lineEdit = new FancyLineEdit; - m_lineEdit->setFiltering(true); - m_lineEdit->setFrame(false); - m_lineEdit->setMinimumHeight(33); - m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); + switch (role) { + case Button::MediumPrimary: return mediumPrimaryTF; + case Button::MediumSecondary: return mediumSecondaryTF; + case Button::SmallPrimary: return smallPrimaryTF; + case Button::SmallSecondary: return smallSecondaryTF; + case Button::SmallList: return (state == WidgetStateDefault) ? smallListDefaultTF + : smallListCheckedTF; + case Button::SmallLink: return (state == WidgetStateDefault) ? smallLinkDefaultTF + : smallLinkHoveredTF; + } + return mediumPrimaryTF; +} - QPalette pal = buttonPalette(false, false, true); - // for the margins - pal.setColor(QPalette::Window, m_lineEdit->palette().color(QPalette::Base)); - // for macOS dark mode - pal.setColor(QPalette::WindowText, themeColor(Theme::Welcome_ForegroundPrimaryColor)); - pal.setColor(QPalette::Text, themeColor(Theme::Welcome_TextColor)); +Button::Button(const QString &text, Role role, QWidget *parent) + : QPushButton(text, parent) + , m_role(role) +{ + // Prevent QMacStyle::subElementRect(SE_PushButtonLayoutItem) from changing our geometry + setFlat(true); + + updateMargins(); + if (m_role == SmallList) + setCheckable(true); + else if (m_role == SmallLink) + setCursor(Qt::PointingHandCursor); +} + +QSize Button::minimumSizeHint() const +{ + const TextFormat &tf = buttonTF(m_role, WidgetStateHovered); + const QFontMetrics fm(tf.font()); + const QSize textS = fm.size(Qt::TextShowMnemonic, text()); + const QMargins margins = contentsMargins(); + return {margins.left() + textS.width() + margins.right(), + margins.top() + tf.lineHeight() + margins.bottom()}; +} + +void Button::paintEvent(QPaintEvent *event) +{ + // Without pixmap + // +----------------+----------------+----------------+ + // | |(VPadding[S|XS])| | + // | +----------------+----------------+ + // |(HPadding[S|XS])|