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])|
Error: %1
").arg(message);
m_errorLabel->setText(txt);
m_errorLabel->setVisible(true);
- searchBox->setVisible(false);
+ m_searcher->setVisible(false);
connect(m_errorLabel, &QLabel::linkActivated,
this, []() { QDesktopServices::openUrl(QUrl("https://marketplace.qt.io")); });
});
diff --git a/src/plugins/projectexplorer/projectwelcomepage.cpp b/src/plugins/projectexplorer/projectwelcomepage.cpp
index 45524bdb169..431fa8ee1d0 100644
--- a/src/plugins/projectexplorer/projectwelcomepage.cpp
+++ b/src/plugins/projectexplorer/projectwelcomepage.cpp
@@ -21,6 +21,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -28,9 +29,6 @@
#include
#include
-#include
-#include
-#include
#include
#include
#include
@@ -42,16 +40,52 @@
using namespace Core;
using namespace Core::WelcomePageHelpers;
using namespace Utils;
+using namespace Utils::StyleHelper::SpacingTokens;
-const int LINK_HEIGHT = 35;
-const int TEXT_OFFSET_HORIZONTAL = 36;
-const int SESSION_LINE_HEIGHT = 28;
-const int SESSION_ARROW_RECT_WIDTH = 24;
const char PROJECT_BASE_ID[] = "Welcome.OpenRecentProject";
namespace ProjectExplorer {
namespace Internal {
+constexpr TextFormat projectNameTF {Theme::Token_Accent_Default, StyleHelper::UiElementH5};
+constexpr TextFormat projectPathTF {Theme::Token_Text_Muted, StyleHelper::UiElementIconActive};
+constexpr TextFormat sessionNameTF {projectNameTF.themeColor, projectNameTF.uiElement};
+constexpr TextFormat sessionProjetNameTF {Theme::Token_Text_Default, projectNameTF.uiElement};
+constexpr TextFormat shortcutNumberTF {Theme::Token_Text_Default,
+ StyleHelper::UiElementCaptionStrong,
+ Qt::AlignCenter | Qt::TextDontClip};
+constexpr TextFormat actionTF {Theme::Token_Text_Default, StyleHelper::UiElementIconActive,
+ Qt::AlignCenter | Qt::TextDontClip};
+constexpr TextFormat actionDisabledTF {Theme::Token_Text_Subtle, actionTF.uiElement,
+ actionTF.drawTextFlags};
+constexpr int shortcutNumberWidth = 16;
+constexpr int actionSepWidth = 1;
+constexpr int sessionScrollBarGap = HPaddingXs;
+
+static int s(const int metric)
+{
+ constexpr int shrinkWhenAbove = 150; // Above this session count, increasingly reduce scale
+ constexpr qreal maxScale = 1.0; // Spacings as defined by design
+ constexpr qreal minScale = 0.2; // Maximum "condensed" layout
+
+ const int sessionsCount = SessionManager::sessionsCount();
+ const qreal scaling = sessionsCount < shrinkWhenAbove
+ ? maxScale
+ : qMax(minScale,
+ maxScale - (sessionsCount - shrinkWhenAbove) * 0.065);
+ return int(qMax(1.0, scaling * metric));
+}
+
+static int itemSpacing()
+{
+ return qMax(int(s(VGapL)), VGapS);
+}
+
+static bool withIcon()
+{
+ return s(100) > 60; // Hide icons if spacings are scaled to below 60%
+}
+
ProjectModel::ProjectModel(QObject *parent)
: QAbstractListModel(parent)
{
@@ -61,7 +95,7 @@ ProjectModel::ProjectModel(QObject *parent)
int ProjectModel::rowCount(const QModelIndex &) const
{
- return m_projects.count();
+ return int(m_projects.count());
}
QVariant ProjectModel::data(const QModelIndex &index, int role) const
@@ -190,21 +224,24 @@ static QColor themeColor(Theme::Color role)
return Utils::creatorTheme()->color(role);
}
-static QFont sizedFont(int size, const QWidget *widget,
- bool underline = false)
-{
- QFont f = widget->font();
- f.setPixelSize(size);
- f.setUnderline(underline);
- return f;
-}
-
-static QPixmap pixmap(const QString &id, const Theme::Color &color)
+static QPixmap pixmap(const QString &id, const Theme::Color color)
{
const QString fileName = QString(":/welcome/images/%1.png").arg(id);
return Icon({{FilePath::fromString(fileName), color}}, Icon::Tint).pixmap();
}
+static void drawBackgroundRect(QPainter *painter, const QRectF &rect, bool hovered)
+{
+ const QColor fill(themeColor(hovered ? Theme::Token_Foreground_Muted
+ : Theme::Token_Background_Muted));
+ const QPen pen(themeColor(hovered ? Theme::Token_Foreground_Muted
+ : Theme::Token_Stroke_Subtle));
+
+ const qreal rounding = s(defaultCardBackgroundRounding * 1000) / 1000.0;
+ const qreal saneRounding = rounding <= 2 ? 0 : rounding;
+ WelcomePageHelpers::drawCardBackground(painter, rect, fill, pen, saneRounding);
+}
+
class BaseDelegate : public QAbstractItemDelegate
{
protected:
@@ -244,6 +281,11 @@ protected:
class SessionDelegate : public BaseDelegate
{
protected:
+ bool expanded(const QModelIndex &idx) const
+ {
+ return m_expandedSessions.contains(idx.data(Qt::DisplayRole).toString());
+ }
+
QString entryType() override
{
return Tr::tr("session", "Appears in \"Open session \"");
@@ -252,150 +294,287 @@ protected:
{
// in expanded state bottom contains 'Clone', 'Rename', etc links, where the tool tip
// would be confusing
- const bool expanded = m_expandedSessions.contains(idx.data(Qt::DisplayRole).toString());
- return expanded ? itemRect.adjusted(0, 0, 0, -LINK_HEIGHT) : itemRect;
+ return expanded(idx) ? itemRect.adjusted(0, 0, 0, -actionButtonHeight()) : itemRect;
+ }
+
+ int shortcutRole() const override
+ {
+ return SessionModel::ShortcutRole;
+ }
+
+ static int actionButtonHeight()
+ {
+ return s(VPaddingXxs) + actionTF.lineHeight() + s(VPaddingXxs);
+ }
+
+ static const QPixmap &icon()
+ {
+ static const QPixmap icon = pixmap("session", Theme::Token_Text_Muted);
+ return icon;
}
- int shortcutRole() const override { return SessionModel::ShortcutRole; }
public:
- void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const final
+ void paint(QPainter *painter, const QStyleOptionViewItem &option,
+ const QModelIndex &idx) const final
{
- static const QPixmap sessionIcon = pixmap("session", Theme::Welcome_ForegroundSecondaryColor);
-
- const QRect rc = option.rect;
- const QString sessionName = idx.data(Qt::DisplayRole).toString();
+ // visible on withIcon() Gap + arrow visible on hover Extra margin right of project item
+ // | | |
+ // +----------+----------+ +--------+-------+ +----------+----------+
+ // | | | | | |
+ //
+ // +------------+--------+--------+------------+--------+-------------+--------+-------+------------+---------------------+ --+
+ // | | | |(VPaddingXs)| |(VPaddingXs) | | | | | |
+ // | | | +------------+ +-------------+ | | | | |
+ // |(HPaddingXs)||(HGapXs)| |(HGapXs)||(HGapXs)|| | | +-- Header
+ // | |(16x16) | +------------+ +-------------+ | | | | |
+ // | | | |(VPaddingXs)| |(VPaddingXs) | | | | | |
+ // |------------+--------+--------+------------+--------+-------------+--------+-------+ | | --+
+ // | +-- | (VPaddingXs) | | | |
+ // | | +------------------------------+(HPaddingXs)| | |
+ // | | | | | | |
+ // | | +------------------------------+ | | |
+ // | Per project in session --+ | (EXSPaddingGapS) | |(sessionScrollBarGap)| |
+ // | | +------------------------------+ | | |
+ // | | | | | | |
+ // | | +------------------------------+ | | +-- Expansion
+ // | +-- | (VPaddingXs) | | | |
+ // +----------------------------------------------+------------------------------------+------------+ | |
+ // | (VPaddingXs) | | |
+ // +----------------------------------------+--------------+----------------------------------------+ | |
+ // +-- | || | | |
+ // | +----------------------------------------+--------------+----------------------------------------+ | |
+ // | | (VPaddingXs) | | |
+ // | +------------------------------------------------------------------------------------------------+---------------------+ --+
+ // | | (VGapL) | +-- Gap between session items
+ // | +----------------------------------------------------------------------------------------------------------------------+ --+
+ // |
+ // \ session action "buttons" and dividers
+ // +-----------------------------------------------+--------+---------+--------+
+ // | (VGapXs) | | | |
+ // +----------------+-------------+----------------+ | | |
+ // |(EXSPaddingGapM)||(EXSPaddingGapM)|(HGapXs)||(HGapXs)|
+ // +----------------+-------------+----------------+ |(w:1) | |
+ // | (VGapXs) | | | |
+ // +-----------------------------------------------+--------+---------+--------+
+ //
+ // | |
+ // +-------------+-------------+
+ // |
+ // omitted after last button
const QPoint mousePos = option.widget->mapFromGlobal(QCursor::pos());
- //const bool hovered = option.state & QStyle::State_MouseOver;
const bool hovered = option.rect.contains(mousePos);
- const bool expanded = m_expandedSessions.contains(sessionName);
- painter->fillRect(rc, themeColor(Theme::Welcome_BackgroundSecondaryColor));
- painter->fillRect(rc.adjusted(0, 0, 0, -ItemGap),
- hovered ? hoverColor : backgroundPrimaryColor);
+ const bool expanded = this->expanded(idx);
- const int x = rc.x();
- const int x1 = x + TEXT_OFFSET_HORIZONTAL;
- const int y = rc.y();
- const int firstBase = y + 18;
+ const QRect bgR = option.rect.adjusted(0, 0, -sessionScrollBarGap, -itemSpacing());
+ const QRect hdR(bgR.topLeft(), QSize(bgR.width(), expanded ? headerHeight()
+ : bgR.height()));
- painter->drawPixmap(x + 11, y + 6, sessionIcon);
+ const QSize iconS = icon().deviceIndependentSize().toSize();
+ static const QPixmap arrow = Icon({{FilePath::fromString(":/core/images/expandarrow"),
+ Theme::Token_Text_Muted}}, Icon::Tint).pixmap();
+ const QSize arrowS = arrow.deviceIndependentSize().toSize();
+ const bool arrowVisible = hovered || expanded;
- if (hovered && !expanded) {
- const QRect arrowRect = rc.adjusted(rc.width() - SESSION_ARROW_RECT_WIDTH, 0, 0, 0);
- const bool arrowRectHovered = arrowRect.contains(mousePos);
- painter->fillRect(arrowRect.adjusted(0, 0, 0, -ItemGap),
- arrowRectHovered ? hoverColor : backgroundPrimaryColor);
+ const QString sessionName = idx.data(Qt::DisplayRole).toString();
+
+ const int x = bgR.x();
+ const int y = bgR.y();
+
+ const int numberX = x + s(HPaddingXs);
+ const int iconX = numberX + shortcutNumberWidth + s(HGapXs);
+ const int arrowX = bgR.right() - s(HPaddingXs) - arrowS.width();
+ const QRect arrowHoverR(arrowX - s(HGapXs) + 1, y,
+ s(HGapXs) + arrowS.width() + s(HPaddingXs), hdR.height());
+ const int textX = withIcon() ? iconX + iconS.width() + s(HGapXs) : iconX;
+
+ const int iconY = y + (hdR.height() - iconS.height()) / 2;
+ const int arrowY = y + (hdR.height() - arrowS.height()) / 2;
+
+ {
+ drawBackgroundRect(painter, bgR, hovered);
}
-
- if (hovered || expanded) {
- static const QPixmap arrowUp = pixmap("expandarrow",Theme::Welcome_ForegroundSecondaryColor);
- static const QPixmap arrowDown = QPixmap::fromImage(arrowUp.toImage().mirrored(false, true));
- painter->drawPixmap(rc.right() - 19, y + 6, expanded ? arrowDown : arrowUp);
- }
-
if (idx.row() < 9) {
- painter->setPen(foregroundSecondaryColor);
- painter->setFont(sizedFont(10, option.widget));
- painter->drawText(x + 3, firstBase, QString::number(idx.row() + 1));
+ painter->setPen(shortcutNumberTF.color());
+ painter->setFont(shortcutNumberTF.font());
+ const QRect numberR(numberX, y, shortcutNumberWidth, hdR.height());
+ const QString numberString = QString::number(idx.row() + 1);
+ painter->drawText(numberR, shortcutNumberTF.drawTextFlags, numberString);
+ }
+ if (withIcon()) {
+ painter->drawPixmap(iconX, iconY, icon());
+ }
+ {
+ const bool isLastSession = idx.data(SessionModel::LastSessionRole).toBool();
+ const bool isActiveSession = idx.data(SessionModel::ActiveSessionRole).toBool();
+ const bool isDefaultVirgin = SessionManager::isDefaultVirgin();
+
+ const int sessionNameWidth = hdR.right()
+ - (arrowVisible ? arrowHoverR.width(): s(HPaddingXs))
+ - textX;
+ const int sessionNameHeight = sessionNameTF.lineHeight();
+ const int sessionNameY = y + (hdR.height() - sessionNameHeight) / 2;
+ const QRect sessionNameR(textX, sessionNameY, sessionNameWidth, sessionNameHeight);
+
+ QString fullSessionName = sessionName;
+ if (isLastSession && isDefaultVirgin)
+ fullSessionName = Tr::tr("%1 (last session)").arg(fullSessionName);
+ if (isActiveSession && !isDefaultVirgin)
+ fullSessionName = Tr::tr("%1 (current session)").arg(fullSessionName);
+ const QRect switchR(x, y, hdR.width() - arrowHoverR.width(),
+ hdR.height() + s(VGapL));
+ const bool switchActive = switchR.contains(mousePos);
+ painter->setPen(sessionNameTF.color());
+ painter->setFont(sessionNameTF.font(switchActive));
+ const QString fullSessionNameElided = painter->fontMetrics().elidedText(
+ fullSessionName, Qt::ElideRight, sessionNameWidth);
+ painter->drawText(sessionNameR, sessionNameTF.drawTextFlags,
+ fullSessionNameElided);
+ if (switchActive)
+ m_activeSwitchToRect = switchR;
+ }
+ if (arrowVisible) {
+ if (arrowHoverR.adjusted(0, 0, 0, s(VGapL)).contains(mousePos)) {
+ m_activeExpandRect = arrowHoverR;
+ } else {
+ painter->save();
+ painter->setClipRect(arrowHoverR);
+ drawBackgroundRect(painter, bgR, false);
+ painter->restore();
+ }
+ static const QPixmap arrowDown =
+ QPixmap::fromImage(arrow.toImage().mirrored(false, true));
+ painter->drawPixmap(arrowX, arrowY, expanded ? arrowDown : arrow);
}
- const bool isLastSession = idx.data(SessionModel::LastSessionRole).toBool();
- const bool isActiveSession = idx.data(SessionModel::ActiveSessionRole).toBool();
- const bool isDefaultVirgin = SessionManager::isDefaultVirgin();
-
- QString fullSessionName = sessionName;
- if (isLastSession && isDefaultVirgin)
- fullSessionName = Tr::tr("%1 (last session)").arg(fullSessionName);
- if (isActiveSession && !isDefaultVirgin)
- fullSessionName = Tr::tr("%1 (current session)").arg(fullSessionName);
-
- const QRect switchRect = QRect(x, y, rc.width() - SESSION_ARROW_RECT_WIDTH, SESSION_LINE_HEIGHT);
- const bool switchActive = switchRect.contains(mousePos);
- const int textSpace = rc.width() - TEXT_OFFSET_HORIZONTAL - 6;
- const int sessionNameTextSpace =
- textSpace -(hovered || expanded ? SESSION_ARROW_RECT_WIDTH : 0);
- painter->setPen(linkColor);
- painter->setFont(sizedFont(13, option.widget, switchActive));
- const QString fullSessionNameElided = painter->fontMetrics().elidedText(
- fullSessionName, Qt::ElideRight, sessionNameTextSpace);
- painter->drawText(x1, firstBase, fullSessionNameElided);
- if (switchActive)
- m_activeSwitchToRect = switchRect;
-
+ int yy = hdR.bottom();
if (expanded) {
- painter->setPen(textColor);
- painter->setFont(sizedFont(12, option.widget));
+ const QFont projectNameFont = sessionProjetNameTF.font();
+ const QFontMetrics projectNameFm(projectNameFont);
+ const int projectNameLineHeight = sessionProjetNameTF.lineHeight();
+ const QFont projectPathFont = projectPathTF.font();
+ const QFontMetrics projectPathFm(projectPathFont);
+ const int projectPathLineHeight = projectPathTF.lineHeight();
+ const int textWidth = bgR.right() - s(HPaddingXs) - textX;
+
const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
- int yy = firstBase + SESSION_LINE_HEIGHT - 3;
- QFontMetrics fm(option.widget->font());
for (const FilePath &projectPath : projects) {
- // Project name.
- QString completeBase = projectPath.completeBaseName();
- painter->setPen(textColor);
- painter->drawText(x1, yy, fm.elidedText(completeBase, Qt::ElideMiddle, textSpace));
- yy += 18;
-
- // Project path.
- const QString displayPath =
- projectPath.osType() == OsTypeWindows ? projectPath.displayName()
- : projectPath.withTildeHomePath();
- painter->setPen(foregroundPrimaryColor);
- painter->drawText(x1, yy, fm.elidedText(displayPath, Qt::ElideMiddle, textSpace));
- yy += 22;
+ yy += s(VPaddingXs);
+ {
+ painter->setFont(projectNameFont);
+ painter->setPen(sessionProjetNameTF.color());
+ const QRect projectNameR(textX, yy, textWidth, projectNameLineHeight);
+ const QString projectNameElided =
+ projectNameFm.elidedText(projectPath.completeBaseName(), Qt::ElideMiddle,
+ textWidth);
+ painter->drawText(projectNameR, sessionProjetNameTF.drawTextFlags,
+ projectNameElided);
+ yy += projectNameLineHeight;
+ yy += s(ExPaddingGapS);
+ }
+ {
+ const QString displayPath =
+ projectPath.osType() == OsTypeWindows ? projectPath.displayName()
+ : projectPath.withTildeHomePath();
+ painter->setFont(projectPathFont);
+ painter->setPen(projectPathTF.color());
+ const QRect projectPathR(textX, yy, textWidth, projectPathLineHeight);
+ const QString projectPathElided =
+ projectPathFm.elidedText(displayPath, Qt::ElideMiddle, textWidth);
+ painter->drawText(projectPathR, projectPathTF.drawTextFlags,
+ projectPathElided);
+ yy += projectPathLineHeight;
+ }
+ yy += s(VPaddingXs);
}
+ yy += s(VGapXs);
- yy += 3;
- int xx = x1;
const QStringList actions = {
Tr::tr("Clone"),
Tr::tr("Rename"),
- Tr::tr("Delete")
+ Tr::tr("Delete"),
};
- for (int i = 0; i < 3; ++i) {
+
+ const QFont actionFont = actionTF.font();
+ const QFontMetrics actionFm(actionTF.font());
+
+ const int gapWidth = s(HGapXs) + actionSepWidth + s(HGapXs);
+ int actionsTotalWidth = gapWidth * int(actions.count() - 1); // dividers
+ const auto textWidths = Utils::transform(actions, [&] (const QString &action) {
+ const int width = actionFm.horizontalAdvance(action);
+ actionsTotalWidth += s(ExPaddingGapM) + width + s(ExPaddingGapM);
+ return width;
+ });
+
+ const int buttonHeight = this->actionButtonHeight();
+ int xx = (bgR.width() - actionsTotalWidth) / 2;
+ for (int i = 0; i < actions.count(); ++i) {
const QString &action = actions.at(i);
- const int ww = fm.horizontalAdvance(action);
- const int spacing = 7; // Between action link and separator line
- const QRect actionRect =
- QRect(xx, yy - 10, ww, 15).adjusted(-spacing, -spacing, spacing, spacing);
- const bool isForcedDisabled = (i != 0 && sessionName == "default");
- const bool isActive = actionRect.contains(mousePos) && !isForcedDisabled;
- painter->setPen(isForcedDisabled ? disabledLinkColor : linkColor);
- painter->setFont(sizedFont(12, option.widget, isActive));
- painter->drawText(xx, yy, action);
- if (i < 2) {
- xx += ww + 2 * spacing;
- int pp = xx - spacing;
- painter->setPen(textColor);
- painter->drawLine(pp, yy - 10, pp, yy);
+ const int ww = textWidths.at(i);
+ const QRect actionR(xx, yy, s(ExPaddingGapM) + ww + s(ExPaddingGapM), buttonHeight);
+ const bool isDisabled = i > 0 && SessionManager::isDefaultSession(sessionName);
+ const bool isActive = actionR.adjusted(-s(VPaddingXs), 0, s(VPaddingXs) + 1, 0)
+ .contains(mousePos) && !isDisabled;
+ if (isActive) {
+ WelcomePageHelpers::drawCardBackground(painter, actionR, Qt::transparent,
+ themeColor(Theme::Token_Text_Muted));
+ m_activeActionRects[i] = actionR;
}
- if (isActive)
- m_activeActionRects[i] = actionRect;
+ painter->setFont(actionFont);
+ painter->setPen((isDisabled ? actionDisabledTF : actionTF).color());
+ const QRect actionTextR = actionR.adjusted(0, 0, 0, -1);
+ painter->drawText(actionTextR, actionTF.drawTextFlags, action);
+ xx += actionR.width();
+ if (i < actions.count() - 1) {
+ const QRect dividerR(xx + s(HGapXs), yy, actionSepWidth, buttonHeight);
+ painter->fillRect(dividerR, themeColor(Theme::Token_Text_Muted));
+ }
+ xx += gapWidth;
}
+ yy += buttonHeight;
+ yy += s(VGapXs);
}
+ QTC_CHECK(option.rect.bottom() == yy + itemSpacing());
+ }
+
+ static int headerHeight()
+ {
+ const int paddingsHeight = s(VPaddingXs + VPaddingXs);
+ const int heightForSessionName = sessionNameTF.lineHeight() + paddingsHeight;
+ const int heightForIcon =
+ withIcon() ? int(icon().deviceIndependentSize().height()) + paddingsHeight : 0;
+ return qMax(heightForSessionName, heightForIcon);
}
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &idx) const final
{
- int h = SESSION_LINE_HEIGHT;
- QString sessionName = idx.data(Qt::DisplayRole).toString();
- if (m_expandedSessions.contains(sessionName)) {
+ int h = headerHeight();
+ if (expanded(idx)) {
+ const QString sessionName = idx.data(Qt::DisplayRole).toString();
const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
- h += projects.size() * 40 + LINK_HEIGHT - 6;
+ const int projectEntryHeight =
+ s(VPaddingXs)
+ + projectNameTF.lineHeight()
+ + s(ExPaddingGapS)
+ + projectPathTF.lineHeight()
+ + s(VPaddingXs);
+ h += projects.size() * projectEntryHeight
+ + s(VGapXs)
+ + actionButtonHeight()
+ + s(VGapXs);
}
- return QSize(380, h + ItemGap);
+ return QSize(-1, h + itemSpacing());
}
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
- const QStyleOptionViewItem &option, const QModelIndex &idx) final
+ const QStyleOptionViewItem &, const QModelIndex &idx) final
{
if (ev->type() == QEvent::MouseButtonRelease) {
const QMouseEvent *mouseEvent = static_cast(ev);
const Qt::MouseButtons button = mouseEvent->button();
const QPoint pos = static_cast(ev)->pos();
- const QRect rc(option.rect.right() - SESSION_ARROW_RECT_WIDTH, option.rect.top(),
- SESSION_ARROW_RECT_WIDTH, SESSION_LINE_HEIGHT);
const QString sessionName = idx.data(Qt::DisplayRole).toString();
- if (rc.contains(pos) || button == Qt::RightButton) {
+ if (m_activeExpandRect.contains(pos) || button == Qt::RightButton) {
// The expand/collapse "button".
if (m_expandedSessions.contains(sessionName))
m_expandedSessions.removeOne(sessionName);
@@ -421,23 +600,15 @@ public:
}
if (ev->type() == QEvent::MouseMove) {
emit model->layoutChanged({QPersistentModelIndex(idx)}); // Somewhat brutish.
- //update(option.rect);
return false;
}
return false;
}
private:
- const QColor hoverColor = themeColor(Theme::Welcome_HoverColor);
- const QColor textColor = themeColor(Theme::Welcome_TextColor);
- const QColor linkColor = themeColor(Theme::Welcome_LinkColor);
- const QColor disabledLinkColor = themeColor(Theme::Welcome_DisabledLinkColor);
- const QColor backgroundPrimaryColor = themeColor(Theme::Welcome_BackgroundPrimaryColor);
- const QColor foregroundPrimaryColor = themeColor(Theme::Welcome_ForegroundPrimaryColor);
- const QColor foregroundSecondaryColor = themeColor(Theme::Welcome_ForegroundSecondaryColor);
-
QStringList m_expandedSessions;
+ mutable QRect m_activeExpandRect;
mutable QRect m_activeSwitchToRect;
mutable QRect m_activeActionRects[3];
};
@@ -453,58 +624,89 @@ class ProjectDelegate : public BaseDelegate
public:
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const final
{
- QRect rc = option.rect;
+ // visible on withIcon() Extra margin right of project item
+ // | |
+ // +-------+-------+ +------+-----+
+ // | | | |
+ //
+ // +------------+--------+--------+------+--------+-------------+------------+------------+
+ // | | | | | | (VPaddingXs)| | |
+ // | | | | | +-------------+ | |
+ // | | | | | || | |
+ // | | | | | +-------------+ | |
+ // |(HPaddingXs)||(HGapXs)||(HGapXs)| (VGapXs) |(HPaddingXs)|(HPaddingXs)|
+ // | |(16x16) | | | +-------------+ | |
+ // | | | | | || | |
+ // | | | | | +-------------+ | |
+ // | | | | | | (VPaddingXs)| | |
+ // +------------+--------+--------+------+--------+-------------+------------+------------+ --+
+ // | (VGapL) | +-- Gap between project items
+ // +--------------------------------------------------------------------------------------+ --+
- const bool hovered = option.widget->isActiveWindow() && option.state & QStyle::State_MouseOver;
- const QRect bgRect = rc.adjusted(0, 0, -ItemGap, -ItemGap);
- painter->fillRect(rc, themeColor(Theme::Welcome_BackgroundSecondaryColor));
- painter->fillRect(bgRect, themeColor(hovered ? Theme::Welcome_HoverColor
- : Theme::Welcome_BackgroundPrimaryColor));
+ const bool hovered = option.widget->isActiveWindow()
+ && option.state & QStyle::State_MouseOver;
- const int x = rc.x();
- const int y = rc.y();
- const int firstBase = y + 18;
- const int secondBase = firstBase + 19;
+ const QRect bgR = option.rect.adjusted(0, 0, -s(HPaddingXs), -itemSpacing());
- static const QPixmap projectIcon =
- pixmap("project", Theme::Welcome_ForegroundSecondaryColor);
- painter->drawPixmap(x + 11, y + 6, projectIcon);
+ static const QPixmap icon = pixmap("project", Theme::Token_Text_Muted);
+ const QSize iconS = icon.deviceIndependentSize().toSize();
- QString projectName = idx.data(Qt::DisplayRole).toString();
- FilePath projectPath = FilePath::fromVariant(idx.data(ProjectModel::FilePathRole));
+ const int x = bgR.x();
+ const int numberX = x + s(HPaddingXs);
+ const int iconX = numberX + shortcutNumberWidth + s(HGapXs);
+ const int iconWidth = iconS.width();
+ const int textX = withIcon() ? iconX + iconWidth + s(HGapXs) : iconX;
+ const int textWidth = bgR.width() - s(HPaddingXs) - textX;
- painter->setPen(themeColor(Theme::Welcome_ForegroundSecondaryColor));
- painter->setFont(sizedFont(10, option.widget));
+ const int y = bgR.y();
+ const int iconHeight = iconS.height();
+ const int iconY = y + (bgR.height() - iconHeight) / 2;
+ const int projectNameY = y + s(VPaddingXs);
+ const QRect projectNameR(textX, projectNameY, textWidth, projectNameTF.lineHeight());
+ const int projectPathY = projectNameY + projectNameR.height() + s(VGapXs);
+ const QRect projectPathR(textX, projectPathY, textWidth, projectPathTF.lineHeight());
- if (idx.row() < 9)
- painter->drawText(x + 3, firstBase, QString::number(idx.row() + 1));
+ QTC_CHECK(option.rect.bottom() == projectPathR.bottom() + s(VPaddingXs) + itemSpacing());
- const int textSpace = rc.width() - TEXT_OFFSET_HORIZONTAL - ItemGap - 6;
-
- painter->setPen(themeColor(Theme::Welcome_LinkColor));
- painter->setFont(sizedFont(13, option.widget, hovered));
- const QString projectNameElided =
- painter->fontMetrics().elidedText(projectName, Qt::ElideRight, textSpace);
- painter->drawText(x + TEXT_OFFSET_HORIZONTAL, firstBase, projectNameElided);
-
- painter->setPen(themeColor(Theme::Welcome_ForegroundPrimaryColor));
- painter->setFont(sizedFont(13, option.widget));
- const QString displayPath =
- projectPath.osType() == OsTypeWindows ? projectPath.displayName()
- : projectPath.withTildeHomePath();
- const QString displayPathElided =
- painter->fontMetrics().elidedText(displayPath, Qt::ElideMiddle, textSpace);
- painter->drawText(x + TEXT_OFFSET_HORIZONTAL, secondBase, displayPathElided);
+ {
+ drawBackgroundRect(painter, bgR, hovered);
+ }
+ if (idx.row() < 9) {
+ painter->setPen(shortcutNumberTF.color());
+ painter->setFont(shortcutNumberTF.font());
+ const QRect numberR(numberX, y, shortcutNumberWidth, bgR.height());
+ const QString numberString = QString::number(idx.row() + 1);
+ painter->drawText(numberR, shortcutNumberTF.drawTextFlags, numberString);
+ }
+ if (withIcon()) {
+ painter->drawPixmap(iconX, iconY, icon);
+ }
+ {
+ painter->setPen(projectNameTF.color());
+ painter->setFont(projectNameTF.font(hovered));
+ const QString projectName = idx.data(Qt::DisplayRole).toString();
+ const QString projectNameElided =
+ painter->fontMetrics().elidedText(projectName, Qt::ElideRight, textWidth);
+ painter->drawText(projectNameR, projectNameTF.drawTextFlags, projectNameElided);
+ }
+ {
+ painter->setPen(projectPathTF.color());
+ painter->setFont(projectPathTF.font());
+ const FilePath projectPath =
+ FilePath::fromVariant(idx.data(ProjectModel::FilePathRole));
+ const QString displayPath =
+ projectPath.osType() == OsTypeWindows ? projectPath.displayName()
+ : projectPath.withTildeHomePath();
+ const QString displayPathElided =
+ painter->fontMetrics().elidedText(displayPath, Qt::ElideMiddle, textWidth);
+ painter->drawText(projectPathR, projectPathTF.drawTextFlags, displayPathElided);
+ }
}
- QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &idx) const final
+ QSize sizeHint([[maybe_unused]] const QStyleOptionViewItem &option,
+ [[maybe_unused]] const QModelIndex &idx) const override
{
- QString projectName = idx.data(Qt::DisplayRole).toString();
- QString projectPath = idx.data(ProjectModel::FilePathRole).toString();
- QFontMetrics fm(sizedFont(13, option.widget));
- int width = std::max(fm.horizontalAdvance(projectName),
- fm.horizontalAdvance(projectPath)) + TEXT_OFFSET_HORIZONTAL;
- return QSize(width, 47 + ItemGap);
+ return QSize(-1, itemHeight() + itemSpacing());
}
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
@@ -541,6 +743,18 @@ public:
}
return false;
}
+
+private:
+ static int itemHeight()
+ {
+ const int height =
+ s(VPaddingXs)
+ + projectNameTF.lineHeight()
+ + s(VGapXs)
+ + projectPathTF.lineHeight()
+ + s(VPaddingXs);
+ return height;
+ }
};
class TreeView : public QTreeView
@@ -559,10 +773,7 @@ public:
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
setFocusPolicy(Qt::NoFocus);
-
- QPalette pal;
- pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundSecondaryColor));
- viewport()->setPalette(pal);
+ setBackgroundColor(viewport(), Theme::Token_Background_Default);
}
};
@@ -577,54 +788,63 @@ public:
if (!projectWelcomePage->m_projectModel)
projectWelcomePage->m_projectModel = new ProjectModel(this);
- auto manageSessionsButton = new WelcomePageButton(this);
- manageSessionsButton->setText(Tr::tr("Manage..."));
- manageSessionsButton->setWithAccentColor(true);
- manageSessionsButton->setOnClicked([] { SessionManager::showSessionManager(); });
+ using namespace Layouting;
- auto sessionsLabel = new QLabel(this);
- sessionsLabel->setText(Tr::tr("Sessions"));
+ auto sessions = new QWidget;
+ {
+ auto sessionsLabel = new Label(Tr::tr("Sessions"), Label::Primary);
+ auto manageSessionsButton = new Button(Tr::tr("Manage..."), Button::MediumSecondary);
+ auto sessionsList = new TreeView(this, "Sessions");
+ sessionsList->setModel(projectWelcomePage->m_sessionModel);
+ sessionsList->header()->setSectionHidden(1, true); // The "last modified" column.
+ sessionsList->setItemDelegate(&m_sessionDelegate);
+ sessionsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ QSizePolicy sessionsSp(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ sessionsSp.setHorizontalStretch(3);
+ sessions->setSizePolicy(sessionsSp);
+ Column {
+ Row {
+ sessionsLabel,
+ st,
+ manageSessionsButton,
+ customMargin({HPaddingS, 0, sessionScrollBarGap, 0}),
+ },
+ sessionsList,
+ spacing(ExPaddingGapL),
+ customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}),
+ }.attachTo(sessions);
+ connect(manageSessionsButton, &Button::clicked,
+ this, &SessionManager::showSessionManager);
+ }
- auto recentProjectsLabel = new QLabel(this);
- recentProjectsLabel->setText(Tr::tr("Projects"));
+ auto projects = new QWidget;
+ {
+ auto projectsLabel = new Label(Tr::tr("Projects"), Label::Primary);
+ auto projectsList = new TreeView(this, "Recent Projects");
+ projectsList->setUniformRowHeights(true);
+ projectsList->setModel(projectWelcomePage->m_projectModel);
+ projectsList->setItemDelegate(&m_projectDelegate);
+ projectsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
+ QSizePolicy projectsSP(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ projectsSP.setHorizontalStretch(5);
+ projects->setSizePolicy(projectsSP);
+ Column {
+ Row {
+ projectsLabel,
+ customMargin({HPaddingS, 0, 0, 0}),
+ },
+ projectsList,
+ spacing(ExPaddingGapL),
+ customMargin({ExVPaddingGapXl - sessionScrollBarGap, ExVPaddingGapXl, 0, 0}),
+ }.attachTo(projects);
+ }
- auto sessionsList = new TreeView(this, "Sessions");
- sessionsList->setModel(projectWelcomePage->m_sessionModel);
- sessionsList->header()->setSectionHidden(1, true); // The "last modified" column.
- sessionsList->setItemDelegate(&m_sessionDelegate);
- sessionsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
-
- auto projectsList = new TreeView(this, "Recent Projects");
- projectsList->setUniformRowHeights(true);
- projectsList->setModel(projectWelcomePage->m_projectModel);
- projectsList->setItemDelegate(&m_projectDelegate);
- projectsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
-
- auto sessionHeader = panelBar(this);
- auto hbox11 = new QHBoxLayout(sessionHeader);
- hbox11->setContentsMargins(12, 0, 0, 0);
- hbox11->addWidget(sessionsLabel);
- hbox11->addStretch(1);
- hbox11->addWidget(manageSessionsButton);
-
- auto projectsHeader = panelBar(this);
- auto hbox21 = new QHBoxLayout(projectsHeader);
- hbox21->setContentsMargins(hbox11->contentsMargins());
- hbox21->addWidget(recentProjectsLabel);
-
- auto grid = new QGridLayout(this);
- grid->setContentsMargins(0, 0, 0, ItemGap);
- grid->setHorizontalSpacing(0);
- grid->setVerticalSpacing(ItemGap);
- grid->addWidget(panelBar(this), 0, 0);
- grid->addWidget(sessionHeader, 0, 1);
- grid->addWidget(sessionsList, 1, 1);
- grid->addWidget(panelBar(this), 0, 2);
- grid->setColumnStretch(1, 9);
- grid->setColumnMinimumWidth(1, 200);
- grid->addWidget(projectsHeader, 0, 3);
- grid->addWidget(projectsList, 1, 3);
- grid->setColumnStretch(3, 20);
+ Row {
+ sessions,
+ projects,
+ spacing(0),
+ noMargin(),
+ }.attachTo(this);
}
SessionDelegate m_sessionDelegate;
diff --git a/src/plugins/python/pythonkitaspect.cpp b/src/plugins/python/pythonkitaspect.cpp
index b3c17977f85..0be12cead0a 100644
--- a/src/plugins/python/pythonkitaspect.cpp
+++ b/src/plugins/python/pythonkitaspect.cpp
@@ -117,11 +117,11 @@ public:
if (!pipIsUsable(path)) {
result << BuildSystemTask(
Task::Warning,
- Tr::tr("Python \"%1\" does not contain a usable pip. Pip is used to install "
- "python "
- "packages from the Python Package Index, like PySide and the python "
- "language server. If you want to use any of that functionality "
- "ensure pip is installed for that python.")
+ Tr::tr("Python \"%1\" does not contain a usable pip. pip is needed to install "
+ "Python "
+ "packages from the Python Package Index, like PySide and the Python "
+ "language server. To use any of that functionality "
+ "ensure that pip is installed for that Python.")
.arg(path.toUserOutput()));
}
if (!venvIsUsable(path)) {
@@ -130,7 +130,7 @@ public:
Tr::tr(
"Python \"%1\" does not contain a usable venv. venv is the recommended way "
"to isolate a development environment for a project from the globally "
- "installed python.")
+ "installed Python.")
.arg(path.toUserOutput()));
}
}
diff --git a/src/plugins/python/pythonproject.cpp b/src/plugins/python/pythonproject.cpp
index 129782773d3..db0831005f1 100644
--- a/src/plugins/python/pythonproject.cpp
+++ b/src/plugins/python/pythonproject.cpp
@@ -35,7 +35,7 @@ Tasks PythonProject::projectIssues(const Kit *k) const
return {};
return {
BuildSystemTask{Task::Error,
- Tr::tr("No python interpreter set for kit \"%1\"").arg(k->displayName())}};
+ Tr::tr("No Python interpreter set for kit \"%1\"").arg(k->displayName())}};
}
PythonProjectNode::PythonProjectNode(const FilePath &path)
diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp
index 6ba748b0c1d..bfa504ed60a 100644
--- a/src/plugins/qmakeprojectmanager/makefileparse.cpp
+++ b/src/plugins/qmakeprojectmanager/makefileparse.cpp
@@ -56,6 +56,7 @@ void MakeFileParse::parseArgs(const QString &args, const QString &project,
static const QRegularExpression regExp(QLatin1String("^([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)$"));
bool after = false;
bool ignoreNext = false;
+ bool nextIsQtConfArg = false;
m_unparsedArguments = args;
ProcessArgs::ArgIterator ait(&m_unparsedArguments);
while (ait.next()) {
@@ -63,11 +64,18 @@ void MakeFileParse::parseArgs(const QString &args, const QString &project,
// Ignoring
ignoreNext = false;
ait.deleteArg();
+ } else if (nextIsQtConfArg) {
+ nextIsQtConfArg = false;
+ m_qtConfFile = FilePath::fromUserInput(ait.value());
+ ait.deleteArg();
} else if (ait.value() == project) {
ait.deleteArg();
} else if (ait.value() == QLatin1String("-after")) {
after = true;
ait.deleteArg();
+ } else if (ait.value() == "-qtconf") {
+ nextIsQtConfArg = true;
+ ait.deleteArg();
} else if (ait.value().contains(QLatin1Char('='))) {
const QRegularExpressionMatch match = regExp.match(ait.value());
if (match.hasMatch()) {
diff --git a/src/plugins/qmakeprojectmanager/makefileparse.h b/src/plugins/qmakeprojectmanager/makefileparse.h
index e9e5be0812c..18be3f801a4 100644
--- a/src/plugins/qmakeprojectmanager/makefileparse.h
+++ b/src/plugins/qmakeprojectmanager/makefileparse.h
@@ -30,6 +30,7 @@ public:
MakefileState makeFileState() const;
Utils::FilePath qmakePath() const;
Utils::FilePath srcProFile() const;
+ Utils::FilePath qtConfPath() const { return m_qtConfFile;}
QMakeStepConfig config() const;
QString unparsedArguments() const;
@@ -59,6 +60,7 @@ private:
MakefileState m_state;
Utils::FilePath m_qmakePath;
Utils::FilePath m_srcProFile;
+ Utils::FilePath m_qtConfFile;
QmakeBuildConfig m_qmakeBuildConfig;
QMakeStepConfig m_config;
diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
index 55b31efae08..fb82f839d8f 100644
--- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp
@@ -490,7 +490,9 @@ QmakeBuildConfiguration::MakefileState QmakeBuildConfiguration::compareToImportF
return MakefileIncompatible;
}
- if (version->qmakeFilePath() != parse.qmakePath()) {
+ if (version->qmakeFilePath() != parse.qmakePath()
+ && (parse.qtConfPath().isEmpty() // QTCREATORBUG-30354
+ || version->qmakeFilePath().parentDir() != parse.qtConfPath().parentDir())) {
qCDebug(logs) << "**Different Qt versions, buildconfiguration:" << version->qmakeFilePath()
<< " Makefile:" << parse.qmakePath();
return MakefileForWrongProject;
diff --git a/src/plugins/qtsupport/exampleslistmodel.cpp b/src/plugins/qtsupport/exampleslistmodel.cpp
index 18aa6cc3811..e81535acb42 100644
--- a/src/plugins/qtsupport/exampleslistmodel.cpp
+++ b/src/plugins/qtsupport/exampleslistmodel.cpp
@@ -250,7 +250,7 @@ static QPixmap fetchPixmapAndUpdatePixmapCache(const QString &url)
const int dpr = qApp->devicePixelRatio();
// boundedTo -> don't scale thumbnails up
const QSize scaledSize =
- WelcomePageHelpers::GridItemImageSize.boundedTo(img.size()) * dpr;
+ WelcomePageHelpers::WelcomeThumbnailSize.boundedTo(img.size()) * dpr;
const QImage scaled = img.isNull() ? img
: img.scaled(scaledSize,
Qt::KeepAspectRatio,
diff --git a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
index f5b5c0ac49f..f02a433fbe7 100644
--- a/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
+++ b/src/plugins/qtsupport/gettingstartedwelcomepage.cpp
@@ -21,13 +21,13 @@
#include
#include
+#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
@@ -249,8 +249,9 @@ protected:
painter->setFont(option.font);
painter->setCompositionMode(QPainter::CompositionMode_Difference);
painter->setPen(Qt::white);
- painter->drawText(currentPixmapRect.translated(0, -WelcomePageHelpers::ItemGap),
- exampleItem->videoLength, Qt::AlignBottom | Qt::AlignHCenter);
+ painter->drawText(
+ currentPixmapRect.translated(0, -StyleHelper::SpacingTokens::VPaddingXxs),
+ exampleItem->videoLength, Qt::AlignBottom | Qt::AlignHCenter);
painter->restore();
static const QPixmap playOverlay =
StyleHelper::dpiSpecificImageFile(":/qtsupport/images/icons/playoverlay.png");
@@ -274,29 +275,25 @@ public:
{
m_exampleDelegate.setShowExamples(isExamples);
- auto searchBox = new SearchBox(this);
- m_searcher = searchBox->m_lineEdit;
+ using namespace StyleHelper::SpacingTokens;
- auto grid = new QGridLayout(this);
- grid->setContentsMargins(0, 0, 0, WelcomePageHelpers::ItemGap);
- grid->setHorizontalSpacing(0);
- grid->setVerticalSpacing(WelcomePageHelpers::ItemGap);
+ using namespace Layouting;
+ Row titleRow {
+ customMargin({0, 0, ExVPaddingGapXl, 0}),
+ spacing(ExVPaddingGapXl),
+ };
- auto searchBar = WelcomePageHelpers::panelBar(this);
- auto hbox = new QHBoxLayout(searchBar);
- hbox->setContentsMargins(0, 0, 0, 0);
+ m_searcher = new SearchBox;
if (m_isExamples) {
m_searcher->setPlaceholderText(Tr::tr("Search in Examples..."));
- auto exampleSetSelector = new QComboBox(this);
- QPalette pal = exampleSetSelector->palette();
- // for macOS dark mode
- pal.setColor(QPalette::Text, Utils::creatorTheme()->color(Theme::Welcome_TextColor));
- exampleSetSelector->setPalette(pal);
- exampleSetSelector->setMinimumWidth(Core::WelcomePageHelpers::GridItemWidth);
- exampleSetSelector->setMaximumWidth(Core::WelcomePageHelpers::GridItemWidth);
+ auto exampleSetSelector = new ComboBox;
+ exampleSetSelector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ exampleSetSelector->setMinimumWidth(ListItemDelegate::itemSize().width()
+ - ExVPaddingGapXl);
exampleSetSelector->setModel(s_exampleSetModel);
exampleSetSelector->setCurrentIndex(s_exampleSetModel->selectedExampleSet());
+ titleRow.addItem(exampleSetSelector);
connect(exampleSetSelector,
&QComboBox::activated,
s_exampleSetModel,
@@ -305,23 +302,23 @@ public:
&ExampleSetModel::selectedExampleSetChanged,
exampleSetSelector,
&QComboBox::setCurrentIndex);
-
- hbox->setSpacing(Core::WelcomePageHelpers::HSpacing);
- hbox->addWidget(exampleSetSelector);
} else {
m_searcher->setPlaceholderText(Tr::tr("Search in Tutorials..."));
}
- hbox->addWidget(searchBox);
- grid->addWidget(WelcomePageHelpers::panelBar(this), 0, 0);
- grid->addWidget(searchBar, 0, 1);
- grid->addWidget(WelcomePageHelpers::panelBar(this), 0, 2);
+ titleRow.addItem(m_searcher);
- auto gridView = new SectionedGridView(this);
+ auto gridView = new SectionedGridView;
m_viewController
= new ExamplesViewController(s_exampleSetModel, gridView, m_searcher, isExamples, this);
gridView->setItemDelegate(&m_exampleDelegate);
- grid->addWidget(gridView, 1, 1, 1, 2);
+
+ Column {
+ titleRow,
+ gridView,
+ spacing(ExVPaddingGapXl),
+ customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}),
+ }.attachTo(this);
connect(&m_exampleDelegate, &ExampleDelegate::tagClicked,
this, &ExamplesPageWidget::onTagClicked);
diff --git a/src/plugins/remotelinux/genericdeploystep.cpp b/src/plugins/remotelinux/genericdeploystep.cpp
index 95a0a8360ff..3d1f0e5e3ab 100644
--- a/src/plugins/remotelinux/genericdeploystep.cpp
+++ b/src/plugins/remotelinux/genericdeploystep.cpp
@@ -155,11 +155,16 @@ GroupItem GenericDeployStep::transferTask(const Storage &storag
}
}
if (!m_emittedDowngradeWarning && transferMethod != preferredTransferMethod) {
- addWarningMessage(Tr::tr("Transfer method was downgraded from \"%1\" to \"%2\". If "
- "this is unexpected, please re-test device \"%3\".")
- .arg(FileTransfer::transferMethodName(preferredTransferMethod),
- FileTransfer::transferMethodName(transferMethod),
- deviceConfiguration()->displayName()));
+ const QString message
+ = Tr::tr("Transfer method was downgraded from \"%1\" to \"%2\". If "
+ "this is unexpected, please re-test device \"%3\".")
+ .arg(FileTransfer::transferMethodName(preferredTransferMethod),
+ FileTransfer::transferMethodName(transferMethod),
+ deviceConfiguration()->displayName());
+ if (transferMethod == FileTransferMethod::GenericCopy)
+ addWarningMessage(message);
+ else
+ addProgressMessage(message);
m_emittedDowngradeWarning = true;
}
transfer.setTransferMethod(transferMethod);
diff --git a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
index 09895a7ad76..8afafabdeda 100644
--- a/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
+++ b/src/plugins/remotelinux/genericlinuxdeviceconfigurationwidget.cpp
@@ -98,7 +98,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
const int dmCount = dm->deviceCount();
for (int i = 0; i < dmCount; ++i) {
IDevice::ConstPtr dev = dm->deviceAt(i);
- m_linkDeviceComboBox->addItem(dev->displayName(), dev->id().toSetting());
+ if (dev->id() != device->id())
+ m_linkDeviceComboBox->addItem(dev->displayName(), dev->id().toSetting());
}
auto sshPortLabel = new QLabel(Tr::tr("&SSH port:"));
@@ -307,13 +308,18 @@ void GenericLinuxDeviceConfigurationWidget::initGui()
Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
auto dm = DeviceManager::instance();
int found = -1;
+ int minus = 0;
for (int i = 0, n = dm->deviceCount(); i < n; ++i) {
- if (dm->deviceAt(i)->id() == linkDeviceId) {
+ const auto otherId = dm->deviceAt(i)->id();
+ if (otherId == linkDeviceId) {
found = i;
break;
+ } else if (otherId == device()->id()) {
+ // Since we ourselves do not appear in the combo box, we need to adjust the index.
+ minus = 1;
}
}
- m_linkDeviceComboBox->setCurrentIndex(found + 1); // There's the "Direct" entry first.
+ m_linkDeviceComboBox->setCurrentIndex(found + 1 - minus); // There's the "Direct" entry first.
m_hostLineEdit->setText(sshParams.host());
m_sshPortSpinBox->setValue(sshParams.port());
diff --git a/src/plugins/texteditor/fontsettings.cpp b/src/plugins/texteditor/fontsettings.cpp
index 5b79514d92b..e74a37c0d37 100644
--- a/src/plugins/texteditor/fontsettings.cpp
+++ b/src/plugins/texteditor/fontsettings.cpp
@@ -356,13 +356,14 @@ QFont FontSettings::font() const
{
QFont f(family(), fontSize());
f.setStyleStrategy(m_antialias ? QFont::PreferAntialias : QFont::NoAntialias);
+ f.setWeight(fontNormalWeight());
return f;
}
-int FontSettings::fontNormalWeight() const
+QFont::Weight FontSettings::fontNormalWeight() const
{
// TODO: Fix this when we upgrade "Source Code Pro" to a version greater than 2.0.30
- int weight = QFont::Normal;
+ QFont::Weight weight = QFont::Normal;
if (Utils::HostOsInfo::isMacHost() && m_family == g_sourceCodePro)
weight = QFont::Medium;
return weight;
diff --git a/src/plugins/texteditor/fontsettings.h b/src/plugins/texteditor/fontsettings.h
index 338948d9e96..2cf23ec5a3c 100644
--- a/src/plugins/texteditor/fontsettings.h
+++ b/src/plugins/texteditor/fontsettings.h
@@ -61,7 +61,7 @@ public:
void setRelativeLineSpacing(int relativeLineSpacing);
QFont font() const;
- int fontNormalWeight() const;
+ QFont::Weight fontNormalWeight() const;
bool antialias() const;
void setAntialias(bool antialias);
diff --git a/src/plugins/texteditor/syntaxhighlighter.cpp b/src/plugins/texteditor/syntaxhighlighter.cpp
index 0ed00af389d..34f4b1c7772 100644
--- a/src/plugins/texteditor/syntaxhighlighter.cpp
+++ b/src/plugins/texteditor/syntaxhighlighter.cpp
@@ -81,6 +81,10 @@ void SyntaxHighlighter::delayedRehighlight()
if (!d->rehighlightPending)
return;
d->rehighlightPending = false;
+
+ if (document()->isEmpty())
+ return;
+
rehighlight();
}
@@ -197,6 +201,10 @@ void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int ch
QList vecRes;
+ SyntaxHighlighter::Result resStart;
+ resStart.m_state = SyntaxHighlighter::State::Start;
+ vecRes << resStart;
+
while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
if (QThread::currentThread()->isInterruptionRequested())
break;
@@ -758,7 +766,10 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
SyntaxHighlighter::Result res;
res.m_formatRanges = block.layout()->formats();
res.fillByBlock(block);
- emit resultsReady({res});
+ res.m_state = SyntaxHighlighter::State::Extras;
+ SyntaxHighlighter::Result resDone;
+ resDone.m_state = SyntaxHighlighter::State::Done;
+ emit resultsReady({res, resDone});
document()->markContentsDirty(block.position(), blockLength - 1);
d->inReformatBlocks = wasInReformatBlocks;
@@ -784,7 +795,10 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block)
SyntaxHighlighter::Result res;
res.m_formatRanges = block.layout()->formats();
res.fillByBlock(block);
- emit resultsReady({res});
+ res.m_state = SyntaxHighlighter::State::Extras;
+ SyntaxHighlighter::Result resDone;
+ resDone.m_state = SyntaxHighlighter::State::Done;
+ emit resultsReady({res, resDone});
document()->markContentsDirty(block.position(), blockLength - 1);
d->inReformatBlocks = wasInReformatBlocks;
diff --git a/src/plugins/texteditor/syntaxhighlighter.h b/src/plugins/texteditor/syntaxhighlighter.h
index 94cee8a65ef..533c628b9cd 100644
--- a/src/plugins/texteditor/syntaxhighlighter.h
+++ b/src/plugins/texteditor/syntaxhighlighter.h
@@ -53,8 +53,10 @@ public:
virtual void setFontSettings(const TextEditor::FontSettings &fontSettings);
TextEditor::FontSettings fontSettings() const;
- enum State {
+ enum State {
+ Start,
InProgress,
+ Extras,
Done
};
@@ -71,7 +73,6 @@ public:
m_hasBlockUserData = true;
m_foldingIndent = userDate->foldingIndent();
- m_folded = userDate->folded();
m_ifdefedOut = userDate->ifdefedOut();
m_foldingStartIncluded = userDate->foldingStartIncluded();
m_foldingEndIncluded = userDate->foldingEndIncluded();
@@ -83,28 +84,27 @@ public:
{
block.setUserState(m_userState);
- if (m_hasBlockUserData) {
- TextBlockUserData *data = TextDocumentLayout::userData(block);
- data->setExpectedRawStringSuffix(m_expectedRawStringSuffix);
- data->setFolded(m_folded);
- data->setFoldingIndent(m_foldingIndent);
- data->setFoldingStartIncluded(m_foldingStartIncluded);
- data->setFoldingEndIncluded(m_foldingEndIncluded);
+ if (!m_hasBlockUserData)
+ return;
- if (m_ifdefedOut)
- data->setIfdefedOut();
- else
- data->clearIfdefedOut();
+ TextBlockUserData *data = TextDocumentLayout::userData(block);
+ data->setExpectedRawStringSuffix(m_expectedRawStringSuffix);
+ data->setFoldingIndent(m_foldingIndent);
+ data->setFoldingStartIncluded(m_foldingStartIncluded);
+ data->setFoldingEndIncluded(m_foldingEndIncluded);
- data->setParentheses(m_parentheses);
- }
+ if (m_ifdefedOut)
+ data->setIfdefedOut();
+ else
+ data->clearIfdefedOut();
+
+ data->setParentheses(m_parentheses);
}
int m_blockNumber;
bool m_hasBlockUserData = false;
int m_foldingIndent : 16;
- uint m_folded : 1;
uint m_ifdefedOut : 1;
uint m_foldingStartIncluded : 1;
uint m_foldingEndIncluded : 1;
diff --git a/src/plugins/texteditor/syntaxhighlighterrunner.cpp b/src/plugins/texteditor/syntaxhighlighterrunner.cpp
index bf53d90561b..228b8668493 100644
--- a/src/plugins/texteditor/syntaxhighlighterrunner.cpp
+++ b/src/plugins/texteditor/syntaxhighlighterrunner.cpp
@@ -100,7 +100,6 @@ public:
SyntaxHighlighter *m_highlighter = nullptr;
QTextDocument *m_document = nullptr;
-
signals:
void resultsReady(const QList &result);
@@ -130,6 +129,8 @@ SyntaxHighlighterRunner::SyntaxHighlighterRunner(SyntaxHighlighter *highlighter,
&QTextDocument::contentsChange,
this,
&SyntaxHighlighterRunner::changeDocument);
+
+ m_foldValidator.setup(qobject_cast(document->documentLayout()));
} else {
connect(d,
&SyntaxHighlighterRunnerPrivate::resultsReady,
@@ -169,7 +170,12 @@ void SyntaxHighlighterRunner::applyFormatRanges(const QListformats()) {
- TextDocumentLayout::FoldValidator foldValidator;
- foldValidator.setup(qobject_cast(m_document->documentLayout()));
docBlock.layout()->setFormats(result.m_formatRanges);
m_document->markContentsDirty(docBlock.position(), docBlock.length());
- foldValidator.process(docBlock);
}
+ if (m_syntaxInfoUpdated != SyntaxHighlighter::State::Extras)
+ m_foldValidator.process(docBlock);
}
}
diff --git a/src/plugins/texteditor/syntaxhighlighterrunner.h b/src/plugins/texteditor/syntaxhighlighterrunner.h
index ccb292ea086..5540bd666a9 100644
--- a/src/plugins/texteditor/syntaxhighlighterrunner.h
+++ b/src/plugins/texteditor/syntaxhighlighterrunner.h
@@ -56,6 +56,7 @@ private:
bool m_useGenericHighlighter = false;
QString m_definitionName;
std::optional m_thread;
+ TextDocumentLayout::FoldValidator m_foldValidator;
};
} // namespace TextEditor
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index 70239b286e0..baf027d124d 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -3492,13 +3492,36 @@ QByteArray TextEditorWidget::saveState() const
return state;
}
+bool TextEditorWidget::singleShotAfterHighlightingDone(std::function &&f)
+{
+ if (d->m_document->syntaxHighlighterRunner()
+ && !d->m_document->syntaxHighlighterRunner()->syntaxInfoUpdated()) {
+ connect(d->m_document->syntaxHighlighterRunner(),
+ &SyntaxHighlighterRunner::highlightingFinished,
+ this,
+ [f = std::move(f)] { f(); }, Qt::SingleShotConnection);
+ return true;
+ }
+ return false;
+}
+
void TextEditorWidget::restoreState(const QByteArray &state)
{
+ const auto callFoldLicenseHeader = [this] {
+ auto callFold = [this] {
+ if (d->m_displaySettings.m_autoFoldFirstComment)
+ d->foldLicenseHeader();
+ };
+
+ if (!singleShotAfterHighlightingDone(callFold))
+ callFold();
+ };
+
if (state.isEmpty()) {
- if (d->m_displaySettings.m_autoFoldFirstComment)
- d->foldLicenseHeader();
+ callFoldLicenseHeader();
return;
}
+
int version;
int vval;
int hval;
@@ -3514,24 +3537,27 @@ void TextEditorWidget::restoreState(const QByteArray &state)
if (version >= 1) {
QList collapsedBlocks;
stream >> collapsedBlocks;
- QTextDocument *doc = document();
- bool layoutChanged = false;
- for (const int blockNumber : std::as_const(collapsedBlocks)) {
- QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
- if (block.isValid()) {
- TextDocumentLayout::doFoldOrUnfold(block, false);
- layoutChanged = true;
+ auto foldingRestore = [this, collapsedBlocks] {
+ QTextDocument *doc = document();
+ bool layoutChanged = false;
+ for (const int blockNumber : std::as_const(collapsedBlocks)) {
+ QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
+ if (block.isValid()) {
+ TextDocumentLayout::doFoldOrUnfold(block, false);
+ layoutChanged = true;
+ }
}
- }
- if (layoutChanged) {
- auto documentLayout = qobject_cast(doc->documentLayout());
- QTC_ASSERT(documentLayout, return );
- documentLayout->requestUpdate();
- documentLayout->emitDocumentSizeChanged();
- }
+ if (layoutChanged) {
+ auto documentLayout = qobject_cast(doc->documentLayout());
+ QTC_ASSERT(documentLayout, return);
+ documentLayout->requestUpdate();
+ documentLayout->emitDocumentSizeChanged();
+ }
+ };
+ if (!singleShotAfterHighlightingDone(foldingRestore))
+ foldingRestore();
} else {
- if (d->m_displaySettings.m_autoFoldFirstComment)
- d->foldLicenseHeader();
+ callFoldLicenseHeader();
}
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
@@ -6642,6 +6668,9 @@ void TextEditorWidget::ensureCursorVisible()
void TextEditorWidget::ensureBlockIsUnfolded(QTextBlock block)
{
+ if (singleShotAfterHighlightingDone([this, block] { ensureBlockIsUnfolded(block); }))
+ return;
+
if (!block.isVisible()) {
auto documentLayout = qobject_cast(document()->documentLayout());
QTC_ASSERT(documentLayout, return);
@@ -8215,6 +8244,9 @@ void TextEditorWidget::foldCurrentBlock()
void TextEditorWidget::fold(const QTextBlock &block)
{
+ if (singleShotAfterHighlightingDone([this, block] { fold(block); }))
+ return;
+
QTextDocument *doc = document();
auto documentLayout = qobject_cast(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
@@ -8235,6 +8267,9 @@ void TextEditorWidget::fold(const QTextBlock &block)
void TextEditorWidget::unfold(const QTextBlock &block)
{
+ if (singleShotAfterHighlightingDone([this, block] { unfold(block); }))
+ return;
+
QTextDocument *doc = document();
auto documentLayout = qobject_cast(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
@@ -8254,6 +8289,9 @@ void TextEditorWidget::unfoldCurrentBlock()
void TextEditorWidget::unfoldAll()
{
+ if (singleShotAfterHighlightingDone([this] { unfoldAll(); }))
+ return;
+
QTextDocument *doc = document();
auto documentLayout = qobject_cast(doc->documentLayout());
QTC_ASSERT(documentLayout, return);
diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h
index 33b8cb84f09..85344a8dbd0 100644
--- a/src/plugins/texteditor/texteditor.h
+++ b/src/plugins/texteditor/texteditor.h
@@ -645,6 +645,7 @@ private:
friend class Internal::TextEditorOverlay;
friend class RefactorOverlay;
+ bool singleShotAfterHighlightingDone(std::function &&f);
void updateVisualWrapColumn();
};
diff --git a/src/plugins/welcome/images/expandarrow.png b/src/plugins/welcome/images/expandarrow.png
deleted file mode 100644
index 1c80e4eaac4..00000000000
Binary files a/src/plugins/welcome/images/expandarrow.png and /dev/null differ
diff --git a/src/plugins/welcome/images/expandarrow@2x.png b/src/plugins/welcome/images/expandarrow@2x.png
deleted file mode 100644
index 30b7d23aed7..00000000000
Binary files a/src/plugins/welcome/images/expandarrow@2x.png and /dev/null differ
diff --git a/src/plugins/welcome/images/link.png b/src/plugins/welcome/images/link.png
new file mode 100644
index 00000000000..3fff0753bc9
Binary files /dev/null and b/src/plugins/welcome/images/link.png differ
diff --git a/src/plugins/welcome/images/link@2x.png b/src/plugins/welcome/images/link@2x.png
new file mode 100644
index 00000000000..be4f2789aef
Binary files /dev/null and b/src/plugins/welcome/images/link@2x.png differ
diff --git a/src/plugins/welcome/images/project.png b/src/plugins/welcome/images/project.png
index 1fd5370e7e5..30862beb287 100644
Binary files a/src/plugins/welcome/images/project.png and b/src/plugins/welcome/images/project.png differ
diff --git a/src/plugins/welcome/images/project@2x.png b/src/plugins/welcome/images/project@2x.png
index df357dfa95f..c5cc11155be 100644
Binary files a/src/plugins/welcome/images/project@2x.png and b/src/plugins/welcome/images/project@2x.png differ
diff --git a/src/plugins/welcome/images/session.png b/src/plugins/welcome/images/session.png
index 7a1e2c51d25..aa14f11de84 100644
Binary files a/src/plugins/welcome/images/session.png and b/src/plugins/welcome/images/session.png differ
diff --git a/src/plugins/welcome/images/session@2x.png b/src/plugins/welcome/images/session@2x.png
index d146a1a3e9c..c53cf12bfa6 100644
Binary files a/src/plugins/welcome/images/session@2x.png and b/src/plugins/welcome/images/session@2x.png differ
diff --git a/src/plugins/welcome/welcome.qrc b/src/plugins/welcome/welcome.qrc
index 6734370f951..3158be85682 100644
--- a/src/plugins/welcome/welcome.qrc
+++ b/src/plugins/welcome/welcome.qrc
@@ -8,8 +8,8 @@
images/project@2x.png
images/session.png
images/session@2x.png
- images/expandarrow.png
- images/expandarrow@2x.png
images/border.png
+ images/link.png
+ images/link@2x.png
diff --git a/src/plugins/welcome/welcomeplugin.cpp b/src/plugins/welcome/welcomeplugin.cpp
index 0590823ea63..2513ebaa10f 100644
--- a/src/plugins/welcome/welcomeplugin.cpp
+++ b/src/plugins/welcome/welcomeplugin.cpp
@@ -22,12 +22,14 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
#include
#include
#include
@@ -42,31 +44,15 @@ using namespace Core;
using namespace Core::WelcomePageHelpers;
using namespace ExtensionSystem;
using namespace Utils;
+using namespace StyleHelper::SpacingTokens;
namespace Welcome {
namespace Internal {
class TopArea;
class SideArea;
-class BottomArea;
const char currentPageSettingsKeyC[] = "Welcome2Tab";
-constexpr int buttonSpacing = 16;
-
-static QColor themeColor(Theme::Color role)
-{
- return Utils::creatorTheme()->color(role);
-}
-
-static void addWeakVerticalSpacerToLayout(QVBoxLayout *layout, int maximumSize)
-{
- auto weakSpacer = new QWidget;
- weakSpacer->setMaximumHeight(maximumSize);
- weakSpacer->setMinimumHeight(buttonSpacing);
- weakSpacer->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Maximum);
- layout->addWidget(weakSpacer);
- layout->setStretchFactor(weakSpacer, 1);
-}
class WelcomeMode : public IMode
{
@@ -85,9 +71,9 @@ private:
QStackedWidget *m_pageStack;
TopArea *m_topArea;
SideArea *m_sideArea;
- BottomArea *m_bottomArea;
QList m_pluginList;
- QList m_pageButtons;
+ QList m_pageButtons;
+ QButtonGroup *m_buttonGroup;
Id m_activePage;
Id m_defaultPage;
};
@@ -140,55 +126,43 @@ public:
TopArea(QWidget *parent = nullptr)
: QWidget(parent)
{
- setAutoFillBackground(true);
- setMinimumHeight(11); // For compact state
- setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
- setPalette(themeColor(Theme::Welcome_BackgroundPrimaryColor));
+ setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
- m_title = new QWidget;
-
- auto hbox = new QHBoxLayout(m_title);
- hbox->setSpacing(0);
- hbox->setContentsMargins(HSpacing - 5, 2, 0, 2);
+ constexpr TextFormat welcomeTF {Theme::Token_Text_Default, StyleHelper::UiElementH2};
+ auto ideIconLabel = new QLabel;
{
- auto ideIconLabel = new QLabel;
const QPixmap logo = Core::Icons::QTCREATORLOGO_BIG.pixmap();
- ideIconLabel->setPixmap(logo.scaled(logo.size() * 0.6, Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation));
- hbox->addWidget(ideIconLabel, 0);
-
- hbox->addSpacing(16);
-
- const QFont welcomeFont = StyleHelper::uiFont(StyleHelper::UiElementH1);
-
- auto welcomeLabel = new QLabel("Welcome to");
- welcomeLabel->setFont(welcomeFont);
- hbox->addWidget(welcomeLabel, 0);
-
- hbox->addSpacing(8);
-
- auto ideNameLabel = new QLabel(QGuiApplication::applicationDisplayName());
- ideNameLabel->setFont(welcomeFont);
- ideNameLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
- QPalette pal = palette();
- pal.setColor(QPalette::WindowText, themeColor(Theme::Welcome_AccentColor));
- ideNameLabel->setPalette(pal);
- hbox->addWidget(ideNameLabel, 1);
+ const int size = logo.width();
+ const QRect cropR = size == 128 ? QRect(9, 22, 110, 84) : QRect(17, 45, 222, 166);
+ const QPixmap croppedLogo = logo.copy(cropR);
+ const int lineHeight = welcomeTF.lineHeight();
+ const QPixmap scaledCroppedLogo =
+ croppedLogo.scaledToHeight((lineHeight - 12) * devicePixelRatioF(),
+ Qt::SmoothTransformation);
+ ideIconLabel->setPixmap(scaledCroppedLogo);
+ ideIconLabel->setFixedHeight(lineHeight);
}
- auto mainLayout = new QHBoxLayout(this);
- mainLayout->setContentsMargins(0, 0, 0, 0);
- mainLayout->addWidget(m_title);
- }
+ auto welcomeLabel = new QLabel(Tr::tr("Welcome to %1")
+ .arg(QGuiApplication::applicationDisplayName()));
+ {
+ welcomeLabel->setFont(welcomeTF.font());
+ QPalette pal = palette();
+ pal.setColor(QPalette::WindowText, welcomeTF.color());
+ welcomeLabel->setPalette(pal);
+ }
- void setCompact(bool compact)
- {
- m_title->setVisible(!compact);
- }
+ using namespace Layouting;
-private:
- QWidget *m_title;
+ Row {
+ ideIconLabel,
+ welcomeLabel,
+ st,
+ spacing(ExVPaddingGapXl),
+ customMargin({HPaddingM, VPaddingM, HPaddingM, VPaddingM}),
+ }.attachTo(this);
+ }
};
class SideArea : public QScrollArea
@@ -201,114 +175,103 @@ public:
{
setWidgetResizable(true);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setFrameShape(QFrame::NoFrame);
- setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Ignored);
+ setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
- auto mainWidget = new QWidget(this);
- mainWidget->setAutoFillBackground(true);
- mainWidget->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
- mainWidget->setPalette(themeColor(Theme::Welcome_BackgroundPrimaryColor));
+ using namespace Layouting;
- auto vbox = new QVBoxLayout(mainWidget);
- vbox->setSpacing(0);
- vbox->setContentsMargins(HSpacing, 0, HSpacing, 0);
+ Column mainLayout {
+ spacing(0),
+ customMargin({ExVPaddingGapXl, 0, ExVPaddingGapXl, 0}),
+ };
+
+ m_essentials = new QWidget;
+ Column essentials {
+ spacing(0),
+ noMargin(),
+ };
{
- auto projectVBox = new QVBoxLayout;
- projectVBox->setSpacing(buttonSpacing);
- auto newButton = new WelcomePageButton(mainWidget);
- newButton->setText(Tr::tr("Create Project..."));
- newButton->setWithAccentColor(true);
- newButton->setOnClicked([] {
- QAction *openAction = ActionManager::command(Core::Constants::NEW)->action();
- openAction->trigger();
- });
+ auto newButton = new Button(Tr::tr("Create Project..."), Button::MediumPrimary);
+ auto openButton = new Button(Tr::tr("Open Project..."), Button::MediumSecondary);
- auto openButton = new WelcomePageButton(mainWidget);
- openButton->setText(Tr::tr("Open Project..."));
- openButton->setWithAccentColor(true);
- openButton->setOnClicked([] {
+ Column projectButtons {
+ newButton,
+ openButton,
+ spacing(ExPaddingGapL),
+ customMargin({0, ExVPaddingGapXl, 0, ExVPaddingGapXl}),
+ };
+
+ essentials.addItem(projectButtons);
+
+ connect(openButton, &Button::clicked, this, [] {
QAction *openAction = ActionManager::command(Core::Constants::OPEN)->action();
openAction->trigger();
});
-
- projectVBox->addWidget(newButton);
- projectVBox->addWidget(openButton);
- vbox->addItem(projectVBox);
- }
-
- addWeakVerticalSpacerToLayout(vbox, 34);
-
- {
- auto newVBox = new QVBoxLayout;
- newVBox->setSpacing(buttonSpacing / 3);
- vbox->addItem(newVBox);
-
- auto newLabel = new QLabel(Tr::tr("New to Qt?"), mainWidget);
- newLabel->setAlignment(Qt::AlignHCenter);
- newVBox->addWidget(newLabel);
-
- auto getStartedButton = new WelcomePageButton(mainWidget);
- getStartedButton->setText(Tr::tr("Get Started"));
- getStartedButton->setOnClicked([] {
- QDesktopServices::openUrl(
- QString("qthelp://org.qt-project.qtcreator/doc/creator-getting-started.html"));
+ connect(newButton, &Button::clicked, this, [] {
+ QAction *openAction = ActionManager::command(Core::Constants::NEW)->action();
+ openAction->trigger();
});
- newVBox->addWidget(getStartedButton);
}
- addWeakVerticalSpacerToLayout(vbox, 56);
-
{
auto l = m_pluginButtons = new QVBoxLayout;
- l->setSpacing(buttonSpacing);
- vbox->addItem(l);
+ l->setSpacing(VGapL);
+ l->setContentsMargins({});
+ essentials.addItem(l);
}
- vbox->addStretch(1);
+ essentials.attachTo(m_essentials);
+ mainLayout.addItem(m_essentials);
+ mainLayout.addItem(st);
+ {
+ auto label = new Label(Tr::tr("Explore more"), Label::Secondary);
+ label->setContentsMargins(HPaddingXxs, 0, 0, 0); // Is indented in Figma design
+
+ Column linksLayout {
+ label,
+ spacing(VGapS),
+ customMargin({0, VGapL, 0, ExVPaddingGapXl}),
+ };
+
+ const struct {
+ const QString label;
+ const QString url;
+ } links [] =
+ {
+ { Tr::tr("Get Started"), "qthelp://org.qt-project.qtcreator/doc/creator-getting-started.html" },
+ { Tr::tr("Get Qt"), "https://www.qt.io/download" },
+ { Tr::tr("Qt Account"), "https://account.qt.io" },
+ { Tr::tr("Online Community"), "https://forum.qt.io" },
+ { Tr::tr("Blogs"), "https://planet.qt.io" },
+ { Tr::tr("User Guide"), "qthelp://org.qt-project.qtcreator/doc/index.html" },
+ };
+ for (auto &link : links) {
+ auto button = new Button(link.label, Button::SmallLink, this);
+ connect(button, &Button::clicked, this, [link]{
+ QDesktopServices::openUrl(link.url);});
+ button->setToolTip(link.url);
+ static const QPixmap icon = Icon({{":/welcome/images/link.png",
+ Theme::Token_Accent_Default}},
+ Icon::Tint).pixmap();
+ button->setPixmap(icon);
+ linksLayout.addItem(button);
+ }
+
+ m_links = new QWidget;
+ linksLayout.attachTo(m_links);
+ mainLayout.addItem(m_links);
+ }
+
+ QWidget *mainWidget = mainLayout.emerge();
setWidget(mainWidget);
}
QVBoxLayout *m_pluginButtons = nullptr;
-};
-
-class BottomArea : public QWidget
-{
- Q_OBJECT
-
-public:
- BottomArea(QWidget *parent = nullptr)
- : QWidget(parent)
- {
- setAutoFillBackground(true);
- setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
- setPalette(themeColor(Theme::Welcome_BackgroundPrimaryColor));
-
- auto hbox = new QHBoxLayout(this);
- hbox->setSpacing(0);
- hbox->setContentsMargins(0, 2 * ItemGap, HSpacing, 2 * ItemGap);
-
- const QList > links {
- { Tr::tr("Get Qt"), "https://www.qt.io/download" },
- { Tr::tr("Qt Account"), "https://account.qt.io" },
- { Tr::tr("Online Community"), "https://forum.qt.io" },
- { Tr::tr("Blogs"), "https://planet.qt.io" },
- { Tr::tr("User Guide"), "qthelp://org.qt-project.qtcreator/doc/index.html" },
- };
- for (const QPair &link : links) {
- auto button = new WelcomePageButton(this);
- button->setSize(WelcomePageButton::SizeSmall);
- button->setText(link.first);
- button->setOnClicked([link]{ QDesktopServices::openUrl(link.second); });
- button->setWithAccentColor(true);
- button->setMaximumWidth(220);
- button->setToolTip(link.second);
- if (hbox->count() > 0)
- hbox->addStretch(1);
- hbox->addWidget(button, 20);
- }
- }
+ QWidget *m_essentials = nullptr;
+ QWidget *m_links = nullptr;
};
WelcomeMode::WelcomeMode()
@@ -327,41 +290,51 @@ WelcomeMode::WelcomeMode()
setContextHelp("Qt Creator Manual");
setContext(Context(Constants::C_WELCOME_MODE));
- QPalette palette = creatorTheme()->palette();
- palette.setColor(QPalette::Window, themeColor(Theme::Welcome_BackgroundPrimaryColor));
-
m_modeWidget = new ResizeSignallingWidget;
- m_modeWidget->setPalette(palette);
+ setBackgroundColor(m_modeWidget, Theme::Token_Background_Default);
connect(m_modeWidget, &ResizeSignallingWidget::resized, this,
[this](const QSize &size, const QSize &) {
- const bool hideSideArea = size.width() <= 750;
- const bool hideBottomArea = size.width() <= 850;
- const bool compactVertically = size.height() <= 530;
- m_sideArea->setVisible(!hideSideArea);
- m_bottomArea->setVisible(!(hideBottomArea || compactVertically));
- m_topArea->setCompact(compactVertically);
+ const QSize essentialsS = m_sideArea->m_essentials->size();
+ const QSize linksS = m_sideArea->m_links->size();
+ const QSize sideAreaS = m_sideArea->size();
+ const QSize topAreaS = m_topArea->size();
+ const QSize mainWindowS = ICore::mainWindow()->size();
+
+ const bool showSideArea = sideAreaS.width() < size.width() / 4;
+ const bool showTopArea = topAreaS.height() < mainWindowS.height() / 7.75;
+ const bool showLinks =
+ linksS.height() + essentialsS.height() < sideAreaS.height() && showTopArea;
+
+ m_sideArea->m_links->setVisible(showLinks);
+ m_sideArea->setVisible(showSideArea);
+ m_topArea->setVisible(showTopArea);
});
m_sideArea = new SideArea(m_modeWidget);
+ m_buttonGroup = new QButtonGroup(m_modeWidget);
+ m_buttonGroup->setExclusive(true);
+
m_pageStack = new QStackedWidget(m_modeWidget);
- palette.setColor(QPalette::Window, themeColor(Theme::Welcome_BackgroundSecondaryColor));
- m_pageStack->setPalette(palette);
m_pageStack->setObjectName("WelcomeScreenStackedWidget");
m_pageStack->setAutoFillBackground(true);
m_topArea = new TopArea;
- m_bottomArea = new BottomArea;
- auto layout = new QGridLayout(m_modeWidget);
- layout->addWidget(new StyledBar(m_modeWidget), 0, 0, 1, 2);
- layout->addWidget(m_topArea, 1, 0, 1, 2);
- layout->addWidget(m_sideArea, 2, 0, 2, 1);
- layout->addWidget(m_pageStack, 2, 1, 1, 1);
- layout->setColumnStretch(1, 10);
- layout->addWidget(m_bottomArea, 3, 1, 1, 1);
- layout->setContentsMargins(0, 0, 0, 0);
- layout->setSpacing(0);
+ using namespace Layouting;
+
+ Column {
+ new StyledBar,
+ m_topArea,
+ createRule(Qt::Horizontal),
+ Row {
+ m_sideArea,
+ createRule(Qt::Vertical),
+ m_pageStack,
+ },
+ noMargin(),
+ spacing(0),
+ }.attachTo(m_modeWidget);
setWidget(m_modeWidget);
}
@@ -402,11 +375,11 @@ void WelcomeMode::addPage(IWelcomePage *page)
if (m_pluginList.at(idx)->priority() >= pagePriority)
break;
}
- auto pageButton = new WelcomePageButton(m_sideArea->widget());
+ auto pageButton = new Button(page->title(), Button::SmallList, m_sideArea->widget());
auto pageId = page->id();
pageButton->setText(page->title());
- pageButton->setActiveChecker([this, pageId] { return m_activePage == pageId; });
+ m_buttonGroup->addButton(pageButton);
m_pluginList.insert(idx, page);
m_pageButtons.insert(idx, pageButton);
@@ -417,6 +390,7 @@ void WelcomeMode::addPage(IWelcomePage *page)
m_pageStack->insertWidget(idx, stackPage);
connect(page, &QObject::destroyed, this, [this, page, stackPage, pageButton] {
+ m_buttonGroup->removeButton(pageButton);
m_pluginList.removeOne(page);
m_pageButtons.removeOne(pageButton);
delete pageButton;
@@ -426,13 +400,13 @@ void WelcomeMode::addPage(IWelcomePage *page)
auto onClicked = [this, pageId, stackPage] {
m_activePage = pageId;
m_pageStack->setCurrentWidget(stackPage);
- for (WelcomePageButton *pageButton : std::as_const(m_pageButtons))
- pageButton->recheckActive();
};
- pageButton->setOnClicked(onClicked);
- if (pageId == m_activePage)
+ connect(pageButton, &Button::clicked, this, onClicked);
+ if (pageId == m_activePage) {
onClicked();
+ pageButton->setChecked(true);
+ }
}
} // namespace Internal
diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg
index ddec488f3cb..9ced69d3199 100644
--- a/src/tools/icons/qtcreatoricons.svg
+++ b/src/tools/icons/qtcreatoricons.svg
@@ -664,6 +664,14 @@
width="100%"
height="100%"
transform="matrix(1.5,0,0,1.5,0,-242)" />
+
-
+
+ id="path8101"
+ style="fill:none;stroke:#000000;stroke-width:2;stroke-linejoin:round"
+ d="m 219,210 6,4 -6,4 z m -7,-5 h 18 v 18 h -18 z" />
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+