From 536bb51c5abf69104b4e49dbf068b594ca195435 Mon Sep 17 00:00:00 2001
From: Marcus Tillmanns
Date: Tue, 27 Feb 2024 08:43:26 +0100
Subject: [PATCH 01/38] Android: Validate selected Java version
Change-Id: If77e5b524fdfea7d87ff4f221ef81d2f723f2f82
Reviewed-by: hjk
---
src/plugins/android/androidsettingswidget.cpp | 60 +++++++++++++++++--
1 file changed, 55 insertions(+), 5 deletions(-)
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index ca0ff057dfb..0001c37de5f 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -13,13 +13,14 @@
#include
+#include
#include
#include
#include
#include
#include
-#include
#include
+#include
#include
#include
@@ -203,6 +204,47 @@ 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 FilePath bin = jdkPath.pathAppended("bin/javac" QTC_HOST_EXE_SUFFIX);
+
+ if (!bin.isExecutableFile())
+ return make_unexpected(
+ Tr::tr("The selected path does not contain an executable bin/javac."));
+
+ QVersionNumber jdkVersion;
+
+ Process javacProcess;
+ CommandLine cmd(bin, {"-version"});
+ 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. (javac -version failed: %1)")
+ .arg(stdOut));
+
+ // We expect "javac " where is "major.minor.patch"
+ if (!stdOut.startsWith("javac "))
+ return make_unexpected(Tr::tr("Unexpected output from \"javac -version\": %1").arg(stdOut));
+
+ jdkVersion = QVersionNumber::fromString(stdOut.mid(6).split('\n').first());
+
+ if (jdkVersion.isNull() || jdkVersion.majorVersion() != 17) {
+ return make_unexpected(Tr::tr("Unsupported JDK version (needs to be 17): %1 (parsed: %2)")
+ .arg(stdOut)
+ .arg(jdkVersion.toString()));
+ }
+
+ return {};
+}
+
AndroidSettingsWidget::AndroidSettingsWidget()
{
setWindowTitle(Tr::tr("Android Configuration"));
@@ -307,6 +349,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 +584,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();
From 7c98f2aa9ea3c1464c490e3dfc9636e7e1f51510 Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Mon, 26 Feb 2024 17:44:14 +0100
Subject: [PATCH 02/38] Axivion: Unuglify issues view frame
BaseTreeView's c'tor calls setFrameStyle(QFrame::NoFrame), that's
why the frame was lacking. Bring back the Qt defaults explicitly.
Change-Id: If48c97cc0b0afa3fdb71b5ba30284371da35192f
Reviewed-by: hjk
---
src/plugins/axivion/axivionoutputpane.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/plugins/axivion/axivionoutputpane.cpp b/src/plugins/axivion/axivionoutputpane.cpp
index a13b63dd3a2..31a03f0d569 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);
From 0114933deba8d0f75a9e76f0699358748e8be33d Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Mon, 26 Feb 2024 20:59:11 +0100
Subject: [PATCH 03/38] AndroidRunWorker: Remove redundant variables
Change-Id: I6677ce5d4640fdd68d3571d833f9c3651068facc
Reviewed-by: hjk
Reviewed-by:
---
src/plugins/android/androidrunnerworker.cpp | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 6cba25a0179..7452e975811 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.
From e95ad3778fef7bf198bc8296e3c5ba1894ae0e1a Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Mon, 26 Feb 2024 21:02:42 +0100
Subject: [PATCH 04/38] AndroidRunnerWorker: Avoid deleting process directly
from its handler
Task-number: QTCREATORBUG-29928
Change-Id: If25c742e57ddaa90ed3342d09dafe626288f0783
Reviewed-by: hjk
---
src/plugins/android/androidrunnerworker.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/plugins/android/androidrunnerworker.cpp b/src/plugins/android/androidrunnerworker.cpp
index 7452e975811..d18f645165f 100644
--- a/src/plugins/android/androidrunnerworker.cpp
+++ b/src/plugins/android/androidrunnerworker.cpp
@@ -850,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});
+ });
}
}
From 4dcba2ad259514fe21cda500120c05e0f7d1f094 Mon Sep 17 00:00:00 2001
From: Christian Stenger
Date: Tue, 27 Feb 2024 08:27:06 +0100
Subject: [PATCH 05/38] Core: Correct log view thread handling
Fixes a crash when log messages are received from
multiple threads.
Fixes: QTCREATORBUG-30444
Change-Id: I51c78656da1dd30bcb51a801083d1714e474d8e5
Reviewed-by: Marcus Tillmanns
---
src/plugins/coreplugin/loggingviewer.cpp | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
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:
From 82399ac9c54dcdbe03e4240c5b2ffe6850a95afd Mon Sep 17 00:00:00 2001
From: David Schulz
Date: Tue, 27 Feb 2024 08:34:28 +0100
Subject: [PATCH 06/38] ClangFormat: Fix compile against latest llvm main
branch
Change-Id: I6fe414721103d00eb3212040c1aafa2ef123b9d1
Reviewed-by: Artem Sokolovskii
Reviewed-by:
---
src/plugins/clangformat/clangformatutils.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
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;
From 5152a35048b7800ff8b10e1f254357a046a9543e Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Tue, 27 Feb 2024 12:00:51 +0100
Subject: [PATCH 07/38] Axivion: Change the credential key
Make it user @ server.
Change-Id: I62e2c88ade5cc984ba11f718c3db292e4075e5e4
Reviewed-by: Andreas Loth
Reviewed-by: hjk
---
src/plugins/axivion/axivionplugin.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp
index c559e2b7d3a..20e7871b14e 100644
--- a/src/plugins/axivion/axivionplugin.cpp
+++ b/src/plugins/axivion/axivionplugin.cpp
@@ -122,7 +122,7 @@ 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)
From a57a925b76785c1269ca5bcbacd553f01c0f238d Mon Sep 17 00:00:00 2001
From: David Schulz
Date: Mon, 12 Feb 2024 14:03:00 +0100
Subject: [PATCH 08/38] Debugger: defer type look up
Change-Id: I425c2bfc3c88ebf46af161c5434c0c05a3bb9c97
Reviewed-by: Christian Stenger
---
share/qtcreator/debugger/cdbbridge.py | 493 +++++++++++++++++++++++--
share/qtcreator/debugger/dumper.py | 68 ++--
share/qtcreator/debugger/lldbbridge.py | 2 +-
src/libs/qtcreatorcdbext/pytype.cpp | 46 +--
src/libs/qtcreatorcdbext/pytype.h | 1 +
5 files changed, 533 insertions(+), 77 deletions(-)
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..c6aae0c2e48 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
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
{
From 226806e0fbbd01d9967b34567dfdb10b1a170d87 Mon Sep 17 00:00:00 2001
From: Christian Kandeler
Date: Tue, 27 Feb 2024 12:53:59 +0100
Subject: [PATCH 09/38] RemoteLinux: Make deployment downgrade warning less
spammy
Keep the message, but make it a warning only for the "generic copy"
case, which almost no one ever wants.
Otherwise, it will annoy Windows users, who typically have no rsync
installed.
Change-Id: I2044b9ea2b199e03dc4e9421d05a2698d92ad76a
Reviewed-by: hjk
---
src/plugins/remotelinux/genericdeploystep.cpp | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/src/plugins/remotelinux/genericdeploystep.cpp b/src/plugins/remotelinux/genericdeploystep.cpp
index bddc7bfcd3d..6071c4c8cae 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);
From c536712707d5ba42eeebddcf78702fc7e06dc984 Mon Sep 17 00:00:00 2001
From: Alessandro Portale
Date: Tue, 27 Feb 2024 11:07:28 +0100
Subject: [PATCH 10/38] Android: Remove command line and version numbers from
translations
Change-Id: Ib4fcd6cbf0a023e074aaad4b265c0ab60edac01a
Reviewed-by: Marcus Tillmanns
---
src/plugins/android/androidsettingswidget.cpp | 27 ++++++++++++-------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index 0001c37de5f..c75ba299c5b 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -209,16 +209,20 @@ 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 FilePath bin = jdkPath.pathAppended("bin/javac" QTC_HOST_EXE_SUFFIX);
+ 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("The selected path does not contain an executable bin/javac."));
+ Tr::tr("Could not find \"%1\" in the selected path.")
+ .arg(bin.toUserOutput()));
QVersionNumber jdkVersion;
Process javacProcess;
- CommandLine cmd(bin, {"-version"});
+ const CommandLine cmd(bin, {versionParameter});
javacProcess.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels);
javacProcess.setCommand(cmd);
javacProcess.runBlocking();
@@ -227,17 +231,22 @@ static expected_str testJavaC(const FilePath &jdkPath)
if (javacProcess.exitCode() != 0)
return make_unexpected(
- Tr::tr("The selected path does not contain a valid JDK. (javac -version failed: %1)")
+ 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"
- if (!stdOut.startsWith("javac "))
- return make_unexpected(Tr::tr("Unexpected output from \"javac -version\": %1").arg(stdOut));
+ 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(6).split('\n').first());
+ jdkVersion = QVersionNumber::fromString(stdOut.mid(outputPrefix.length()).split('\n').first());
- if (jdkVersion.isNull() || jdkVersion.majorVersion() != 17) {
- return make_unexpected(Tr::tr("Unsupported JDK version (needs to be 17): %1 (parsed: %2)")
+ 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()));
}
From b60e0cfee7e346adc934d910216bbc0e47ccffb6 Mon Sep 17 00:00:00 2001
From: Eike Ziller
Date: Tue, 27 Feb 2024 12:45:36 +0100
Subject: [PATCH 11/38] Update change log for 13.0.0
Change-Id: I7760acdfeb1b0a309e8d939bf37494aa3a0a7a36
Reviewed-by: Leena Miettinen
---
dist/changelog/changes-13.0.0.md | 11 +++++++++++
1 file changed, 11 insertions(+)
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
From e8b3887dbf584460973ed0212a66f9b400bcc464 Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Tue, 27 Feb 2024 13:51:54 +0100
Subject: [PATCH 12/38] Axivion: Make use of Dto::IssueKind enum
Use Dto::IssueKindMeta::enumToStr() to convert it to string.
Rename icons accordingly.
Change-Id: I8a955f2f0075793eea761cd4a41374a65d2aaea2
Reviewed-by: hjk
---
src/plugins/axivion/axivion.qrc | 24 +++++++++---------
src/plugins/axivion/axivionoutputpane.cpp | 2 +-
src/plugins/axivion/axivionplugin.cpp | 20 ++++++++-------
src/plugins/axivion/axivionplugin.h | 2 +-
.../images/{button-av.png => button-AV.png} | Bin
.../{button-av@2x.png => button-AV@2x.png} | Bin
.../images/{button-cl.png => button-CL.png} | Bin
.../{button-cl@2x.png => button-CL@2x.png} | Bin
.../images/{button-cy.png => button-CY.png} | Bin
.../{button-cy@2x.png => button-CY@2x.png} | Bin
.../images/{button-de.png => button-DE.png} | Bin
.../{button-de@2x.png => button-DE@2x.png} | Bin
.../images/{button-mv.png => button-MV.png} | Bin
.../{button-mv@2x.png => button-MV@2x.png} | Bin
.../images/{button-sv.png => button-SV.png} | Bin
.../{button-sv@2x.png => button-SV@2x.png} | Bin
src/tools/icons/qtcreatoricons.svg | 12 ++++-----
17 files changed, 31 insertions(+), 29 deletions(-)
rename src/plugins/axivion/images/{button-av.png => button-AV.png} (100%)
rename src/plugins/axivion/images/{button-av@2x.png => button-AV@2x.png} (100%)
rename src/plugins/axivion/images/{button-cl.png => button-CL.png} (100%)
rename src/plugins/axivion/images/{button-cl@2x.png => button-CL@2x.png} (100%)
rename src/plugins/axivion/images/{button-cy.png => button-CY.png} (100%)
rename src/plugins/axivion/images/{button-cy@2x.png => button-CY@2x.png} (100%)
rename src/plugins/axivion/images/{button-de.png => button-DE.png} (100%)
rename src/plugins/axivion/images/{button-de@2x.png => button-DE@2x.png} (100%)
rename src/plugins/axivion/images/{button-mv.png => button-MV.png} (100%)
rename src/plugins/axivion/images/{button-mv@2x.png => button-MV@2x.png} (100%)
rename src/plugins/axivion/images/{button-sv.png => button-SV.png} (100%)
rename src/plugins/axivion/images/{button-sv@2x.png => button-SV@2x.png} (100%)
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 31a03f0d569..70e44057e9f 100644
--- a/src/plugins/axivion/axivionoutputpane.cpp
+++ b/src/plugins/axivion/axivionoutputpane.cpp
@@ -564,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 20e7871b14e..6e04af7e30d 100644
--- a/src/plugins/axivion/axivionplugin.cpp
+++ b/src/plugins/axivion/axivionplugin.cpp
@@ -63,16 +63,18 @@ 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());
+ static QHash prefixToIcon;
+ auto it = prefixToIcon.constFind(*issueKind);
+ if (it == prefixToIcon.constEnd()) {
+ const auto prefix = Dto::IssueKindMeta::enumToStr(*issueKind);
+ const Icon icon({{FilePath::fromString(":/axivion/images/button-" + prefix + ".png"),
+ Theme::PaletteButtonText}}, Icon::Tint);
+ it = prefixToIcon.insert(*issueKind, icon.icon());
}
return it.value();
}
@@ -226,7 +228,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);
diff --git a/src/plugins/axivion/axivionplugin.h b/src/plugins/axivion/axivionplugin.h
index c60aad6e9e9..18cdae6dded 100644
--- a/src/plugins/axivion/axivionplugin.h
+++ b/src/plugins/axivion/axivionplugin.h
@@ -71,7 +71,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/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/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg
index ddec488f3cb..07452dc8c1e 100644
--- a/src/tools/icons/qtcreatoricons.svg
+++ b/src/tools/icons/qtcreatoricons.svg
@@ -3813,7 +3813,7 @@
r="1.5" />
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/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/welcome/images/expandarrow.png b/src/plugins/welcome/images/expandarrow.png
deleted file mode 100644
index 1c80e4eaac41ff43b09a465571e5f4ecdcfff1cb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 106
zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4h9AW2CEqh_A)Rq7<#%mhDc0J{?X4Sk)bx>
z7z5kOg2wo53j3b&w@iC}iHAqXf5KUdbLXl!u3ngFocS_{pDV_3WBX$C_qkkaeD}elF{r5}E*R?l4LK
diff --git a/src/plugins/welcome/images/link.png b/src/plugins/welcome/images/link.png
new file mode 100644
index 0000000000000000000000000000000000000000..3fff0753bc9d6bf9aefc246b38b3d7709620bb63
GIT binary patch
literal 186
zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@S^+*Gt_%ze8X6ipIy$<#
zx;8d8{r&wDCQNw#{Q1k5FJHZS_4@VeH*em&ef#$R|NqY>s;^;SVDR>IaSV}|n!KZ%
zMPkLForf4$A}q__-wSYfWMZgjVa|I*aL&n|tp$7mkM!m+PiZL5)5y59_chbg29*T8
p9lVno3fCEZRkA4GwBlJXgKyH^YnA4_&I}9;44$rjF6*2UngFs`NE`qF
literal 0
HcmV?d00001
diff --git a/src/plugins/welcome/images/link@2x.png b/src/plugins/welcome/images/link@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..be4f2789aefaec3764de97cce167fc30a8acbc71
GIT binary patch
literal 236
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwlFX-CFf%i=u&_u;
zNl8mfFIlo=#flZHR;^mIX3gf!oB#j+e}BWLJ_ZJcHcuDF5R21qFRm4AFyLu>cwi%w
z+)*YCnPP>B9>)%BVmj`>oaIu;mY+%&1b*M~zU6dI;@82p)|8+Zam_z9=N)X=Ym~4+
z=8)_Z0kuaB_M7DdEMRI>
q&hbc`s?6xY+_-tBVZ%?om?IsVzE}&-U(3M2z~JfX=d#Wzp$P!&m{~di
literal 0
HcmV?d00001
diff --git a/src/plugins/welcome/images/project.png b/src/plugins/welcome/images/project.png
index 1fd5370e7e5e27f2b6a45c715916d19efb8c0e08..30862beb287d48c9587ec24d55e99d3a2881f712 100644
GIT binary patch
literal 175
zcmeAS@N?(olHy`uVBq!ia0y~yV2}V|7G?$p2BAoW(+ms@(g8jpt_%zeva+)B^71Mw
zD$UK!TeoiAv17-dKY#xJ|9>covz38?!Qa!xF+^f&@(Twho+CwGED{nfS_@>@*%UrB
z6tNgtSqZ5ukeRSfZGp;S>nWGB7@ll&uvEBktm6oy@ygSTmh5@_Hx}{)h&Svxz)&px
cGLeD7jJ@(z&>_vO3=9kmp00i_>zopr0IugZ7XSbN
literal 106
zcmeAS@N?(olHy`uVBq!ia0y~yU=RRdCT0c(hNQXTpBNYzI0Jk_Tp1V`%FE0D|NpP?
zAS{r9fkDR8#W92D?D8sLp+YZy}FUN*+8K6
zVSRHjTLOpK2d2!Ik9ckUxK?a#WLJ=Nd;WQ*h+uDg=10#Loc1^Bd6@qQFHw-0IKfK6
zJtV2@V7JyqW=`dkjEpn37wtIfP|(2e`ezC25ypm9;a7f7y(g7m7lr7|Nqx`
z5EjV5z@X&m;us<^H93Nnm1l{egv6!<9!-g6-eUja
znp#>~IyyR*mXTWo2dM<>e<%oH%*%9NLd`Hqk3xLiFwW+&DLFDWO&PdWSx$7;Xwul1_n=8KbLh*2~7aMKSaa;
literal 118
zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4h9AW2CEqh_A)Rq*m}A+hDc0J{?X4Sv4O)t
zf$uau>b%7
diff --git a/src/plugins/welcome/images/session@2x.png b/src/plugins/welcome/images/session@2x.png
index d146a1a3e9ca2385ca59350ee7b01632bc22063e..c53cf12bfa6cd4c360deb9c8839fb272cae62c8a 100644
GIT binary patch
literal 255
zcmeAS@N?(olHy`uVBq!ia0y~yU@!n-7G?$phNU`BwtzSRJ|V6Q3=CXcT)e!zQc_aV
z($coJwk|F%9UUDrX3Us5bLQsFn{VH~{ouiazkmP!`}gnv|Nl3CSJ^T!Fm!pkIEGl9
zPX5u)Y!DEdWXR06fyvtB8cCm{(je!0%0V(&M4TNpe^n4XZ3u;!@Z!2@M-
z?JUhdYS##H@dRXFQ0+Uo#&P-&^UlV`QhTw^|Mfx=5?eNS{;6Nc&}Ac}QXtuSxoFP0
zqc0ujI5rsz6tY;K>o_F2wOLPf(yYY=X?}BV&U}{2$#7DFBO^i3UYLP_fx*+&&t;uc
GLK6UJJzx3&
literal 161
zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4h9AWhA=@@4F(2=3{Mxw5R22vKl~XCE_gCB
z^BD+mi4_S1eE8oZ!^rwS-V($tw`64f|6l)+$^wRyiWVXYGELr$cN8r||9Bi-|6rwR
zopztsqw*&{W_#}anb~E(;K8-W0Sz1Eb~CBGKIvm%5@K+mQkcQ$3130*(U5GA-JY&~
JF6*2UngC>IHX8r{
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 07452dc8c1e..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" />
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Date: Thu, 15 Feb 2024 15:01:16 +0100
Subject: [PATCH 18/38] QmakeProjectManager: Fix Makefile compatibility check
E.g. qmake "binaries" for Android are actually shell scripts that call a
Desktop qmake in a different location, which lead Qt Creator to conclude
that the directory contains an incompatible build.
Fix this by checking that the -qtconf argument passed to qmake points
into the real qmake's parent directory, as is the case in the
abovementioned scenario.
Fixes: QTCREATORBUG-30354
Change-Id: Id6e878fab3379a3a8893389447514a1b7226784c
Reviewed-by:
Reviewed-by: Alessandro Portale
Reviewed-by: Qt CI Bot
---
src/plugins/qmakeprojectmanager/makefileparse.cpp | 8 ++++++++
src/plugins/qmakeprojectmanager/makefileparse.h | 2 ++
.../qmakeprojectmanager/qmakebuildconfiguration.cpp | 4 +++-
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/plugins/qmakeprojectmanager/makefileparse.cpp b/src/plugins/qmakeprojectmanager/makefileparse.cpp
index 60a04cee0a8..9d2bda465f7 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 798eae306f6..f3a71728d00 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;
From dc21bd6af2fd736256856dcf100cc98f0a5f4ab8 Mon Sep 17 00:00:00 2001
From: Christian Stenger
Date: Tue, 27 Feb 2024 14:44:40 +0100
Subject: [PATCH 19/38] AutoTest: Delay reparsing of postponed files
Task-number: QTCREATORBUG-30447
Change-Id: Ica1dba358e05edfe2ad7ed6fd92fc8afde291718
Reviewed-by: Christian Kandeler
Reviewed-by: David Schulz
---
src/plugins/autotest/testcodeparser.cpp | 26 +++++--------------------
1 file changed, 5 insertions(+), 21 deletions(-)
diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp
index 1824b316d59..9e46232c5bc 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:
From 2b525b424603c13a6c68652cbfb081dba05af800 Mon Sep 17 00:00:00 2001
From: Christian Stenger
Date: Tue, 27 Feb 2024 15:28:34 +0100
Subject: [PATCH 20/38] AutoTest: Suppress progress bar more aggressively
Task-number: QTCREATORBUG-30447
Change-Id: I4386d522819b9f361f179ccda6a0fd3c759c389d
Reviewed-by: David Schulz
Reviewed-by: Christian Kandeler
---
src/plugins/autotest/testcodeparser.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/plugins/autotest/testcodeparser.cpp b/src/plugins/autotest/testcodeparser.cpp
index 9e46232c5bc..9962a08c621 100644
--- a/src/plugins/autotest/testcodeparser.cpp
+++ b/src/plugins/autotest/testcodeparser.cpp
@@ -361,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";
From 95743d5c9fb448af231f8948dcfb4ac20333a1e4 Mon Sep 17 00:00:00 2001
From: Marcus Tillmanns
Date: Tue, 27 Feb 2024 15:50:16 +0100
Subject: [PATCH 21/38] RemoteLinux: Don't show self in "Access via"
Task-number: QTCREATORBUG-30008
Change-Id: I90ee2df7243a2b07b494b1d1911930b7ae242669
Reviewed-by:
Reviewed-by: hjk
---
.../genericlinuxdeviceconfigurationwidget.cpp | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
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());
From d0a10592059b501ae4163c17fdaad1f1f5514b96 Mon Sep 17 00:00:00 2001
From: Leena Miettinen
Date: Tue, 27 Feb 2024 16:59:33 +0100
Subject: [PATCH 22/38] Doc: Describe selecting all text in the terminal
Task-number: QTCREATORBUG-30209
Change-Id: I65bd04dac6b81448c326e27e68352faddc8b3065
Reviewed-by: Marcus Tillmanns
---
.../creator-only/creator-reference-terminal-view.qdoc | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
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.
From 581e6ddf4f6dc020102d83cf34db4a7254eb84fa Mon Sep 17 00:00:00 2001
From: hjk
Date: Wed, 28 Feb 2024 09:41:39 +0100
Subject: [PATCH 23/38] Debugger: Require Python 3.5 in the docs
... and make the adapter/mode description a bit simpler.
Change-Id: Ie1b7c6cc26780f38be60c8e51e3bc0bc82db0bc6
Reviewed-by: Leena Miettinen
---
.../creator-only/creator-debugger-setup.qdoc | 27 +++++++------------
1 file changed, 9 insertions(+), 18 deletions(-)
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.
From 3f785928319716c76cc4eb23eebb0dc8c60a7429 Mon Sep 17 00:00:00 2001
From: Alessandro Portale
Date: Wed, 28 Feb 2024 11:32:06 +0100
Subject: [PATCH 24/38] Utils: Remove soft assert for reading theme flags
An included theme file will usually not define all flags.
Change-Id: I733d8558009ba1b660f66b92c7c2d044035bb0ae
Reviewed-by: hjk
Reviewed-by: Eike Ziller
---
src/libs/utils/theme/theme.cpp | 1 -
1 file changed, 1 deletion(-)
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();
From 95a5f010962ab642a3dbd9d6f8f96f3b2e917b79 Mon Sep 17 00:00:00 2001
From: Artem Sokolovskii
Date: Fri, 23 Feb 2024 17:37:03 +0100
Subject: [PATCH 25/38] TextEditor: Fix folding for async syntax highlighter
- Made restoreState, ensureBlockIsUnfolded, fold, unfold and unfoldAll
functions to be called only after highlighting is done
- Improved management of foldValidator in async case
- Removed optimizations in cpphighlighter and glshighlighter.
The highlighters are async now and optimization is not necessary.
In these optimizations in the function highlightBlock the highlighting
changes not only for currentBlock but and for several next. Which is
contradict with the function name.
Change-Id: Ib413e6b982eb39d52f36c3066ff0fa8c28fbe231
Reviewed-by: David Schulz
---
src/plugins/cppeditor/cpphighlighter.cpp | 21 ----------
src/plugins/cppeditor/cpptoolstestcase.cpp | 14 ++++++-
src/plugins/glsleditor/glslhighlighter.cpp | 18 ---------
src/plugins/texteditor/syntaxhighlighter.cpp | 18 ++++++++-
src/plugins/texteditor/syntaxhighlighter.h | 32 +++++++--------
.../texteditor/syntaxhighlighterrunner.cpp | 13 +++++--
.../texteditor/syntaxhighlighterrunner.h | 1 +
src/plugins/texteditor/texteditor.cpp | 39 +++++++++++++++++--
src/plugins/texteditor/texteditor.h | 1 +
9 files changed, 92 insertions(+), 65 deletions(-)
diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp
index 591881c6c5b..becebd11886 100644
--- a/src/plugins/cppeditor/cpphighlighter.cpp
+++ b/src/plugins/cppeditor/cpphighlighter.cpp
@@ -242,27 +242,6 @@ void CppHighlighter::highlightBlock(const QString &text)
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
- // optimization: if only the brace depth changes, we adjust subsequent blocks
- // to have QSyntaxHighlighter stop the rehighlighting
- int currentState = currentBlockState();
- if (currentState != -1) {
- int oldState = currentState & 0xff;
- int oldBraceDepth = currentState >> 8;
- if (oldState == tokenize.state() && oldBraceDepth != braceDepth) {
- TextDocumentLayout::FoldValidator foldValidor;
- foldValidor.setup(qobject_cast(document()->documentLayout()));
- int delta = braceDepth - oldBraceDepth;
- QTextBlock block = currentBlock().next();
- while (block.isValid() && block.userState() != -1) {
- TextDocumentLayout::changeBraceDepth(block, delta);
- TextDocumentLayout::changeFoldingIndent(block, delta);
- foldValidor.process(block);
- block = block.next();
- }
- foldValidor.finalize();
- }
- }
-
setCurrentBlockState((braceDepth << 8) | tokenize.state());
TextDocumentLayout::setExpectedRawStringSuffix(currentBlock(),
tokenize.expectedRawStringSuffix());
diff --git a/src/plugins/cppeditor/cpptoolstestcase.cpp b/src/plugins/cppeditor/cpptoolstestcase.cpp
index 37b5d855d0a..a55895affcf 100644
--- a/src/plugins/cppeditor/cpptoolstestcase.cpp
+++ b/src/plugins/cppeditor/cpptoolstestcase.cpp
@@ -20,10 +20,11 @@
#include
#include
-#include
#include
#include
#include
+#include
+#include
#include
#include
@@ -221,6 +222,17 @@ bool TestCase::openCppEditor(const FilePath &filePath, TextEditor::BaseTextEdito
s.m_addFinalNewLine = false;
e->textDocument()->setStorageSettings(s);
}
+
+ if (!QTest::qWaitFor(
+ [e] {
+ return e->editorWidget()
+ ->textDocument()
+ ->syntaxHighlighterRunner()
+ ->syntaxInfoUpdated();
+ },
+ 5000))
+ return false;
+
if (editorWidget) {
if (CppEditorWidget *w = dynamic_cast(e->editorWidget())) {
*editorWidget = w;
diff --git a/src/plugins/glsleditor/glslhighlighter.cpp b/src/plugins/glsleditor/glslhighlighter.cpp
index 7c25b0abbf3..eae8419a049 100644
--- a/src/plugins/glsleditor/glslhighlighter.cpp
+++ b/src/plugins/glsleditor/glslhighlighter.cpp
@@ -189,7 +189,6 @@ void GlslHighlighter::highlightBlock(const QString &text)
TextDocumentLayout::setParentheses(currentBlock(), parentheses);
// if the block is ifdefed out, we only store the parentheses, but
-
// do not adjust the brace depth.
if (TextDocumentLayout::ifdefedOut(currentBlock())) {
braceDepth = initialBraceDepth;
@@ -198,23 +197,6 @@ void GlslHighlighter::highlightBlock(const QString &text)
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
- // optimization: if only the brace depth changes, we adjust subsequent blocks
- // to have QSyntaxHighlighter stop the rehighlighting
- int currentState = currentBlockState();
- if (currentState != -1) {
- int oldState = currentState & 0xff;
- int oldBraceDepth = currentState >> 8;
- if (oldState == lex.state() && oldBraceDepth != braceDepth) {
- int delta = braceDepth - oldBraceDepth;
- QTextBlock block = currentBlock().next();
- while (block.isValid() && block.userState() != -1) {
- TextDocumentLayout::changeBraceDepth(block, delta);
- TextDocumentLayout::changeFoldingIndent(block, delta);
- block = block.next();
- }
- }
- }
-
setCurrentBlockState((braceDepth << 8) | lex.state());
}
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..4817c61dd7d 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -3492,13 +3492,35 @@ 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)
{
- if (state.isEmpty()) {
+ if (singleShotAfterHighlightingDone([this, state] { restoreState(state); }))
+ return;
+
+ const auto callFoldLicenseHeader = [this] {
if (d->m_displaySettings.m_autoFoldFirstComment)
d->foldLicenseHeader();
+ };
+
+ if (state.isEmpty()) {
+ callFoldLicenseHeader();
return;
}
+
int version;
int vval;
int hval;
@@ -3530,8 +3552,7 @@ void TextEditorWidget::restoreState(const QByteArray &state)
documentLayout->emitDocumentSizeChanged();
}
} else {
- if (d->m_displaySettings.m_autoFoldFirstComment)
- d->foldLicenseHeader();
+ callFoldLicenseHeader();
}
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
@@ -6642,6 +6663,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 +8239,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 +8262,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 +8284,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();
};
From 64a77d8c83a739c82814e5b095df95e0cc7c65fa Mon Sep 17 00:00:00 2001
From: Cristian Adam
Date: Wed, 28 Feb 2024 12:52:06 +0100
Subject: [PATCH 26/38] TextEditor: Set the special weight of Source Code Pro
font family
The text editor font is used also for output panes, make sure that on
macOS we use QFont::Medium so that the font is not too skinny.
Amends 13dc66a08d3120fe4eb9c1e124618a3d17408d8e
Change-Id: Idad26738d42452c8d683df7a40a4a2fe44e9793e
Reviewed-by: Marcus Tillmanns
---
src/plugins/texteditor/fontsettings.cpp | 5 +++--
src/plugins/texteditor/fontsettings.h | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
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);
From bcdcaab7b73b8302d21ff30c6b1934cdf3ba2936 Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Wed, 28 Feb 2024 01:17:41 +0100
Subject: [PATCH 27/38] Axivion: Make initial dashboard fetch a part of
authorization
Ensure that after authorization we have fetched the initial
dashboard info. If the fetch fails, we remove the stored
ApiToken from the key chain.
Change-Id: I894b9d8bc8ade8ec6fa29391fafa67ad5b4fabba
Reviewed-by: Christian Stenger
---
src/plugins/axivion/axivionplugin.cpp | 106 ++++++++++++++++----------
1 file changed, 66 insertions(+), 40 deletions(-)
diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp
index 2152755fd14..be9e0be3118 100644
--- a/src/plugins/axivion/axivionplugin.cpp
+++ b/src/plugins/axivion/axivionplugin.cpp
@@ -128,8 +128,27 @@ static QString credentialKey()
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();
@@ -142,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
@@ -376,24 +395,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)
{
@@ -419,7 +420,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();
@@ -432,6 +434,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;
}
@@ -521,8 +524,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;
}
@@ -549,7 +551,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;
@@ -572,8 +574,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)
@@ -607,6 +608,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,
@@ -620,7 +644,7 @@ static Group authorizationRecipe()
Group {
passwordStorage,
dashboardStorage,
- onGroupSetup(onDashboardGroupSetup),
+ onGroupSetup(onPasswordGroupSetup),
Group { // GET DashboardInfoDto
finishAllAndSuccess,
dtoRecipe(dashboardStorage)
@@ -631,6 +655,13 @@ static Group authorizationRecipe()
dtoRecipe(apiTokenStorage),
CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error)
}
+ },
+ Group {
+ finishAllAndSuccess,
+ dashboardStorage,
+ onGroupSetup(onDashboardGroupSetup),
+ dtoRecipe(dashboardStorage),
+ CredentialQueryTask(onDeleteCredentialSetup)
}
}
};
@@ -671,27 +702,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;
}
@@ -770,7 +796,7 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName)
};
const Group root {
- dashboardInfoRecipe(),
+ authorizationRecipe(),
TaskTreeTask(onTaskTreeSetup)
};
m_taskTreeRunner.start(root);
From f3a53dfd8cb41d52b53edd86ab523bc79632a827 Mon Sep 17 00:00:00 2001
From: Jarek Kobus
Date: Wed, 28 Feb 2024 01:25:36 +0100
Subject: [PATCH 28/38] Axivion: Use more dashboard url instead of server url
Change-Id: I1fbc1dad779e9198206cb8ef4a4029a99af6861f
Reviewed-by:
Reviewed-by: Christian Stenger
---
src/plugins/axivion/axivionplugin.cpp | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/src/plugins/axivion/axivionplugin.cpp b/src/plugins/axivion/axivionplugin.cpp
index be9e0be3118..06c0d72aac4 100644
--- a/src/plugins/axivion/axivionplugin.cpp
+++ b/src/plugins/axivion/axivionplugin.cpp
@@ -345,7 +345,9 @@ 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;
@@ -582,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};
@@ -790,8 +792,8 @@ 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;
};
From 68dab97bc567a2236e943653680ba7b29ec25945 Mon Sep 17 00:00:00 2001
From: Christian Kandeler
Date: Tue, 27 Feb 2024 17:19:56 +0100
Subject: [PATCH 29/38] CppEditor: Fix running tests with clangd
Try harder to find a suitable kit:
- Pick one without warnings.
- Pick one with a Qt, as otherwise configureAsExampleProject()
will fail.
Change-Id: I5a38e86bd81840c6794de365980618c904936d3a
Reviewed-by: Christian Stenger
---
.../cppeditor/followsymbol_switchmethoddecldef_test.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
index d18e8dc391c..6597a261c47 100644
--- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
+++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
@@ -494,8 +494,10 @@ void FollowSymbolTest::initTestCase()
return;
// Find suitable kit.
+ // Qt is not actually required for the tests, but we need it for consistency with
+ // configureAsExampleProject().
F2TestCase::m_testKit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
- return k->isValid();
+ return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid();
});
if (!F2TestCase::m_testKit)
QSKIP("This test requires at least one kit to be present");
From 576747d6239f3f47e267e1e614cc5f295ec4b8e2 Mon Sep 17 00:00:00 2001
From: Christian Kandeler
Date: Tue, 27 Feb 2024 18:20:33 +0100
Subject: [PATCH 30/38] CppEditor: Mark some tests as XFAIL when running with
clangd
These are fuzzy decl/def switches, where clangd does not fail, but
returns the original location.
Change-Id: I86814c2dc5e1b8ec9085fd1486c0120e2f98a3b6
Reviewed-by: Christian Stenger
---
.../cppeditor/followsymbol_switchmethoddecldef_test.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
index 6597a261c47..9bef50b0b8a 100644
--- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
+++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp
@@ -396,6 +396,12 @@ F2TestCase::F2TestCase(CppEditorAction action,
BaseTextEditor *currentTextEditor = dynamic_cast(currentEditor);
QVERIFY(currentTextEditor);
+ if (useClangd) {
+ QEXPECT_FAIL("matchFunctionSignatureFuzzy1Forward", "clangd returns decl loc", Abort);
+ QEXPECT_FAIL("matchFunctionSignatureFuzzy2Forward", "clangd returns decl loc", Abort);
+ QEXPECT_FAIL("matchFunctionSignatureFuzzy1Backward", "clangd returns def loc", Abort);
+ QEXPECT_FAIL("matchFunctionSignatureFuzzy2Backward", "clangd returns def loc", Abort);
+ }
QCOMPARE(currentTextEditor->document()->filePath(), targetTestFile->filePath());
int expectedLine, expectedColumn;
if (useClangd && expectedVirtualFunctionProposal.size() == 1) {
@@ -496,6 +502,7 @@ void FollowSymbolTest::initTestCase()
// Find suitable kit.
// Qt is not actually required for the tests, but we need it for consistency with
// configureAsExampleProject().
+ // FIXME: Make configureAsExampleProject() work with non-Qt kits.
F2TestCase::m_testKit = Utils::findOr(KitManager::kits(), nullptr, [](const Kit *k) {
return k->isValid() && !k->hasWarning() && k->value("QtSupport.QtInformation").isValid();
});
From 57874c9c0d652c316f08058ba935f70b6cc3e68d Mon Sep 17 00:00:00 2001
From: Leena Miettinen
Date: Wed, 28 Feb 2024 14:51:26 +0100
Subject: [PATCH 31/38] Doc: Update Git docs
- Git Branches view now shows the differences between
local branch and origin
- The + button in Git Branches adds a branch
Task-number: QTCREATORBUG-30209
Change-Id: I75909c7c196b0fd81ca38749e456bc80187b822d
Reviewed-by: Orgad Shaneh
---
.../images/qtcreator-git-add-branch.webp | Bin 0 -> 3614 bytes
.../images/qtcreator-git-branches.webp | Bin 0 -> 5506 bytes
.../images/qtcreator-vcs-gitbranch.png | Bin 6812 -> 0 bytes
doc/qtcreator/src/vcs/creator-vcs-git.qdoc | 21 +++++++++++++++---
4 files changed, 18 insertions(+), 3 deletions(-)
create mode 100644 doc/qtcreator/images/qtcreator-git-add-branch.webp
create mode 100644 doc/qtcreator/images/qtcreator-git-branches.webp
delete mode 100644 doc/qtcreator/images/qtcreator-vcs-gitbranch.png
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 0000000000000000000000000000000000000000..19782ad4fe953721e3275841b53ce959f9ef60e7
GIT binary patch
literal 3614
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
literal 0
HcmV?d00001
diff --git a/doc/qtcreator/images/qtcreator-git-branches.webp b/doc/qtcreator/images/qtcreator-git-branches.webp
new file mode 100644
index 0000000000000000000000000000000000000000..a2b343461d19c08817e599b798fb889414bd3da7
GIT binary patch
literal 5506
zcmWIYbaSf`Wnc(*bqWXzu<*$hWnj=>>KMpymF@AiUYX9n(`zfgUouuwJMf`bTWOPy
z^8%gd-|INoR=@a{tH$oW!st@=6Rqbw8C=CS_TrYS1$nDpB+uGtb;bVW+nUtV@BW|6
zePT0d)4gl^ez7*~V|#q(_l(KwYR}2d{UgNmN1|rwmH*%Whi}vnKY8Yct#!4qh~m*V
zoY@*phi|S?U|FSU%HfrLSVfvr1>)ep;GYxJFYqC8lTfjcu!+c_^
zxHP?;^vgTBTey+kSe-wngiAe-WBw
zZ|56WG2sD2L3Ma@zh8>=yt|ifryQHn7W4P-pP$WU4<)*~ulZ!|OHmeVd);C>@!UN-
z%b5u>CfkLT?&ogN=y)=F;YM{n)9>bM1D&1(_Fhjua5$9tYNOKug@qT76)Cy#IaUa`
z3-+^dU#!}6ql~qt_qA1CvOt%FOVWZbZpq2=Zb=(H2t9Ua3wdgE?`7^bbo~fLEWX75T6&~ldm5aOD+14v?eGoX)cZlKok;T39
zZDfOecF4`o3swJdw0lL_o?T1X%g*rWuRHhgmu~YZlPS;y9
z8xNR%dX*BF@$pG=S45!)TYs6}N3~-Wb-$mz{CQ^QRi7L0Gd16TtFUh?n=7z|arGAQ
zzy~Tzrky!y(PMQ-OEk3pjeRJ?HKmGG&(0gAW*&Hd-)BL!=ePND*R%6HRDY5GDK~L~
zh}21Mo>g|6H+Jqk_d=j%M%+wIi}!AeP8-C?9uk=$$j!ky`RKfbR*rT*)fAKLCvaO#
zzhunZ7d0W^dE&Ca6Y`}mNjC2~pJO1AeKNPJvv5@*+qwJK4lr_a%X8#4@TWX+V*0~y
zZrMME;%n=VwaivwE~!d2mtOz1=?=vMoT-iDO
zh-mfqcN)CM6AUM=4hU=suyDF?)IxOkH|N*yW43InO)E&YV}2;jsa||y-?y}1-rfeu
zk^(z8Syhi%ytL)fZrOOE-8C<{EUfi+=*7C!TPOT2KX9s~Eu6A6e8$wJ*RR+eo|d*&?D4JI
z_kR~nIU(NLqZb&rMTlYkSqE{?kB=U?dp_E<^ZVPSJBuPbrwTqe95W$m)7h`5_ZfXK
z?OkGVL8jIA>bg6=nen-DZ&x33oxSAzfz<1_Y7=v=9df9CH>Dvp;NrZbCjD8m+Cot?
zH0SFH3JHWid{J^yO7p-^$=9Nn*myJkuljoT-KATadXE_n7QQRfzV+jgfyB>A7kTpP
zRF7J4EaRt4ex?pXQmr!KQfX&$<>Ah&AGrSx=Z&yOXA_w
zV>8!n=em?wdy%_u1KLreMDHJQh^vga}=fATnSP@KWUexcb_?yX0ox21_@J&QJo
zUS`Js-dikn<<#$ddi%bfe=pnr{(tGY|3Cj7be2R=>I`?`~_UiM`zYBzVeY&{WI$t+f@Q8zF)Q{|^
zf8=8QgRA}r$0j&MOs*i*oA#cWG{$#otN-M{7I|AoAwiVhwPGLkBeT^SKJ>tEcyuy%)6wDj_`
z*N?qCsaCDT=iVijtHS#>K-|NFL6%YNVf9wiY3tNOtwdH%VLa*jRpg*sma_ovT0tv7X%=giqN+y!S_xBfn)AtuJ69o6Nz&T+!EEUTVpJGj(7-Q4~^WxdM#I4(_}-(HJ1
zPT}>LwA3=PYB7_~S;OA7)iV`!*mxR_U(S_W(0njUNB?x>B-vLG0eVdokx>%;l@j*MhcUiEfq151$`t9p(VE?Z%;F_%M!jYW3W;RL?^
z9sUQNs4n;t!>#fvs=3Qj@x5fyH4{bEC}TnSEZ>~jOverg$cRkXs^66P$0o_kR_u|O
z*ZGA9?zUFMA9^5Vbz}C&8&0e#GSO`>-bb8d{M7m}c3#V&MF(8G!(yCnW|_|6u=Z #%Lln;8$6D2b&t#FShq
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
T9zON|*F1|o-^)WTGcy1HJ^ivg
literal 0
HcmV?d00001
diff --git a/doc/qtcreator/images/qtcreator-vcs-gitbranch.png b/doc/qtcreator/images/qtcreator-vcs-gitbranch.png
deleted file mode 100644
index f6a5586eac207f7f4b873f9bee634ee64fdd983f..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 6812
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 |