Merge remote-tracking branch 'origin/13.0'
Conflicts: src/plugins/android/androidsettingswidget.cpp Change-Id: Ifcb16aa16c7bc2792de25d0ee7a22cf0e39a05f8
11
dist/changelog/changes-13.0.0.md
vendored
@@ -39,6 +39,8 @@ General
|
|||||||
for searching in `Files in File System`
|
for searching in `Files in File System`
|
||||||
* Added `Copy to Clipboard` to the `About Qt Creator` dialog
|
* Added `Copy to Clipboard` to the `About Qt Creator` dialog
|
||||||
([QTCREATORBUG-29886](https://bugreports.qt.io/browse/QTCREATORBUG-29886))
|
([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
|
Editing
|
||||||
-------
|
-------
|
||||||
@@ -78,6 +80,8 @@ Editing
|
|||||||
* Clangd
|
* Clangd
|
||||||
* Fixed that `Follow Symbol Under Cursor` only worked for exact matches
|
* Fixed that `Follow Symbol Under Cursor` only worked for exact matches
|
||||||
([QTCREATORBUG-29814](https://bugreports.qt.io/browse/QTCREATORBUG-29814))
|
([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
|
### QML
|
||||||
|
|
||||||
@@ -133,6 +137,8 @@ Projects
|
|||||||
([QTCREATORBUG-29530](https://bugreports.qt.io/browse/QTCREATORBUG-29530))
|
([QTCREATORBUG-29530](https://bugreports.qt.io/browse/QTCREATORBUG-29530))
|
||||||
* Added a file wizard for Qt translation (`.ts`) files
|
* Added a file wizard for Qt translation (`.ts`) files
|
||||||
([QTCREATORBUG-29775](https://bugreports.qt.io/browse/QTCREATORBUG-29775))
|
([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
|
* Improved the environment settings by making the changes explicit in a
|
||||||
separate, text-based editor
|
separate, text-based editor
|
||||||
* Increased the maximum width of the target selector
|
* Increased the maximum width of the target selector
|
||||||
@@ -184,6 +190,9 @@ Debugging
|
|||||||
### C++
|
### C++
|
||||||
|
|
||||||
* Added a pretty printer for `std::tuple`
|
* 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
|
* Fixed that breakpoints were not hit while the message dialog about missing
|
||||||
debug information was shown
|
debug information was shown
|
||||||
([QTCREATORBUG-30168](https://bugreports.qt.io/browse/QTCREATORBUG-30168))
|
([QTCREATORBUG-30168](https://bugreports.qt.io/browse/QTCREATORBUG-30168))
|
||||||
@@ -270,6 +279,7 @@ Andre Hartmann
|
|||||||
André Pönitz
|
André Pönitz
|
||||||
Andreas Loth
|
Andreas Loth
|
||||||
Artem Sokolovskii
|
Artem Sokolovskii
|
||||||
|
Assam Boudjelthia
|
||||||
Brook Cronin
|
Brook Cronin
|
||||||
Burak Hancerli
|
Burak Hancerli
|
||||||
Christian Kandeler
|
Christian Kandeler
|
||||||
@@ -306,6 +316,7 @@ Robert Löhning
|
|||||||
Sami Shalayel
|
Sami Shalayel
|
||||||
Samuel Jose Raposo Vieira Mira
|
Samuel Jose Raposo Vieira Mira
|
||||||
Samuel Mira
|
Samuel Mira
|
||||||
|
Semih Yavuz
|
||||||
Serg Kryvonos
|
Serg Kryvonos
|
||||||
Shrief Gabr
|
Shrief Gabr
|
||||||
Sivert Krøvel
|
Sivert Krøvel
|
||||||
|
BIN
doc/qtcreator/images/qtcreator-git-add-branch.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
doc/qtcreator/images/qtcreator-git-branches.webp
Normal file
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 6.7 KiB |
@@ -99,7 +99,7 @@
|
|||||||
\section2 Supported GDB Versions
|
\section2 Supported GDB Versions
|
||||||
|
|
||||||
Use GDB 7.5, or later, with the Python scripting extension and Python version
|
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
|
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.
|
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.
|
On Linux, the minimum supported version is LLDB 3.8.
|
||||||
|
|
||||||
\omit
|
\section2 GDB Run Modes
|
||||||
|
|
||||||
\section2 GDB Adapter Modes
|
|
||||||
|
|
||||||
[Advanced Topic]
|
|
||||||
|
|
||||||
The GDB native debugger used internally by the debugger plugin runs in
|
The GDB native debugger used internally by the debugger plugin runs in
|
||||||
different adapter modes to cope with the variety of supported platforms and
|
different modes to cope with the variety of supported platforms and
|
||||||
environments. All GDB adapters inherit from AbstractGdbAdapter:
|
environments:
|
||||||
|
|
||||||
\list
|
\list
|
||||||
|
|
||||||
\li PlainGdbAdapter debugs locally started GUI processes. It is
|
\li Plain mode debugs locally started processes that do not need console input.
|
||||||
physically split into parts that are relevant only when Python is
|
|
||||||
available, parts relevant only when Python is not available, and
|
|
||||||
mixed code.
|
|
||||||
|
|
||||||
\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
|
\endlist
|
||||||
|
|
||||||
\endomit
|
|
||||||
|
|
||||||
\section1 Installing Native Debuggers
|
\section1 Installing Native Debuggers
|
||||||
|
|
||||||
The following sections describe installing native debuggers.
|
The following sections describe installing native debuggers.
|
||||||
|
@@ -238,6 +238,10 @@
|
|||||||
|
|
||||||
\image qtcreator-qml-js-editing.webp {QML/JS Editing preferences}
|
\image qtcreator-qml-js-editing.webp {QML/JS Editing preferences}
|
||||||
|
|
||||||
|
When using \c qmlls from Qt 6.7 or later, it is recommended
|
||||||
|
to set \l{QT_QML_GENERATE_QMLLS_INI} to \c{ON} in
|
||||||
|
\uicontrol {Initial Configuration}.
|
||||||
|
|
||||||
\sa {Manage Language Servers}{How To: Manage Language Servers},
|
\sa {Manage Language Servers}{How To: Manage Language Servers},
|
||||||
{Enabling and Disabling Messages}, {Language Servers}
|
{Enabling and Disabling Messages}, {Language Servers}
|
||||||
*/
|
*/
|
||||||
|
@@ -15,9 +15,8 @@
|
|||||||
application or the \uicontrol {Open Terminal} button to open a terminal,
|
application or the \uicontrol {Open Terminal} button to open a terminal,
|
||||||
it opens as an output view.
|
it opens as an output view.
|
||||||
|
|
||||||
To open the terminal in a separate window, select \preferences >
|
To open the terminal in a separate window, go to \preferences >
|
||||||
\uicontrol Terminal, and deselet the \uicontrol {Use internal terminal}
|
\uicontrol Terminal, and clear \uicontrol {Use internal terminal}.
|
||||||
check box.
|
|
||||||
|
|
||||||
On Linux and \macos, you can set the terminal to open by selecting
|
On Linux and \macos, you can set the terminal to open by selecting
|
||||||
\preferences > \uicontrol Environment > \uicontrol System.
|
\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,
|
\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
|
\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.
|
\l Projects view, hover the mouse over them, and press \key Ctrl.
|
||||||
|
@@ -309,10 +309,11 @@
|
|||||||
\section2 Working with Branches
|
\section2 Working with Branches
|
||||||
|
|
||||||
To work with Git branches, select \uicontrol {Branches}. The
|
To work with Git branches, select \uicontrol {Branches}. The
|
||||||
\uicontrol {Git Branches} sidebar view shows the checked out
|
\uicontrol {Git Branches} view shows a list of branches, as well
|
||||||
branch in bold and underlined in the list of branches.
|
as the differences between your local branches and their origin.
|
||||||
|
The branch you checked out is shown in bold and underlined.
|
||||||
|
|
||||||
\image qtcreator-vcs-gitbranch.png {Git Branches sidebar view}
|
\image qtcreator-git-branches.webp {Git Branches view}
|
||||||
|
|
||||||
Old entries and tags are filtered out of the list of branches
|
Old entries and tags are filtered out of the list of branches
|
||||||
by default. To include them, select \inlineimage icons/filtericon.png
|
by default. To include them, select \inlineimage icons/filtericon.png
|
||||||
@@ -332,6 +333,20 @@
|
|||||||
To refresh the list of branches, select \inlineimage icons/reload_gray.png
|
To refresh the list of branches, select \inlineimage icons/reload_gray.png
|
||||||
(\uicontrol Refresh).
|
(\uicontrol Refresh).
|
||||||
|
|
||||||
|
\section3 Adding Branches
|
||||||
|
|
||||||
|
To create a new tracking or non-tracking branch, select
|
||||||
|
\inlineimage icons/plus.png.
|
||||||
|
|
||||||
|
\image qtcreator-git-add-branch.webp {Add Branch dialog}
|
||||||
|
|
||||||
|
To check out the branch when creating it, select
|
||||||
|
\uicontrol {Checkout new branch}.
|
||||||
|
|
||||||
|
To track the selected branch, select \uicontrol {Track local branch}.
|
||||||
|
|
||||||
|
\section3 Managing Branches
|
||||||
|
|
||||||
The context menu for a branch has the following functions:
|
The context menu for a branch has the following functions:
|
||||||
|
|
||||||
\table
|
\table
|
||||||
|
@@ -11,7 +11,7 @@ from utils import TypeCode
|
|||||||
|
|
||||||
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
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):
|
class FakeVoidType(cdbext.Type):
|
||||||
@@ -84,10 +84,9 @@ class Dumper(DumperBase):
|
|||||||
self.check(isinstance(nativeValue, cdbext.Value))
|
self.check(isinstance(nativeValue, cdbext.Value))
|
||||||
val = self.Value(self)
|
val = self.Value(self)
|
||||||
val.name = nativeValue.name()
|
val.name = nativeValue.name()
|
||||||
val._type = self.fromNativeType(nativeValue.type())
|
|
||||||
# There is no cdb api for the size of bitfields.
|
# There is no cdb api for the size of bitfields.
|
||||||
# Workaround this issue by parsing the native debugger text for integral types.
|
# 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:
|
try:
|
||||||
integerString = nativeValue.nativeDebuggerValue()
|
integerString = nativeValue.nativeDebuggerValue()
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
@@ -106,16 +105,18 @@ class Dumper(DumperBase):
|
|||||||
base = 16
|
base = 16
|
||||||
else:
|
else:
|
||||||
base = 10
|
base = 10
|
||||||
signed = not val._type.name.startswith('unsigned')
|
signed = not nativeValue.type().name().startswith('unsigned')
|
||||||
try:
|
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)
|
byteorder='little', signed=signed)
|
||||||
except:
|
except:
|
||||||
# read raw memory in case the integerString can not be interpreted
|
# read raw memory in case the integerString can not be interpreted
|
||||||
pass
|
pass
|
||||||
if val._type.code == TypeCode.Enum:
|
if nativeValue.type().code() == TypeCode.Enum:
|
||||||
val.ldisplay = self.enumValue(nativeValue)
|
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.nativeValue = nativeValue
|
||||||
val.laddress = nativeValue.address()
|
val.laddress = nativeValue.address()
|
||||||
val.lbitsize = nativeValue.bitsize()
|
val.lbitsize = nativeValue.bitsize()
|
||||||
@@ -136,6 +137,9 @@ class Dumper(DumperBase):
|
|||||||
for f in nativeType.fields()])
|
for f in nativeType.fields()])
|
||||||
return typeId
|
return typeId
|
||||||
|
|
||||||
|
def nativeValueType(self, nativeValue):
|
||||||
|
return self.fromNativeType(nativeValue.type())
|
||||||
|
|
||||||
def fromNativeType(self, nativeType):
|
def fromNativeType(self, nativeType):
|
||||||
self.check(isinstance(nativeType, cdbext.Type))
|
self.check(isinstance(nativeType, cdbext.Type))
|
||||||
typeId = self.nativeTypeId(nativeType)
|
typeId = self.nativeTypeId(nativeType)
|
||||||
@@ -150,51 +154,66 @@ class Dumper(DumperBase):
|
|||||||
if nativeType.name().startswith('<function>'):
|
if nativeType.name().startswith('<function>'):
|
||||||
code = TypeCode.Function
|
code = TypeCode.Function
|
||||||
elif nativeType.targetName() != nativeType.name():
|
elif nativeType.targetName() != nativeType.name():
|
||||||
targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId())
|
return self.createPointerType(nativeType.targetName())
|
||||||
if targetType is not None and targetType is not nativeType:
|
|
||||||
return self.createPointerType(targetType)
|
|
||||||
|
|
||||||
if code == TypeCode.Array:
|
if code == TypeCode.Array:
|
||||||
# cdb reports virtual function tables as arrays those ar handled separetly by
|
# cdb reports virtual function tables as arrays those ar handled separetly by
|
||||||
# the DumperBase. Declare those types as structs prevents a lookup to a
|
# the DumperBase. Declare those types as structs prevents a lookup to a
|
||||||
# none existing type
|
# none existing type
|
||||||
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
|
if not nativeType.name().startswith('__fptr()') and not nativeType.name().startswith('<gentype '):
|
||||||
targetType = self.lookupType(nativeType.targetName(), nativeType.moduleId())
|
targetName = nativeType.targetName()
|
||||||
if targetType is not None:
|
count = nativeType.arrayElements()
|
||||||
return self.createArrayType(targetType, nativeType.arrayElements())
|
if targetName.endswith(']'):
|
||||||
|
(prefix, suffix, inner_count) = self.splitArrayType(targetName)
|
||||||
|
type_name = '%s[%d][%d]%s' % (prefix, count, inner_count, suffix)
|
||||||
|
else:
|
||||||
|
type_name = '%s[%d]' % (targetName, count)
|
||||||
|
tdata = self.TypeData(self, typeId)
|
||||||
|
tdata.name = type_name
|
||||||
|
tdata.code = TypeCode.Array
|
||||||
|
tdata.ltarget = targetName
|
||||||
|
tdata.lbitsize = lambda: nativeType.bitsize()
|
||||||
|
return self.Type(self, typeId)
|
||||||
|
|
||||||
code = TypeCode.Struct
|
code = TypeCode.Struct
|
||||||
|
|
||||||
tdata = self.TypeData(self, typeId)
|
tdata = self.TypeData(self, typeId)
|
||||||
tdata.name = nativeType.name()
|
tdata.name = nativeType.name()
|
||||||
tdata.lbitsize = nativeType.bitsize()
|
tdata.lbitsize = lambda: nativeType.bitsize()
|
||||||
tdata.code = code
|
tdata.code = code
|
||||||
tdata.moduleName = nativeType.module()
|
tdata.moduleName = lambda: nativeType.module()
|
||||||
if code == TypeCode.Struct:
|
if code == TypeCode.Struct:
|
||||||
tdata.lfields = lambda value: \
|
tdata.lfields = lambda value: \
|
||||||
self.listFields(nativeType, value)
|
self.listFields(nativeType, value)
|
||||||
tdata.lalignment = lambda: \
|
tdata.lalignment = lambda: \
|
||||||
self.nativeStructAlignment(nativeType)
|
self.nativeStructAlignment(nativeType)
|
||||||
if code == TypeCode.Enum:
|
tdata.enumDisplay = lambda intval, addr, form: \
|
||||||
tdata.enumDisplay = lambda intval, addr, form: \
|
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
||||||
self.nativeTypeEnumDisplay(nativeType, intval, form)
|
|
||||||
tdata.templateArguments = lambda: \
|
tdata.templateArguments = lambda: \
|
||||||
self.listTemplateParameters(nativeType.name())
|
self.listTemplateParameters(nativeType.name())
|
||||||
return self.Type(self, typeId)
|
return self.Type(self, typeId)
|
||||||
|
|
||||||
def listFields(self, nativeType, value):
|
def listNativeValueChildren(self, nativeValue):
|
||||||
if value.address() is None or value.address() == 0:
|
|
||||||
raise Exception("")
|
|
||||||
nativeValue = value.nativeValue
|
|
||||||
if nativeValue is None:
|
|
||||||
nativeValue = cdbext.createValue(value.address(), nativeType)
|
|
||||||
index = 0
|
index = 0
|
||||||
nativeMember = nativeValue.childFromIndex(index)
|
nativeMember = nativeValue.childFromIndex(index)
|
||||||
while nativeMember is not None:
|
while nativeMember:
|
||||||
if nativeMember.address() != 0:
|
if nativeMember.address() != 0:
|
||||||
yield self.fromNativeValue(nativeMember)
|
yield self.fromNativeValue(nativeMember)
|
||||||
index += 1
|
index += 1
|
||||||
nativeMember = nativeValue.childFromIndex(index)
|
nativeMember = nativeValue.childFromIndex(index)
|
||||||
|
|
||||||
|
def listValueChildren(self, value):
|
||||||
|
nativeValue = value.nativeValue
|
||||||
|
if nativeValue is None:
|
||||||
|
nativeValue = cdbext.createValue(value.address(), self.lookupNativeType(value.type.name, 0))
|
||||||
|
return self.listNativeValueChildren(nativeValue)
|
||||||
|
|
||||||
|
def listFields(self, nativeType, value):
|
||||||
|
nativeValue = value.nativeValue
|
||||||
|
if nativeValue is None:
|
||||||
|
nativeValue = cdbext.createValue(value.address(), nativeType)
|
||||||
|
return self.listNativeValueChildren(nativeValue)
|
||||||
|
|
||||||
def nativeStructAlignment(self, nativeType):
|
def nativeStructAlignment(self, nativeType):
|
||||||
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
|
#DumperBase.warn("NATIVE ALIGN FOR %s" % nativeType.name)
|
||||||
def handleItem(nativeFieldType, align):
|
def handleItem(nativeFieldType, align):
|
||||||
@@ -307,6 +326,7 @@ class Dumper(DumperBase):
|
|||||||
namespaceIndex = name.find('::')
|
namespaceIndex = name.find('::')
|
||||||
if namespaceIndex > 0:
|
if namespaceIndex > 0:
|
||||||
namespace = name[:namespaceIndex + 2]
|
namespace = name[:namespaceIndex + 2]
|
||||||
|
self.qtNamespace = lambda: namespace
|
||||||
self.qtCustomEventFunc = self.parseAndEvaluate(
|
self.qtCustomEventFunc = self.parseAndEvaluate(
|
||||||
'%s!%sQObject::customEvent' %
|
'%s!%sQObject::customEvent' %
|
||||||
(self.qtCoreModuleName(), namespace)).address()
|
(self.qtCoreModuleName(), namespace)).address()
|
||||||
@@ -498,7 +518,7 @@ class Dumper(DumperBase):
|
|||||||
else:
|
else:
|
||||||
val = self.Value(self)
|
val = self.Value(self)
|
||||||
val.laddress = value.pointer()
|
val.laddress = value.pointer()
|
||||||
val._type = value.type.dereference()
|
val._type = DumperBase.Type(self, value.type.targetName)
|
||||||
val.nativeValue = value.nativeValue
|
val.nativeValue = value.nativeValue
|
||||||
|
|
||||||
return val
|
return val
|
||||||
@@ -519,3 +539,424 @@ class Dumper(DumperBase):
|
|||||||
def symbolAddress(self, symbolName):
|
def symbolAddress(self, symbolName):
|
||||||
res = self.nativeParseAndEvaluate(symbolName)
|
res = self.nativeParseAndEvaluate(symbolName)
|
||||||
return None if res is None else res.address()
|
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
|
||||||
|
@@ -3041,7 +3041,7 @@ class DumperBase():
|
|||||||
or self.type.name.startswith('unsigned ') \
|
or self.type.name.startswith('unsigned ') \
|
||||||
or self.type.name.find(' unsigned ') != -1
|
or self.type.name.find(' unsigned ') != -1
|
||||||
if bitsize is None:
|
if bitsize is None:
|
||||||
bitsize = self.type.bitsize()
|
bitsize = self.type.lbitsize
|
||||||
return self.extractInteger(bitsize, unsigned)
|
return self.extractInteger(bitsize, unsigned)
|
||||||
|
|
||||||
def floatingPoint(self):
|
def floatingPoint(self):
|
||||||
@@ -3512,26 +3512,40 @@ class DumperBase():
|
|||||||
tdata.moduleName = self.moduleName
|
tdata.moduleName = self.moduleName
|
||||||
return tdata
|
return tdata
|
||||||
|
|
||||||
|
@property
|
||||||
|
def bitsize(self):
|
||||||
|
if callable(self.lbitsize):
|
||||||
|
self.lbitsize = self.lbitsize()
|
||||||
|
return self.lbitsize
|
||||||
|
|
||||||
class Type():
|
class Type():
|
||||||
def __init__(self, dumper, typeId):
|
def __init__(self, dumper, typeId):
|
||||||
self.typeId = typeId
|
self.typeId = typeId.replace('@', dumper.qtNamespace())
|
||||||
self.dumper = dumper
|
self.dumper = dumper
|
||||||
self.tdata = dumper.typeData.get(typeId, None)
|
self.initialized = False
|
||||||
if self.tdata is None:
|
|
||||||
#DumperBase.warn('USING : %s' % self.typeId)
|
|
||||||
self.dumper.lookupType(self.typeId)
|
|
||||||
self.tdata = self.dumper.typeData.get(self.typeId)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
#return self.typeId
|
#return self.typeId
|
||||||
return self.stringify()
|
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
|
@property
|
||||||
def name(self):
|
def name(self):
|
||||||
tdata = self.dumper.typeData.get(self.typeId)
|
return self.typeId if self.tdata is None else self.tdata.name
|
||||||
if tdata is None:
|
|
||||||
return self.typeId
|
|
||||||
return tdata.name
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def code(self):
|
def code(self):
|
||||||
@@ -3539,7 +3553,7 @@ class DumperBase():
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def lbitsize(self):
|
def lbitsize(self):
|
||||||
return self.tdata.lbitsize
|
return self.tdata.bitsize
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lbitpos(self):
|
def lbitpos(self):
|
||||||
@@ -3547,15 +3561,25 @@ class DumperBase():
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def ltarget(self):
|
def ltarget(self):
|
||||||
|
if isinstance(self.tdata.ltarget, str):
|
||||||
|
self.tdata.ltarget = self.dumper.createType(self.tdata.ltarget)
|
||||||
return 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
|
@property
|
||||||
def moduleName(self):
|
def moduleName(self):
|
||||||
|
if callable(self.tdata.moduleName):
|
||||||
|
self.tdata.moduleName = self.tdata.moduleName()
|
||||||
return self.tdata.moduleName
|
return self.tdata.moduleName
|
||||||
|
|
||||||
def stringify(self):
|
def stringify(self):
|
||||||
return 'Type(name="%s",bsize=%s,code=%s)' \
|
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):
|
def __getitem__(self, index):
|
||||||
if self.dumper.isInt(index):
|
if self.dumper.isInt(index):
|
||||||
@@ -3659,7 +3683,7 @@ class DumperBase():
|
|||||||
|
|
||||||
def alignment(self):
|
def alignment(self):
|
||||||
if self.tdata.code == TypeCode.Typedef:
|
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.code in (TypeCode.Integral, TypeCode.Float, TypeCode.Enum):
|
||||||
if self.tdata.name in ('double', 'long long', 'unsigned long long'):
|
if self.tdata.name in ('double', 'long long', 'unsigned long long'):
|
||||||
# Crude approximation.
|
# Crude approximation.
|
||||||
@@ -3678,7 +3702,7 @@ class DumperBase():
|
|||||||
return self.dumper.createPointerType(self)
|
return self.dumper.createPointerType(self)
|
||||||
|
|
||||||
def target(self):
|
def target(self):
|
||||||
return self.tdata.ltarget
|
return self.ltarget
|
||||||
|
|
||||||
def stripTypedefs(self):
|
def stripTypedefs(self):
|
||||||
if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef:
|
if isinstance(self, self.dumper.Type) and self.code != TypeCode.Typedef:
|
||||||
@@ -3687,7 +3711,7 @@ class DumperBase():
|
|||||||
return self.ltarget
|
return self.ltarget
|
||||||
|
|
||||||
def size(self):
|
def size(self):
|
||||||
bs = self.bitsize()
|
bs = self.lbitsize
|
||||||
if bs % 8 != 0:
|
if bs % 8 != 0:
|
||||||
DumperBase.warn('ODD SIZE: %s' % self)
|
DumperBase.warn('ODD SIZE: %s' % self)
|
||||||
return (7 + bs) >> 3
|
return (7 + bs) >> 3
|
||||||
@@ -3797,12 +3821,12 @@ class DumperBase():
|
|||||||
return val
|
return val
|
||||||
|
|
||||||
def createPointerType(self, targetType):
|
def createPointerType(self, targetType):
|
||||||
if not isinstance(targetType, self.Type):
|
if not isinstance(targetType, (str, self.Type)):
|
||||||
raise RuntimeError('Expected type in createPointerType(), got %s'
|
raise RuntimeError('Expected type or str in createPointerType(), got %s'
|
||||||
% type(targetType))
|
% type(targetType))
|
||||||
typeId = targetType.typeId + ' *'
|
typeId = (targetType if isinstance(targetType, str) else targetType.typeId) + ' *'
|
||||||
tdata = self.TypeData(self, 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.lbitsize = 8 * self.ptrSize()
|
||||||
tdata.code = TypeCode.Pointer
|
tdata.code = TypeCode.Pointer
|
||||||
tdata.ltarget = targetType
|
tdata.ltarget = targetType
|
||||||
@@ -3927,7 +3951,7 @@ class DumperBase():
|
|||||||
tdata = self.typeData.get(typish, None)
|
tdata = self.typeData.get(typish, None)
|
||||||
if tdata is not None:
|
if tdata is not None:
|
||||||
if tdata.lbitsize 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)
|
return self.Type(self, typish)
|
||||||
|
|
||||||
knownType = self.lookupType(typish)
|
knownType = self.lookupType(typish)
|
||||||
@@ -3944,7 +3968,7 @@ class DumperBase():
|
|||||||
if typish.endswith('*'):
|
if typish.endswith('*'):
|
||||||
tdata.code = TypeCode.Pointer
|
tdata.code = TypeCode.Pointer
|
||||||
tdata.lbitsize = 8 * self.ptrSize()
|
tdata.lbitsize = 8 * self.ptrSize()
|
||||||
tdata.ltarget = self.createType(typish[:-1].strip())
|
tdata.ltarget = typish[:-1].strip()
|
||||||
|
|
||||||
typeobj = self.Type(self, tdata.typeId)
|
typeobj = self.Type(self, tdata.typeId)
|
||||||
#DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify())
|
#DumperBase.warn('CREATE TYPE: %s' % typeobj.stringify())
|
||||||
|
@@ -419,7 +419,7 @@ class Dumper(DumperBase):
|
|||||||
targetTypeName = typeName[0:pos1].strip()
|
targetTypeName = typeName[0:pos1].strip()
|
||||||
#DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName)
|
#DumperBase.warn("TARGET TYPENAME: %s" % targetTypeName)
|
||||||
targetType = self.fromNativeType(nativeTargetType)
|
targetType = self.fromNativeType(nativeTargetType)
|
||||||
targetType.tdata = targetType.tdata.copy()
|
targetType.setTdata(targetType.tdata.copy())
|
||||||
targetType.tdata.name = targetTypeName
|
targetType.tdata.name = targetTypeName
|
||||||
return self.createArrayType(targetType, count)
|
return self.createArrayType(targetType, count)
|
||||||
if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
|
if hasattr(nativeType, 'GetVectorElementType'): # New in 3.8(?) / 350.x
|
||||||
@@ -1583,7 +1583,8 @@ class Dumper(DumperBase):
|
|||||||
result += ',ignorecount="%d"' % loc.GetIgnoreCount()
|
result += ',ignorecount="%d"' % loc.GetIgnoreCount()
|
||||||
result += ',file="%s"' % toCString(lineEntry.GetFileSpec())
|
result += ',file="%s"' % toCString(lineEntry.GetFileSpec())
|
||||||
result += ',line="%d"' % lineEntry.GetLine()
|
result += ',line="%d"' % lineEntry.GetLine()
|
||||||
result += ',addr="%s"},' % addr.GetFileAddress()
|
result += ',addr="%s"' % addr.GetLoadAddress(self.target)
|
||||||
|
result += ',faddr="%s"},' % addr.GetFileAddress()
|
||||||
result += ']'
|
result += ']'
|
||||||
if lineEntry is not None:
|
if lineEntry is not None:
|
||||||
result += ',file="%s"' % toCString(lineEntry.GetFileSpec())
|
result += ',file="%s"' % toCString(lineEntry.GetFileSpec())
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=dark.figmatokens
|
||||||
ThemeName=Dark
|
ThemeName=Dark
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
DefaultTextEditorColorScheme=dark.xml
|
DefaultTextEditorColorScheme=dark.xml
|
||||||
@@ -406,39 +407,6 @@ Debugger_WatchItem_ValueChanged=ffff6666
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - dark mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff8f8f8
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ff1f1f1f
|
|
||||||
Token_Background_Muted=ff262626
|
|
||||||
Token_Background_Subtle=ff2e2e2e
|
|
||||||
Token_Foreground_Default=ff5a5a5a
|
|
||||||
Token_Foreground_Muted=ff3e3e3e
|
|
||||||
Token_Foreground_Subtle=ff303030
|
|
||||||
Token_Text_Default=fff8f8f8
|
|
||||||
Token_Text_Muted=ffaeaeae
|
|
||||||
Token_Text_Subtle=ff595959
|
|
||||||
Token_Stroke_Strong=ffeeeeee
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ff3a3a3a
|
|
||||||
Token_Notification_Alert=ffc98014
|
|
||||||
Token_Notification_Success=ff1f9b5d
|
|
||||||
Token_Notification_Neutral=ff016876
|
|
||||||
Token_Notification_Danger=ffb22245
|
|
||||||
|
|
||||||
Welcome_TextColor=text
|
|
||||||
Welcome_ForegroundPrimaryColor=ffa3a3a3
|
|
||||||
Welcome_ForegroundSecondaryColor=ff808080
|
|
||||||
Welcome_BackgroundPrimaryColor=normalBackground
|
|
||||||
Welcome_BackgroundSecondaryColor=shadowBackground
|
|
||||||
Welcome_HoverColor=ff404040
|
|
||||||
Welcome_AccentColor=ff57d658
|
|
||||||
Welcome_LinkColor=ff67e668
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=text
|
Timeline_TextColor=text
|
||||||
Timeline_BackgroundColor1=normalBackground
|
Timeline_BackgroundColor1=normalBackground
|
||||||
Timeline_BackgroundColor2=ff444444
|
Timeline_BackgroundColor2=ff444444
|
||||||
|
31
share/qtcreator/themes/dark.figmatokens
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
; Qt Creator Color Tokens - dark mode
|
||||||
|
|
||||||
|
[Colors]
|
||||||
|
|
||||||
|
Token_Basic_Black=ff131313
|
||||||
|
Token_Basic_White=fff8f8f8
|
||||||
|
|
||||||
|
Token_Accent_Default=ff23b26a
|
||||||
|
Token_Accent_Muted=ff1f9b5d
|
||||||
|
Token_Accent_Subtle=ff1a8550
|
||||||
|
|
||||||
|
Token_Background_Default=ff1f1f1f
|
||||||
|
Token_Background_Muted=ff262626
|
||||||
|
Token_Background_Subtle=ff2e2e2e
|
||||||
|
|
||||||
|
Token_Foreground_Default=ff5a5a5a
|
||||||
|
Token_Foreground_Muted=ff3e3e3e
|
||||||
|
Token_Foreground_Subtle=ff303030
|
||||||
|
|
||||||
|
Token_Text_Default=fff8f8f8
|
||||||
|
Token_Text_Muted=ffaeaeae
|
||||||
|
Token_Text_Subtle=ff595959
|
||||||
|
|
||||||
|
Token_Stroke_Strong=ffeeeeee
|
||||||
|
Token_Stroke_Muted=ff727272
|
||||||
|
Token_Stroke_Subtle=ff3a3a3a
|
||||||
|
|
||||||
|
Token_Notification_Alert=ffc98014
|
||||||
|
Token_Notification_Success=ff1f9b5d
|
||||||
|
Token_Notification_Neutral=ff016876
|
||||||
|
Token_Notification_Danger=ffb22245
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=light.figmatokens
|
||||||
ThemeName=Classic
|
ThemeName=Classic
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
|
|
||||||
@@ -398,39 +399,6 @@ Debugger_WatchItem_ValueChanged=ffc80000
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - light mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff2f2f2
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ffe3e3e3
|
|
||||||
Token_Background_Muted=ffeeeeee
|
|
||||||
Token_Background_Subtle=fffbfbfb
|
|
||||||
Token_Foreground_Default=ffcdcdcd
|
|
||||||
Token_Foreground_Muted=ffd5d5d5
|
|
||||||
Token_Foreground_Subtle=ffdddddd
|
|
||||||
Token_Text_Default=ff393939
|
|
||||||
Token_Text_Muted=ff7c7c7c
|
|
||||||
Token_Text_Subtle=ffbebebe
|
|
||||||
Token_Stroke_Strong=ff464646
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ffcdcdcd
|
|
||||||
Token_Notification_Alert=ffeb991f
|
|
||||||
Token_Notification_Success=ff23b26a
|
|
||||||
Token_Notification_Neutral=ff0e7887
|
|
||||||
Token_Notification_Danger=ffdc1343
|
|
||||||
|
|
||||||
Welcome_TextColor=ff000000
|
|
||||||
Welcome_ForegroundPrimaryColor=shadowBackground
|
|
||||||
Welcome_ForegroundSecondaryColor=ff939393
|
|
||||||
Welcome_BackgroundPrimaryColor=fffafafa
|
|
||||||
Welcome_BackgroundSecondaryColor=ffffffff
|
|
||||||
Welcome_HoverColor=ffefefef
|
|
||||||
Welcome_AccentColor=ff45ce55
|
|
||||||
Welcome_LinkColor=ff20a020
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=darkText
|
Timeline_TextColor=darkText
|
||||||
Timeline_BackgroundColor1=ffffffff
|
Timeline_BackgroundColor1=ffffffff
|
||||||
Timeline_BackgroundColor2=fff6f6f6
|
Timeline_BackgroundColor2=fff6f6f6
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=light.figmatokens
|
||||||
ThemeName=Design Light
|
ThemeName=Design Light
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
|
|
||||||
@@ -410,39 +411,6 @@ Debugger_WatchItem_ValueChanged=ffbf0303
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - light mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff2f2f2
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ffe3e3e3
|
|
||||||
Token_Background_Muted=ffeeeeee
|
|
||||||
Token_Background_Subtle=fffbfbfb
|
|
||||||
Token_Foreground_Default=ffcdcdcd
|
|
||||||
Token_Foreground_Muted=ffd5d5d5
|
|
||||||
Token_Foreground_Subtle=ffdddddd
|
|
||||||
Token_Text_Default=ff393939
|
|
||||||
Token_Text_Muted=ff7c7c7c
|
|
||||||
Token_Text_Subtle=ffbebebe
|
|
||||||
Token_Stroke_Strong=ff464646
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ffcdcdcd
|
|
||||||
Token_Notification_Alert=ffeb991f
|
|
||||||
Token_Notification_Success=ff23b26a
|
|
||||||
Token_Notification_Neutral=ff0e7887
|
|
||||||
Token_Notification_Danger=ffdc1343
|
|
||||||
|
|
||||||
Welcome_TextColor=ff000000
|
|
||||||
Welcome_ForegroundPrimaryColor=ff404040
|
|
||||||
Welcome_ForegroundSecondaryColor=ff727272
|
|
||||||
Welcome_BackgroundPrimaryColor=ffeaeaea
|
|
||||||
Welcome_BackgroundSecondaryColor=ffefefef
|
|
||||||
Welcome_HoverColor=ffe1e1e1
|
|
||||||
Welcome_AccentColor=ff25709a
|
|
||||||
Welcome_LinkColor=ff104090
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=text
|
Timeline_TextColor=text
|
||||||
Timeline_BackgroundColor1=normalBackground
|
Timeline_BackgroundColor1=normalBackground
|
||||||
Timeline_BackgroundColor2=fff6f6f6
|
Timeline_BackgroundColor2=fff6f6f6
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=dark.figmatokens
|
||||||
ThemeName=Design Dark
|
ThemeName=Design Dark
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
DefaultTextEditorColorScheme=creator-dark.xml
|
DefaultTextEditorColorScheme=creator-dark.xml
|
||||||
@@ -414,39 +415,6 @@ Debugger_WatchItem_ValueChanged=ffff6666
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - dark mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff8f8f8
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ff1f1f1f
|
|
||||||
Token_Background_Muted=ff262626
|
|
||||||
Token_Background_Subtle=ff2e2e2e
|
|
||||||
Token_Foreground_Default=ff5a5a5a
|
|
||||||
Token_Foreground_Muted=ff3e3e3e
|
|
||||||
Token_Foreground_Subtle=ff303030
|
|
||||||
Token_Text_Default=fff8f8f8
|
|
||||||
Token_Text_Muted=ffaeaeae
|
|
||||||
Token_Text_Subtle=ff595959
|
|
||||||
Token_Stroke_Strong=ffeeeeee
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ff3a3a3a
|
|
||||||
Token_Notification_Alert=ffc98014
|
|
||||||
Token_Notification_Success=ff1f9b5d
|
|
||||||
Token_Notification_Neutral=ff016876
|
|
||||||
Token_Notification_Danger=ffb22245
|
|
||||||
|
|
||||||
Welcome_TextColor=text
|
|
||||||
Welcome_ForegroundPrimaryColor=ffa3a3a3
|
|
||||||
Welcome_ForegroundSecondaryColor=ff808080
|
|
||||||
Welcome_BackgroundPrimaryColor=ff242424
|
|
||||||
Welcome_BackgroundSecondaryColor=ff1c1c1c
|
|
||||||
Welcome_HoverColor=ff2b2a2a
|
|
||||||
Welcome_AccentColor=ff3f8ccc
|
|
||||||
Welcome_LinkColor=ff5fafef
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=text
|
Timeline_TextColor=text
|
||||||
Timeline_BackgroundColor1=normalBackground
|
Timeline_BackgroundColor1=normalBackground
|
||||||
Timeline_BackgroundColor2=ff444444
|
Timeline_BackgroundColor2=ff444444
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=dark.figmatokens
|
||||||
ThemeName=Flat Dark
|
ThemeName=Flat Dark
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
DefaultTextEditorColorScheme=creator-dark.xml
|
DefaultTextEditorColorScheme=creator-dark.xml
|
||||||
@@ -410,39 +411,6 @@ Debugger_WatchItem_ValueChanged=ffff6666
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - dark mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff8f8f8
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ff1f1f1f
|
|
||||||
Token_Background_Muted=ff262626
|
|
||||||
Token_Background_Subtle=ff2e2e2e
|
|
||||||
Token_Foreground_Default=ff5a5a5a
|
|
||||||
Token_Foreground_Muted=ff3e3e3e
|
|
||||||
Token_Foreground_Subtle=ff303030
|
|
||||||
Token_Text_Default=fff8f8f8
|
|
||||||
Token_Text_Muted=ffaeaeae
|
|
||||||
Token_Text_Subtle=ff595959
|
|
||||||
Token_Stroke_Strong=ffeeeeee
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ff3a3a3a
|
|
||||||
Token_Notification_Alert=ffc98014
|
|
||||||
Token_Notification_Success=ff1f9b5d
|
|
||||||
Token_Notification_Neutral=ff016876
|
|
||||||
Token_Notification_Danger=ffb22245
|
|
||||||
|
|
||||||
Welcome_TextColor=text
|
|
||||||
Welcome_ForegroundPrimaryColor=ff999999
|
|
||||||
Welcome_ForegroundSecondaryColor=ff808080
|
|
||||||
Welcome_BackgroundPrimaryColor=normalBackground
|
|
||||||
Welcome_BackgroundSecondaryColor=ff242628
|
|
||||||
Welcome_HoverColor=ff404243
|
|
||||||
Welcome_AccentColor=ff36c148
|
|
||||||
Welcome_LinkColor=ff5fcf4f
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=text
|
Timeline_TextColor=text
|
||||||
Timeline_BackgroundColor1=normalBackground
|
Timeline_BackgroundColor1=normalBackground
|
||||||
Timeline_BackgroundColor2=ff444444
|
Timeline_BackgroundColor2=ff444444
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=light.figmatokens
|
||||||
ThemeName=Flat Light
|
ThemeName=Flat Light
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
|
|
||||||
@@ -407,39 +408,6 @@ Debugger_WatchItem_ValueChanged=ffbf0303
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - light mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff2f2f2
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ffe3e3e3
|
|
||||||
Token_Background_Muted=ffeeeeee
|
|
||||||
Token_Background_Subtle=fffbfbfb
|
|
||||||
Token_Foreground_Default=ffcdcdcd
|
|
||||||
Token_Foreground_Muted=ffd5d5d5
|
|
||||||
Token_Foreground_Subtle=ffdddddd
|
|
||||||
Token_Text_Default=ff393939
|
|
||||||
Token_Text_Muted=ff7c7c7c
|
|
||||||
Token_Text_Subtle=ffbebebe
|
|
||||||
Token_Stroke_Strong=ff464646
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ffcdcdcd
|
|
||||||
Token_Notification_Alert=ffeb991f
|
|
||||||
Token_Notification_Success=ff23b26a
|
|
||||||
Token_Notification_Neutral=ff0e7887
|
|
||||||
Token_Notification_Danger=ffdc1343
|
|
||||||
|
|
||||||
Welcome_TextColor=ff000000
|
|
||||||
Welcome_ForegroundPrimaryColor=ff232323
|
|
||||||
Welcome_ForegroundSecondaryColor=ff939393
|
|
||||||
Welcome_BackgroundPrimaryColor=fffafafa
|
|
||||||
Welcome_BackgroundSecondaryColor=ffffffff
|
|
||||||
Welcome_HoverColor=ffefefef
|
|
||||||
Welcome_AccentColor=ff45ce55
|
|
||||||
Welcome_LinkColor=ff20a020
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=text
|
Timeline_TextColor=text
|
||||||
Timeline_BackgroundColor1=normalBackground
|
Timeline_BackgroundColor1=normalBackground
|
||||||
Timeline_BackgroundColor2=fff6f6f6
|
Timeline_BackgroundColor2=fff6f6f6
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
[General]
|
[General]
|
||||||
|
Includes=light.figmatokens
|
||||||
ThemeName=Flat
|
ThemeName=Flat
|
||||||
PreferredStyles=
|
PreferredStyles=
|
||||||
|
|
||||||
@@ -405,39 +406,6 @@ Debugger_WatchItem_ValueChanged=ffbf0303
|
|||||||
|
|
||||||
Debugger_Breakpoint_TextMarkColor=ffff4040
|
Debugger_Breakpoint_TextMarkColor=ffff4040
|
||||||
|
|
||||||
; Qt Creator Color Tokens - light mode
|
|
||||||
Token_Basic_Black=ff131313
|
|
||||||
Token_Basic_White=fff2f2f2
|
|
||||||
Token_Accent_Default=ff23b26a
|
|
||||||
Token_Accent_Muted=ff1f9b5d
|
|
||||||
Token_Accent_Subtle=ff1a8550
|
|
||||||
Token_Background_Default=ffe3e3e3
|
|
||||||
Token_Background_Muted=ffeeeeee
|
|
||||||
Token_Background_Subtle=fffbfbfb
|
|
||||||
Token_Foreground_Default=ffcdcdcd
|
|
||||||
Token_Foreground_Muted=ffd5d5d5
|
|
||||||
Token_Foreground_Subtle=ffdddddd
|
|
||||||
Token_Text_Default=ff393939
|
|
||||||
Token_Text_Muted=ff7c7c7c
|
|
||||||
Token_Text_Subtle=ffbebebe
|
|
||||||
Token_Stroke_Strong=ff464646
|
|
||||||
Token_Stroke_Muted=ff727272
|
|
||||||
Token_Stroke_Subtle=ffcdcdcd
|
|
||||||
Token_Notification_Alert=ffeb991f
|
|
||||||
Token_Notification_Success=ff23b26a
|
|
||||||
Token_Notification_Neutral=ff0e7887
|
|
||||||
Token_Notification_Danger=ffdc1343
|
|
||||||
|
|
||||||
Welcome_TextColor=ff000000
|
|
||||||
Welcome_ForegroundPrimaryColor=shadowBackground
|
|
||||||
Welcome_ForegroundSecondaryColor=ff939393
|
|
||||||
Welcome_BackgroundPrimaryColor=fffafafa
|
|
||||||
Welcome_BackgroundSecondaryColor=ffffffff
|
|
||||||
Welcome_HoverColor=ffefefef
|
|
||||||
Welcome_AccentColor=ff45ce55
|
|
||||||
Welcome_LinkColor=ff20a020
|
|
||||||
Welcome_DisabledLinkColor=textDisabled
|
|
||||||
|
|
||||||
Timeline_TextColor=text
|
Timeline_TextColor=text
|
||||||
Timeline_BackgroundColor1=normalBackground
|
Timeline_BackgroundColor1=normalBackground
|
||||||
Timeline_BackgroundColor2=fff6f6f6
|
Timeline_BackgroundColor2=fff6f6f6
|
||||||
|
31
share/qtcreator/themes/light.figmatokens
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
; Qt Creator Color Tokens - light mode
|
||||||
|
|
||||||
|
[Colors]
|
||||||
|
|
||||||
|
Token_Basic_Black=ff131313
|
||||||
|
Token_Basic_White=fff2f2f2
|
||||||
|
|
||||||
|
Token_Accent_Default=ff23b26a
|
||||||
|
Token_Accent_Muted=ff1f9b5d
|
||||||
|
Token_Accent_Subtle=ff1a8550
|
||||||
|
|
||||||
|
Token_Background_Default=fffcfcfc
|
||||||
|
Token_Background_Muted=ffefefef
|
||||||
|
Token_Background_Subtle=ffe7e7e7
|
||||||
|
|
||||||
|
Token_Foreground_Default=ffcdcdcd
|
||||||
|
Token_Foreground_Muted=ffd5d5d5
|
||||||
|
Token_Foreground_Subtle=ffdddddd
|
||||||
|
|
||||||
|
Token_Text_Default=ff393939
|
||||||
|
Token_Text_Muted=ff6a6a6a
|
||||||
|
Token_Text_Subtle=ffbebebe
|
||||||
|
|
||||||
|
Token_Stroke_Strong=ff464646
|
||||||
|
Token_Stroke_Muted=ff727272
|
||||||
|
Token_Stroke_Subtle=ffcdcdcd
|
||||||
|
|
||||||
|
Token_Notification_Alert=ffeb991f
|
||||||
|
Token_Notification_Success=ff23b26a
|
||||||
|
Token_Notification_Neutral=ff0e7887
|
||||||
|
Token_Notification_Danger=ffdc1343
|
@@ -121,6 +121,9 @@ static std::string stripPointerType(const std::string &typeNameIn)
|
|||||||
std::string typeName = typeNameIn;
|
std::string typeName = typeNameIn;
|
||||||
if (typeName.back() == '*') {
|
if (typeName.back() == '*') {
|
||||||
typeName.pop_back();
|
typeName.pop_back();
|
||||||
|
trimBack(typeName);
|
||||||
|
if (endsWith(typeName, "const"))
|
||||||
|
typeName = typeName.erase(typeName.size() - 5, 5);
|
||||||
} else {
|
} else {
|
||||||
const auto arrayPosition = typeName.find_first_of('[');
|
const auto arrayPosition = typeName.find_first_of('[');
|
||||||
if (arrayPosition != std::string::npos
|
if (arrayPosition != std::string::npos
|
||||||
@@ -296,35 +299,19 @@ int PyType::code() const
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!resolve())
|
if (m_tag >= 0) {
|
||||||
return parseTypeName(name()).value_or(TypeCodeUnresolvable);
|
switch (m_tag) {
|
||||||
|
case SymTagUDT: return TypeCodeStruct;
|
||||||
if (m_tag < 0) {
|
case SymTagEnum: return TypeCodeEnum;
|
||||||
if (const std::optional<TypeCodes> typeCode = parseTypeName(name()))
|
case SymTagTypedef: return TypeCodeTypedef;
|
||||||
return *typeCode;
|
case SymTagFunctionType: return TypeCodeFunction;
|
||||||
|
case SymTagPointerType: return TypeCodePointer;
|
||||||
IDebugSymbolGroup2 *sg = 0;
|
case SymTagArrayType: return TypeCodeArray;
|
||||||
if (FAILED(ExtensionCommandContext::instance()->symbols()->CreateSymbolGroup2(&sg)))
|
case SymTagBaseType: return isIntegralType(name()) ? TypeCodeIntegral : TypeCodeFloat;
|
||||||
return TypeCodeStruct;
|
default: break;
|
||||||
|
}
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
switch (m_tag) {
|
return parseTypeName(name()).value_or(TypeCodeStruct);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PyType PyType::target() const
|
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_RET_STD_STRING(module, PY_OBJ_NAME)
|
||||||
PY_FUNC(moduleId, PY_OBJ_NAME, "K")
|
PY_FUNC(moduleId, PY_OBJ_NAME, "K")
|
||||||
PY_FUNC(arrayElements, 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_FUNC_DECL(templateArguments, PY_OBJ_NAME)
|
||||||
{
|
{
|
||||||
PY_IMPL_GUARD;
|
PY_IMPL_GUARD;
|
||||||
@@ -568,6 +556,8 @@ static PyMethodDef typeMethods[] = {
|
|||||||
"Returns the number of elements in an array or 0 for non array types"},
|
"Returns the number of elements in an array or 0 for non array types"},
|
||||||
{"templateArguments", PyCFunction(templateArguments), METH_NOARGS,
|
{"templateArguments", PyCFunction(templateArguments), METH_NOARGS,
|
||||||
"Returns all template arguments."},
|
"Returns all template arguments."},
|
||||||
|
{"resolved", PyCFunction(resolved), METH_NOARGS,
|
||||||
|
"Returns whether the type is resolved"},
|
||||||
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
@@ -29,6 +29,7 @@ public:
|
|||||||
std::string module() const;
|
std::string module() const;
|
||||||
ULONG64 moduleId() const;
|
ULONG64 moduleId() const;
|
||||||
int arrayElements() const;
|
int arrayElements() const;
|
||||||
|
bool resolved() const { return m_resolved.value_or(false); }
|
||||||
|
|
||||||
struct TemplateArgument
|
struct TemplateArgument
|
||||||
{
|
{
|
||||||
|
@@ -933,7 +933,17 @@ QColor StyleHelper::ensureReadableOn(const QColor &background, const QColor &des
|
|||||||
return foreground;
|
return foreground;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QStringList brandFontFamilies()
|
static const QStringList &applicationFontFamilies()
|
||||||
|
{
|
||||||
|
const static QStringList families = [] {
|
||||||
|
const QLatin1String familyName("Inter");
|
||||||
|
// Font is either installed in the system, or was loaded from share/qtcreator/fonts/
|
||||||
|
return QFontDatabase::hasFamily(familyName) ? QStringList(familyName) : QStringList();
|
||||||
|
}();
|
||||||
|
return families;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const QStringList &brandFontFamilies()
|
||||||
{
|
{
|
||||||
const static QStringList families = []{
|
const static QStringList families = []{
|
||||||
const int id = QFontDatabase::addApplicationFont(":/studiofonts/TitilliumWeb-Regular.ttf");
|
const int id = QFontDatabase::addApplicationFont(":/studiofonts/TitilliumWeb-Regular.ttf");
|
||||||
@@ -959,10 +969,14 @@ static const UiFontMetrics& uiFontMetrics(StyleHelper::UiElement element)
|
|||||||
{StyleHelper::UiElementH5, {14, 16, QFont::DemiBold}},
|
{StyleHelper::UiElementH5, {14, 16, QFont::DemiBold}},
|
||||||
{StyleHelper::UiElementH6, {12, 14, QFont::DemiBold}},
|
{StyleHelper::UiElementH6, {12, 14, QFont::DemiBold}},
|
||||||
{StyleHelper::UiElementH6Capital, {12, 14, QFont::DemiBold}},
|
{StyleHelper::UiElementH6Capital, {12, 14, QFont::DemiBold}},
|
||||||
|
{StyleHelper::UiElementBody1, {14, 20, QFont::Light}},
|
||||||
|
{StyleHelper::UiElementBody2, {12, 20, QFont::Light}},
|
||||||
|
{StyleHelper::UiElementButtonMedium, {12, 16, QFont::Bold}},
|
||||||
|
{StyleHelper::UiElementButtonSmall, {10, 12, QFont::Bold}},
|
||||||
{StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}},
|
{StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}},
|
||||||
{StyleHelper::UiElementCaption, {10, 12, QFont::Normal}},
|
{StyleHelper::UiElementCaption, {10, 12, QFont::Normal}},
|
||||||
{StyleHelper::UIElementIconStandard, {12, 16, QFont::Normal}},
|
{StyleHelper::UiElementIconStandard, {12, 16, QFont::Medium}},
|
||||||
{StyleHelper::UIElementIconActive, {12, 16, QFont::DemiBold}},
|
{StyleHelper::UiElementIconActive, {12, 16, QFont::DemiBold}},
|
||||||
};
|
};
|
||||||
QTC_ASSERT(metrics.count(element) > 0, return metrics.at(StyleHelper::UiElementCaptionStrong));
|
QTC_ASSERT(metrics.count(element) > 0, return metrics.at(StyleHelper::UiElementCaptionStrong));
|
||||||
return metrics.at(element);
|
return metrics.at(element);
|
||||||
@@ -983,8 +997,10 @@ QFont StyleHelper::uiFont(UiElement element)
|
|||||||
case UiElementH3:
|
case UiElementH3:
|
||||||
case UiElementH6Capital:
|
case UiElementH6Capital:
|
||||||
font.setCapitalization(QFont::AllUppercase);
|
font.setCapitalization(QFont::AllUppercase);
|
||||||
break;
|
[[fallthrough]];
|
||||||
default:
|
default:
|
||||||
|
if (!applicationFontFamilies().isEmpty())
|
||||||
|
font.setFamilies(applicationFontFamilies());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,15 +42,15 @@ constexpr char C_TOOLBAR_ACTIONWIDGET[] = "toolbar_actionWidget";
|
|||||||
constexpr char C_QT_SCALE_FACTOR_ROUNDING_POLICY[] = "QT_SCALE_FACTOR_ROUNDING_POLICY";
|
constexpr char C_QT_SCALE_FACTOR_ROUNDING_POLICY[] = "QT_SCALE_FACTOR_ROUNDING_POLICY";
|
||||||
|
|
||||||
namespace SpacingTokens {
|
namespace SpacingTokens {
|
||||||
constexpr int VPaddingXXS = 4; // Top and bottom padding within the component
|
constexpr int VPaddingXxs = 4; // Top and bottom padding within the component
|
||||||
constexpr int HPaddingXXS = 4; // Left and right padding within the component
|
constexpr int HPaddingXxs = 4; // Left and right padding within the component
|
||||||
constexpr int VGapXXS = 4; // Vertical Space between TEXT LINE within the Component
|
constexpr int VGapXxs = 4; // Vertical Space between TEXT LINE within the Component
|
||||||
constexpr int HGapXXS = 4; // Horizontal Space between elements within the Component
|
constexpr int HGapXxs = 4; // Horizontal Space between elements within the Component
|
||||||
|
|
||||||
constexpr int VPaddingXS = 8;
|
constexpr int VPaddingXs = 8;
|
||||||
constexpr int HPaddingXS = 8;
|
constexpr int HPaddingXs = 8;
|
||||||
constexpr int VGapXS = 4;
|
constexpr int VGapXs = 4;
|
||||||
constexpr int HGapXS = 8;
|
constexpr int HGapXs = 8;
|
||||||
|
|
||||||
constexpr int VPaddingS = 8;
|
constexpr int VPaddingS = 8;
|
||||||
constexpr int HPaddingS = 16;
|
constexpr int HPaddingS = 16;
|
||||||
@@ -62,10 +62,15 @@ namespace SpacingTokens {
|
|||||||
constexpr int VGapM = 4;
|
constexpr int VGapM = 4;
|
||||||
constexpr int HGapM = 16;
|
constexpr int HGapM = 16;
|
||||||
|
|
||||||
constexpr int VPaddingL = 12;
|
constexpr int VPaddingL = 16;
|
||||||
constexpr int HPaddingL = 24;
|
constexpr int HPaddingL = 24;
|
||||||
constexpr int VGapL = 8;
|
constexpr int VGapL = 8;
|
||||||
constexpr int HGapL = 16;
|
constexpr int HGapL = 16;
|
||||||
|
|
||||||
|
constexpr int ExPaddingGapS = 2;
|
||||||
|
constexpr int ExPaddingGapM = 6;
|
||||||
|
constexpr int ExPaddingGapL = 12;
|
||||||
|
constexpr int ExVPaddingGapXl = 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ToolbarStyle {
|
enum ToolbarStyle {
|
||||||
@@ -82,10 +87,14 @@ enum UiElement {
|
|||||||
UiElementH5,
|
UiElementH5,
|
||||||
UiElementH6,
|
UiElementH6,
|
||||||
UiElementH6Capital,
|
UiElementH6Capital,
|
||||||
|
UiElementBody1,
|
||||||
|
UiElementBody2,
|
||||||
|
UiElementButtonMedium,
|
||||||
|
UiElementButtonSmall,
|
||||||
UiElementCaptionStrong,
|
UiElementCaptionStrong,
|
||||||
UiElementCaption,
|
UiElementCaption,
|
||||||
UIElementIconStandard,
|
UiElementIconStandard,
|
||||||
UIElementIconActive,
|
UiElementIconActive,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Height of the project explorer navigation bar
|
// Height of the project explorer navigation bar
|
||||||
|
@@ -245,7 +245,6 @@ void Theme::readSettingsInternal(QSettings &settings)
|
|||||||
QMetaEnum e = m.enumerator(m.indexOfEnumerator("Flag"));
|
QMetaEnum e = m.enumerator(m.indexOfEnumerator("Flag"));
|
||||||
for (int i = 0, total = e.keyCount(); i < total; ++i) {
|
for (int i = 0, total = e.keyCount(); i < total; ++i) {
|
||||||
const QString key = QLatin1String(e.key(i));
|
const QString key = QLatin1String(e.key(i));
|
||||||
QTC_ASSERT(settings.contains(key), return );
|
|
||||||
d->flags[i] = settings.value(key).toBool();
|
d->flags[i] = settings.value(key).toBool();
|
||||||
}
|
}
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
@@ -247,18 +247,6 @@ public:
|
|||||||
Token_Notification_Neutral,
|
Token_Notification_Neutral,
|
||||||
Token_Notification_Danger,
|
Token_Notification_Danger,
|
||||||
|
|
||||||
/* Welcome Plugin */
|
|
||||||
|
|
||||||
Welcome_TextColor,
|
|
||||||
Welcome_ForegroundPrimaryColor,
|
|
||||||
Welcome_ForegroundSecondaryColor,
|
|
||||||
Welcome_BackgroundPrimaryColor,
|
|
||||||
Welcome_BackgroundSecondaryColor,
|
|
||||||
Welcome_HoverColor,
|
|
||||||
Welcome_AccentColor,
|
|
||||||
Welcome_LinkColor,
|
|
||||||
Welcome_DisabledLinkColor,
|
|
||||||
|
|
||||||
/* Timeline Library */
|
/* Timeline Library */
|
||||||
Timeline_TextColor,
|
Timeline_TextColor,
|
||||||
Timeline_BackgroundColor1,
|
Timeline_BackgroundColor1,
|
||||||
|
@@ -819,15 +819,13 @@ void AndroidRunnerWorker::removeForwardPort(const QString &port)
|
|||||||
|
|
||||||
void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser)
|
void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser)
|
||||||
{
|
{
|
||||||
qint64 pid = pidUser.first;
|
|
||||||
qint64 user = pidUser.second;
|
|
||||||
// Don't write to m_psProc from a different thread
|
// Don't write to m_psProc from a different thread
|
||||||
QTC_ASSERT(QThread::currentThread() == thread(), return);
|
QTC_ASSERT(QThread::currentThread() == thread(), return);
|
||||||
qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID
|
qCDebug(androidRunWorkerLog) << "Process ID changed from:" << m_processPID
|
||||||
<< "to:" << pid;
|
<< "to:" << pidUser.first;
|
||||||
m_processPID = pid;
|
m_processPID = pidUser.first;
|
||||||
m_processUser = user;
|
m_processUser = pidUser.second;
|
||||||
if (pid == -1) {
|
if (m_processPID == -1) {
|
||||||
emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.")
|
emit remoteProcessFinished(QLatin1String("\n\n") + Tr::tr("\"%1\" died.")
|
||||||
.arg(m_packageName));
|
.arg(m_packageName));
|
||||||
// App died/killed. Reset log, monitor, jdb & gdbserver/lldb-server processes.
|
// App died/killed. Reset log, monitor, jdb & gdbserver/lldb-server processes.
|
||||||
@@ -852,7 +850,10 @@ void AndroidRunnerWorker::onProcessIdChanged(PidUserPair pidUser)
|
|||||||
QTC_ASSERT(m_psIsAlive, return);
|
QTC_ASSERT(m_psIsAlive, return);
|
||||||
m_psIsAlive->setObjectName("IsAliveProcess");
|
m_psIsAlive->setObjectName("IsAliveProcess");
|
||||||
m_psIsAlive->setProcessChannelMode(QProcess::MergedChannels);
|
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});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -13,14 +13,15 @@
|
|||||||
|
|
||||||
#include <projectexplorer/projectexplorerconstants.h>
|
#include <projectexplorer/projectexplorerconstants.h>
|
||||||
|
|
||||||
|
#include <utils/async.h>
|
||||||
#include <utils/detailswidget.h>
|
#include <utils/detailswidget.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/infolabel.h>
|
#include <utils/infolabel.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
#include <utils/progressindicator.h>
|
#include <utils/progressindicator.h>
|
||||||
#include <utils/qtcprocess.h>
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/qtcprocess.h>
|
||||||
#include <utils/utilsicons.h>
|
#include <utils/utilsicons.h>
|
||||||
|
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
@@ -203,6 +204,56 @@ enum OpenSslValidation {
|
|||||||
OpenSslCmakeListsPathExists
|
OpenSslCmakeListsPathExists
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static expected_str<void> testJavaC(const FilePath &jdkPath)
|
||||||
|
{
|
||||||
|
if (!jdkPath.isReadableDir())
|
||||||
|
return make_unexpected(Tr::tr("The selected path does not exist or is not readable."));
|
||||||
|
|
||||||
|
const QString javacCommand("javac");
|
||||||
|
const QString versionParameter("-version");
|
||||||
|
constexpr int requiredMajorVersion = 17;
|
||||||
|
const FilePath bin = jdkPath / "bin" / (javacCommand + QTC_HOST_EXE_SUFFIX);
|
||||||
|
|
||||||
|
if (!bin.isExecutableFile())
|
||||||
|
return make_unexpected(
|
||||||
|
Tr::tr("Could not find \"%1\" in the selected path.")
|
||||||
|
.arg(bin.toUserOutput()));
|
||||||
|
|
||||||
|
QVersionNumber jdkVersion;
|
||||||
|
|
||||||
|
Process javacProcess;
|
||||||
|
const CommandLine cmd(bin, {versionParameter});
|
||||||
|
javacProcess.setProcessChannelMode(QProcess::ProcessChannelMode::MergedChannels);
|
||||||
|
javacProcess.setCommand(cmd);
|
||||||
|
javacProcess.runBlocking();
|
||||||
|
|
||||||
|
const QString stdOut = javacProcess.stdOut().trimmed();
|
||||||
|
|
||||||
|
if (javacProcess.exitCode() != 0)
|
||||||
|
return make_unexpected(
|
||||||
|
Tr::tr("The selected path does not contain a valid JDK. (%1 failed: %2)")
|
||||||
|
.arg(cmd.toUserOutput())
|
||||||
|
.arg(stdOut));
|
||||||
|
|
||||||
|
// We expect "javac <version>" where <version> is "major.minor.patch"
|
||||||
|
const QString outputPrefix = javacCommand + " ";
|
||||||
|
if (!stdOut.startsWith(outputPrefix))
|
||||||
|
return make_unexpected(Tr::tr("Unexpected output from \"%1\": %2")
|
||||||
|
.arg(cmd.toUserOutput())
|
||||||
|
.arg(stdOut));
|
||||||
|
|
||||||
|
jdkVersion = QVersionNumber::fromString(stdOut.mid(outputPrefix.length()).split('\n').first());
|
||||||
|
|
||||||
|
if (jdkVersion.isNull() || jdkVersion.majorVersion() != requiredMajorVersion) {
|
||||||
|
return make_unexpected(Tr::tr("Unsupported JDK version (needs to be %1): %2 (parsed: %3)")
|
||||||
|
.arg(requiredMajorVersion)
|
||||||
|
.arg(stdOut)
|
||||||
|
.arg(jdkVersion.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
AndroidSettingsWidget::AndroidSettingsWidget()
|
AndroidSettingsWidget::AndroidSettingsWidget()
|
||||||
{
|
{
|
||||||
setWindowTitle(Tr::tr("Android Configuration"));
|
setWindowTitle(Tr::tr("Android Configuration"));
|
||||||
@@ -307,6 +358,15 @@ AndroidSettingsWidget::AndroidSettingsWidget()
|
|||||||
Tr::tr("OpenSSL settings have errors."),
|
Tr::tr("OpenSSL settings have errors."),
|
||||||
openSslDetailsWidget);
|
openSslDetailsWidget);
|
||||||
|
|
||||||
|
m_openJdkLocationPathChooser->setValidationFunction([](const QString &s) {
|
||||||
|
return Utils::asyncRun([s]() -> expected_str<QString> {
|
||||||
|
expected_str<void> test = testJavaC(FilePath::fromUserInput(s));
|
||||||
|
if (!test)
|
||||||
|
return make_unexpected(test.error());
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
connect(m_openJdkLocationPathChooser, &PathChooser::rawPathChanged,
|
connect(m_openJdkLocationPathChooser, &PathChooser::rawPathChanged,
|
||||||
this, &AndroidSettingsWidget::validateJdk);
|
this, &AndroidSettingsWidget::validateJdk);
|
||||||
if (androidConfig().openJDKLocation().isEmpty())
|
if (androidConfig().openJDKLocation().isEmpty())
|
||||||
@@ -533,10 +593,9 @@ bool AndroidSettingsWidget::isDefaultNdkSelected() const
|
|||||||
void AndroidSettingsWidget::validateJdk()
|
void AndroidSettingsWidget::validateJdk()
|
||||||
{
|
{
|
||||||
androidConfig().setOpenJDKLocation(m_openJdkLocationPathChooser->filePath());
|
androidConfig().setOpenJDKLocation(m_openJdkLocationPathChooser->filePath());
|
||||||
bool jdkPathExists = androidConfig().openJDKLocation().exists();
|
expected_str<void> test = testJavaC(androidConfig().openJDKLocation());
|
||||||
const FilePath bin = androidConfig().openJDKLocation()
|
|
||||||
.pathAppended("bin/javac" QTC_HOST_EXE_SUFFIX);
|
m_androidSummary->setPointValid(JavaPathExistsAndWritableRow, test.has_value());
|
||||||
m_androidSummary->setPointValid(JavaPathExistsAndWritableRow, jdkPathExists && bin.exists());
|
|
||||||
|
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|
||||||
|
@@ -79,14 +79,16 @@ void CatchCodeParser::handleIdentifier()
|
|||||||
|| unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_SIG") {
|
|| unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_SIG") {
|
||||||
handleParameterizedTestCase(false);
|
handleParameterizedTestCase(false);
|
||||||
} else if (unprefixed == "TEST_CASE_METHOD") {
|
} else if (unprefixed == "TEST_CASE_METHOD") {
|
||||||
handleFixtureOrRegisteredTestCase(true);
|
handleFixtureOrRegisteredTestCase(/*fixture=*/true, /*scenario=*/false);
|
||||||
|
} else if (unprefixed == "SCENARIO_METHOD") {
|
||||||
|
handleFixtureOrRegisteredTestCase(/*fixture=*/true, /*scenario=*/true);
|
||||||
} else if (unprefixed == "TEMPLATE_TEST_CASE_METHOD_SIG"
|
} else if (unprefixed == "TEMPLATE_TEST_CASE_METHOD_SIG"
|
||||||
|| unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"
|
|| unprefixed == "TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"
|
||||||
|| unprefixed == "TEMPLATE_TEST_CASE_METHOD"
|
|| unprefixed == "TEMPLATE_TEST_CASE_METHOD"
|
||||||
|| unprefixed == "TEMPLATE_LIST_TEST_CASE_METHOD") {
|
|| unprefixed == "TEMPLATE_LIST_TEST_CASE_METHOD") {
|
||||||
handleParameterizedTestCase(true);
|
handleParameterizedTestCase(true);
|
||||||
} else if (unprefixed == "METHOD_AS_TEST_CASE" || unprefixed == "REGISTER_TEST_CASE") {
|
} else if (unprefixed == "METHOD_AS_TEST_CASE" || unprefixed == "REGISTER_TEST_CASE") {
|
||||||
handleFixtureOrRegisteredTestCase(false);
|
handleFixtureOrRegisteredTestCase(/*fixture=*/false, /*scenario=*/false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ void CatchCodeParser::handleParameterizedTestCase(bool isFixture)
|
|||||||
if (!skipCommentsUntil(T_LPAREN))
|
if (!skipCommentsUntil(T_LPAREN))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isFixture && !skipFixtureParameter())
|
if (isFixture && !skipParameter())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
CatchTestCodeLocationAndType locationAndType
|
CatchTestCodeLocationAndType locationAndType
|
||||||
@@ -154,18 +156,13 @@ void CatchCodeParser::handleParameterizedTestCase(bool isFixture)
|
|||||||
m_testCases.append(locationAndType);
|
m_testCases.append(locationAndType);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatchCodeParser::handleFixtureOrRegisteredTestCase(bool isFixture)
|
void CatchCodeParser::handleFixtureOrRegisteredTestCase(bool isFixture, bool isScenario)
|
||||||
{
|
{
|
||||||
if (!skipCommentsUntil(T_LPAREN))
|
if (!skipCommentsUntil(T_LPAREN))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (isFixture) {
|
if (!skipParameter())
|
||||||
if (!skipFixtureParameter())
|
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
if (!skipFunctionParameter())
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CatchTestCodeLocationAndType locationAndType
|
CatchTestCodeLocationAndType locationAndType
|
||||||
= locationAndTypeFromToken(m_tokens.at(m_currentIndex));
|
= locationAndTypeFromToken(m_tokens.at(m_currentIndex));
|
||||||
@@ -183,6 +180,9 @@ void CatchCodeParser::handleFixtureOrRegisteredTestCase(bool isFixture)
|
|||||||
if (stoppedAt != T_RPAREN)
|
if (stoppedAt != T_RPAREN)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (isScenario)
|
||||||
|
testCaseName.prepend("Scenario: "); // use a flag?
|
||||||
|
|
||||||
locationAndType.m_name = testCaseName;
|
locationAndType.m_name = testCaseName;
|
||||||
locationAndType.tags = parseTags(tagsString);
|
locationAndType.tags = parseTags(tagsString);
|
||||||
if (isFixture)
|
if (isFixture)
|
||||||
@@ -245,19 +245,12 @@ Kind CatchCodeParser::skipUntilCorrespondingRParen()
|
|||||||
return T_ERROR;
|
return T_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CatchCodeParser::skipFixtureParameter()
|
bool CatchCodeParser::skipParameter()
|
||||||
{
|
|
||||||
if (!skipCommentsUntil(T_IDENTIFIER))
|
|
||||||
return false;
|
|
||||||
return skipCommentsUntil(T_COMMA);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CatchCodeParser::skipFunctionParameter()
|
|
||||||
{
|
{
|
||||||
if (!skipCommentsUntil(T_IDENTIFIER))
|
if (!skipCommentsUntil(T_IDENTIFIER))
|
||||||
return false;
|
return false;
|
||||||
if (skipCommentsUntil(T_COLON_COLON))
|
if (skipCommentsUntil(T_COLON_COLON))
|
||||||
return skipFunctionParameter();
|
return skipParameter();
|
||||||
|
|
||||||
return skipCommentsUntil(T_COMMA);
|
return skipCommentsUntil(T_COMMA);
|
||||||
}
|
}
|
||||||
|
@@ -22,13 +22,12 @@ private:
|
|||||||
void handleIdentifier();
|
void handleIdentifier();
|
||||||
void handleTestCase(bool isScenario);
|
void handleTestCase(bool isScenario);
|
||||||
void handleParameterizedTestCase(bool isFixture);
|
void handleParameterizedTestCase(bool isFixture);
|
||||||
void handleFixtureOrRegisteredTestCase(bool isFixture);
|
void handleFixtureOrRegisteredTestCase(bool isFixture, bool isScenario);
|
||||||
|
|
||||||
QString getStringLiteral(CPlusPlus::Kind &stoppedAtKind);
|
QString getStringLiteral(CPlusPlus::Kind &stoppedAtKind);
|
||||||
bool skipCommentsUntil(CPlusPlus::Kind nextExpectedKind); // moves currentIndex if succeeds
|
bool skipCommentsUntil(CPlusPlus::Kind nextExpectedKind); // moves currentIndex if succeeds
|
||||||
CPlusPlus::Kind skipUntilCorrespondingRParen(); // moves currentIndex
|
CPlusPlus::Kind skipUntilCorrespondingRParen(); // moves currentIndex
|
||||||
bool skipFixtureParameter();
|
bool skipParameter();
|
||||||
bool skipFunctionParameter();
|
|
||||||
|
|
||||||
const QByteArray &m_source;
|
const QByteArray &m_source;
|
||||||
const CPlusPlus::LanguageFeatures &m_features;
|
const CPlusPlus::LanguageFeatures &m_features;
|
||||||
|
@@ -29,7 +29,9 @@ static bool isCatchTestCaseMacro(const QString ¯oName)
|
|||||||
QStringLiteral("TEMPLATE_TEST_CASE_SIG"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_SIG"),
|
QStringLiteral("TEMPLATE_TEST_CASE_SIG"), QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_SIG"),
|
||||||
QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD"),
|
QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD"),
|
||||||
QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD"),
|
QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD"),
|
||||||
QStringLiteral("TEST_CASE_METHOD"), QStringLiteral("TEMPLATE_TEST_CASE_METHOD_SIG"),
|
QStringLiteral("TEST_CASE_METHOD"),
|
||||||
|
QStringLiteral("SCENARIO_METHOD"),
|
||||||
|
QStringLiteral("TEMPLATE_TEST_CASE_METHOD_SIG"),
|
||||||
QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"),
|
QStringLiteral("TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG"),
|
||||||
QStringLiteral("TEMPLATE_TEST_CASE_METHOD"),
|
QStringLiteral("TEMPLATE_TEST_CASE_METHOD"),
|
||||||
QStringLiteral("TEMPLATE_LIST_TEST_CASE_METHOD"),
|
QStringLiteral("TEMPLATE_LIST_TEST_CASE_METHOD"),
|
||||||
@@ -105,7 +107,7 @@ bool CatchTestParser::processDocument(QPromise<TestParseResultPtr> &promise,
|
|||||||
|
|
||||||
if (!hasCatchNames(doc)) {
|
if (!hasCatchNames(doc)) {
|
||||||
static const QRegularExpression regex("\\b(CATCH_)?"
|
static const QRegularExpression regex("\\b(CATCH_)?"
|
||||||
"(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|"
|
"(SCENARIO(_METHOD)?|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|"
|
||||||
"TEMPLATE_TEST_CASE(_METHOD)?_SIG|"
|
"TEMPLATE_TEST_CASE(_METHOD)?_SIG|"
|
||||||
"TEMPLATE_PRODUCT_TEST_CASE(_METHOD)?_SIG|"
|
"TEMPLATE_PRODUCT_TEST_CASE(_METHOD)?_SIG|"
|
||||||
"TEMPLATE_LIST_TEST_CASE_METHOD|METHOD_AS_TEST_CASE|"
|
"TEMPLATE_LIST_TEST_CASE_METHOD|METHOD_AS_TEST_CASE|"
|
||||||
|
@@ -55,6 +55,7 @@ TestCodeParser::TestCodeParser()
|
|||||||
m_qmlEditorRev.remove(filePath);
|
m_qmlEditorRev.remove(filePath);
|
||||||
});
|
});
|
||||||
m_reparseTimer.setSingleShot(true);
|
m_reparseTimer.setSingleShot(true);
|
||||||
|
m_reparseTimer.setInterval(1000);
|
||||||
connect(&m_reparseTimer, &QTimer::timeout, this, &TestCodeParser::parsePostponedFiles);
|
connect(&m_reparseTimer, &QTimer::timeout, this, &TestCodeParser::parsePostponedFiles);
|
||||||
connect(&m_taskTreeRunner, &TaskTreeRunner::aboutToStart, this, [this](TaskTree *taskTree) {
|
connect(&m_taskTreeRunner, &TaskTreeRunner::aboutToStart, this, [this](TaskTree *taskTree) {
|
||||||
if (m_withTaskProgress) {
|
if (m_withTaskProgress) {
|
||||||
@@ -238,27 +239,10 @@ bool TestCodeParser::postponed(const QSet<FilePath> &filePaths)
|
|||||||
if (filePaths.size() == 1) {
|
if (filePaths.size() == 1) {
|
||||||
if (m_reparseTimerTimedOut)
|
if (m_reparseTimerTimedOut)
|
||||||
return false;
|
return false;
|
||||||
const FilePath filePath = *filePaths.begin();
|
|
||||||
switch (m_postponedFiles.size()) {
|
m_postponedFiles.insert(*filePaths.begin());
|
||||||
case 0:
|
m_reparseTimer.start();
|
||||||
m_postponedFiles.insert(filePath);
|
return true;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
case PartialParse:
|
case PartialParse:
|
||||||
@@ -377,7 +361,7 @@ void TestCodeParser::scanForTests(const QSet<FilePath> &filePaths,
|
|||||||
return true;
|
return true;
|
||||||
return cppSnapshot.contains(fn);
|
return cppSnapshot.contains(fn);
|
||||||
});
|
});
|
||||||
m_withTaskProgress = filteredFiles.size() > 5;
|
m_withTaskProgress = isFullParse || filteredFiles.size() > 20;
|
||||||
|
|
||||||
qCDebug(LOG) << "Starting scan of" << filteredFiles.size() << "(" << files.size() << ")"
|
qCDebug(LOG) << "Starting scan of" << filteredFiles.size() << "(" << files.size() << ")"
|
||||||
<< "files with" << codeParsers.size() << "parsers";
|
<< "files with" << codeParsers.size() << "parsers";
|
||||||
|
@@ -2,18 +2,18 @@
|
|||||||
<qresource prefix="/axivion">
|
<qresource prefix="/axivion">
|
||||||
<file>images/axivion.png</file>
|
<file>images/axivion.png</file>
|
||||||
<file>images/axivion@2x.png</file>
|
<file>images/axivion@2x.png</file>
|
||||||
<file>images/button-av.png</file>
|
<file>images/button-AV.png</file>
|
||||||
<file>images/button-av@2x.png</file>
|
<file>images/button-AV@2x.png</file>
|
||||||
<file>images/button-cl.png</file>
|
<file>images/button-CL.png</file>
|
||||||
<file>images/button-cl@2x.png</file>
|
<file>images/button-CL@2x.png</file>
|
||||||
<file>images/button-cy.png</file>
|
<file>images/button-CY.png</file>
|
||||||
<file>images/button-cy@2x.png</file>
|
<file>images/button-CY@2x.png</file>
|
||||||
<file>images/button-de.png</file>
|
<file>images/button-DE.png</file>
|
||||||
<file>images/button-de@2x.png</file>
|
<file>images/button-DE@2x.png</file>
|
||||||
<file>images/button-mv.png</file>
|
<file>images/button-MV.png</file>
|
||||||
<file>images/button-mv@2x.png</file>
|
<file>images/button-MV@2x.png</file>
|
||||||
<file>images/button-sv.png</file>
|
<file>images/button-SV.png</file>
|
||||||
<file>images/button-sv@2x.png</file>
|
<file>images/button-SV@2x.png</file>
|
||||||
<file>images/sortAsc.png</file>
|
<file>images/sortAsc.png</file>
|
||||||
<file>images/sortAsc@2x.png</file>
|
<file>images/sortAsc@2x.png</file>
|
||||||
<file>images/sortDesc.png</file>
|
<file>images/sortDesc.png</file>
|
||||||
|
@@ -334,6 +334,8 @@ IssuesWidget::IssuesWidget(QWidget *parent)
|
|||||||
connect(m_pathGlobFilter, &QLineEdit::textEdited, this, &IssuesWidget::onSearchParameterChanged);
|
connect(m_pathGlobFilter, &QLineEdit::textEdited, this, &IssuesWidget::onSearchParameterChanged);
|
||||||
|
|
||||||
m_issuesView = new BaseTreeView(this);
|
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);
|
m_headerView = new IssueHeaderView(this);
|
||||||
connect(m_headerView, &IssueHeaderView::sortTriggered,
|
connect(m_headerView, &IssueHeaderView::sortTriggered,
|
||||||
this, &IssuesWidget::onSearchParameterChanged);
|
this, &IssuesWidget::onSearchParameterChanged);
|
||||||
@@ -562,7 +564,7 @@ void IssuesWidget::updateBasicProjectInfo(std::optional<Dto::ProjectInfoDto> inf
|
|||||||
int buttonId = 0;
|
int buttonId = 0;
|
||||||
for (const Dto::IssueKindInfoDto &kind : issueKinds) {
|
for (const Dto::IssueKindInfoDto &kind : issueKinds) {
|
||||||
auto button = new QToolButton(this);
|
auto button = new QToolButton(this);
|
||||||
button->setIcon(iconForIssue(kind.prefix));
|
button->setIcon(iconForIssue(kind.getOptionalPrefixEnum()));
|
||||||
button->setToolTip(kind.nicePluralName);
|
button->setToolTip(kind.nicePluralName);
|
||||||
button->setCheckable(true);
|
button->setCheckable(true);
|
||||||
connect(button, &QToolButton::clicked, this, [this, prefix = kind.prefix]{
|
connect(button, &QToolButton::clicked, this, [this, prefix = kind.prefix]{
|
||||||
|
@@ -63,18 +63,21 @@ using namespace Utils;
|
|||||||
|
|
||||||
namespace Axivion::Internal {
|
namespace Axivion::Internal {
|
||||||
|
|
||||||
QIcon iconForIssue(const QString &prefix)
|
QIcon iconForIssue(const std::optional<Dto::IssueKind> &issueKind)
|
||||||
{
|
{
|
||||||
static QHash<QString, QIcon> prefixToIcon;
|
if (!issueKind)
|
||||||
auto it = prefixToIcon.find(prefix);
|
return {};
|
||||||
|
|
||||||
if (it == prefixToIcon.end()) {
|
static QHash<Dto::IssueKind, QIcon> prefixToIcon;
|
||||||
Icon icon({{FilePath::fromString(":/axivion/images/button-" + prefix.toLower() + ".png"),
|
|
||||||
Theme::PaletteButtonText}},
|
auto it = prefixToIcon.constFind(*issueKind);
|
||||||
Icon::Tint);
|
if (it != prefixToIcon.constEnd())
|
||||||
it = prefixToIcon.insert(prefix, icon.icon());
|
return *it;
|
||||||
}
|
|
||||||
return it.value();
|
const QLatin1String prefix = Dto::IssueKindMeta::enumToStr(*issueKind);
|
||||||
|
const Icon icon({{FilePath::fromString(":/axivion/images/button-" + prefix + ".png"),
|
||||||
|
Theme::PaletteButtonText}}, Icon::Tint);
|
||||||
|
return prefixToIcon.insert(*issueKind, icon.icon()).value();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString anyToSimpleString(const Dto::Any &any)
|
QString anyToSimpleString(const Dto::Any &any)
|
||||||
@@ -122,11 +125,30 @@ static QString credentialKey()
|
|||||||
QString escaped = string;
|
QString escaped = string;
|
||||||
return escaped.replace('\\', "\\\\").replace('@', "\\@");
|
return escaped.replace('\\', "\\\\").replace('@', "\\@");
|
||||||
};
|
};
|
||||||
return escape(settings().server.dashboard) + '@' + escape(settings().server.username);
|
return escape(settings().server.username) + '@' + escape(settings().server.dashboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInfoDto &infoDto)
|
template <typename DtoType>
|
||||||
|
struct GetDtoStorage
|
||||||
{
|
{
|
||||||
|
QUrl url;
|
||||||
|
std::optional<QByteArray> credential;
|
||||||
|
std::optional<DtoType> dtoData;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename DtoType>
|
||||||
|
struct PostDtoStorage
|
||||||
|
{
|
||||||
|
QUrl url;
|
||||||
|
std::optional<QByteArray> credential;
|
||||||
|
QByteArray csrfToken;
|
||||||
|
QByteArray writeData;
|
||||||
|
std::optional<DtoType> dtoData;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DashboardInfo toDashboardInfo(const GetDtoStorage<Dto::DashboardInfoDto> &dashboardStorage)
|
||||||
|
{
|
||||||
|
const Dto::DashboardInfoDto &infoDto = *dashboardStorage.dtoData;
|
||||||
const QVersionNumber versionNumber = infoDto.dashboardVersionNumber
|
const QVersionNumber versionNumber = infoDto.dashboardVersionNumber
|
||||||
? QVersionNumber::fromString(*infoDto.dashboardVersionNumber) : QVersionNumber();
|
? QVersionNumber::fromString(*infoDto.dashboardVersionNumber) : QVersionNumber();
|
||||||
|
|
||||||
@@ -139,7 +161,7 @@ static DashboardInfo toDashboardInfo(const QUrl &source, const Dto::DashboardInf
|
|||||||
projectUrls.insert(project.name, project.url);
|
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
|
QString IssueListSearch::toQuery() const
|
||||||
@@ -226,7 +248,7 @@ public:
|
|||||||
const QString markText = issue.description;
|
const QString markText = issue.description;
|
||||||
const QString id = issue.kind + QString::number(issue.id.value_or(-1));
|
const QString id = issue.kind + QString::number(issue.id.value_or(-1));
|
||||||
setToolTip(id + '\n' + markText);
|
setToolTip(id + '\n' + markText);
|
||||||
setIcon(iconForIssue(issue.kind));
|
setIcon(iconForIssue(issue.getOptionalKindEnum()));
|
||||||
if (color)
|
if (color)
|
||||||
setColor(*color);
|
setColor(*color);
|
||||||
setPriority(TextMark::NormalPriority);
|
setPriority(TextMark::NormalPriority);
|
||||||
@@ -323,18 +345,26 @@ void AxivionPluginPrivate::onStartupProjectChanged(Project *project)
|
|||||||
|
|
||||||
static QUrl urlForProject(const QString &projectName)
|
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;
|
static constexpr int httpStatusCodeOk = 200;
|
||||||
constexpr char s_htmlContentType[] = "text/html";
|
constexpr char s_htmlContentType[] = "text/html";
|
||||||
constexpr char s_jsonContentType[] = "application/json";
|
constexpr char s_jsonContentType[] = "application/json";
|
||||||
|
|
||||||
|
static bool isServerAccessEstablished()
|
||||||
|
{
|
||||||
|
return dd->m_serverAccess == ServerAccess::NoAuthorization
|
||||||
|
|| (dd->m_serverAccess == ServerAccess::WithAuthorization && dd->m_apiToken);
|
||||||
|
}
|
||||||
|
|
||||||
static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QByteArray &)> &handler)
|
static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QByteArray &)> &handler)
|
||||||
{
|
{
|
||||||
// TODO: Refactor so that it's a common code with fetchDataRecipe().
|
// TODO: Refactor so that it's a common code with fetchDataRecipe().
|
||||||
const auto onQuerySetup = [url](NetworkQuery &query) {
|
const auto onQuerySetup = [url](NetworkQuery &query) {
|
||||||
if (dd->m_serverAccess == ServerAccess::Unknown)
|
if (!isServerAccessEstablished())
|
||||||
return SetupResult::StopWithError; // TODO: start authorizationRecipe()?
|
return SetupResult::StopWithError; // TODO: start authorizationRecipe()?
|
||||||
|
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
@@ -367,24 +397,6 @@ static Group fetchHtmlRecipe(const QUrl &url, const std::function<void(const QBy
|
|||||||
return {NetworkQueryTask(onQuerySetup, onQueryDone)};
|
return {NetworkQueryTask(onQuerySetup, onQueryDone)};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DtoType>
|
|
||||||
struct GetDtoStorage
|
|
||||||
{
|
|
||||||
QUrl url;
|
|
||||||
std::optional<QByteArray> credential;
|
|
||||||
std::optional<DtoType> dtoData;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename DtoType>
|
|
||||||
struct PostDtoStorage
|
|
||||||
{
|
|
||||||
QUrl url;
|
|
||||||
std::optional<QByteArray> credential;
|
|
||||||
QByteArray csrfToken;
|
|
||||||
QByteArray writeData;
|
|
||||||
std::optional<DtoType> dtoData;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename DtoType, template <typename> typename DtoStorageType>
|
template <typename DtoType, template <typename> typename DtoStorageType>
|
||||||
static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
|
static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
|
||||||
{
|
{
|
||||||
@@ -410,7 +422,8 @@ static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
|
|||||||
query.setNetworkAccessManager(&dd->m_networkAccessManager);
|
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();
|
QNetworkReply *reply = query.reply();
|
||||||
const QNetworkReply::NetworkError error = reply->error();
|
const QNetworkReply::NetworkError error = reply->error();
|
||||||
const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
@@ -423,6 +436,7 @@ static Group dtoRecipe(const Storage<DtoStorageType<DtoType>> &dtoStorage)
|
|||||||
if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk
|
if (doneWith == DoneWith::Success && statusCode == httpStatusCodeOk
|
||||||
&& contentType == s_jsonContentType) {
|
&& contentType == s_jsonContentType) {
|
||||||
*storage = reply->readAll();
|
*storage = reply->readAll();
|
||||||
|
dtoStorage->url = reply->url();
|
||||||
return DoneResult::Success;
|
return DoneResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +517,7 @@ static Group authorizationRecipe()
|
|||||||
{
|
{
|
||||||
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> unauthorizedDashboardStorage;
|
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> unauthorizedDashboardStorage;
|
||||||
const auto onUnauthorizedGroupSetup = [unauthorizedDashboardStorage] {
|
const auto onUnauthorizedGroupSetup = [unauthorizedDashboardStorage] {
|
||||||
if (dd->m_serverAccess != ServerAccess::NoAuthorization)
|
if (isServerAccessEstablished())
|
||||||
return SetupResult::StopWithSuccess;
|
return SetupResult::StopWithSuccess;
|
||||||
|
|
||||||
unauthorizedDashboardStorage->url = QUrl(settings().server.dashboard);
|
unauthorizedDashboardStorage->url = QUrl(settings().server.dashboard);
|
||||||
@@ -512,8 +526,7 @@ static Group authorizationRecipe()
|
|||||||
const auto onUnauthorizedGroupDone = [unauthorizedDashboardStorage] {
|
const auto onUnauthorizedGroupDone = [unauthorizedDashboardStorage] {
|
||||||
if (unauthorizedDashboardStorage->dtoData) {
|
if (unauthorizedDashboardStorage->dtoData) {
|
||||||
dd->m_serverAccess = ServerAccess::NoAuthorization;
|
dd->m_serverAccess = ServerAccess::NoAuthorization;
|
||||||
dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard,
|
dd->m_dashboardInfo = toDashboardInfo(*unauthorizedDashboardStorage);
|
||||||
*unauthorizedDashboardStorage->dtoData);
|
|
||||||
} else {
|
} else {
|
||||||
dd->m_serverAccess = ServerAccess::WithAuthorization;
|
dd->m_serverAccess = ServerAccess::WithAuthorization;
|
||||||
}
|
}
|
||||||
@@ -540,7 +553,7 @@ static Group authorizationRecipe()
|
|||||||
|
|
||||||
const Storage<QString> passwordStorage;
|
const Storage<QString> passwordStorage;
|
||||||
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> dashboardStorage;
|
const Storage<GetDtoStorage<Dto::DashboardInfoDto>> dashboardStorage;
|
||||||
const auto onDashboardGroupSetup = [passwordStorage, dashboardStorage] {
|
const auto onPasswordGroupSetup = [passwordStorage, dashboardStorage] {
|
||||||
if (dd->m_apiToken)
|
if (dd->m_apiToken)
|
||||||
return SetupResult::StopWithSuccess;
|
return SetupResult::StopWithSuccess;
|
||||||
|
|
||||||
@@ -563,8 +576,7 @@ static Group authorizationRecipe()
|
|||||||
if (!dashboardStorage->dtoData)
|
if (!dashboardStorage->dtoData)
|
||||||
return SetupResult::StopWithSuccess;
|
return SetupResult::StopWithSuccess;
|
||||||
|
|
||||||
dd->m_dashboardInfo = toDashboardInfo(settings().server.dashboard,
|
dd->m_dashboardInfo = toDashboardInfo(*dashboardStorage);
|
||||||
*dashboardStorage->dtoData);
|
|
||||||
|
|
||||||
const Dto::DashboardInfoDto &dashboardDto = *dashboardStorage->dtoData;
|
const Dto::DashboardInfoDto &dashboardDto = *dashboardStorage->dtoData;
|
||||||
if (!dashboardDto.userApiTokenUrl)
|
if (!dashboardDto.userApiTokenUrl)
|
||||||
@@ -572,7 +584,7 @@ static Group authorizationRecipe()
|
|||||||
|
|
||||||
apiTokenStorage->credential = dashboardStorage->credential;
|
apiTokenStorage->credential = dashboardStorage->credential;
|
||||||
apiTokenStorage->url
|
apiTokenStorage->url
|
||||||
= QUrl(settings().server.dashboard).resolved(*dashboardDto.userApiTokenUrl);
|
= dd->m_dashboardInfo->source.resolved(*dashboardDto.userApiTokenUrl);
|
||||||
apiTokenStorage->csrfToken = dashboardDto.csrfToken.toUtf8();
|
apiTokenStorage->csrfToken = dashboardDto.csrfToken.toUtf8();
|
||||||
const Dto::ApiTokenCreationRequestDto requestDto{*passwordStorage, "IdePlugin",
|
const Dto::ApiTokenCreationRequestDto requestDto{*passwordStorage, "IdePlugin",
|
||||||
apiTokenDescription(), 0};
|
apiTokenDescription(), 0};
|
||||||
@@ -598,6 +610,29 @@ static Group authorizationRecipe()
|
|||||||
return DoneResult::Success;
|
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 {
|
return {
|
||||||
Group {
|
Group {
|
||||||
unauthorizedDashboardStorage,
|
unauthorizedDashboardStorage,
|
||||||
@@ -611,7 +646,7 @@ static Group authorizationRecipe()
|
|||||||
Group {
|
Group {
|
||||||
passwordStorage,
|
passwordStorage,
|
||||||
dashboardStorage,
|
dashboardStorage,
|
||||||
onGroupSetup(onDashboardGroupSetup),
|
onGroupSetup(onPasswordGroupSetup),
|
||||||
Group { // GET DashboardInfoDto
|
Group { // GET DashboardInfoDto
|
||||||
finishAllAndSuccess,
|
finishAllAndSuccess,
|
||||||
dtoRecipe(dashboardStorage)
|
dtoRecipe(dashboardStorage)
|
||||||
@@ -622,6 +657,13 @@ static Group authorizationRecipe()
|
|||||||
dtoRecipe(apiTokenStorage),
|
dtoRecipe(apiTokenStorage),
|
||||||
CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error)
|
CredentialQueryTask(onSetCredentialSetup, onSetCredentialDone, CallDoneIf::Error)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Group {
|
||||||
|
finishAllAndSuccess,
|
||||||
|
dashboardStorage,
|
||||||
|
onGroupSetup(onDashboardGroupSetup),
|
||||||
|
dtoRecipe(dashboardStorage),
|
||||||
|
CredentialQueryTask(onDeleteCredentialSetup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -633,10 +675,11 @@ static Group fetchDataRecipe(const QUrl &url, const std::function<void(const Dto
|
|||||||
const Storage<GetDtoStorage<DtoType>> dtoStorage;
|
const Storage<GetDtoStorage<DtoType>> dtoStorage;
|
||||||
|
|
||||||
const auto onDtoSetup = [dtoStorage, url] {
|
const auto onDtoSetup = [dtoStorage, url] {
|
||||||
if (!dd->m_apiToken)
|
if (!isServerAccessEstablished())
|
||||||
return SetupResult::StopWithError;
|
return SetupResult::StopWithError;
|
||||||
|
|
||||||
dtoStorage->credential = "AxToken " + *dd->m_apiToken;
|
if (dd->m_serverAccess == ServerAccess::WithAuthorization && dd->m_apiToken)
|
||||||
|
dtoStorage->credential = "AxToken " + *dd->m_apiToken;
|
||||||
dtoStorage->url = url;
|
dtoStorage->url = url;
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
@@ -661,27 +704,22 @@ Group dashboardInfoRecipe(const DashboardInfoHandler &handler)
|
|||||||
{
|
{
|
||||||
const auto onSetup = [handler] {
|
const auto onSetup = [handler] {
|
||||||
if (dd->m_dashboardInfo) {
|
if (dd->m_dashboardInfo) {
|
||||||
if (handler)
|
handler(*dd->m_dashboardInfo);
|
||||||
handler(*dd->m_dashboardInfo);
|
|
||||||
return SetupResult::StopWithSuccess;
|
return SetupResult::StopWithSuccess;
|
||||||
}
|
}
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
const auto onDone = [handler] {
|
const auto onDone = [handler](DoneWith result) {
|
||||||
if (handler)
|
if (result == DoneWith::Success && dd->m_dashboardInfo)
|
||||||
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)
|
|
||||||
handler(*dd->m_dashboardInfo);
|
handler(*dd->m_dashboardInfo);
|
||||||
|
else
|
||||||
|
handler(make_unexpected(QString("Error"))); // TODO: Collect error message in the storage.
|
||||||
};
|
};
|
||||||
|
|
||||||
const Group root {
|
const Group root {
|
||||||
onGroupSetup(onSetup), // Stops if cache exists.
|
onGroupSetup(onSetup), // Stops if cache exists.
|
||||||
fetchDataRecipe<Dto::DashboardInfoDto>(settings().server.dashboard, resultHandler),
|
authorizationRecipe(),
|
||||||
onGroupDone(onDone, CallDoneIf::Error)
|
onGroupDone(onDone)
|
||||||
};
|
};
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
@@ -754,13 +792,13 @@ void AxivionPluginPrivate::fetchProjectInfo(const QString &projectName)
|
|||||||
handleOpenedDocs();
|
handleOpenedDocs();
|
||||||
};
|
};
|
||||||
|
|
||||||
const QUrl url(settings().server.dashboard);
|
taskTree.setRecipe(
|
||||||
taskTree.setRecipe(fetchDataRecipe<Dto::ProjectInfoDto>(url.resolved(*it), handler));
|
fetchDataRecipe<Dto::ProjectInfoDto>(m_dashboardInfo->source.resolved(*it), handler));
|
||||||
return SetupResult::Continue;
|
return SetupResult::Continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Group root {
|
const Group root {
|
||||||
dashboardInfoRecipe(),
|
authorizationRecipe(),
|
||||||
TaskTreeTask(onTaskTreeSetup)
|
TaskTreeTask(onTaskTreeSetup)
|
||||||
};
|
};
|
||||||
m_taskTreeRunner.start(root);
|
m_taskTreeRunner.start(root);
|
||||||
|
@@ -23,6 +23,8 @@ namespace Utils { class FilePath; }
|
|||||||
|
|
||||||
namespace Axivion::Internal {
|
namespace Axivion::Internal {
|
||||||
|
|
||||||
|
constexpr int DefaultSearchLimit = 2048;
|
||||||
|
|
||||||
struct IssueListSearch
|
struct IssueListSearch
|
||||||
{
|
{
|
||||||
QString kind;
|
QString kind;
|
||||||
@@ -33,7 +35,7 @@ struct IssueListSearch
|
|||||||
QString filter_path;
|
QString filter_path;
|
||||||
QString sort;
|
QString sort;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int limit = 150;
|
int limit = DefaultSearchLimit;
|
||||||
bool computeTotalRowCount = false;
|
bool computeTotalRowCount = false;
|
||||||
|
|
||||||
QString toQuery() const;
|
QString toQuery() const;
|
||||||
@@ -71,7 +73,7 @@ void fetchProjectInfo(const QString &projectName);
|
|||||||
std::optional<Dto::ProjectInfoDto> projectInfo();
|
std::optional<Dto::ProjectInfoDto> projectInfo();
|
||||||
bool handleCertificateIssue();
|
bool handleCertificateIssue();
|
||||||
|
|
||||||
QIcon iconForIssue(const QString &prefix);
|
QIcon iconForIssue(const std::optional<Dto::IssueKind> &issueKind);
|
||||||
QString anyToSimpleString(const Dto::Any &any);
|
QString anyToSimpleString(const Dto::Any &any);
|
||||||
void fetchIssueInfo(const QString &id);
|
void fetchIssueInfo(const QString &id);
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "dynamiclistmodel.h"
|
#include "dynamiclistmodel.h"
|
||||||
|
|
||||||
|
#include "axivionplugin.h"
|
||||||
#include "axiviontr.h"
|
#include "axiviontr.h"
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -10,8 +11,6 @@
|
|||||||
|
|
||||||
namespace Axivion::Internal {
|
namespace Axivion::Internal {
|
||||||
|
|
||||||
constexpr int pageSize = 150;
|
|
||||||
|
|
||||||
DynamicListModel::DynamicListModel(QObject *parent)
|
DynamicListModel::DynamicListModel(QObject *parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
{
|
{
|
||||||
@@ -161,7 +160,7 @@ QModelIndex DynamicListModel::indexForItem(const ListItem *item) const
|
|||||||
void DynamicListModel::onNeedFetch(int row)
|
void DynamicListModel::onNeedFetch(int row)
|
||||||
{
|
{
|
||||||
m_fetchStart = row;
|
m_fetchStart = row;
|
||||||
m_fetchEnd = row + pageSize;
|
m_fetchEnd = row + DefaultSearchLimit;
|
||||||
if (m_fetchStart < 0)
|
if (m_fetchStart < 0)
|
||||||
return;
|
return;
|
||||||
m_fetchMoreTimer.start();
|
m_fetchMoreTimer.start();
|
||||||
@@ -171,14 +170,14 @@ void DynamicListModel::fetchNow()
|
|||||||
{
|
{
|
||||||
const int old = m_lastFetch;
|
const int old = m_lastFetch;
|
||||||
m_lastFetch = m_fetchStart; // we need the "original" fetch request to avoid endless loop
|
m_lastFetch = m_fetchStart; // we need the "original" fetch request to avoid endless loop
|
||||||
m_lastFetchEnd = m_fetchStart + pageSize;
|
m_lastFetchEnd = m_fetchStart + DefaultSearchLimit;
|
||||||
|
|
||||||
if (old != -1) {
|
if (old != -1) {
|
||||||
const int diff = old - m_fetchStart;
|
const int diff = old - m_fetchStart;
|
||||||
if (0 < diff && diff < pageSize) {
|
if (0 < diff && diff < DefaultSearchLimit) {
|
||||||
m_fetchStart = qMax(old - pageSize, 0);
|
m_fetchStart = qMax(old - DefaultSearchLimit, 0);
|
||||||
} else if (0 > diff && diff > -pageSize) {
|
} else if (0 > diff && diff > - DefaultSearchLimit) {
|
||||||
m_fetchStart = old + pageSize;
|
m_fetchStart = old + DefaultSearchLimit;
|
||||||
if (m_expectedRowCount && m_fetchStart > *m_expectedRowCount)
|
if (m_expectedRowCount && m_fetchStart > *m_expectedRowCount)
|
||||||
m_fetchStart = *m_expectedRowCount;
|
m_fetchStart = *m_expectedRowCount;
|
||||||
}
|
}
|
||||||
@@ -186,7 +185,7 @@ void DynamicListModel::fetchNow()
|
|||||||
|
|
||||||
QTC_CHECK(m_expectedRowCount ? m_fetchStart <= *m_expectedRowCount
|
QTC_CHECK(m_expectedRowCount ? m_fetchStart <= *m_expectedRowCount
|
||||||
: m_fetchStart >= m_children.size());
|
: m_fetchStart >= m_children.size());
|
||||||
emit fetchRequested(m_fetchStart, pageSize);
|
emit fetchRequested(m_fetchStart, DefaultSearchLimit);
|
||||||
m_fetchStart = -1;
|
m_fetchStart = -1;
|
||||||
m_fetchEnd = -1;
|
m_fetchEnd = -1;
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 179 B After Width: | Height: | Size: 179 B |
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 263 B After Width: | Height: | Size: 263 B |
Before Width: | Height: | Size: 205 B After Width: | Height: | Size: 205 B |
Before Width: | Height: | Size: 241 B After Width: | Height: | Size: 241 B |
Before Width: | Height: | Size: 212 B After Width: | Height: | Size: 212 B |
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 397 B |
Before Width: | Height: | Size: 173 B After Width: | Height: | Size: 173 B |
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 175 B After Width: | Height: | Size: 175 B |
Before Width: | Height: | Size: 318 B After Width: | Height: | Size: 318 B |
@@ -61,8 +61,10 @@ void IssueHeaderView::mousePressEvent(QMouseEvent *event)
|
|||||||
if (y > 1 && y < height() - 2) { // TODO improve
|
if (y > 1 && y < height() - 2) { // TODO improve
|
||||||
const int pos = position.x();
|
const int pos = position.x();
|
||||||
const int logical = logicalIndexAt(pos);
|
const int logical = logicalIndexAt(pos);
|
||||||
const int end = sectionViewportPosition(logical) + sectionSize(logical);
|
m_lastToggleLogicalPos = logical;
|
||||||
const int start = end - ICON_SIZE - 2;
|
const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this);
|
||||||
|
const int end = sectionViewportPosition(logical) + sectionSize(logical) - margin;
|
||||||
|
const int start = end - ICON_SIZE;
|
||||||
m_maybeToggleSort = start < pos && end > pos;
|
m_maybeToggleSort = start < pos && end > pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +81,8 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
const QPoint position = event->position().toPoint();
|
const QPoint position = event->position().toPoint();
|
||||||
const int y = position.y();
|
const int y = position.y();
|
||||||
const int logical = logicalIndexAt(position.x());
|
const int logical = logicalIndexAt(position.x());
|
||||||
if (logical > -1 && logical < m_sortableColumns.size()) {
|
if (logical == m_lastToggleLogicalPos
|
||||||
|
&& logical > -1 && logical < m_sortableColumns.size()) {
|
||||||
if (m_sortableColumns.at(logical)) { // ignore non-sortable
|
if (m_sortableColumns.at(logical)) { // ignore non-sortable
|
||||||
if (y < height() / 2) // TODO improve
|
if (y < height() / 2) // TODO improve
|
||||||
onToggleSort(logical, SortOrder::Ascending);
|
onToggleSort(logical, SortOrder::Ascending);
|
||||||
@@ -88,6 +91,7 @@ void IssueHeaderView::mouseReleaseEvent(QMouseEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
m_lastToggleLogicalPos = -1;
|
||||||
QHeaderView::mouseReleaseEvent(event);
|
QHeaderView::mouseReleaseEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +122,10 @@ QSize IssueHeaderView::sectionSizeFromContents(int logicalIndex) const
|
|||||||
const QSize oldSize = QHeaderView::sectionSizeFromContents(logicalIndex);
|
const QSize oldSize = QHeaderView::sectionSizeFromContents(logicalIndex);
|
||||||
const QSize newSize = logicalIndex < m_columnWidths.size()
|
const QSize newSize = logicalIndex < m_columnWidths.size()
|
||||||
? QSize(qMax(m_columnWidths.at(logicalIndex), oldSize.width()), oldSize.height()) : oldSize;
|
? QSize(qMax(m_columnWidths.at(logicalIndex), oldSize.width()), oldSize.height()) : oldSize;
|
||||||
// add icon size and margin (2)
|
|
||||||
return QSize{newSize.width() + ICON_SIZE + 2, qMax(newSize.height(), ICON_SIZE)};
|
const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this);
|
||||||
|
// add icon size and margin (default resize handle margin + 1)
|
||||||
|
return QSize{newSize.width() + ICON_SIZE + margin, qMax(newSize.height(), ICON_SIZE)};
|
||||||
}
|
}
|
||||||
|
|
||||||
void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
|
void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const
|
||||||
@@ -132,9 +138,10 @@ void IssueHeaderView::paintSection(QPainter *painter, const QRect &rect, int log
|
|||||||
if (!m_sortableColumns.at(logicalIndex))
|
if (!m_sortableColumns.at(logicalIndex))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const int margin = style()->pixelMetric(QStyle::PM_HeaderGripMargin, nullptr, this);
|
||||||
const QIcon icon = iconForSorted(logicalIndex == m_currentSortIndex ? m_currentSortOrder : SortOrder::None);
|
const QIcon icon = iconForSorted(logicalIndex == m_currentSortIndex ? m_currentSortOrder : SortOrder::None);
|
||||||
const int offset = qMax((rect.height() - ICON_SIZE), 0) / 2;
|
const int offset = qMax((rect.height() - ICON_SIZE), 0) / 2;
|
||||||
const QRect iconRect(rect.left() + rect.width() - ICON_SIZE - 2, offset, ICON_SIZE, ICON_SIZE);
|
const QRect iconRect(rect.left() + rect.width() - ICON_SIZE - margin, offset, ICON_SIZE, ICON_SIZE);
|
||||||
icon.paint(painter, iconRect);
|
icon.paint(painter, iconRect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -35,6 +35,7 @@ private:
|
|||||||
void onToggleSort(int index, SortOrder order);
|
void onToggleSort(int index, SortOrder order);
|
||||||
bool m_dragging = false;
|
bool m_dragging = false;
|
||||||
bool m_maybeToggleSort = false;
|
bool m_maybeToggleSort = false;
|
||||||
|
int m_lastToggleLogicalPos = -1;
|
||||||
int m_currentSortIndex = -1;
|
int m_currentSortIndex = -1;
|
||||||
SortOrder m_currentSortOrder = SortOrder::None;
|
SortOrder m_currentSortOrder = SortOrder::None;
|
||||||
QList<bool> m_sortableColumns;
|
QList<bool> m_sortableColumns;
|
||||||
|
@@ -59,9 +59,14 @@ clang::format::FormatStyle calculateQtcStyle()
|
|||||||
style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline;
|
style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline;
|
||||||
style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
|
style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never;
|
||||||
style.AllowShortLoopsOnASingleLine = false;
|
style.AllowShortLoopsOnASingleLine = false;
|
||||||
style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None;
|
|
||||||
style.AlwaysBreakBeforeMultilineStrings = false;
|
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;
|
style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes;
|
||||||
|
#endif
|
||||||
style.BinPackArguments = false;
|
style.BinPackArguments = false;
|
||||||
style.BinPackParameters = false;
|
style.BinPackParameters = false;
|
||||||
style.BraceWrapping.AfterClass = true;
|
style.BraceWrapping.AfterClass = true;
|
||||||
|
@@ -301,8 +301,24 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const FilePath buildDir = relativeLibs ? buildDirectory : currentBuildDir;
|
const FilePath buildDir = relativeLibs ? buildDirectory : currentBuildDir;
|
||||||
FilePath tmp = buildDir.resolvePath(part);
|
std::optional<QString> dllName;
|
||||||
|
|
||||||
|
if (buildDir.osType() == OsTypeWindows && (f.role == "libraries")) {
|
||||||
|
// Skip object libraries on Windows. This case can happen with static qml plugins
|
||||||
|
if (part.endsWith(".obj") || part.endsWith(".o"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Only consider dlls, not static libraries
|
||||||
|
for (const QString &suffix :
|
||||||
|
{QString(".lib"), QString(".dll.a"), QString(".a")}) {
|
||||||
|
if (part.endsWith(suffix) && !dllName)
|
||||||
|
dllName = FilePath::fromUserInput(
|
||||||
|
part.chopped(suffix.length()).append(".dll"))
|
||||||
|
.fileName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FilePath tmp = buildDir.resolvePath(part);
|
||||||
if (f.role == "libraries")
|
if (f.role == "libraries")
|
||||||
tmp = tmp.parentDir();
|
tmp = tmp.parentDir();
|
||||||
|
|
||||||
@@ -312,19 +328,20 @@ static CMakeBuildTarget toBuildTarget(const TargetDetails &t,
|
|||||||
// "/usr/local/lib" since these are usually in the standard search
|
// "/usr/local/lib" since these are usually in the standard search
|
||||||
// paths. There probably are more, but the naming schemes are arbitrary
|
// paths. There probably are more, but the naming schemes are arbitrary
|
||||||
// so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR").
|
// so we'd need to ask the linker ("ld --verbose | grep SEARCH_DIR").
|
||||||
if (buildDir.osType() == OsTypeWindows
|
if (buildDir.osType() != OsTypeWindows
|
||||||
|| !isChildOf(tmp,
|
&& !isChildOf(tmp,
|
||||||
{"/lib",
|
{"/lib", "/lib64", "/usr/lib", "/usr/lib64", "/usr/local/lib"}))
|
||||||
"/lib64",
|
|
||||||
"/usr/lib",
|
|
||||||
"/usr/lib64",
|
|
||||||
"/usr/local/lib"})) {
|
|
||||||
librarySeachPaths.append(tmp);
|
librarySeachPaths.append(tmp);
|
||||||
|
|
||||||
|
if (buildDir.osType() == OsTypeWindows && dllName) {
|
||||||
|
if (tmp.pathAppended(*dllName).exists())
|
||||||
|
librarySeachPaths.append(tmp);
|
||||||
|
|
||||||
// Libraries often have their import libs in ../lib and the
|
// Libraries often have their import libs in ../lib and the
|
||||||
// actual dll files in ../bin on windows. Qt is one example of that.
|
// actual dll files in ../bin on windows. Qt is one example of that.
|
||||||
if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) {
|
if (tmp.fileName() == "lib" && buildDir.osType() == OsTypeWindows) {
|
||||||
const FilePath path = tmp.parentDir().pathAppended("bin");
|
const FilePath path = tmp.parentDir().pathAppended("bin");
|
||||||
if (path.isDir())
|
if (path.isDir() && path.pathAppended(*dllName).exists())
|
||||||
librarySeachPaths.append(path);
|
librarySeachPaths.append(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/core">
|
<qresource prefix="/core">
|
||||||
|
<file>images/expandarrow.png</file>
|
||||||
|
<file>images/expandarrow@2x.png</file>
|
||||||
|
<file>images/search.png</file>
|
||||||
|
<file>images/search@2x.png</file>
|
||||||
<file>images/settingscategory_core.png</file>
|
<file>images/settingscategory_core.png</file>
|
||||||
<file>images/settingscategory_core@2x.png</file>
|
<file>images/settingscategory_core@2x.png</file>
|
||||||
<file>images/settingscategory_design.png</file>
|
<file>images/settingscategory_design.png</file>
|
||||||
|
BIN
src/plugins/coreplugin/images/expandarrow.png
Normal file
After Width: | Height: | Size: 169 B |
BIN
src/plugins/coreplugin/images/expandarrow@2x.png
Normal file
After Width: | Height: | Size: 209 B |
BIN
src/plugins/coreplugin/images/search.png
Normal file
After Width: | Height: | Size: 179 B |
BIN
src/plugins/coreplugin/images/search@2x.png
Normal file
After Width: | Height: | Size: 361 B |
@@ -3,25 +3,8 @@
|
|||||||
|
|
||||||
#include "iwelcomepage.h"
|
#include "iwelcomepage.h"
|
||||||
|
|
||||||
#include <utils/icon.h>
|
|
||||||
#include <utils/theme/theme.h>
|
|
||||||
#include <utils/stylehelper.h>
|
|
||||||
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPainterPath>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QUrl>
|
|
||||||
|
|
||||||
#include <qdrawutil.h>
|
|
||||||
|
|
||||||
using namespace Utils;
|
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
const char WITHACCENTCOLOR_PROPERTY_NAME[] = "_withAccentColor";
|
|
||||||
|
|
||||||
static QList<IWelcomePage *> g_welcomePages;
|
static QList<IWelcomePage *> g_welcomePages;
|
||||||
|
|
||||||
const QList<IWelcomePage *> IWelcomePage::allWelcomePages()
|
const QList<IWelcomePage *> IWelcomePage::allWelcomePages()
|
||||||
@@ -39,171 +22,4 @@ IWelcomePage::~IWelcomePage()
|
|||||||
g_welcomePages.removeOne(this);
|
g_welcomePages.removeOne(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPalette WelcomePageFrame::buttonPalette(bool isActive, bool isCursorInside, bool forText)
|
|
||||||
{
|
|
||||||
QPalette pal;
|
|
||||||
pal.setBrush(QPalette::Window, {});
|
|
||||||
pal.setBrush(QPalette::WindowText, {});
|
|
||||||
|
|
||||||
Theme *theme = Utils::creatorTheme();
|
|
||||||
if (isActive) {
|
|
||||||
if (forText) {
|
|
||||||
pal.setColor(QPalette::Window, theme->color(Theme::Welcome_ForegroundPrimaryColor));
|
|
||||||
pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_BackgroundPrimaryColor));
|
|
||||||
} else {
|
|
||||||
pal.setColor(QPalette::Window, theme->color(Theme::Welcome_AccentColor));
|
|
||||||
pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_AccentColor));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (isCursorInside) {
|
|
||||||
if (forText) {
|
|
||||||
pal.setColor(QPalette::Window, theme->color(Theme::Welcome_HoverColor));
|
|
||||||
pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_TextColor));
|
|
||||||
} else {
|
|
||||||
pal.setColor(QPalette::Window, theme->color(Theme::Welcome_HoverColor));
|
|
||||||
pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_ForegroundSecondaryColor));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (forText) {
|
|
||||||
pal.setColor(QPalette::Window, theme->color(Theme::Welcome_ForegroundPrimaryColor));
|
|
||||||
pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_TextColor));
|
|
||||||
} else {
|
|
||||||
pal.setColor(QPalette::Window, theme->color(Theme::Welcome_BackgroundPrimaryColor));
|
|
||||||
pal.setColor(QPalette::WindowText, theme->color(Theme::Welcome_ForegroundSecondaryColor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return pal;
|
|
||||||
}
|
|
||||||
|
|
||||||
WelcomePageFrame::WelcomePageFrame(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
setContentsMargins(1, 1, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageFrame::paintEvent(QPaintEvent *event)
|
|
||||||
{
|
|
||||||
QWidget::paintEvent(event);
|
|
||||||
QPainter p(this);
|
|
||||||
|
|
||||||
qDrawPlainRect(&p, rect(), palette().color(QPalette::WindowText), 1);
|
|
||||||
|
|
||||||
if (property(WITHACCENTCOLOR_PROPERTY_NAME).toBool()) {
|
|
||||||
const int accentRectWidth = 10;
|
|
||||||
const QRect accentRect = rect().adjusted(width() - accentRectWidth, 0, 0, 0);
|
|
||||||
p.fillRect(accentRect, creatorTheme()->color(Theme::Welcome_AccentColor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WelcomePageButtonPrivate
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit WelcomePageButtonPrivate(WelcomePageButton *parent)
|
|
||||||
: q(parent) {}
|
|
||||||
bool isActive() const;
|
|
||||||
void doUpdate(bool cursorInside);
|
|
||||||
|
|
||||||
WelcomePageButton *q;
|
|
||||||
QHBoxLayout *m_layout = nullptr;
|
|
||||||
QLabel *m_label = nullptr;
|
|
||||||
|
|
||||||
std::function<void()> onClicked;
|
|
||||||
std::function<bool()> activeChecker;
|
|
||||||
};
|
|
||||||
|
|
||||||
WelcomePageButton::WelcomePageButton(QWidget *parent)
|
|
||||||
: WelcomePageFrame(parent), d(new WelcomePageButtonPrivate(this))
|
|
||||||
{
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
setPalette(buttonPalette(false, false, false));
|
|
||||||
setContentsMargins(0, 1, 0, 1);
|
|
||||||
|
|
||||||
d->m_label = new QLabel(this);
|
|
||||||
d->m_label->setPalette(buttonPalette(false, false, true));
|
|
||||||
d->m_label->setAlignment(Qt::AlignCenter);
|
|
||||||
|
|
||||||
d->m_layout = new QHBoxLayout;
|
|
||||||
d->m_layout->setSpacing(0);
|
|
||||||
d->m_layout->addWidget(d->m_label);
|
|
||||||
setSize(SizeLarge);
|
|
||||||
setLayout(d->m_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
WelcomePageButton::~WelcomePageButton()
|
|
||||||
{
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::mousePressEvent(QMouseEvent *)
|
|
||||||
{
|
|
||||||
if (d->onClicked)
|
|
||||||
d->onClicked();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::enterEvent(QEnterEvent *)
|
|
||||||
{
|
|
||||||
d->doUpdate(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::leaveEvent(QEvent *)
|
|
||||||
{
|
|
||||||
d->doUpdate(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WelcomePageButtonPrivate::isActive() const
|
|
||||||
{
|
|
||||||
return activeChecker && activeChecker();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButtonPrivate::doUpdate(bool cursorInside)
|
|
||||||
{
|
|
||||||
const bool active = isActive();
|
|
||||||
q->setPalette(WelcomePageFrame::buttonPalette(active, cursorInside, false));
|
|
||||||
const QPalette lpal = WelcomePageFrame::buttonPalette(active, cursorInside, true);
|
|
||||||
m_label->setPalette(lpal);
|
|
||||||
q->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::setText(const QString &text)
|
|
||||||
{
|
|
||||||
d->m_label->setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::setSize(Size size)
|
|
||||||
{
|
|
||||||
const int hMargin = size == SizeSmall ? 12 : 26;
|
|
||||||
const int vMargin = size == SizeSmall ? 2 : 4;
|
|
||||||
d->m_layout->setContentsMargins(hMargin, vMargin, hMargin, vMargin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::setWithAccentColor(bool withAccent)
|
|
||||||
{
|
|
||||||
setProperty(WITHACCENTCOLOR_PROPERTY_NAME, withAccent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::setActiveChecker(const std::function<bool ()> &value)
|
|
||||||
{
|
|
||||||
d->activeChecker = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::recheckActive()
|
|
||||||
{
|
|
||||||
bool isActive = d->isActive();
|
|
||||||
d->doUpdate(isActive);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::click()
|
|
||||||
{
|
|
||||||
if (d->onClicked)
|
|
||||||
d->onClicked();
|
|
||||||
}
|
|
||||||
|
|
||||||
void WelcomePageButton::setOnClicked(const std::function<void ()> &value)
|
|
||||||
{
|
|
||||||
d->onClicked = value;
|
|
||||||
if (d->isActive())
|
|
||||||
click();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@@ -7,13 +7,10 @@
|
|||||||
|
|
||||||
#include <utils/id.h>
|
#include <utils/id.h>
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QPixmap;
|
class QWidget;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -37,43 +34,4 @@ public:
|
|||||||
static const QList<IWelcomePage *> allWelcomePages();
|
static const QList<IWelcomePage *> allWelcomePages();
|
||||||
};
|
};
|
||||||
|
|
||||||
class WelcomePageButtonPrivate;
|
|
||||||
|
|
||||||
class CORE_EXPORT WelcomePageFrame : public QWidget
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
WelcomePageFrame(QWidget *parent);
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
static QPalette buttonPalette(bool isActive, bool isCursorInside, bool forText);
|
|
||||||
};
|
|
||||||
|
|
||||||
class CORE_EXPORT WelcomePageButton : public WelcomePageFrame
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum Size {
|
|
||||||
SizeSmall,
|
|
||||||
SizeLarge,
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit WelcomePageButton(QWidget *parent = nullptr);
|
|
||||||
~WelcomePageButton() override;
|
|
||||||
|
|
||||||
void mousePressEvent(QMouseEvent *) override;
|
|
||||||
void enterEvent(QEnterEvent *) override;
|
|
||||||
void leaveEvent(QEvent *) override;
|
|
||||||
|
|
||||||
void setText(const QString &text);
|
|
||||||
void setSize(enum Size);
|
|
||||||
void setWithAccentColor(bool withAccent);
|
|
||||||
void setOnClicked(const std::function<void ()> &value);
|
|
||||||
void setActiveChecker(const std::function<bool ()> &value);
|
|
||||||
void recheckActive();
|
|
||||||
void click();
|
|
||||||
|
|
||||||
private:
|
|
||||||
WelcomePageButtonPrivate *d;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // Core
|
} // Core
|
||||||
|
@@ -559,10 +559,16 @@ private:
|
|||||||
|
|
||||||
const QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz");
|
const QString timestamp = QDateTime::currentDateTime().toString("HH:mm:ss.zzz");
|
||||||
|
|
||||||
if (rowCount() >= 1000000) // limit log to 1000000 items
|
auto append = [this, timestamp, type, category, msg] {
|
||||||
destroyItem(itemForIndex(index(0, 0)));
|
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:
|
private:
|
||||||
|
@@ -267,6 +267,11 @@ QDateTime SessionManager::lastActiveTime(const QString &session)
|
|||||||
return d->m_lastActiveTimes.value(session);
|
return d->m_lastActiveTimes.value(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SessionManager::sessionsCount()
|
||||||
|
{
|
||||||
|
return d->m_sessions.count();
|
||||||
|
}
|
||||||
|
|
||||||
FilePath SessionManager::sessionNameToFileName(const QString &session)
|
FilePath SessionManager::sessionNameToFileName(const QString &session)
|
||||||
{
|
{
|
||||||
return ICore::userResourcePath(session + ".qws");
|
return ICore::userResourcePath(session + ".qws");
|
||||||
|
@@ -34,6 +34,7 @@ public:
|
|||||||
static QStringList sessions();
|
static QStringList sessions();
|
||||||
static QDateTime sessionDateTime(const QString &session);
|
static QDateTime sessionDateTime(const QString &session);
|
||||||
static QDateTime lastActiveTime(const QString &session);
|
static QDateTime lastActiveTime(const QString &session);
|
||||||
|
static int sessionsCount();
|
||||||
|
|
||||||
static bool createSession(const QString &session);
|
static bool createSession(const QString &session);
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fancylineedit.h>
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/icon.h>
|
||||||
#include <utils/layoutbuilder.h>
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stylehelper.h>
|
#include <utils/stylehelper.h>
|
||||||
@@ -26,36 +27,34 @@
|
|||||||
|
|
||||||
#include <qdrawutil.h>
|
#include <qdrawutil.h>
|
||||||
|
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
using namespace WelcomePageHelpers;
|
using namespace WelcomePageHelpers;
|
||||||
|
using namespace StyleHelper::SpacingTokens;
|
||||||
|
|
||||||
static QColor themeColor(Theme::Color role)
|
static QColor themeColor(Theme::Color role)
|
||||||
{
|
{
|
||||||
return creatorTheme()->color(role);
|
return creatorTheme()->color(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QFont sizedFont(int size, const QWidget *widget)
|
|
||||||
{
|
|
||||||
QFont f = widget->font();
|
|
||||||
f.setPixelSize(size);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace WelcomePageHelpers {
|
namespace WelcomePageHelpers {
|
||||||
|
|
||||||
QWidget *panelBar(QWidget *parent)
|
void setBackgroundColor(QWidget *widget, Theme::Color colorRole)
|
||||||
{
|
{
|
||||||
auto frame = new QWidget(parent);
|
QPalette palette = creatorTheme()->palette();
|
||||||
frame->setAutoFillBackground(true);
|
const QPalette::ColorRole role = QPalette::Window;
|
||||||
frame->setMinimumWidth(WelcomePageHelpers::HSpacing);
|
palette.setBrush(role, {});
|
||||||
QPalette pal;
|
palette.setColor(role, creatorTheme()->color(colorRole));
|
||||||
pal.setBrush(QPalette::Window, {});
|
widget->setPalette(palette);
|
||||||
pal.setColor(QPalette::Window, themeColor(Theme::Welcome_BackgroundPrimaryColor));
|
widget->setBackgroundRole(role);
|
||||||
frame->setPalette(pal);
|
widget->setAutoFillBackground(true);
|
||||||
return frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawCardBackground(QPainter *painter, const QRectF &rect,
|
void drawCardBackground(QPainter *painter, const QRectF &rect,
|
||||||
@@ -77,30 +76,351 @@ void drawCardBackground(QPainter *painter, const QRectF &rect,
|
|||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget *createRule(Qt::Orientation orientation, QWidget *parent)
|
||||||
|
{
|
||||||
|
auto rule = new QWidget(parent);
|
||||||
|
if (orientation == Qt::Horizontal)
|
||||||
|
rule->setFixedHeight(1);
|
||||||
|
else
|
||||||
|
rule->setFixedWidth(1);
|
||||||
|
setBackgroundColor(rule, Theme::Token_Stroke_Subtle);
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace WelcomePageHelpers
|
} // namespace WelcomePageHelpers
|
||||||
|
|
||||||
SearchBox::SearchBox(QWidget *parent)
|
enum WidgetState {
|
||||||
: WelcomePageFrame(parent)
|
WidgetStateDefault,
|
||||||
|
WidgetStateChecked,
|
||||||
|
WidgetStateHovered,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TextFormat &buttonTF(Button::Role role, WidgetState state)
|
||||||
{
|
{
|
||||||
setAutoFillBackground(true);
|
using namespace WelcomePageHelpers;
|
||||||
|
static const TextFormat mediumPrimaryTF
|
||||||
|
{Theme::Token_Basic_White, StyleHelper::UiElement::UiElementButtonMedium,
|
||||||
|
Qt::AlignCenter | Qt::TextDontClip};
|
||||||
|
static const TextFormat mediumSecondaryTF
|
||||||
|
{Theme::Token_Text_Default, mediumPrimaryTF.uiElement, mediumPrimaryTF.drawTextFlags};
|
||||||
|
static const TextFormat smallPrimaryTF
|
||||||
|
{mediumPrimaryTF.themeColor, StyleHelper::UiElement::UiElementButtonSmall,
|
||||||
|
mediumPrimaryTF.drawTextFlags};
|
||||||
|
static const TextFormat smallSecondaryTF
|
||||||
|
{mediumSecondaryTF.themeColor, smallPrimaryTF.uiElement, smallPrimaryTF.drawTextFlags};
|
||||||
|
static const TextFormat smallListDefaultTF
|
||||||
|
{Theme::Token_Text_Default, StyleHelper::UiElement::UiElementIconStandard,
|
||||||
|
Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip};
|
||||||
|
static const TextFormat smallListCheckedTF
|
||||||
|
{smallListDefaultTF.themeColor, StyleHelper::UiElement::UiElementIconActive,
|
||||||
|
smallListDefaultTF.drawTextFlags};
|
||||||
|
static const TextFormat smallLinkDefaultTF
|
||||||
|
{Theme::Token_Text_Default, smallListDefaultTF.uiElement, smallListDefaultTF.drawTextFlags};
|
||||||
|
static const TextFormat smallLinkHoveredTF
|
||||||
|
{Theme::Token_Accent_Default, smallListCheckedTF.uiElement,
|
||||||
|
smallLinkDefaultTF.drawTextFlags};
|
||||||
|
|
||||||
m_lineEdit = new FancyLineEdit;
|
switch (role) {
|
||||||
m_lineEdit->setFiltering(true);
|
case Button::MediumPrimary: return mediumPrimaryTF;
|
||||||
m_lineEdit->setFrame(false);
|
case Button::MediumSecondary: return mediumSecondaryTF;
|
||||||
m_lineEdit->setMinimumHeight(33);
|
case Button::SmallPrimary: return smallPrimaryTF;
|
||||||
m_lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false);
|
case Button::SmallSecondary: return smallSecondaryTF;
|
||||||
|
case Button::SmallList: return (state == WidgetStateDefault) ? smallListDefaultTF
|
||||||
|
: smallListCheckedTF;
|
||||||
|
case Button::SmallLink: return (state == WidgetStateDefault) ? smallLinkDefaultTF
|
||||||
|
: smallLinkHoveredTF;
|
||||||
|
}
|
||||||
|
return mediumPrimaryTF;
|
||||||
|
}
|
||||||
|
|
||||||
QPalette pal = buttonPalette(false, false, true);
|
Button::Button(const QString &text, Role role, QWidget *parent)
|
||||||
// for the margins
|
: QPushButton(text, parent)
|
||||||
pal.setColor(QPalette::Window, m_lineEdit->palette().color(QPalette::Base));
|
, m_role(role)
|
||||||
// for macOS dark mode
|
{
|
||||||
pal.setColor(QPalette::WindowText, themeColor(Theme::Welcome_ForegroundPrimaryColor));
|
// Prevent QMacStyle::subElementRect(SE_PushButtonLayoutItem) from changing our geometry
|
||||||
pal.setColor(QPalette::Text, themeColor(Theme::Welcome_TextColor));
|
setFlat(true);
|
||||||
|
|
||||||
|
updateMargins();
|
||||||
|
if (m_role == SmallList)
|
||||||
|
setCheckable(true);
|
||||||
|
else if (m_role == SmallLink)
|
||||||
|
setCursor(Qt::PointingHandCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize Button::minimumSizeHint() const
|
||||||
|
{
|
||||||
|
const TextFormat &tf = buttonTF(m_role, WidgetStateHovered);
|
||||||
|
const QFontMetrics fm(tf.font());
|
||||||
|
const QSize textS = fm.size(Qt::TextShowMnemonic, text());
|
||||||
|
const QMargins margins = contentsMargins();
|
||||||
|
return {margins.left() + textS.width() + margins.right(),
|
||||||
|
margins.top() + tf.lineHeight() + margins.bottom()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
// Without pixmap
|
||||||
|
// +----------------+----------------+----------------+
|
||||||
|
// | |(VPadding[S|XS])| |
|
||||||
|
// | +----------------+----------------+
|
||||||
|
// |(HPadding[S|XS])| <label> |(HPadding[S|XS])|
|
||||||
|
// | +----------------+----------------+
|
||||||
|
// | |(VPadding[S|XS])| |
|
||||||
|
// +----------------+---------------------------------+
|
||||||
|
//
|
||||||
|
// With pixmap
|
||||||
|
// +--------+------------+---------------------------------+
|
||||||
|
// | | |(VPadding[S|XS])| |
|
||||||
|
// | | +----------------+ |
|
||||||
|
// |<pixmap>|(HGap[S|XS])| <label> |(HPadding[S|XS])|
|
||||||
|
// | | +----------------+ |
|
||||||
|
// | | |(VPadding[S|XS])| |
|
||||||
|
// +--------+------------+----------------+----------------+
|
||||||
|
|
||||||
|
using namespace WelcomePageHelpers;
|
||||||
|
const bool hovered = underMouse();
|
||||||
|
const WidgetState state = isChecked() ? WidgetStateChecked : hovered ? WidgetStateHovered
|
||||||
|
: WidgetStateDefault;
|
||||||
|
const TextFormat &tf = buttonTF(m_role, state);
|
||||||
|
const QMargins margins = contentsMargins();
|
||||||
|
const QRect bgR = rect();
|
||||||
|
|
||||||
|
QPainter p(this);
|
||||||
|
|
||||||
|
const qreal brRectRounding = 3.75;
|
||||||
|
switch (m_role) {
|
||||||
|
case MediumPrimary:
|
||||||
|
case SmallPrimary: {
|
||||||
|
const QBrush fill(creatorTheme()->color(isDown()
|
||||||
|
? Theme::Token_Accent_Subtle
|
||||||
|
: hovered ? Theme::Token_Accent_Muted
|
||||||
|
: Theme::Token_Accent_Default));
|
||||||
|
drawCardBackground(&p, bgR, fill, QPen(Qt::NoPen), brRectRounding);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MediumSecondary:
|
||||||
|
case SmallSecondary: {
|
||||||
|
const QPen outline(creatorTheme()->color(Theme::Token_Text_Default), hovered ? 2 : 1);
|
||||||
|
drawCardBackground(&p, bgR, QBrush(Qt::NoBrush), outline, brRectRounding);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SmallList: {
|
||||||
|
if (isChecked() || hovered) {
|
||||||
|
const QBrush fill(creatorTheme()->color(isChecked() ? Theme::Token_Foreground_Muted
|
||||||
|
: Theme::Token_Foreground_Subtle));
|
||||||
|
drawCardBackground(&p, bgR, fill, QPen(Qt::NoPen), brRectRounding);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SmallLink:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_pixmap.isNull()) {
|
||||||
|
const int pixmapHeight = int(m_pixmap.deviceIndependentSize().height());
|
||||||
|
const int pixmapY = (bgR.height() - pixmapHeight) / 2;
|
||||||
|
p.drawPixmap(0, pixmapY, m_pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int availableLabelWidth = event->rect().width() - margins.left() - margins.right();
|
||||||
|
const QFont font = tf.font();
|
||||||
|
const QFontMetrics fm(font);
|
||||||
|
const QString elidedLabelText = fm.elidedText(text(), Qt::ElideRight, availableLabelWidth);
|
||||||
|
const QRect labelR(margins.left(), margins.top(), availableLabelWidth, tf.lineHeight());
|
||||||
|
p.setFont(font);
|
||||||
|
p.setPen(tf.color());
|
||||||
|
p.drawText(labelR, tf.drawTextFlags, elidedLabelText);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::setPixmap(const QPixmap &pixmap)
|
||||||
|
{
|
||||||
|
m_pixmap = pixmap;
|
||||||
|
updateMargins();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Button::updateMargins()
|
||||||
|
{
|
||||||
|
const bool tokenSizeS = m_role == MediumPrimary || m_role == MediumSecondary
|
||||||
|
|| m_role == SmallList || m_role == SmallLink;
|
||||||
|
const int gap = tokenSizeS ? HGapS : HGapXs;
|
||||||
|
const int hPaddingR = tokenSizeS ? HPaddingS : HPaddingXs;
|
||||||
|
const int hPaddingL = m_pixmap.isNull() ? hPaddingR
|
||||||
|
: int(m_pixmap.deviceIndependentSize().width()) + gap;
|
||||||
|
const int vPadding = tokenSizeS ? VPaddingS : VPaddingXs;
|
||||||
|
setContentsMargins(hPaddingL, vPadding, hPaddingR, vPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
Label::Label(const QString &text, Role role, QWidget *parent)
|
||||||
|
: QLabel(text, parent)
|
||||||
|
, m_role(role)
|
||||||
|
{
|
||||||
|
using namespace WelcomePageHelpers;
|
||||||
|
static const TextFormat primaryTF
|
||||||
|
{Theme::Token_Text_Muted, StyleHelper::UiElement::UiElementH3};
|
||||||
|
static const TextFormat secondaryTF
|
||||||
|
{primaryTF.themeColor, StyleHelper::UiElement::UiElementH6Capital};
|
||||||
|
|
||||||
|
const TextFormat &tF = m_role == Primary ? primaryTF : secondaryTF;
|
||||||
|
const int vPadding = m_role == Primary ? ExPaddingGapM : VPaddingS;
|
||||||
|
|
||||||
|
setFixedHeight(vPadding + tF.lineHeight() + vPadding);
|
||||||
|
setFont(tF.font());
|
||||||
|
QPalette pal = palette();
|
||||||
|
pal.setColor(QPalette::WindowText, tF.color());
|
||||||
|
setPalette(pal);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr TextFormat searchBoxTextTF
|
||||||
|
{Theme::Token_Text_Default, StyleHelper::UiElement::UiElementBody2};
|
||||||
|
constexpr TextFormat searchBoxPlaceholderTF
|
||||||
|
{Theme::Token_Text_Muted, searchBoxTextTF.uiElement};
|
||||||
|
|
||||||
|
static const QPixmap &searchBoxIcon()
|
||||||
|
{
|
||||||
|
static const QPixmap icon = Icon({{FilePath::fromString(":/core/images/search"),
|
||||||
|
Theme::Token_Text_Muted}}, Icon::Tint).pixmap();
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchBox::SearchBox(QWidget *parent)
|
||||||
|
: QLineEdit(parent)
|
||||||
|
{
|
||||||
|
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||||
|
setAutoFillBackground(false);
|
||||||
|
setFont(searchBoxTextTF.font());
|
||||||
|
setFrame(false);
|
||||||
|
setMouseTracking(true);
|
||||||
|
|
||||||
|
QPalette pal = palette();
|
||||||
|
pal.setColor(QPalette::Base, Qt::transparent);
|
||||||
|
pal.setColor(QPalette::PlaceholderText, searchBoxPlaceholderTF.color());
|
||||||
|
pal.setColor(QPalette::Text, searchBoxTextTF.color());
|
||||||
setPalette(pal);
|
setPalette(pal);
|
||||||
|
|
||||||
auto box = new QHBoxLayout(this);
|
const QSize iconSize = searchBoxIcon().deviceIndependentSize().toSize();
|
||||||
box->setContentsMargins(10, 0, 1, 0);
|
setContentsMargins({HPaddingXs, ExPaddingGapM,
|
||||||
box->addWidget(m_lineEdit);
|
HPaddingXs + iconSize.width() + HPaddingXs, ExPaddingGapM});
|
||||||
|
setFixedHeight(ExPaddingGapM + searchBoxTextTF.lineHeight() + ExPaddingGapM);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize SearchBox::minimumSizeHint() const
|
||||||
|
{
|
||||||
|
const QFontMetrics fm(searchBoxTextTF.font());
|
||||||
|
const QSize textS = fm.size(Qt::TextSingleLine, text());
|
||||||
|
const QMargins margins = contentsMargins();
|
||||||
|
return {margins.left() + textS.width() + margins.right(),
|
||||||
|
margins.top() + searchBoxTextTF.lineHeight() + margins.bottom()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchBox::enterEvent(QEnterEvent *event)
|
||||||
|
{
|
||||||
|
QLineEdit::enterEvent(event);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchBox::leaveEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
QLineEdit::leaveEvent(event);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void paintCommonBackground(QPainter *p, const QRectF &rect, const QWidget *widget)
|
||||||
|
{
|
||||||
|
const QBrush fill(creatorTheme()->color(Theme::Token_Background_Muted));
|
||||||
|
const Theme::Color c = widget->hasFocus() ? Theme::Token_Stroke_Strong :
|
||||||
|
widget->underMouse() ? Theme::Token_Stroke_Muted
|
||||||
|
: Theme::Token_Stroke_Subtle;
|
||||||
|
const QPen pen(creatorTheme()->color(c));
|
||||||
|
drawCardBackground(p, rect, fill, pen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchBox::paintEvent(QPaintEvent *event)
|
||||||
|
{
|
||||||
|
// +------------+---------------+------------+------+------------+
|
||||||
|
// | |(ExPaddingGapM)| | | |
|
||||||
|
// | +---------------+ | | |
|
||||||
|
// |(HPaddingXs)| <lineEdit> |(HPaddingXs)|<icon>|(HPaddingXs)|
|
||||||
|
// | +---------------+ | | |
|
||||||
|
// | |(ExPaddingGapM)| | | |
|
||||||
|
// +------------+---------------+------------+------+------------+
|
||||||
|
|
||||||
|
QPainter p(this);
|
||||||
|
|
||||||
|
paintCommonBackground(&p, rect(), this);
|
||||||
|
const QPixmap icon = searchBoxIcon();
|
||||||
|
const QSize iconS = icon.deviceIndependentSize().toSize();
|
||||||
|
const QPoint iconPos(width() - HPaddingXs - iconS.width(), (height() - iconS.height()) / 2);
|
||||||
|
p.drawPixmap(iconPos, icon);
|
||||||
|
|
||||||
|
QLineEdit::paintEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr TextFormat ComboBoxTf
|
||||||
|
{Theme::Token_Text_Muted, StyleHelper::UiElementIconActive,
|
||||||
|
Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip};
|
||||||
|
|
||||||
|
static const QPixmap &comboBoxIcon()
|
||||||
|
{
|
||||||
|
static const QPixmap icon = Icon({{FilePath::fromString(":/core/images/expandarrow"),
|
||||||
|
ComboBoxTf.themeColor}}, Icon::Tint).pixmap();
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox::ComboBox(QWidget *parent)
|
||||||
|
: QComboBox(parent)
|
||||||
|
{
|
||||||
|
setFont(ComboBoxTf.font());
|
||||||
|
setMouseTracking(true);
|
||||||
|
|
||||||
|
const QSize iconSize = comboBoxIcon().deviceIndependentSize().toSize();
|
||||||
|
setContentsMargins({HPaddingXs, VPaddingXs,
|
||||||
|
HGapXxs + iconSize.width() + HPaddingXs, VPaddingXs});
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ComboBox::sizeHint() const
|
||||||
|
{
|
||||||
|
const QSize parentS = QComboBox::sizeHint();
|
||||||
|
const QMargins margins = contentsMargins();
|
||||||
|
return {margins.left() + parentS.width() + margins.right(),
|
||||||
|
margins.top() + ComboBoxTf.lineHeight() + margins.bottom()};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComboBox::enterEvent(QEnterEvent *event)
|
||||||
|
{
|
||||||
|
QComboBox::enterEvent(event);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComboBox::leaveEvent(QEvent *event)
|
||||||
|
{
|
||||||
|
QComboBox::leaveEvent(event);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComboBox::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
// +------------+-------------+---------+-------+------------+
|
||||||
|
// | | (VPaddingXs)| | | |
|
||||||
|
// | +-------------+ | | |
|
||||||
|
// |(HPaddingXs)|<currentItem>|(HGapXxs)|<arrow>|(HPaddingXs)|
|
||||||
|
// | +-------------+ | | |
|
||||||
|
// | | (VPaddingXs)| | | |
|
||||||
|
// +------------+-------------+---------+-------+------------+
|
||||||
|
|
||||||
|
QPainter p(this);
|
||||||
|
paintCommonBackground(&p, rect(), this);
|
||||||
|
|
||||||
|
const QMargins margins = contentsMargins();
|
||||||
|
const QRect textR(margins.left(), margins.top(),
|
||||||
|
width() - margins.right(), ComboBoxTf.lineHeight());
|
||||||
|
p.setFont(ComboBoxTf.font());
|
||||||
|
p.setPen(ComboBoxTf.color());
|
||||||
|
p.drawText(textR, ComboBoxTf.drawTextFlags, currentText());
|
||||||
|
|
||||||
|
const QPixmap icon = comboBoxIcon();
|
||||||
|
const QSize iconS = icon.deviceIndependentSize().toSize();
|
||||||
|
const QPoint iconPos(width() - HPaddingXs - iconS.width(), (height() - iconS.height()) / 2);
|
||||||
|
p.drawPixmap(iconPos, icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
GridView::GridView(QWidget *parent)
|
GridView::GridView(QWidget *parent)
|
||||||
@@ -115,7 +435,7 @@ GridView::GridView(QWidget *parent)
|
|||||||
setUniformItemSizes(true);
|
setUniformItemSizes(true);
|
||||||
|
|
||||||
QPalette pal;
|
QPalette pal;
|
||||||
pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundSecondaryColor));
|
pal.setColor(QPalette::Base, themeColor(Theme::Token_Background_Default));
|
||||||
setPalette(pal); // Makes a difference on Mac.
|
setPalette(pal); // Makes a difference on Mac.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,10 +467,11 @@ bool SectionGridView::hasHeightForWidth() const
|
|||||||
|
|
||||||
int SectionGridView::heightForWidth(int width) const
|
int SectionGridView::heightForWidth(int width) const
|
||||||
{
|
{
|
||||||
const int columnCount = qMax(1, width / Core::WelcomePageHelpers::GridItemWidth);
|
const QSize itemSize = ListItemDelegate::itemSize();
|
||||||
|
const int columnCount = qMax(1, width / itemSize.width());
|
||||||
const int rowCount = (model()->rowCount() + columnCount - 1) / columnCount;
|
const int rowCount = (model()->rowCount() + columnCount - 1) / columnCount;
|
||||||
const int maxRowCount = m_maxRows ? std::min(*m_maxRows, rowCount) : rowCount;
|
const int maxRowCount = m_maxRows ? std::min(*m_maxRows, rowCount) : rowCount;
|
||||||
return maxRowCount * Core::WelcomePageHelpers::GridItemHeight;
|
return maxRowCount * itemSize.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SectionGridView::wheelEvent(QWheelEvent *e)
|
void SectionGridView::wheelEvent(QWheelEvent *e)
|
||||||
@@ -165,8 +486,9 @@ bool SectionGridView::event(QEvent *e)
|
|||||||
{
|
{
|
||||||
if (e->type() == QEvent::Resize) {
|
if (e->type() == QEvent::Resize) {
|
||||||
const auto itemsFit = [this](const QSize &size) {
|
const auto itemsFit = [this](const QSize &size) {
|
||||||
const int maxColumns = std::max(size.width() / WelcomePageHelpers::GridItemWidth, 1);
|
const QSize itemSize = ListItemDelegate::itemSize();
|
||||||
const int maxRows = std::max(size.height() / WelcomePageHelpers::GridItemHeight, 1);
|
const int maxColumns = std::max(size.width() / itemSize.width(), 1);
|
||||||
|
const int maxRows = std::max(size.height() / itemSize.height(), 1);
|
||||||
const int maxItems = maxColumns * maxRows;
|
const int maxItems = maxColumns * maxRows;
|
||||||
const int items = model()->rowCount();
|
const int items = model()->rowCount();
|
||||||
return maxItems >= items;
|
return maxItems >= items;
|
||||||
@@ -426,47 +748,123 @@ bool ListModelFilter::leaveFilterAcceptsRowBeforeFiltering(const ListItem *, boo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ListItemDelegate::ListItemDelegate()
|
constexpr TextFormat titleTF {Theme::Token_Text_Default, StyleHelper::UiElementIconActive};
|
||||||
: backgroundPrimaryColor(themeColor(Theme::Welcome_BackgroundPrimaryColor))
|
constexpr TextFormat descriptionTF {titleTF.themeColor, StyleHelper::UiElementCaption};
|
||||||
, backgroundSecondaryColor(themeColor(Theme::Welcome_BackgroundSecondaryColor))
|
constexpr TextFormat tagsLabelTF {Theme::Token_Text_Muted, StyleHelper::UiElementCaptionStrong};
|
||||||
, foregroundPrimaryColor(themeColor(Theme::Welcome_ForegroundPrimaryColor))
|
constexpr TextFormat tagsTF {Theme::Token_Accent_Default, tagsLabelTF.uiElement};
|
||||||
, foregroundSecondaryColor(themeColor(Theme::Welcome_ForegroundSecondaryColor))
|
|
||||||
, hoverColor(themeColor(Theme::Welcome_HoverColor))
|
constexpr qreal itemOutlineWidth = 1;
|
||||||
, textColor(themeColor(Theme::Welcome_TextColor))
|
constexpr qreal itemCornerRounding = 6;
|
||||||
|
constexpr int thumbnailAreaBorderWidth = 1;
|
||||||
|
constexpr QSize thumbnailAreaSize =
|
||||||
|
WelcomeThumbnailSize.grownBy({thumbnailAreaBorderWidth, thumbnailAreaBorderWidth,
|
||||||
|
thumbnailAreaBorderWidth, thumbnailAreaBorderWidth});
|
||||||
|
constexpr int tagsRowsCount = 1;
|
||||||
|
constexpr int tagsHGap = ExPaddingGapM;
|
||||||
|
|
||||||
|
constexpr QEasingCurve::Type hoverEasing = QEasingCurve::OutCubic;
|
||||||
|
constexpr std::chrono::milliseconds hoverDuration(300);
|
||||||
|
constexpr int hoverBlurRadius = 50;
|
||||||
|
constexpr qreal hoverBlurOpacity = 0.175;
|
||||||
|
|
||||||
|
QSize ListItemDelegate::itemSize()
|
||||||
{
|
{
|
||||||
|
const int tagsTfLineHeight = tagsTF.lineHeight();
|
||||||
|
const int width =
|
||||||
|
ExPaddingGapL
|
||||||
|
+ thumbnailAreaSize.width()
|
||||||
|
+ ExPaddingGapL;
|
||||||
|
const int height =
|
||||||
|
ExPaddingGapL
|
||||||
|
+ thumbnailAreaSize.height()
|
||||||
|
+ VGapL
|
||||||
|
+ titleTF.lineHeight()
|
||||||
|
+ VGapL
|
||||||
|
+ tagsTfLineHeight
|
||||||
|
+ (tagsTfLineHeight + ExPaddingGapS) * (tagsRowsCount - 1) // If more than one row
|
||||||
|
+ ExPaddingGapL;
|
||||||
|
return {width + ExVPaddingGapXl, height + ExVPaddingGapXl};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
const QModelIndex &index) const
|
const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
|
// Unhovered tile
|
||||||
|
// +---------------+------------------+---------------+-----------------+
|
||||||
|
// | | (ExPaddingGapL) | | |
|
||||||
|
// | +------------------+ | |
|
||||||
|
// | | <thumbnail> | | |
|
||||||
|
// | +------------------+ | |
|
||||||
|
// | | (VGapL) | | |
|
||||||
|
// | +------------------+ | |
|
||||||
|
// |(ExPaddingGapL)| <title> |(ExPaddingGapL)|(ExVPaddingGapXs)|
|
||||||
|
// | +------------------+ | |
|
||||||
|
// | | (VGapL) | | |
|
||||||
|
// | +-----------+------+ | |
|
||||||
|
// | |<tagsLabel>|<tags>| | |
|
||||||
|
// | +-----------+------+ | |
|
||||||
|
// | | (ExPaddingGapL) | | |
|
||||||
|
// +---------------+------------------+---------------+-----------------+
|
||||||
|
// | (ExVPaddingGapXs) |
|
||||||
|
// +--------------------------------------------------------------------+
|
||||||
|
//
|
||||||
|
// Hovered, final animation state of the are above tagsLabel
|
||||||
|
// +---------------+------------------+---------------+
|
||||||
|
// | | (ExPaddingGapL) | |
|
||||||
|
// | +------------------+ |
|
||||||
|
// | | <title> | |
|
||||||
|
// | +------------------+ |
|
||||||
|
// | | (ExPaddingGapS) | |
|
||||||
|
// |(ExPaddingGapL)+------------------+(ExPaddingGapL)| ...
|
||||||
|
// | | <hr> | |
|
||||||
|
// | +------------------+ |
|
||||||
|
// | | (ExPaddingGapS) | |
|
||||||
|
// | +------------------+ |
|
||||||
|
// | | <description> | |
|
||||||
|
// +---------------+------------------+---------------+
|
||||||
|
// ...
|
||||||
|
|
||||||
const ListItem *item = index.data(ListModel::ItemRole).value<Core::ListItem *>();
|
const ListItem *item = index.data(ListModel::ItemRole).value<Core::ListItem *>();
|
||||||
|
|
||||||
const QRect rc = option.rect;
|
const QFont tagsLabelFont = tagsLabelTF.font();
|
||||||
const QRect tileRect(0, 0, rc.width() - GridItemGap, rc.height() - GridItemGap);
|
const QFontMetrics tagsLabelFM(tagsLabelFont);
|
||||||
const QSize thumbnailBgSize = GridItemImageSize.grownBy(QMargins(1, 1, 1, 1));
|
const QFont descriptionFont = descriptionTF.font();
|
||||||
const QRect thumbnailBgRect((tileRect.width() - thumbnailBgSize.width()) / 2, GridItemGap,
|
const QFontMetrics descriptionFM(descriptionFont);
|
||||||
thumbnailBgSize.width(), thumbnailBgSize.height());
|
|
||||||
const QRect textArea = tileRect.adjusted(GridItemGap, GridItemGap, -GridItemGap, -GridItemGap);
|
const QRect bgRGlobal = option.rect.adjusted(0, 0, -ExVPaddingGapXl, -ExVPaddingGapXl);
|
||||||
|
const QRect bgR = bgRGlobal.translated(-option.rect.topLeft());
|
||||||
|
const QRect thumbnailAreaR(bgR.left() + ExPaddingGapL, bgR.top() + ExPaddingGapL,
|
||||||
|
thumbnailAreaSize.width(), thumbnailAreaSize.height());
|
||||||
|
const QRect titleR(thumbnailAreaR.left(), thumbnailAreaR.bottom() + VGapL + 1,
|
||||||
|
thumbnailAreaR.width(), titleTF.lineHeight());
|
||||||
|
const QString tagsLabelText = Tr::tr("Tags:");
|
||||||
|
const int tagsLabelTextWidth = tagsLabelFM.horizontalAdvance(tagsLabelText);
|
||||||
|
const QRect tagsLabelR(titleR.left(), titleR.bottom() + VGapL + 1,
|
||||||
|
tagsLabelTextWidth, tagsTF.lineHeight());
|
||||||
|
const QRect tagsR(tagsLabelR.right() + 1 + tagsHGap, tagsLabelR.top(),
|
||||||
|
bgR.right() - ExPaddingGapL - tagsLabelR.right() - tagsHGap,
|
||||||
|
tagsLabelR.height()
|
||||||
|
+ (tagsLabelR.height() + ExPaddingGapS) * (tagsRowsCount - 1));
|
||||||
|
QTC_CHECK(option.rect.height() == tagsR.bottom() + 1 + ExPaddingGapL + ExVPaddingGapXl);
|
||||||
|
QTC_CHECK(option.rect.width() == tagsR.right() + 1 + ExPaddingGapL + ExVPaddingGapXl);
|
||||||
|
|
||||||
|
QTextOption wrapTO;
|
||||||
|
wrapTO.setWrapMode(QTextOption::WordWrap);
|
||||||
|
|
||||||
const bool hovered = option.state & QStyle::State_MouseOver;
|
const bool hovered = option.state & QStyle::State_MouseOver;
|
||||||
|
|
||||||
constexpr int TagsSeparatorY = GridItemHeight - GridItemGap - 52;
|
|
||||||
constexpr int tagsBase = TagsSeparatorY + 17;
|
|
||||||
constexpr int shiftY = TagsSeparatorY - 16;
|
|
||||||
constexpr int nameY = TagsSeparatorY - 20;
|
|
||||||
|
|
||||||
const QRect textRect = textArea.translated(0, nameY);
|
|
||||||
const QFont descriptionFont = sizedFont(11, option.widget);
|
|
||||||
|
|
||||||
painter->save();
|
painter->save();
|
||||||
painter->translate(rc.topLeft());
|
painter->translate(bgRGlobal.topLeft());
|
||||||
|
|
||||||
painter->fillRect(tileRect, hovered ? hoverColor : backgroundPrimaryColor);
|
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), itemOutlineWidth);
|
||||||
|
WelcomePageHelpers::drawCardBackground(painter, bgR, fill, pen, itemCornerRounding);
|
||||||
|
|
||||||
QTextOption wrapped;
|
const int shiftY = thumbnailAreaR.bottom();
|
||||||
wrapped.setWrapMode(QTextOption::WordWrap);
|
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
float animationProgress = 0; // Linear increase from 0.0 to 1.0 during hover animation
|
qreal animationProgress = 0; // Linear increase from 0.0 to 1.0 during hover animation
|
||||||
if (hovered) {
|
if (hovered) {
|
||||||
if (index != m_previousIndex) {
|
if (index != m_previousIndex) {
|
||||||
m_previousIndex = index;
|
m_previousIndex = index;
|
||||||
@@ -476,12 +874,12 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
|
|||||||
m_currentWidget = qobject_cast<QAbstractItemView *>(
|
m_currentWidget = qobject_cast<QAbstractItemView *>(
|
||||||
const_cast<QWidget *>(option.widget));
|
const_cast<QWidget *>(option.widget));
|
||||||
}
|
}
|
||||||
constexpr float hoverAnimationDuration = 260;
|
animationProgress = qreal(m_startTime.elapsed()) / hoverDuration.count();
|
||||||
animationProgress = m_startTime.elapsed() / hoverAnimationDuration;
|
|
||||||
if (animationProgress < 1) {
|
if (animationProgress < 1) {
|
||||||
static const QEasingCurve animationCurve(QEasingCurve::OutCubic);
|
static const QEasingCurve animationCurve(hoverEasing);
|
||||||
offset = animationCurve.valueForProgress(animationProgress) * shiftY;
|
offset = animationCurve.valueForProgress(animationProgress) * shiftY;
|
||||||
QTimer::singleShot(10, this, &ListItemDelegate::goon);
|
using namespace std::chrono_literals;
|
||||||
|
QTimer::singleShot(10ms, this, &ListItemDelegate::goon);
|
||||||
} else {
|
} else {
|
||||||
offset = shiftY;
|
offset = shiftY;
|
||||||
}
|
}
|
||||||
@@ -489,124 +887,125 @@ void ListItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &opti
|
|||||||
m_previousIndex = QModelIndex();
|
m_previousIndex = QModelIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
const QRect shiftedTextRect = textRect.adjusted(0, -offset, 0, -offset);
|
|
||||||
|
|
||||||
// The pixmap.
|
// The pixmap.
|
||||||
const QPixmap pm = index.data(ListModel::ItemImageRole).value<QPixmap>();
|
const QPixmap pm = index.data(ListModel::ItemImageRole).value<QPixmap>();
|
||||||
QPoint thumbnailPos = thumbnailBgRect.center();
|
QPoint thumbnailPos = thumbnailAreaR.center();
|
||||||
if (!pm.isNull()) {
|
if (!pm.isNull()) {
|
||||||
painter->fillRect(thumbnailBgRect, backgroundSecondaryColor);
|
painter->fillRect(thumbnailAreaR, themeColor(Theme::Token_Background_Default));
|
||||||
|
|
||||||
thumbnailPos.rx() -= pm.width() / pm.devicePixelRatio() / 2 - 1;
|
thumbnailPos.rx() -= pm.width() / pm.devicePixelRatio() / 2 - 1;
|
||||||
thumbnailPos.ry() -= pm.height() / pm.devicePixelRatio() / 2 - 1;
|
thumbnailPos.ry() -= pm.height() / pm.devicePixelRatio() / 2 - 1;
|
||||||
painter->drawPixmap(thumbnailPos, pm);
|
painter->drawPixmap(thumbnailPos, pm);
|
||||||
|
|
||||||
painter->setPen(foregroundPrimaryColor);
|
painter->setPen(titleTF.color());
|
||||||
drawPixmapOverlay(item, painter, option, thumbnailBgRect);
|
drawPixmapOverlay(item, painter, option, thumbnailAreaR);
|
||||||
} else {
|
} else {
|
||||||
// The description text as fallback.
|
// The description text as fallback.
|
||||||
painter->setPen(textColor);
|
painter->setPen(descriptionTF.color());
|
||||||
painter->setFont(descriptionFont);
|
painter->setFont(descriptionTF.font());
|
||||||
painter->drawText(textArea, item->description, wrapped);
|
painter->drawText(thumbnailAreaR, item->description, wrapTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The description background
|
// The description background
|
||||||
|
QRect backgroundPortionR = bgR;
|
||||||
if (offset) {
|
if (offset) {
|
||||||
QRect backgroundPortionRect = tileRect;
|
backgroundPortionR.setTop(shiftY - offset);
|
||||||
backgroundPortionRect.setTop(shiftY - offset);
|
|
||||||
if (!pm.isNull()) {
|
if (!pm.isNull()) {
|
||||||
if (m_blurredThumbnail.isNull()) {
|
if (m_blurredThumbnail.isNull()) {
|
||||||
constexpr int blurRadius = 50;
|
constexpr int filterMargin = hoverBlurRadius;
|
||||||
QImage thumbnail(tileRect.size() + QSize(blurRadius, blurRadius) * 2,
|
QImage thumbnail(bgR.size() + QSize(filterMargin, filterMargin) * 2,
|
||||||
QImage::Format_ARGB32_Premultiplied);
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
thumbnail.fill(hoverColor);
|
thumbnail.fill(themeColor(Theme::Token_Foreground_Muted));
|
||||||
QPainter thumbnailPainter(&thumbnail);
|
QPainter thumbnailPainter(&thumbnail);
|
||||||
thumbnailPainter.translate(blurRadius, blurRadius);
|
thumbnailPainter.translate(filterMargin, filterMargin);
|
||||||
thumbnailPainter.fillRect(thumbnailBgRect, backgroundSecondaryColor);
|
thumbnailPainter.fillRect(thumbnailAreaR,
|
||||||
|
themeColor(Theme::Token_Background_Default));
|
||||||
thumbnailPainter.drawPixmap(thumbnailPos, pm);
|
thumbnailPainter.drawPixmap(thumbnailPos, pm);
|
||||||
thumbnailPainter.setPen(foregroundPrimaryColor);
|
thumbnailPainter.setPen(titleTF.color());
|
||||||
drawPixmapOverlay(item, &thumbnailPainter, option, thumbnailBgRect);
|
drawPixmapOverlay(item, &thumbnailPainter, option, thumbnailAreaR);
|
||||||
|
thumbnailPainter.setOpacity(1.0 - hoverBlurOpacity);
|
||||||
|
thumbnailPainter.fillRect(thumbnail.rect(),
|
||||||
|
themeColor(Theme::Token_Foreground_Muted));
|
||||||
thumbnailPainter.end();
|
thumbnailPainter.end();
|
||||||
|
qt_blurImage(thumbnail, hoverBlurRadius, false, false);
|
||||||
|
|
||||||
m_blurredThumbnail = QPixmap(tileRect.size());
|
QImage mask(thumbnail.size(), QImage::Format_Grayscale8);
|
||||||
QPainter blurredThumbnailPainter(&m_blurredThumbnail);
|
mask.fill(Qt::black);
|
||||||
blurredThumbnailPainter.translate(-blurRadius, -blurRadius);
|
QPainter maskPainter(&mask);
|
||||||
qt_blurImage(&blurredThumbnailPainter, thumbnail, blurRadius, false, false);
|
const QRect maskR = bgR.translated(filterMargin, filterMargin)
|
||||||
blurredThumbnailPainter.setOpacity(0.825);
|
.adjusted(1, 1, -1, -1);
|
||||||
blurredThumbnailPainter.fillRect(tileRect, hoverColor);
|
WelcomePageHelpers::drawCardBackground(&maskPainter, maskR,
|
||||||
|
Qt::white, Qt::NoPen, itemCornerRounding);
|
||||||
|
thumbnail.setAlphaChannel(mask);
|
||||||
|
|
||||||
|
m_blurredThumbnail = QPixmap::fromImage(
|
||||||
|
thumbnail.copy({filterMargin, filterMargin, bgR.width(), bgR.height()}));
|
||||||
}
|
}
|
||||||
const QPixmap thumbnailPortionPM = m_blurredThumbnail.copy(backgroundPortionRect);
|
const QPixmap thumbnailPortionPM = m_blurredThumbnail.copy(backgroundPortionR);
|
||||||
painter->drawPixmap(backgroundPortionRect.topLeft(), thumbnailPortionPM);
|
painter->drawPixmap(backgroundPortionR.topLeft(), thumbnailPortionPM);
|
||||||
} else {
|
} else {
|
||||||
painter->fillRect(backgroundPortionRect, hoverColor);
|
painter->fillRect(thumbnailAreaR, themeColor(Theme::Token_Foreground_Muted));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The description Text (unhovered or hovered)
|
// The description Text (unhovered or hovered)
|
||||||
painter->setPen(textColor);
|
painter->setPen(titleTF.color());
|
||||||
painter->setFont(sizedFont(13, option.widget)); // Title font
|
painter->setFont(titleTF.font());
|
||||||
if (offset) {
|
if (offset) {
|
||||||
// The title of the example
|
// The title of the example
|
||||||
const QRectF nameRect = painter->boundingRect(shiftedTextRect, item->name, wrapped);
|
const QRect shiftedTitleR = thumbnailAreaR.translated(backgroundPortionR.topLeft());
|
||||||
painter->drawText(nameRect, item->name, wrapped);
|
const QRect titleR = painter->boundingRect(shiftedTitleR, item->name, wrapTO).toRect();
|
||||||
|
painter->drawText(shiftedTitleR, item->name, wrapTO);
|
||||||
|
|
||||||
|
painter->setOpacity(animationProgress); // "fade in" separator line and description
|
||||||
|
|
||||||
// The separator line below the example title.
|
// The separator line below the example title.
|
||||||
const int ll = nameRect.height() + 3;
|
const QRect hrR(titleR.x(), titleR.bottom() + 1 + ExPaddingGapS, thumbnailAreaR.width(), 1);
|
||||||
const QLine line = QLine(0, ll, textArea.width(), ll).translated(shiftedTextRect.topLeft());
|
painter->fillRect(hrR, themeColor(Theme::Token_Stroke_Muted));
|
||||||
painter->setPen(foregroundSecondaryColor);
|
|
||||||
painter->setOpacity(animationProgress); // "fade in" separator line and description
|
|
||||||
painter->drawLine(line);
|
|
||||||
|
|
||||||
// The description text.
|
// The description text.
|
||||||
const int dd = ll + 5;
|
const QRect descriptionR(hrR.x(), hrR.bottom() + 1 + ExPaddingGapS,
|
||||||
const QRect descRect = shiftedTextRect.adjusted(0, dd, 0, dd);
|
thumbnailAreaR.width(), shiftY);
|
||||||
painter->setPen(textColor);
|
painter->setPen(descriptionTF.color());
|
||||||
painter->setFont(descriptionFont);
|
painter->setFont(descriptionTF.font());
|
||||||
painter->drawText(descRect, item->description, wrapped);
|
painter->drawText(descriptionR, item->description, wrapTO);
|
||||||
painter->setOpacity(1);
|
painter->setOpacity(1);
|
||||||
} else {
|
} else {
|
||||||
// The title of the example
|
// The title of the example
|
||||||
const QString elidedName = painter->fontMetrics()
|
const QString elidedName = painter->fontMetrics()
|
||||||
.elidedText(item->name, Qt::ElideRight, textRect.width());
|
.elidedText(item->name, Qt::ElideRight, titleR.width());
|
||||||
painter->drawText(textRect, elidedName);
|
painter->drawText(titleR, titleTF.drawTextFlags, elidedName);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Separator line between text and 'Tags:' section
|
|
||||||
painter->setPen(foregroundSecondaryColor);
|
|
||||||
painter->drawLine(QLineF(textArea.topLeft(), textArea.topRight())
|
|
||||||
.translated(0, TagsSeparatorY));
|
|
||||||
|
|
||||||
// The 'Tags:' section
|
// The 'Tags:' section
|
||||||
painter->setPen(foregroundPrimaryColor);
|
painter->setPen(tagsLabelTF.color());
|
||||||
const QFont tagsFont = sizedFont(10, option.widget);
|
painter->setFont(tagsLabelTF.font());
|
||||||
painter->setFont(tagsFont);
|
painter->drawText(tagsLabelR, tagsLabelTF.drawTextFlags, tagsLabelText);
|
||||||
const QFontMetrics fm = painter->fontMetrics();
|
|
||||||
const QString tagsLabelText = Tr::tr("Tags:");
|
|
||||||
constexpr int tagsHorSpacing = 5;
|
|
||||||
const QRect tagsLabelRect =
|
|
||||||
QRect(0, 0, fm.horizontalAdvance(tagsLabelText) + tagsHorSpacing, fm.height())
|
|
||||||
.translated(textArea.x(), tagsBase);
|
|
||||||
painter->drawText(tagsLabelRect, tagsLabelText);
|
|
||||||
|
|
||||||
painter->setPen(themeColor(Theme::Welcome_LinkColor));
|
const QFontMetrics fm = painter->fontMetrics();
|
||||||
int emptyTagRowsLeft = 2;
|
|
||||||
|
painter->setPen(tagsTF.color());
|
||||||
|
painter->setFont(tagsTF.font());
|
||||||
|
int emptyTagRowsLeft = tagsRowsCount;
|
||||||
int xx = 0;
|
int xx = 0;
|
||||||
int yy = 0;
|
int yy = 0;
|
||||||
const bool populateTagsRects = m_currentTagRects.empty();
|
const bool populateTagsRects = m_currentTagRects.empty();
|
||||||
for (const QString &tag : item->tags) {
|
for (const QString &tag : item->tags) {
|
||||||
const int ww = fm.horizontalAdvance(tag) + tagsHorSpacing;
|
const int ww = fm.horizontalAdvance(tag);
|
||||||
if (xx + ww > textArea.width() - tagsLabelRect.width()) {
|
if (xx + ww > tagsR.width()) {
|
||||||
if (--emptyTagRowsLeft == 0)
|
if (--emptyTagRowsLeft == 0)
|
||||||
break;
|
break;
|
||||||
yy += fm.lineSpacing();
|
yy += fm.lineSpacing();
|
||||||
xx = 0;
|
xx = 0;
|
||||||
}
|
}
|
||||||
const QRect tagRect = QRect(xx, yy, ww, tagsLabelRect.height())
|
const QRect tagRect = QRect(xx, yy, ww, tagsLabelR.height()).translated(tagsR.topLeft());
|
||||||
.translated(tagsLabelRect.topRight());
|
painter->drawText(tagRect, tagsTF.drawTextFlags, tag);
|
||||||
painter->drawText(tagRect, tag);
|
if (populateTagsRects) {
|
||||||
if (populateTagsRects)
|
constexpr int grow = tagsHGap / 2;
|
||||||
m_currentTagRects.append({ tag, tagRect });
|
const QRect tagMouseArea = tagRect.adjusted(-grow, -grow, grow, grow);
|
||||||
xx += ww;
|
m_currentTagRects.append({ tag, tagMouseArea });
|
||||||
|
}
|
||||||
|
xx += ww + tagsHGap;
|
||||||
}
|
}
|
||||||
|
|
||||||
painter->restore();
|
painter->restore();
|
||||||
@@ -642,7 +1041,7 @@ bool ListItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
|
|||||||
|
|
||||||
QSize ListItemDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
|
QSize ListItemDelegate::sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const
|
||||||
{
|
{
|
||||||
return {GridItemWidth, GridItemHeight};
|
return itemSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ListItemDelegate::drawPixmapOverlay(const ListItem *, QPainter *,
|
void ListItemDelegate::drawPixmapOverlay(const ListItem *, QPainter *,
|
||||||
@@ -682,6 +1081,7 @@ SectionedGridView::SectionedGridView(QWidget *parent)
|
|||||||
|
|
||||||
auto sectionedView = new QWidget;
|
auto sectionedView = new QWidget;
|
||||||
auto layout = new QVBoxLayout;
|
auto layout = new QVBoxLayout;
|
||||||
|
layout->setSpacing(0);
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
layout->addStretch(1);
|
layout->addStretch(1);
|
||||||
sectionedView->setLayout(layout);
|
sectionedView->setLayout(layout);
|
||||||
@@ -744,24 +1144,24 @@ void SectionedGridView::setSearchString(const QString &searchString)
|
|||||||
filterModel->setSearchString(searchString);
|
filterModel->setSearchString(searchString);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QWidget *createSeparator(QWidget *parent)
|
static QLabel *createTitleLabel(const QString &text, QWidget *parent = nullptr)
|
||||||
{
|
{
|
||||||
QWidget *line = Layouting::createHr(parent);
|
constexpr TextFormat headerTitleTF {Theme::Token_Text_Muted, StyleHelper::UiElementH4};
|
||||||
QSizePolicy linePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored);
|
auto link = new QLabel(text, parent);
|
||||||
linePolicy.setHorizontalStretch(2);
|
link->setFont(headerTitleTF.font());
|
||||||
line->setSizePolicy(linePolicy);
|
QPalette pal = link->palette();
|
||||||
QPalette pal = line->palette();
|
pal.setColor(QPalette::WindowText, headerTitleTF.color());
|
||||||
pal.setColor(QPalette::Dark, Qt::transparent);
|
link->setPalette(pal);
|
||||||
pal.setColor(QPalette::Light, themeColor(Theme::Welcome_ForegroundSecondaryColor));
|
return link;
|
||||||
line->setPalette(pal);
|
|
||||||
return line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QLabel *createLinkLabel(const QString &text, QWidget *parent)
|
static QLabel *createLinkLabel(const QString &text, QWidget *parent)
|
||||||
{
|
{
|
||||||
const QString linkColor = themeColor(Theme::Welcome_LinkColor).name();
|
constexpr TextFormat headerLinkTF {Theme::Token_Accent_Default, StyleHelper::UiElementH6};
|
||||||
|
const QString linkColor = themeColor(headerLinkTF.themeColor).name();
|
||||||
auto link = new QLabel("<a href=\"link\" style=\"color: " + linkColor + ";\">"
|
auto link = new QLabel("<a href=\"link\" style=\"color: " + linkColor + ";\">"
|
||||||
+ text + "</a>", parent);
|
+ text + "</a>", parent);
|
||||||
|
link->setFont(headerLinkTF.font());
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,14 +1197,13 @@ ListModel *SectionedGridView::addSection(const Section §ion, const QList<Lis
|
|||||||
connect(seeAllLink, &QLabel::linkActivated, this, [this, section] { zoomInSection(section); });
|
connect(seeAllLink, &QLabel::linkActivated, this, [this, section] { zoomInSection(section); });
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
QWidget *sectionLabel = Row {
|
QWidget *sectionLabel = Row {
|
||||||
section.name,
|
createTitleLabel(section.name),
|
||||||
createSeparator(this),
|
st,
|
||||||
seeAllLink,
|
seeAllLink,
|
||||||
Space(HSpacing),
|
Space(ExVPaddingGapXl),
|
||||||
noMargin
|
customMargin({0, ExPaddingGapL, 0, VPaddingL}),
|
||||||
}.emerge();
|
}.emerge();
|
||||||
m_sectionLabels.append(sectionLabel);
|
m_sectionLabels.append(sectionLabel);
|
||||||
sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
|
|
||||||
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
|
auto scrollArea = qobject_cast<QScrollArea *>(widget(0));
|
||||||
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
|
auto vbox = qobject_cast<QVBoxLayout *>(scrollArea->widget()->layout());
|
||||||
|
|
||||||
@@ -858,6 +1257,7 @@ void SectionedGridView::zoomInSection(const Section §ion)
|
|||||||
auto zoomedInWidget = new QWidget(this);
|
auto zoomedInWidget = new QWidget(this);
|
||||||
auto layout = new QVBoxLayout;
|
auto layout = new QVBoxLayout;
|
||||||
layout->setContentsMargins(0, 0, 0, 0);
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
layout->setSpacing(0);
|
||||||
zoomedInWidget->setLayout(layout);
|
zoomedInWidget->setLayout(layout);
|
||||||
|
|
||||||
QLabel *backLink = createLinkLabel("< " + Tr::tr("Back"), this);
|
QLabel *backLink = createLinkLabel("< " + Tr::tr("Back"), this);
|
||||||
@@ -868,13 +1268,12 @@ void SectionedGridView::zoomInSection(const Section §ion)
|
|||||||
});
|
});
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
QWidget *sectionLabel = Row {
|
QWidget *sectionLabel = Row {
|
||||||
section.name,
|
createTitleLabel(section.name),
|
||||||
createSeparator(this),
|
st,
|
||||||
backLink,
|
backLink,
|
||||||
Space(HSpacing),
|
Space(ExVPaddingGapXl),
|
||||||
noMargin
|
customMargin({0, ExPaddingGapL, 0, VPaddingL}),
|
||||||
}.emerge();
|
}.emerge();
|
||||||
sectionLabel->setContentsMargins(0, ItemGap, 0, 0);
|
|
||||||
|
|
||||||
auto gridView = new GridView(zoomedInWidget);
|
auto gridView = new GridView(zoomedInWidget);
|
||||||
gridView->setItemDelegate(m_itemDelegate);
|
gridView->setItemDelegate(m_itemDelegate);
|
||||||
|
@@ -6,10 +6,17 @@
|
|||||||
#include "core_global.h"
|
#include "core_global.h"
|
||||||
#include "iwelcomepage.h"
|
#include "iwelcomepage.h"
|
||||||
|
|
||||||
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/stylehelper.h>
|
||||||
|
#include <utils/theme/theme.h>
|
||||||
|
|
||||||
|
#include <QComboBox>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
|
#include <QLabel>
|
||||||
#include <QListView>
|
#include <QListView>
|
||||||
#include <QPen>
|
#include <QPen>
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
|
#include <QPushButton>
|
||||||
#include <QSortFilterProxyModel>
|
#include <QSortFilterProxyModel>
|
||||||
#include <QStackedWidget>
|
#include <QStackedWidget>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
@@ -24,31 +31,111 @@ namespace Core {
|
|||||||
|
|
||||||
namespace WelcomePageHelpers {
|
namespace WelcomePageHelpers {
|
||||||
|
|
||||||
constexpr int HSpacing = 20;
|
constexpr QSize WelcomeThumbnailSize(214, 160);
|
||||||
constexpr int ItemGap = 4;
|
|
||||||
|
|
||||||
constexpr int GridItemGap = 3 * ItemGap;
|
class CORE_EXPORT TextFormat {
|
||||||
constexpr int GridItemWidth = 240 + GridItemGap; // Extra GridItemGap as "spacing"
|
public:
|
||||||
constexpr int GridItemHeight = GridItemWidth;
|
QColor color() const
|
||||||
constexpr QSize GridItemImageSize(GridItemWidth - GridItemGap
|
{
|
||||||
- 2 * (GridItemGap + 1), // Horizontal margins + 1 pixel
|
return Utils::creatorTheme()->color(themeColor);
|
||||||
GridItemHeight - GridItemGap
|
}
|
||||||
- GridItemGap - 1 // Upper margin + 1 pixel
|
|
||||||
- 67); // Bottom margin (for title + tags)
|
|
||||||
|
|
||||||
CORE_EXPORT QWidget *panelBar(QWidget *parent = nullptr);
|
QFont font(bool underlined = false) const
|
||||||
|
{
|
||||||
|
QFont result = Utils::StyleHelper::uiFont(uiElement);
|
||||||
|
result.setUnderline(underlined);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lineHeight() const
|
||||||
|
{
|
||||||
|
return Utils::StyleHelper::uiFontLineHeight(uiElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Utils::Theme::Color themeColor;
|
||||||
|
const Utils::StyleHelper::UiElement uiElement;
|
||||||
|
const int drawTextFlags = Qt::AlignLeft | Qt::AlignBottom | Qt::TextDontClip;
|
||||||
|
};
|
||||||
|
|
||||||
|
CORE_EXPORT void setBackgroundColor(QWidget *widget, Utils::Theme::Color colorRole);
|
||||||
|
constexpr qreal defaultCardBackgroundRounding = 3.75;
|
||||||
CORE_EXPORT void drawCardBackground(QPainter *painter, const QRectF &rect,
|
CORE_EXPORT void drawCardBackground(QPainter *painter, const QRectF &rect,
|
||||||
const QBrush &fill, const QPen &pen = QPen(Qt::NoPen),
|
const QBrush &fill, const QPen &pen = QPen(Qt::NoPen),
|
||||||
qreal rounding = 5.0);
|
qreal rounding = defaultCardBackgroundRounding);
|
||||||
|
CORE_EXPORT QWidget *createRule(Qt::Orientation orientation, QWidget *parent = nullptr);
|
||||||
|
|
||||||
} // namespace WelcomePageHelpers
|
} // namespace WelcomePageHelpers
|
||||||
|
|
||||||
class CORE_EXPORT SearchBox : public WelcomePageFrame
|
class CORE_EXPORT Button : public QPushButton
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Role {
|
||||||
|
MediumPrimary,
|
||||||
|
MediumSecondary,
|
||||||
|
SmallPrimary,
|
||||||
|
SmallSecondary,
|
||||||
|
SmallList,
|
||||||
|
SmallLink,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit Button(const QString &text, Role role, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
QSize minimumSizeHint() const override;
|
||||||
|
|
||||||
|
void setPixmap(const QPixmap &newPixmap);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateMargins();
|
||||||
|
|
||||||
|
const Role m_role = MediumPrimary;
|
||||||
|
QPixmap m_pixmap;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CORE_EXPORT Label : public QLabel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum Role {
|
||||||
|
Primary,
|
||||||
|
Secondary,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit Label(const QString &text, Role role, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Role m_role = Primary;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CORE_EXPORT SearchBox : public QLineEdit
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit SearchBox(QWidget *parent = nullptr);
|
explicit SearchBox(QWidget *parent = nullptr);
|
||||||
|
|
||||||
Utils::FancyLineEdit *m_lineEdit = nullptr;
|
QSize minimumSizeHint() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void enterEvent(QEnterEvent *event) override;
|
||||||
|
void leaveEvent(QEvent *event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CORE_EXPORT ComboBox : public QComboBox
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ComboBox(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
QSize sizeHint() const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void enterEvent(QEnterEvent *event) override;
|
||||||
|
void leaveEvent(QEvent *event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CORE_EXPORT GridView : public QListView
|
class CORE_EXPORT GridView : public QListView
|
||||||
@@ -146,7 +233,9 @@ class CORE_EXPORT ListItemDelegate : public QStyledItemDelegate
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
ListItemDelegate();
|
ListItemDelegate() = default;
|
||||||
|
|
||||||
|
static QSize itemSize();
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
void paint(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
const QModelIndex &index) const override;
|
const QModelIndex &index) const override;
|
||||||
|
|
||||||
@@ -165,13 +254,6 @@ protected:
|
|||||||
|
|
||||||
void goon();
|
void goon();
|
||||||
|
|
||||||
const QColor backgroundPrimaryColor;
|
|
||||||
const QColor backgroundSecondaryColor;
|
|
||||||
const QColor foregroundPrimaryColor;
|
|
||||||
const QColor foregroundSecondaryColor;
|
|
||||||
const QColor hoverColor;
|
|
||||||
const QColor textColor;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable QPersistentModelIndex m_previousIndex;
|
mutable QPersistentModelIndex m_previousIndex;
|
||||||
mutable QElapsedTimer m_startTime;
|
mutable QElapsedTimer m_startTime;
|
||||||
|
@@ -242,27 +242,6 @@ void CppHighlighter::highlightBlock(const QString &text)
|
|||||||
|
|
||||||
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
|
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<TextDocumentLayout *>(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());
|
setCurrentBlockState((braceDepth << 8) | tokenize.state());
|
||||||
TextDocumentLayout::setExpectedRawStringSuffix(currentBlock(),
|
TextDocumentLayout::setExpectedRawStringSuffix(currentBlock(),
|
||||||
tokenize.expectedRawStringSuffix());
|
tokenize.expectedRawStringSuffix());
|
||||||
|
@@ -20,10 +20,11 @@
|
|||||||
#include <projectexplorer/projectexplorer.h>
|
#include <projectexplorer/projectexplorer.h>
|
||||||
#include <projectexplorer/projectmanager.h>
|
#include <projectexplorer/projectmanager.h>
|
||||||
|
|
||||||
#include <texteditor/texteditor.h>
|
|
||||||
#include <texteditor/codeassist/iassistproposal.h>
|
#include <texteditor/codeassist/iassistproposal.h>
|
||||||
#include <texteditor/codeassist/iassistproposalmodel.h>
|
#include <texteditor/codeassist/iassistproposalmodel.h>
|
||||||
#include <texteditor/storagesettings.h>
|
#include <texteditor/storagesettings.h>
|
||||||
|
#include <texteditor/syntaxhighlighterrunner.h>
|
||||||
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
@@ -221,6 +222,17 @@ bool TestCase::openCppEditor(const FilePath &filePath, TextEditor::BaseTextEdito
|
|||||||
s.m_addFinalNewLine = false;
|
s.m_addFinalNewLine = false;
|
||||||
e->textDocument()->setStorageSettings(s);
|
e->textDocument()->setStorageSettings(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!QTest::qWaitFor(
|
||||||
|
[e] {
|
||||||
|
return e->editorWidget()
|
||||||
|
->textDocument()
|
||||||
|
->syntaxHighlighterRunner()
|
||||||
|
->syntaxInfoUpdated();
|
||||||
|
},
|
||||||
|
5000))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (editorWidget) {
|
if (editorWidget) {
|
||||||
if (CppEditorWidget *w = dynamic_cast<CppEditorWidget *>(e->editorWidget())) {
|
if (CppEditorWidget *w = dynamic_cast<CppEditorWidget *>(e->editorWidget())) {
|
||||||
*editorWidget = w;
|
*editorWidget = w;
|
||||||
|
@@ -396,6 +396,12 @@ F2TestCase::F2TestCase(CppEditorAction action,
|
|||||||
BaseTextEditor *currentTextEditor = dynamic_cast<BaseTextEditor*>(currentEditor);
|
BaseTextEditor *currentTextEditor = dynamic_cast<BaseTextEditor*>(currentEditor);
|
||||||
QVERIFY(currentTextEditor);
|
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());
|
QCOMPARE(currentTextEditor->document()->filePath(), targetTestFile->filePath());
|
||||||
int expectedLine, expectedColumn;
|
int expectedLine, expectedColumn;
|
||||||
if (useClangd && expectedVirtualFunctionProposal.size() == 1) {
|
if (useClangd && expectedVirtualFunctionProposal.size() == 1) {
|
||||||
@@ -494,8 +500,11 @@ void FollowSymbolTest::initTestCase()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Find suitable kit.
|
// 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) {
|
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)
|
if (!F2TestCase::m_testKit)
|
||||||
QSKIP("This test requires at least one kit to be present");
|
QSKIP("This test requires at least one kit to be present");
|
||||||
|
@@ -29,14 +29,6 @@ using namespace Utils;
|
|||||||
|
|
||||||
namespace ExtensionManager::Internal {
|
namespace ExtensionManager::Internal {
|
||||||
|
|
||||||
static QWidget *createVr(QWidget *parent = nullptr)
|
|
||||||
{
|
|
||||||
auto vr = new QWidget(parent);
|
|
||||||
vr->setFixedWidth(1);
|
|
||||||
setBackgroundColor(vr, Theme::Token_Stroke_Subtle);
|
|
||||||
return vr;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CollapsingWidget : public QWidget
|
class CollapsingWidget : public QWidget
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -79,13 +71,13 @@ ExtensionManagerWidget::ExtensionManagerWidget()
|
|||||||
|
|
||||||
using namespace Layouting;
|
using namespace Layouting;
|
||||||
Row {
|
Row {
|
||||||
createVr(),
|
WelcomePageHelpers::createRule(Qt::Vertical),
|
||||||
m_secondaryDescription,
|
m_secondaryDescription,
|
||||||
noMargin(), spacing(0),
|
noMargin(), spacing(0),
|
||||||
}.attachTo(m_secondarDescriptionWidget);
|
}.attachTo(m_secondarDescriptionWidget);
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
createVr(),
|
WelcomePageHelpers::createRule(Qt::Vertical),
|
||||||
Row {
|
Row {
|
||||||
m_primaryDescription,
|
m_primaryDescription,
|
||||||
noMargin(),
|
noMargin(),
|
||||||
@@ -95,13 +87,13 @@ ExtensionManagerWidget::ExtensionManagerWidget()
|
|||||||
}.attachTo(descriptionColumns);
|
}.attachTo(descriptionColumns);
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
Space(WelcomePageHelpers::HSpacing),
|
Space(StyleHelper::SpacingTokens::ExVPaddingGapXl),
|
||||||
m_leftColumn,
|
m_leftColumn,
|
||||||
descriptionColumns,
|
descriptionColumns,
|
||||||
noMargin(), spacing(0),
|
noMargin(), spacing(0),
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
|
|
||||||
setBackgroundColor(this, Theme::Token_Background_Default);
|
WelcomePageHelpers::setBackgroundColor(this, Theme::Token_Background_Default);
|
||||||
|
|
||||||
connect(m_leftColumn, &ExtensionsBrowser::itemSelected,
|
connect(m_leftColumn, &ExtensionsBrowser::itemSelected,
|
||||||
this, &ExtensionManagerWidget::updateView);
|
this, &ExtensionManagerWidget::updateView);
|
||||||
@@ -132,7 +124,7 @@ void ExtensionManagerWidget::updateView(const QModelIndex ¤t,
|
|||||||
"margin-left: %3px; margin-right: %3px;")
|
"margin-left: %3px; margin-right: %3px;")
|
||||||
.arg(creatorTheme()->color(Theme::Token_Text_Default).name())
|
.arg(creatorTheme()->color(Theme::Token_Text_Default).name())
|
||||||
.arg(creatorTheme()->color(Theme::Token_Background_Muted).name())
|
.arg(creatorTheme()->color(Theme::Token_Background_Muted).name())
|
||||||
.arg(WelcomePageHelpers::HSpacing);
|
.arg(StyleHelper::SpacingTokens::ExVPaddingGapXl);
|
||||||
const QString htmlStart = QString(R"(
|
const QString htmlStart = QString(R"(
|
||||||
<html>
|
<html>
|
||||||
<body style="%1">
|
<body style="%1">
|
||||||
|
@@ -41,7 +41,7 @@ using PluginSpecList = QList<const PluginSpec *>;
|
|||||||
using Tags = QStringList;
|
using Tags = QStringList;
|
||||||
|
|
||||||
constexpr QSize itemSize = {330, 86};
|
constexpr QSize itemSize = {330, 86};
|
||||||
constexpr int gapSize = 2 * WelcomePageHelpers::GridItemGap;
|
constexpr int gapSize = StyleHelper::SpacingTokens::ExVPaddingGapXl;
|
||||||
constexpr QSize cellSize = {itemSize.width() + gapSize, itemSize.height() + gapSize};
|
constexpr QSize cellSize = {itemSize.width() + gapSize, itemSize.height() + gapSize};
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
@@ -62,16 +62,6 @@ ItemData itemData(const QModelIndex &index)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void setBackgroundColor(QWidget *widget, Theme::Color colorRole)
|
|
||||||
{
|
|
||||||
QPalette palette = creatorTheme()->palette();
|
|
||||||
palette.setColor(QPalette::Window,
|
|
||||||
creatorTheme()->color(colorRole));
|
|
||||||
widget->setPalette(palette);
|
|
||||||
widget->setBackgroundRole(QPalette::Window);
|
|
||||||
widget->setAutoFillBackground(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static QColor colorForExtensionName(const QString &name)
|
static QColor colorForExtensionName(const QString &name)
|
||||||
{
|
{
|
||||||
const size_t hash = qHash(name);
|
const size_t hash = qHash(name);
|
||||||
@@ -390,7 +380,7 @@ public:
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
constexpr int textX = 80;
|
constexpr int textX = 80;
|
||||||
constexpr int rightMargin = 2 * WelcomePageHelpers::ItemGap;
|
constexpr int rightMargin = StyleHelper::SpacingTokens::ExVPaddingGapXl;
|
||||||
constexpr int maxTextWidth = itemSize.width() - textX - rightMargin;
|
constexpr int maxTextWidth = itemSize.width() - textX - rightMargin;
|
||||||
constexpr Qt::TextElideMode elideMode = Qt::ElideRight;
|
constexpr Qt::TextElideMode elideMode = Qt::ElideRight;
|
||||||
|
|
||||||
@@ -440,8 +430,7 @@ ExtensionsBrowser::ExtensionsBrowser()
|
|||||||
m_searchBox = new Core::SearchBox;
|
m_searchBox = new Core::SearchBox;
|
||||||
m_searchBox->setFixedWidth(itemSize.width());
|
m_searchBox->setFixedWidth(itemSize.width());
|
||||||
|
|
||||||
m_updateButton = new WelcomePageButton;
|
m_updateButton = new Button(Tr::tr("Install..."), Button::MediumPrimary);
|
||||||
m_updateButton->setText(Tr::tr("Install..."));
|
|
||||||
|
|
||||||
m_filterProxyModel = new QSortFilterProxyModel(this);
|
m_filterProxyModel = new QSortFilterProxyModel(this);
|
||||||
m_filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
m_filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||||
@@ -469,9 +458,10 @@ ExtensionsBrowser::ExtensionsBrowser()
|
|||||||
noMargin(), spacing(0),
|
noMargin(), spacing(0),
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
|
|
||||||
setBackgroundColor(this, Theme::Token_Background_Default);
|
WelcomePageHelpers::setBackgroundColor(this, Theme::Token_Background_Default);
|
||||||
setBackgroundColor(m_extensionsView, Theme::Token_Background_Default);
|
WelcomePageHelpers::setBackgroundColor(m_extensionsView, Theme::Token_Background_Default);
|
||||||
setBackgroundColor(m_extensionsView->viewport(), Theme::Token_Background_Default);
|
WelcomePageHelpers::setBackgroundColor(m_extensionsView->viewport(),
|
||||||
|
Theme::Token_Background_Default);
|
||||||
|
|
||||||
auto updateModel = [this] {
|
auto updateModel = [this] {
|
||||||
m_model.reset(extensionsModel());
|
m_model.reset(extensionsModel());
|
||||||
@@ -488,7 +478,7 @@ ExtensionsBrowser::ExtensionsBrowser()
|
|||||||
|
|
||||||
connect(ExtensionSystem::PluginManager::instance(),
|
connect(ExtensionSystem::PluginManager::instance(),
|
||||||
&ExtensionSystem::PluginManager::pluginsChanged, this, updateModel);
|
&ExtensionSystem::PluginManager::pluginsChanged, this, updateModel);
|
||||||
connect(m_searchBox->m_lineEdit, &Utils::FancyLineEdit::textChanged,
|
connect(m_searchBox, &QLineEdit::textChanged,
|
||||||
m_filterProxyModel, &QSortFilterProxyModel::setFilterWildcard);
|
m_filterProxyModel, &QSortFilterProxyModel::setFilterWildcard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,7 +10,9 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QItemSelectionModel;
|
class QItemSelectionModel;
|
||||||
|
class QLineEdit;
|
||||||
class QListView;
|
class QListView;
|
||||||
|
class QPushButton;
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
@@ -19,11 +21,6 @@ namespace ExtensionSystem
|
|||||||
class PluginSpec;
|
class PluginSpec;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class SearchBox;
|
|
||||||
class WelcomePageButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ExtensionManager::Internal {
|
namespace ExtensionManager::Internal {
|
||||||
|
|
||||||
using PluginSpecList = QList<const ExtensionSystem::PluginSpec *>;
|
using PluginSpecList = QList<const ExtensionSystem::PluginSpec *>;
|
||||||
@@ -42,7 +39,6 @@ struct ItemData {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ItemData itemData(const QModelIndex &index);
|
ItemData itemData(const QModelIndex &index);
|
||||||
void setBackgroundColor(QWidget *widget, Utils::Theme::Color colorRole);
|
|
||||||
|
|
||||||
class ExtensionsBrowser final : public QWidget
|
class ExtensionsBrowser final : public QWidget
|
||||||
{
|
{
|
||||||
@@ -61,8 +57,8 @@ private:
|
|||||||
int extraListViewWidth() const; // Space for scrollbar, etc.
|
int extraListViewWidth() const; // Space for scrollbar, etc.
|
||||||
|
|
||||||
QScopedPointer<QStandardItemModel> m_model;
|
QScopedPointer<QStandardItemModel> m_model;
|
||||||
Core::SearchBox *m_searchBox;
|
QLineEdit *m_searchBox;
|
||||||
Core::WelcomePageButton *m_updateButton;
|
QPushButton *m_updateButton;
|
||||||
QListView *m_extensionsView;
|
QListView *m_extensionsView;
|
||||||
QItemSelectionModel *m_selectionModel = nullptr;
|
QItemSelectionModel *m_selectionModel = nullptr;
|
||||||
QSortFilterProxyModel *m_filterProxyModel;
|
QSortFilterProxyModel *m_filterProxyModel;
|
||||||
|
@@ -189,7 +189,6 @@ void GlslHighlighter::highlightBlock(const QString &text)
|
|||||||
TextDocumentLayout::setParentheses(currentBlock(), parentheses);
|
TextDocumentLayout::setParentheses(currentBlock(), parentheses);
|
||||||
|
|
||||||
// if the block is ifdefed out, we only store the parentheses, but
|
// if the block is ifdefed out, we only store the parentheses, but
|
||||||
|
|
||||||
// do not adjust the brace depth.
|
// do not adjust the brace depth.
|
||||||
if (TextDocumentLayout::ifdefedOut(currentBlock())) {
|
if (TextDocumentLayout::ifdefedOut(currentBlock())) {
|
||||||
braceDepth = initialBraceDepth;
|
braceDepth = initialBraceDepth;
|
||||||
@@ -198,23 +197,6 @@ void GlslHighlighter::highlightBlock(const QString &text)
|
|||||||
|
|
||||||
TextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent);
|
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());
|
setCurrentBlockState((braceDepth << 8) | lex.state());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -269,7 +269,7 @@ void SectionedProducts::onImageDownloadFinished(QNetworkReply *reply)
|
|||||||
if (pixmap.loadFromData(data, imageFormat.toLatin1())) {
|
if (pixmap.loadFromData(data, imageFormat.toLatin1())) {
|
||||||
const QString url = imageUrl.toString();
|
const QString url = imageUrl.toString();
|
||||||
const int dpr = qApp->devicePixelRatio();
|
const int dpr = qApp->devicePixelRatio();
|
||||||
pixmap = pixmap.scaled(WelcomePageHelpers::GridItemImageSize * dpr,
|
pixmap = pixmap.scaled(WelcomePageHelpers::WelcomeThumbnailSize * dpr,
|
||||||
Qt::KeepAspectRatio,
|
Qt::KeepAspectRatio,
|
||||||
Qt::SmoothTransformation);
|
Qt::SmoothTransformation);
|
||||||
pixmap.setDevicePixelRatio(dpr);
|
pixmap.setDevicePixelRatio(dpr);
|
||||||
|
@@ -9,9 +9,10 @@
|
|||||||
#include <coreplugin/welcomepagehelper.h>
|
#include <coreplugin/welcomepagehelper.h>
|
||||||
|
|
||||||
#include <utils/fancylineedit.h>
|
#include <utils/fancylineedit.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/progressindicator.h>
|
#include <utils/progressindicator.h>
|
||||||
#include <utils/theme/theme.h>
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/theme/theme.h>
|
||||||
|
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@@ -44,38 +45,34 @@ class QtMarketplacePageWidget : public QWidget
|
|||||||
public:
|
public:
|
||||||
QtMarketplacePageWidget()
|
QtMarketplacePageWidget()
|
||||||
{
|
{
|
||||||
auto searchBox = new Core::SearchBox(this);
|
m_searcher = new Core::SearchBox(this);
|
||||||
m_searcher = searchBox->m_lineEdit;
|
|
||||||
m_searcher->setPlaceholderText(Tr::tr("Search in Marketplace..."));
|
m_searcher->setPlaceholderText(Tr::tr("Search in Marketplace..."));
|
||||||
|
|
||||||
auto vbox = new QVBoxLayout(this);
|
|
||||||
vbox->setContentsMargins(0, 0, 0, Core::WelcomePageHelpers::ItemGap);
|
|
||||||
vbox->setSpacing(Core::WelcomePageHelpers::ItemGap);
|
|
||||||
|
|
||||||
auto searchBar = Core::WelcomePageHelpers::panelBar();
|
|
||||||
auto hbox = new QHBoxLayout(searchBar);
|
|
||||||
hbox->setContentsMargins(Core::WelcomePageHelpers::HSpacing, 0,
|
|
||||||
Core::WelcomePageHelpers::HSpacing, 0);
|
|
||||||
hbox->addWidget(searchBox);
|
|
||||||
vbox->addWidget(searchBar);
|
|
||||||
m_errorLabel = new QLabel(this);
|
m_errorLabel = new QLabel(this);
|
||||||
m_errorLabel->setVisible(false);
|
m_errorLabel->setVisible(false);
|
||||||
vbox->addWidget(m_errorLabel);
|
|
||||||
|
|
||||||
auto resultWidget = new QWidget(this);
|
|
||||||
auto resultHBox = new QHBoxLayout(resultWidget);
|
|
||||||
resultHBox->setContentsMargins(Core::WelcomePageHelpers::HSpacing, 0, 0, 0);
|
|
||||||
m_sectionedProducts = new SectionedProducts(this);
|
m_sectionedProducts = new SectionedProducts(this);
|
||||||
auto progressIndicator = new Utils::ProgressIndicator(ProgressIndicatorSize::Large, this);
|
auto progressIndicator = new Utils::ProgressIndicator(ProgressIndicatorSize::Large, this);
|
||||||
progressIndicator->attachToWidget(m_sectionedProducts);
|
progressIndicator->attachToWidget(m_sectionedProducts);
|
||||||
progressIndicator->hide();
|
progressIndicator->hide();
|
||||||
resultHBox->addWidget(m_sectionedProducts);
|
|
||||||
vbox->addWidget(resultWidget);
|
using namespace StyleHelper::SpacingTokens;
|
||||||
|
|
||||||
|
using namespace Layouting;
|
||||||
|
Column {
|
||||||
|
Row {
|
||||||
|
m_searcher,
|
||||||
|
customMargin({0, 0, ExVPaddingGapXl, 0}),
|
||||||
|
},
|
||||||
|
m_sectionedProducts,
|
||||||
|
spacing(VPaddingL),
|
||||||
|
customMargin({ExVPaddingGapXl, ExVPaddingGapXl, 0, 0}),
|
||||||
|
}.attachTo(this);
|
||||||
|
|
||||||
connect(m_sectionedProducts, &SectionedProducts::toggleProgressIndicator,
|
connect(m_sectionedProducts, &SectionedProducts::toggleProgressIndicator,
|
||||||
progressIndicator, &Utils::ProgressIndicator::setVisible);
|
progressIndicator, &Utils::ProgressIndicator::setVisible);
|
||||||
connect(m_sectionedProducts, &SectionedProducts::errorOccurred, this,
|
connect(m_sectionedProducts, &SectionedProducts::errorOccurred, this,
|
||||||
[this, progressIndicator, searchBox](int, const QString &message) {
|
[this, progressIndicator](int, const QString &message) {
|
||||||
progressIndicator->hide();
|
progressIndicator->hide();
|
||||||
progressIndicator->deleteLater();
|
progressIndicator->deleteLater();
|
||||||
m_errorLabel->setAlignment(Qt::AlignHCenter);
|
m_errorLabel->setAlignment(Qt::AlignHCenter);
|
||||||
@@ -89,7 +86,7 @@ public:
|
|||||||
"</p><br/><p><small><i>Error: %1</i></small></p>").arg(message);
|
"</p><br/><p><small><i>Error: %1</i></small></p>").arg(message);
|
||||||
m_errorLabel->setText(txt);
|
m_errorLabel->setText(txt);
|
||||||
m_errorLabel->setVisible(true);
|
m_errorLabel->setVisible(true);
|
||||||
searchBox->setVisible(false);
|
m_searcher->setVisible(false);
|
||||||
connect(m_errorLabel, &QLabel::linkActivated,
|
connect(m_errorLabel, &QLabel::linkActivated,
|
||||||
this, []() { QDesktopServices::openUrl(QUrl("https://marketplace.qt.io")); });
|
this, []() { QDesktopServices::openUrl(QUrl("https://marketplace.qt.io")); });
|
||||||
});
|
});
|
||||||
|
@@ -21,6 +21,7 @@
|
|||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/icon.h>
|
#include <utils/icon.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stringutils.h>
|
#include <utils/stringutils.h>
|
||||||
#include <utils/stylehelper.h>
|
#include <utils/stylehelper.h>
|
||||||
@@ -28,9 +29,6 @@
|
|||||||
|
|
||||||
#include <QAbstractItemDelegate>
|
#include <QAbstractItemDelegate>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QBoxLayout>
|
|
||||||
#include <QDir>
|
|
||||||
#include <QFileInfo>
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QHelpEvent>
|
#include <QHelpEvent>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
@@ -42,16 +40,52 @@
|
|||||||
using namespace Core;
|
using namespace Core;
|
||||||
using namespace Core::WelcomePageHelpers;
|
using namespace Core::WelcomePageHelpers;
|
||||||
using namespace Utils;
|
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";
|
const char PROJECT_BASE_ID[] = "Welcome.OpenRecentProject";
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
namespace Internal {
|
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)
|
ProjectModel::ProjectModel(QObject *parent)
|
||||||
: QAbstractListModel(parent)
|
: QAbstractListModel(parent)
|
||||||
{
|
{
|
||||||
@@ -61,7 +95,7 @@ ProjectModel::ProjectModel(QObject *parent)
|
|||||||
|
|
||||||
int ProjectModel::rowCount(const QModelIndex &) const
|
int ProjectModel::rowCount(const QModelIndex &) const
|
||||||
{
|
{
|
||||||
return m_projects.count();
|
return int(m_projects.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ProjectModel::data(const QModelIndex &index, int role) const
|
QVariant ProjectModel::data(const QModelIndex &index, int role) const
|
||||||
@@ -190,21 +224,24 @@ static QColor themeColor(Theme::Color role)
|
|||||||
return Utils::creatorTheme()->color(role);
|
return Utils::creatorTheme()->color(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QFont sizedFont(int size, const QWidget *widget,
|
static QPixmap pixmap(const QString &id, const Theme::Color color)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
const QString fileName = QString(":/welcome/images/%1.png").arg(id);
|
const QString fileName = QString(":/welcome/images/%1.png").arg(id);
|
||||||
return Icon({{FilePath::fromString(fileName), color}}, Icon::Tint).pixmap();
|
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
|
class BaseDelegate : public QAbstractItemDelegate
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -244,6 +281,11 @@ protected:
|
|||||||
class SessionDelegate : public BaseDelegate
|
class SessionDelegate : public BaseDelegate
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
bool expanded(const QModelIndex &idx) const
|
||||||
|
{
|
||||||
|
return m_expandedSessions.contains(idx.data(Qt::DisplayRole).toString());
|
||||||
|
}
|
||||||
|
|
||||||
QString entryType() override
|
QString entryType() override
|
||||||
{
|
{
|
||||||
return Tr::tr("session", "Appears in \"Open session <name>\"");
|
return Tr::tr("session", "Appears in \"Open session <name>\"");
|
||||||
@@ -252,150 +294,287 @@ protected:
|
|||||||
{
|
{
|
||||||
// in expanded state bottom contains 'Clone', 'Rename', etc links, where the tool tip
|
// in expanded state bottom contains 'Clone', 'Rename', etc links, where the tool tip
|
||||||
// would be confusing
|
// would be confusing
|
||||||
const bool expanded = m_expandedSessions.contains(idx.data(Qt::DisplayRole).toString());
|
return expanded(idx) ? itemRect.adjusted(0, 0, 0, -actionButtonHeight()) : itemRect;
|
||||||
return expanded ? itemRect.adjusted(0, 0, 0, -LINK_HEIGHT) : 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:
|
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);
|
// visible on withIcon() Gap + arrow visible on hover Extra margin right of project item
|
||||||
|
// | | |
|
||||||
const QRect rc = option.rect;
|
// +----------+----------+ +--------+-------+ +----------+----------+
|
||||||
const QString sessionName = idx.data(Qt::DisplayRole).toString();
|
// | | | | | |
|
||||||
|
//
|
||||||
|
// +------------+--------+--------+------------+--------+-------------+--------+-------+------------+---------------------+ --+
|
||||||
|
// | | | |(VPaddingXs)| |(VPaddingXs) | | | | | |
|
||||||
|
// | | | +------------+ +-------------+ | | | | |
|
||||||
|
// |(HPaddingXs)|<number>|(HGapXs)| <icon> |(HGapXs)|<sessionName>|(HGapXs)|<arrow>| | | +-- Header
|
||||||
|
// | |(16x16) | +------------+ +-------------+ | | | | |
|
||||||
|
// | | | |(VPaddingXs)| |(VPaddingXs) | | | | | |
|
||||||
|
// |------------+--------+--------+------------+--------+-------------+--------+-------+ | | --+
|
||||||
|
// | +-- | (VPaddingXs) | | | |
|
||||||
|
// | | +------------------------------+(HPaddingXs)| | |
|
||||||
|
// | | | <projectName> | | | |
|
||||||
|
// | | +------------------------------+ | | |
|
||||||
|
// | Per project in session --+ | (EXSPaddingGapS) | |(sessionScrollBarGap)| |
|
||||||
|
// | | +------------------------------+ | | |
|
||||||
|
// | | | <projectPath> | | | |
|
||||||
|
// | | +------------------------------+ | | +-- Expansion
|
||||||
|
// | +-- | (VPaddingXs) | | | |
|
||||||
|
// +----------------------------------------------+------------------------------------+------------+ | |
|
||||||
|
// | (VPaddingXs) | | |
|
||||||
|
// +----------------------------------------+--------------+----------------------------------------+ | |
|
||||||
|
// +-- | <cloneButton>|<renameButton>|<deleteButton> | | |
|
||||||
|
// | +----------------------------------------+--------------+----------------------------------------+ | |
|
||||||
|
// | | (VPaddingXs) | | |
|
||||||
|
// | +------------------------------------------------------------------------------------------------+---------------------+ --+
|
||||||
|
// | | (VGapL) | +-- Gap between session items
|
||||||
|
// | +----------------------------------------------------------------------------------------------------------------------+ --+
|
||||||
|
// |
|
||||||
|
// \ session action "buttons" and dividers
|
||||||
|
// +-----------------------------------------------+--------+---------+--------+
|
||||||
|
// | (VGapXs) | | | |
|
||||||
|
// +----------------+-------------+----------------+ | | |
|
||||||
|
// |(EXSPaddingGapM)|<buttonLabel>|(EXSPaddingGapM)|(HGapXs)|<divider>|(HGapXs)|
|
||||||
|
// +----------------+-------------+----------------+ |(w:1) | |
|
||||||
|
// | (VGapXs) | | | |
|
||||||
|
// +-----------------------------------------------+--------+---------+--------+
|
||||||
|
//
|
||||||
|
// | |
|
||||||
|
// +-------------+-------------+
|
||||||
|
// |
|
||||||
|
// omitted after last button
|
||||||
|
|
||||||
const QPoint mousePos = option.widget->mapFromGlobal(QCursor::pos());
|
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 hovered = option.rect.contains(mousePos);
|
||||||
const bool expanded = m_expandedSessions.contains(sessionName);
|
const bool expanded = this->expanded(idx);
|
||||||
painter->fillRect(rc, themeColor(Theme::Welcome_BackgroundSecondaryColor));
|
|
||||||
painter->fillRect(rc.adjusted(0, 0, 0, -ItemGap),
|
|
||||||
hovered ? hoverColor : backgroundPrimaryColor);
|
|
||||||
|
|
||||||
const int x = rc.x();
|
const QRect bgR = option.rect.adjusted(0, 0, -sessionScrollBarGap, -itemSpacing());
|
||||||
const int x1 = x + TEXT_OFFSET_HORIZONTAL;
|
const QRect hdR(bgR.topLeft(), QSize(bgR.width(), expanded ? headerHeight()
|
||||||
const int y = rc.y();
|
: bgR.height()));
|
||||||
const int firstBase = y + 18;
|
|
||||||
|
|
||||||
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 QString sessionName = idx.data(Qt::DisplayRole).toString();
|
||||||
const QRect arrowRect = rc.adjusted(rc.width() - SESSION_ARROW_RECT_WIDTH, 0, 0, 0);
|
|
||||||
const bool arrowRectHovered = arrowRect.contains(mousePos);
|
const int x = bgR.x();
|
||||||
painter->fillRect(arrowRect.adjusted(0, 0, 0, -ItemGap),
|
const int y = bgR.y();
|
||||||
arrowRectHovered ? hoverColor : backgroundPrimaryColor);
|
|
||||||
|
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) {
|
if (idx.row() < 9) {
|
||||||
painter->setPen(foregroundSecondaryColor);
|
painter->setPen(shortcutNumberTF.color());
|
||||||
painter->setFont(sizedFont(10, option.widget));
|
painter->setFont(shortcutNumberTF.font());
|
||||||
painter->drawText(x + 3, firstBase, QString::number(idx.row() + 1));
|
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();
|
int yy = hdR.bottom();
|
||||||
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;
|
|
||||||
|
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
painter->setPen(textColor);
|
const QFont projectNameFont = sessionProjetNameTF.font();
|
||||||
painter->setFont(sizedFont(12, option.widget));
|
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);
|
const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
|
||||||
int yy = firstBase + SESSION_LINE_HEIGHT - 3;
|
|
||||||
QFontMetrics fm(option.widget->font());
|
|
||||||
for (const FilePath &projectPath : projects) {
|
for (const FilePath &projectPath : projects) {
|
||||||
// Project name.
|
yy += s(VPaddingXs);
|
||||||
QString completeBase = projectPath.completeBaseName();
|
{
|
||||||
painter->setPen(textColor);
|
painter->setFont(projectNameFont);
|
||||||
painter->drawText(x1, yy, fm.elidedText(completeBase, Qt::ElideMiddle, textSpace));
|
painter->setPen(sessionProjetNameTF.color());
|
||||||
yy += 18;
|
const QRect projectNameR(textX, yy, textWidth, projectNameLineHeight);
|
||||||
|
const QString projectNameElided =
|
||||||
// Project path.
|
projectNameFm.elidedText(projectPath.completeBaseName(), Qt::ElideMiddle,
|
||||||
const QString displayPath =
|
textWidth);
|
||||||
projectPath.osType() == OsTypeWindows ? projectPath.displayName()
|
painter->drawText(projectNameR, sessionProjetNameTF.drawTextFlags,
|
||||||
: projectPath.withTildeHomePath();
|
projectNameElided);
|
||||||
painter->setPen(foregroundPrimaryColor);
|
yy += projectNameLineHeight;
|
||||||
painter->drawText(x1, yy, fm.elidedText(displayPath, Qt::ElideMiddle, textSpace));
|
yy += s(ExPaddingGapS);
|
||||||
yy += 22;
|
}
|
||||||
|
{
|
||||||
|
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 = {
|
const QStringList actions = {
|
||||||
Tr::tr("Clone"),
|
Tr::tr("Clone"),
|
||||||
Tr::tr("Rename"),
|
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 QString &action = actions.at(i);
|
||||||
const int ww = fm.horizontalAdvance(action);
|
const int ww = textWidths.at(i);
|
||||||
const int spacing = 7; // Between action link and separator line
|
const QRect actionR(xx, yy, s(ExPaddingGapM) + ww + s(ExPaddingGapM), buttonHeight);
|
||||||
const QRect actionRect =
|
const bool isDisabled = i > 0 && SessionManager::isDefaultSession(sessionName);
|
||||||
QRect(xx, yy - 10, ww, 15).adjusted(-spacing, -spacing, spacing, spacing);
|
const bool isActive = actionR.adjusted(-s(VPaddingXs), 0, s(VPaddingXs) + 1, 0)
|
||||||
const bool isForcedDisabled = (i != 0 && sessionName == "default");
|
.contains(mousePos) && !isDisabled;
|
||||||
const bool isActive = actionRect.contains(mousePos) && !isForcedDisabled;
|
if (isActive) {
|
||||||
painter->setPen(isForcedDisabled ? disabledLinkColor : linkColor);
|
WelcomePageHelpers::drawCardBackground(painter, actionR, Qt::transparent,
|
||||||
painter->setFont(sizedFont(12, option.widget, isActive));
|
themeColor(Theme::Token_Text_Muted));
|
||||||
painter->drawText(xx, yy, action);
|
m_activeActionRects[i] = actionR;
|
||||||
if (i < 2) {
|
|
||||||
xx += ww + 2 * spacing;
|
|
||||||
int pp = xx - spacing;
|
|
||||||
painter->setPen(textColor);
|
|
||||||
painter->drawLine(pp, yy - 10, pp, yy);
|
|
||||||
}
|
}
|
||||||
if (isActive)
|
painter->setFont(actionFont);
|
||||||
m_activeActionRects[i] = actionRect;
|
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
|
QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &idx) const final
|
||||||
{
|
{
|
||||||
int h = SESSION_LINE_HEIGHT;
|
int h = headerHeight();
|
||||||
QString sessionName = idx.data(Qt::DisplayRole).toString();
|
if (expanded(idx)) {
|
||||||
if (m_expandedSessions.contains(sessionName)) {
|
const QString sessionName = idx.data(Qt::DisplayRole).toString();
|
||||||
const FilePaths projects = ProjectManager::projectsForSessionName(sessionName);
|
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,
|
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
|
||||||
const QStyleOptionViewItem &option, const QModelIndex &idx) final
|
const QStyleOptionViewItem &, const QModelIndex &idx) final
|
||||||
{
|
{
|
||||||
if (ev->type() == QEvent::MouseButtonRelease) {
|
if (ev->type() == QEvent::MouseButtonRelease) {
|
||||||
const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(ev);
|
const QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(ev);
|
||||||
const Qt::MouseButtons button = mouseEvent->button();
|
const Qt::MouseButtons button = mouseEvent->button();
|
||||||
const QPoint pos = static_cast<QMouseEvent *>(ev)->pos();
|
const QPoint pos = static_cast<QMouseEvent *>(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();
|
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".
|
// The expand/collapse "button".
|
||||||
if (m_expandedSessions.contains(sessionName))
|
if (m_expandedSessions.contains(sessionName))
|
||||||
m_expandedSessions.removeOne(sessionName);
|
m_expandedSessions.removeOne(sessionName);
|
||||||
@@ -421,23 +600,15 @@ public:
|
|||||||
}
|
}
|
||||||
if (ev->type() == QEvent::MouseMove) {
|
if (ev->type() == QEvent::MouseMove) {
|
||||||
emit model->layoutChanged({QPersistentModelIndex(idx)}); // Somewhat brutish.
|
emit model->layoutChanged({QPersistentModelIndex(idx)}); // Somewhat brutish.
|
||||||
//update(option.rect);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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;
|
QStringList m_expandedSessions;
|
||||||
|
|
||||||
|
mutable QRect m_activeExpandRect;
|
||||||
mutable QRect m_activeSwitchToRect;
|
mutable QRect m_activeSwitchToRect;
|
||||||
mutable QRect m_activeActionRects[3];
|
mutable QRect m_activeActionRects[3];
|
||||||
};
|
};
|
||||||
@@ -453,58 +624,89 @@ class ProjectDelegate : public BaseDelegate
|
|||||||
public:
|
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
|
||||||
{
|
{
|
||||||
QRect rc = option.rect;
|
// visible on withIcon() Extra margin right of project item
|
||||||
|
// | |
|
||||||
|
// +-------+-------+ +------+-----+
|
||||||
|
// | | | |
|
||||||
|
//
|
||||||
|
// +------------+--------+--------+------+--------+-------------+------------+------------+
|
||||||
|
// | | | | | | (VPaddingXs)| | |
|
||||||
|
// | | | | | +-------------+ | |
|
||||||
|
// | | | | | |<projectName>| | |
|
||||||
|
// | | | | | +-------------+ | |
|
||||||
|
// |(HPaddingXs)|<number>|(HGapXs)|<icon>|(HGapXs)| (VGapXs) |(HPaddingXs)|(HPaddingXs)|
|
||||||
|
// | |(16x16) | | | +-------------+ | |
|
||||||
|
// | | | | | |<projectPath>| | |
|
||||||
|
// | | | | | +-------------+ | |
|
||||||
|
// | | | | | | (VPaddingXs)| | |
|
||||||
|
// +------------+--------+--------+------+--------+-------------+------------+------------+ --+
|
||||||
|
// | (VGapL) | +-- Gap between project items
|
||||||
|
// +--------------------------------------------------------------------------------------+ --+
|
||||||
|
|
||||||
const bool hovered = option.widget->isActiveWindow() && option.state & QStyle::State_MouseOver;
|
const bool hovered = option.widget->isActiveWindow()
|
||||||
const QRect bgRect = rc.adjusted(0, 0, -ItemGap, -ItemGap);
|
&& option.state & QStyle::State_MouseOver;
|
||||||
painter->fillRect(rc, themeColor(Theme::Welcome_BackgroundSecondaryColor));
|
|
||||||
painter->fillRect(bgRect, themeColor(hovered ? Theme::Welcome_HoverColor
|
|
||||||
: Theme::Welcome_BackgroundPrimaryColor));
|
|
||||||
|
|
||||||
const int x = rc.x();
|
const QRect bgR = option.rect.adjusted(0, 0, -s(HPaddingXs), -itemSpacing());
|
||||||
const int y = rc.y();
|
|
||||||
const int firstBase = y + 18;
|
|
||||||
const int secondBase = firstBase + 19;
|
|
||||||
|
|
||||||
static const QPixmap projectIcon =
|
static const QPixmap icon = pixmap("project", Theme::Token_Text_Muted);
|
||||||
pixmap("project", Theme::Welcome_ForegroundSecondaryColor);
|
const QSize iconS = icon.deviceIndependentSize().toSize();
|
||||||
painter->drawPixmap(x + 11, y + 6, projectIcon);
|
|
||||||
|
|
||||||
QString projectName = idx.data(Qt::DisplayRole).toString();
|
const int x = bgR.x();
|
||||||
FilePath projectPath = FilePath::fromVariant(idx.data(ProjectModel::FilePathRole));
|
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));
|
const int y = bgR.y();
|
||||||
painter->setFont(sizedFont(10, option.widget));
|
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)
|
QTC_CHECK(option.rect.bottom() == projectPathR.bottom() + s(VPaddingXs) + itemSpacing());
|
||||||
painter->drawText(x + 3, firstBase, QString::number(idx.row() + 1));
|
|
||||||
|
|
||||||
const int textSpace = rc.width() - TEXT_OFFSET_HORIZONTAL - ItemGap - 6;
|
{
|
||||||
|
drawBackgroundRect(painter, bgR, hovered);
|
||||||
painter->setPen(themeColor(Theme::Welcome_LinkColor));
|
}
|
||||||
painter->setFont(sizedFont(13, option.widget, hovered));
|
if (idx.row() < 9) {
|
||||||
const QString projectNameElided =
|
painter->setPen(shortcutNumberTF.color());
|
||||||
painter->fontMetrics().elidedText(projectName, Qt::ElideRight, textSpace);
|
painter->setFont(shortcutNumberTF.font());
|
||||||
painter->drawText(x + TEXT_OFFSET_HORIZONTAL, firstBase, projectNameElided);
|
const QRect numberR(numberX, y, shortcutNumberWidth, bgR.height());
|
||||||
|
const QString numberString = QString::number(idx.row() + 1);
|
||||||
painter->setPen(themeColor(Theme::Welcome_ForegroundPrimaryColor));
|
painter->drawText(numberR, shortcutNumberTF.drawTextFlags, numberString);
|
||||||
painter->setFont(sizedFont(13, option.widget));
|
}
|
||||||
const QString displayPath =
|
if (withIcon()) {
|
||||||
projectPath.osType() == OsTypeWindows ? projectPath.displayName()
|
painter->drawPixmap(iconX, iconY, icon);
|
||||||
: projectPath.withTildeHomePath();
|
}
|
||||||
const QString displayPathElided =
|
{
|
||||||
painter->fontMetrics().elidedText(displayPath, Qt::ElideMiddle, textSpace);
|
painter->setPen(projectNameTF.color());
|
||||||
painter->drawText(x + TEXT_OFFSET_HORIZONTAL, secondBase, displayPathElided);
|
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();
|
return QSize(-1, itemHeight() + itemSpacing());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
|
bool editorEvent(QEvent *ev, QAbstractItemModel *model,
|
||||||
@@ -541,6 +743,18 @@ public:
|
|||||||
}
|
}
|
||||||
return false;
|
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
|
class TreeView : public QTreeView
|
||||||
@@ -559,10 +773,7 @@ public:
|
|||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||||
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||||
setFocusPolicy(Qt::NoFocus);
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
setBackgroundColor(viewport(), Theme::Token_Background_Default);
|
||||||
QPalette pal;
|
|
||||||
pal.setColor(QPalette::Base, themeColor(Theme::Welcome_BackgroundSecondaryColor));
|
|
||||||
viewport()->setPalette(pal);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -577,54 +788,63 @@ public:
|
|||||||
if (!projectWelcomePage->m_projectModel)
|
if (!projectWelcomePage->m_projectModel)
|
||||||
projectWelcomePage->m_projectModel = new ProjectModel(this);
|
projectWelcomePage->m_projectModel = new ProjectModel(this);
|
||||||
|
|
||||||
auto manageSessionsButton = new WelcomePageButton(this);
|
using namespace Layouting;
|
||||||
manageSessionsButton->setText(Tr::tr("Manage..."));
|
|
||||||
manageSessionsButton->setWithAccentColor(true);
|
|
||||||
manageSessionsButton->setOnClicked([] { SessionManager::showSessionManager(); });
|
|
||||||
|
|
||||||
auto sessionsLabel = new QLabel(this);
|
auto sessions = new QWidget;
|
||||||
sessionsLabel->setText(Tr::tr("Sessions"));
|
{
|
||||||
|
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);
|
auto projects = new QWidget;
|
||||||
recentProjectsLabel->setText(Tr::tr("Projects"));
|
{
|
||||||
|
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");
|
Row {
|
||||||
sessionsList->setModel(projectWelcomePage->m_sessionModel);
|
sessions,
|
||||||
sessionsList->header()->setSectionHidden(1, true); // The "last modified" column.
|
projects,
|
||||||
sessionsList->setItemDelegate(&m_sessionDelegate);
|
spacing(0),
|
||||||
sessionsList->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
noMargin(),
|
||||||
|
}.attachTo(this);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SessionDelegate m_sessionDelegate;
|
SessionDelegate m_sessionDelegate;
|
||||||
|
@@ -117,11 +117,11 @@ public:
|
|||||||
if (!pipIsUsable(path)) {
|
if (!pipIsUsable(path)) {
|
||||||
result << BuildSystemTask(
|
result << BuildSystemTask(
|
||||||
Task::Warning,
|
Task::Warning,
|
||||||
Tr::tr("Python \"%1\" does not contain a usable pip. Pip is used to install "
|
Tr::tr("Python \"%1\" does not contain a usable pip. pip is needed to install "
|
||||||
"python "
|
"Python "
|
||||||
"packages from the Python Package Index, like PySide and the python "
|
"packages from the Python Package Index, like PySide and the Python "
|
||||||
"language server. If you want to use any of that functionality "
|
"language server. To use any of that functionality "
|
||||||
"ensure pip is installed for that python.")
|
"ensure that pip is installed for that Python.")
|
||||||
.arg(path.toUserOutput()));
|
.arg(path.toUserOutput()));
|
||||||
}
|
}
|
||||||
if (!venvIsUsable(path)) {
|
if (!venvIsUsable(path)) {
|
||||||
@@ -130,7 +130,7 @@ public:
|
|||||||
Tr::tr(
|
Tr::tr(
|
||||||
"Python \"%1\" does not contain a usable venv. venv is the recommended way "
|
"Python \"%1\" does not contain a usable venv. venv is the recommended way "
|
||||||
"to isolate a development environment for a project from the globally "
|
"to isolate a development environment for a project from the globally "
|
||||||
"installed python.")
|
"installed Python.")
|
||||||
.arg(path.toUserOutput()));
|
.arg(path.toUserOutput()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,7 @@ Tasks PythonProject::projectIssues(const Kit *k) const
|
|||||||
return {};
|
return {};
|
||||||
return {
|
return {
|
||||||
BuildSystemTask{Task::Error,
|
BuildSystemTask{Task::Error,
|
||||||
Tr::tr("No python interpreter set for kit \"%1\"").arg(k->displayName())}};
|
Tr::tr("No Python interpreter set for kit \"%1\"").arg(k->displayName())}};
|
||||||
}
|
}
|
||||||
|
|
||||||
PythonProjectNode::PythonProjectNode(const FilePath &path)
|
PythonProjectNode::PythonProjectNode(const FilePath &path)
|
||||||
|
@@ -56,6 +56,7 @@ void MakeFileParse::parseArgs(const QString &args, const QString &project,
|
|||||||
static const QRegularExpression regExp(QLatin1String("^([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)$"));
|
static const QRegularExpression regExp(QLatin1String("^([^\\s\\+-]*)\\s*(\\+=|=|-=|~=)(.*)$"));
|
||||||
bool after = false;
|
bool after = false;
|
||||||
bool ignoreNext = false;
|
bool ignoreNext = false;
|
||||||
|
bool nextIsQtConfArg = false;
|
||||||
m_unparsedArguments = args;
|
m_unparsedArguments = args;
|
||||||
ProcessArgs::ArgIterator ait(&m_unparsedArguments);
|
ProcessArgs::ArgIterator ait(&m_unparsedArguments);
|
||||||
while (ait.next()) {
|
while (ait.next()) {
|
||||||
@@ -63,11 +64,18 @@ void MakeFileParse::parseArgs(const QString &args, const QString &project,
|
|||||||
// Ignoring
|
// Ignoring
|
||||||
ignoreNext = false;
|
ignoreNext = false;
|
||||||
ait.deleteArg();
|
ait.deleteArg();
|
||||||
|
} else if (nextIsQtConfArg) {
|
||||||
|
nextIsQtConfArg = false;
|
||||||
|
m_qtConfFile = FilePath::fromUserInput(ait.value());
|
||||||
|
ait.deleteArg();
|
||||||
} else if (ait.value() == project) {
|
} else if (ait.value() == project) {
|
||||||
ait.deleteArg();
|
ait.deleteArg();
|
||||||
} else if (ait.value() == QLatin1String("-after")) {
|
} else if (ait.value() == QLatin1String("-after")) {
|
||||||
after = true;
|
after = true;
|
||||||
ait.deleteArg();
|
ait.deleteArg();
|
||||||
|
} else if (ait.value() == "-qtconf") {
|
||||||
|
nextIsQtConfArg = true;
|
||||||
|
ait.deleteArg();
|
||||||
} else if (ait.value().contains(QLatin1Char('='))) {
|
} else if (ait.value().contains(QLatin1Char('='))) {
|
||||||
const QRegularExpressionMatch match = regExp.match(ait.value());
|
const QRegularExpressionMatch match = regExp.match(ait.value());
|
||||||
if (match.hasMatch()) {
|
if (match.hasMatch()) {
|
||||||
|
@@ -30,6 +30,7 @@ public:
|
|||||||
MakefileState makeFileState() const;
|
MakefileState makeFileState() const;
|
||||||
Utils::FilePath qmakePath() const;
|
Utils::FilePath qmakePath() const;
|
||||||
Utils::FilePath srcProFile() const;
|
Utils::FilePath srcProFile() const;
|
||||||
|
Utils::FilePath qtConfPath() const { return m_qtConfFile;}
|
||||||
QMakeStepConfig config() const;
|
QMakeStepConfig config() const;
|
||||||
|
|
||||||
QString unparsedArguments() const;
|
QString unparsedArguments() const;
|
||||||
@@ -59,6 +60,7 @@ private:
|
|||||||
MakefileState m_state;
|
MakefileState m_state;
|
||||||
Utils::FilePath m_qmakePath;
|
Utils::FilePath m_qmakePath;
|
||||||
Utils::FilePath m_srcProFile;
|
Utils::FilePath m_srcProFile;
|
||||||
|
Utils::FilePath m_qtConfFile;
|
||||||
|
|
||||||
QmakeBuildConfig m_qmakeBuildConfig;
|
QmakeBuildConfig m_qmakeBuildConfig;
|
||||||
QMakeStepConfig m_config;
|
QMakeStepConfig m_config;
|
||||||
|
@@ -490,7 +490,9 @@ QmakeBuildConfiguration::MakefileState QmakeBuildConfiguration::compareToImportF
|
|||||||
return MakefileIncompatible;
|
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()
|
qCDebug(logs) << "**Different Qt versions, buildconfiguration:" << version->qmakeFilePath()
|
||||||
<< " Makefile:" << parse.qmakePath();
|
<< " Makefile:" << parse.qmakePath();
|
||||||
return MakefileForWrongProject;
|
return MakefileForWrongProject;
|
||||||
|
@@ -250,7 +250,7 @@ static QPixmap fetchPixmapAndUpdatePixmapCache(const QString &url)
|
|||||||
const int dpr = qApp->devicePixelRatio();
|
const int dpr = qApp->devicePixelRatio();
|
||||||
// boundedTo -> don't scale thumbnails up
|
// boundedTo -> don't scale thumbnails up
|
||||||
const QSize scaledSize =
|
const QSize scaledSize =
|
||||||
WelcomePageHelpers::GridItemImageSize.boundedTo(img.size()) * dpr;
|
WelcomePageHelpers::WelcomeThumbnailSize.boundedTo(img.size()) * dpr;
|
||||||
const QImage scaled = img.isNull() ? img
|
const QImage scaled = img.isNull() ? img
|
||||||
: img.scaled(scaledSize,
|
: img.scaled(scaledSize,
|
||||||
Qt::KeepAspectRatio,
|
Qt::KeepAspectRatio,
|
||||||
|
@@ -21,13 +21,13 @@
|
|||||||
|
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/layoutbuilder.h>
|
||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/stylehelper.h>
|
#include <utils/stylehelper.h>
|
||||||
#include <utils/theme/theme.h>
|
#include <utils/theme/theme.h>
|
||||||
#include <utils/winutils.h>
|
#include <utils/winutils.h>
|
||||||
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QElapsedTimer>
|
#include <QElapsedTimer>
|
||||||
@@ -249,8 +249,9 @@ protected:
|
|||||||
painter->setFont(option.font);
|
painter->setFont(option.font);
|
||||||
painter->setCompositionMode(QPainter::CompositionMode_Difference);
|
painter->setCompositionMode(QPainter::CompositionMode_Difference);
|
||||||
painter->setPen(Qt::white);
|
painter->setPen(Qt::white);
|
||||||
painter->drawText(currentPixmapRect.translated(0, -WelcomePageHelpers::ItemGap),
|
painter->drawText(
|
||||||
exampleItem->videoLength, Qt::AlignBottom | Qt::AlignHCenter);
|
currentPixmapRect.translated(0, -StyleHelper::SpacingTokens::VPaddingXxs),
|
||||||
|
exampleItem->videoLength, Qt::AlignBottom | Qt::AlignHCenter);
|
||||||
painter->restore();
|
painter->restore();
|
||||||
static const QPixmap playOverlay =
|
static const QPixmap playOverlay =
|
||||||
StyleHelper::dpiSpecificImageFile(":/qtsupport/images/icons/playoverlay.png");
|
StyleHelper::dpiSpecificImageFile(":/qtsupport/images/icons/playoverlay.png");
|
||||||
@@ -274,29 +275,25 @@ public:
|
|||||||
{
|
{
|
||||||
m_exampleDelegate.setShowExamples(isExamples);
|
m_exampleDelegate.setShowExamples(isExamples);
|
||||||
|
|
||||||
auto searchBox = new SearchBox(this);
|
using namespace StyleHelper::SpacingTokens;
|
||||||
m_searcher = searchBox->m_lineEdit;
|
|
||||||
|
|
||||||
auto grid = new QGridLayout(this);
|
using namespace Layouting;
|
||||||
grid->setContentsMargins(0, 0, 0, WelcomePageHelpers::ItemGap);
|
Row titleRow {
|
||||||
grid->setHorizontalSpacing(0);
|
customMargin({0, 0, ExVPaddingGapXl, 0}),
|
||||||
grid->setVerticalSpacing(WelcomePageHelpers::ItemGap);
|
spacing(ExVPaddingGapXl),
|
||||||
|
};
|
||||||
|
|
||||||
auto searchBar = WelcomePageHelpers::panelBar(this);
|
m_searcher = new SearchBox;
|
||||||
auto hbox = new QHBoxLayout(searchBar);
|
|
||||||
hbox->setContentsMargins(0, 0, 0, 0);
|
|
||||||
if (m_isExamples) {
|
if (m_isExamples) {
|
||||||
m_searcher->setPlaceholderText(Tr::tr("Search in Examples..."));
|
m_searcher->setPlaceholderText(Tr::tr("Search in Examples..."));
|
||||||
|
|
||||||
auto exampleSetSelector = new QComboBox(this);
|
auto exampleSetSelector = new ComboBox;
|
||||||
QPalette pal = exampleSetSelector->palette();
|
exampleSetSelector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
||||||
// for macOS dark mode
|
exampleSetSelector->setMinimumWidth(ListItemDelegate::itemSize().width()
|
||||||
pal.setColor(QPalette::Text, Utils::creatorTheme()->color(Theme::Welcome_TextColor));
|
- ExVPaddingGapXl);
|
||||||
exampleSetSelector->setPalette(pal);
|
|
||||||
exampleSetSelector->setMinimumWidth(Core::WelcomePageHelpers::GridItemWidth);
|
|
||||||
exampleSetSelector->setMaximumWidth(Core::WelcomePageHelpers::GridItemWidth);
|
|
||||||
exampleSetSelector->setModel(s_exampleSetModel);
|
exampleSetSelector->setModel(s_exampleSetModel);
|
||||||
exampleSetSelector->setCurrentIndex(s_exampleSetModel->selectedExampleSet());
|
exampleSetSelector->setCurrentIndex(s_exampleSetModel->selectedExampleSet());
|
||||||
|
titleRow.addItem(exampleSetSelector);
|
||||||
connect(exampleSetSelector,
|
connect(exampleSetSelector,
|
||||||
&QComboBox::activated,
|
&QComboBox::activated,
|
||||||
s_exampleSetModel,
|
s_exampleSetModel,
|
||||||
@@ -305,23 +302,23 @@ public:
|
|||||||
&ExampleSetModel::selectedExampleSetChanged,
|
&ExampleSetModel::selectedExampleSetChanged,
|
||||||
exampleSetSelector,
|
exampleSetSelector,
|
||||||
&QComboBox::setCurrentIndex);
|
&QComboBox::setCurrentIndex);
|
||||||
|
|
||||||
hbox->setSpacing(Core::WelcomePageHelpers::HSpacing);
|
|
||||||
hbox->addWidget(exampleSetSelector);
|
|
||||||
} else {
|
} else {
|
||||||
m_searcher->setPlaceholderText(Tr::tr("Search in Tutorials..."));
|
m_searcher->setPlaceholderText(Tr::tr("Search in Tutorials..."));
|
||||||
}
|
}
|
||||||
hbox->addWidget(searchBox);
|
titleRow.addItem(m_searcher);
|
||||||
grid->addWidget(WelcomePageHelpers::panelBar(this), 0, 0);
|
|
||||||
grid->addWidget(searchBar, 0, 1);
|
|
||||||
grid->addWidget(WelcomePageHelpers::panelBar(this), 0, 2);
|
|
||||||
|
|
||||||
auto gridView = new SectionedGridView(this);
|
auto gridView = new SectionedGridView;
|
||||||
m_viewController
|
m_viewController
|
||||||
= new ExamplesViewController(s_exampleSetModel, gridView, m_searcher, isExamples, this);
|
= new ExamplesViewController(s_exampleSetModel, gridView, m_searcher, isExamples, this);
|
||||||
|
|
||||||
gridView->setItemDelegate(&m_exampleDelegate);
|
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,
|
connect(&m_exampleDelegate, &ExampleDelegate::tagClicked,
|
||||||
this, &ExamplesPageWidget::onTagClicked);
|
this, &ExamplesPageWidget::onTagClicked);
|
||||||
|
@@ -155,11 +155,16 @@ GroupItem GenericDeployStep::transferTask(const Storage<FilesToTransfer> &storag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!m_emittedDowngradeWarning && transferMethod != preferredTransferMethod) {
|
if (!m_emittedDowngradeWarning && transferMethod != preferredTransferMethod) {
|
||||||
addWarningMessage(Tr::tr("Transfer method was downgraded from \"%1\" to \"%2\". If "
|
const QString message
|
||||||
"this is unexpected, please re-test device \"%3\".")
|
= Tr::tr("Transfer method was downgraded from \"%1\" to \"%2\". If "
|
||||||
.arg(FileTransfer::transferMethodName(preferredTransferMethod),
|
"this is unexpected, please re-test device \"%3\".")
|
||||||
FileTransfer::transferMethodName(transferMethod),
|
.arg(FileTransfer::transferMethodName(preferredTransferMethod),
|
||||||
deviceConfiguration()->displayName()));
|
FileTransfer::transferMethodName(transferMethod),
|
||||||
|
deviceConfiguration()->displayName());
|
||||||
|
if (transferMethod == FileTransferMethod::GenericCopy)
|
||||||
|
addWarningMessage(message);
|
||||||
|
else
|
||||||
|
addProgressMessage(message);
|
||||||
m_emittedDowngradeWarning = true;
|
m_emittedDowngradeWarning = true;
|
||||||
}
|
}
|
||||||
transfer.setTransferMethod(transferMethod);
|
transfer.setTransferMethod(transferMethod);
|
||||||
|
@@ -98,7 +98,8 @@ GenericLinuxDeviceConfigurationWidget::GenericLinuxDeviceConfigurationWidget(
|
|||||||
const int dmCount = dm->deviceCount();
|
const int dmCount = dm->deviceCount();
|
||||||
for (int i = 0; i < dmCount; ++i) {
|
for (int i = 0; i < dmCount; ++i) {
|
||||||
IDevice::ConstPtr dev = dm->deviceAt(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:"));
|
auto sshPortLabel = new QLabel(Tr::tr("&SSH port:"));
|
||||||
@@ -307,13 +308,18 @@ void GenericLinuxDeviceConfigurationWidget::initGui()
|
|||||||
Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
|
Id linkDeviceId = Id::fromSetting(device()->extraData(Constants::LinkDevice));
|
||||||
auto dm = DeviceManager::instance();
|
auto dm = DeviceManager::instance();
|
||||||
int found = -1;
|
int found = -1;
|
||||||
|
int minus = 0;
|
||||||
for (int i = 0, n = dm->deviceCount(); i < n; ++i) {
|
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;
|
found = i;
|
||||||
break;
|
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_hostLineEdit->setText(sshParams.host());
|
||||||
m_sshPortSpinBox->setValue(sshParams.port());
|
m_sshPortSpinBox->setValue(sshParams.port());
|
||||||
|
@@ -356,13 +356,14 @@ QFont FontSettings::font() const
|
|||||||
{
|
{
|
||||||
QFont f(family(), fontSize());
|
QFont f(family(), fontSize());
|
||||||
f.setStyleStrategy(m_antialias ? QFont::PreferAntialias : QFont::NoAntialias);
|
f.setStyleStrategy(m_antialias ? QFont::PreferAntialias : QFont::NoAntialias);
|
||||||
|
f.setWeight(fontNormalWeight());
|
||||||
return f;
|
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
|
// 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)
|
if (Utils::HostOsInfo::isMacHost() && m_family == g_sourceCodePro)
|
||||||
weight = QFont::Medium;
|
weight = QFont::Medium;
|
||||||
return weight;
|
return weight;
|
||||||
|
@@ -61,7 +61,7 @@ public:
|
|||||||
void setRelativeLineSpacing(int relativeLineSpacing);
|
void setRelativeLineSpacing(int relativeLineSpacing);
|
||||||
|
|
||||||
QFont font() const;
|
QFont font() const;
|
||||||
int fontNormalWeight() const;
|
QFont::Weight fontNormalWeight() const;
|
||||||
|
|
||||||
bool antialias() const;
|
bool antialias() const;
|
||||||
void setAntialias(bool antialias);
|
void setAntialias(bool antialias);
|
||||||
|
@@ -81,6 +81,10 @@ void SyntaxHighlighter::delayedRehighlight()
|
|||||||
if (!d->rehighlightPending)
|
if (!d->rehighlightPending)
|
||||||
return;
|
return;
|
||||||
d->rehighlightPending = false;
|
d->rehighlightPending = false;
|
||||||
|
|
||||||
|
if (document()->isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
rehighlight();
|
rehighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +201,10 @@ void SyntaxHighlighterPrivate::reformatBlocks(int from, int charsRemoved, int ch
|
|||||||
|
|
||||||
QList<SyntaxHighlighter::Result> vecRes;
|
QList<SyntaxHighlighter::Result> vecRes;
|
||||||
|
|
||||||
|
SyntaxHighlighter::Result resStart;
|
||||||
|
resStart.m_state = SyntaxHighlighter::State::Start;
|
||||||
|
vecRes << resStart;
|
||||||
|
|
||||||
while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
|
while (block.isValid() && (block.position() < endPosition || forceHighlightOfNextBlock)) {
|
||||||
if (QThread::currentThread()->isInterruptionRequested())
|
if (QThread::currentThread()->isInterruptionRequested())
|
||||||
break;
|
break;
|
||||||
@@ -758,7 +766,10 @@ void SyntaxHighlighter::setExtraFormats(const QTextBlock &block,
|
|||||||
SyntaxHighlighter::Result res;
|
SyntaxHighlighter::Result res;
|
||||||
res.m_formatRanges = block.layout()->formats();
|
res.m_formatRanges = block.layout()->formats();
|
||||||
res.fillByBlock(block);
|
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);
|
document()->markContentsDirty(block.position(), blockLength - 1);
|
||||||
d->inReformatBlocks = wasInReformatBlocks;
|
d->inReformatBlocks = wasInReformatBlocks;
|
||||||
@@ -784,7 +795,10 @@ void SyntaxHighlighter::clearExtraFormats(const QTextBlock &block)
|
|||||||
SyntaxHighlighter::Result res;
|
SyntaxHighlighter::Result res;
|
||||||
res.m_formatRanges = block.layout()->formats();
|
res.m_formatRanges = block.layout()->formats();
|
||||||
res.fillByBlock(block);
|
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);
|
document()->markContentsDirty(block.position(), blockLength - 1);
|
||||||
d->inReformatBlocks = wasInReformatBlocks;
|
d->inReformatBlocks = wasInReformatBlocks;
|
||||||
|
@@ -53,8 +53,10 @@ public:
|
|||||||
virtual void setFontSettings(const TextEditor::FontSettings &fontSettings);
|
virtual void setFontSettings(const TextEditor::FontSettings &fontSettings);
|
||||||
TextEditor::FontSettings fontSettings() const;
|
TextEditor::FontSettings fontSettings() const;
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
|
Start,
|
||||||
InProgress,
|
InProgress,
|
||||||
|
Extras,
|
||||||
Done
|
Done
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,7 +73,6 @@ public:
|
|||||||
|
|
||||||
m_hasBlockUserData = true;
|
m_hasBlockUserData = true;
|
||||||
m_foldingIndent = userDate->foldingIndent();
|
m_foldingIndent = userDate->foldingIndent();
|
||||||
m_folded = userDate->folded();
|
|
||||||
m_ifdefedOut = userDate->ifdefedOut();
|
m_ifdefedOut = userDate->ifdefedOut();
|
||||||
m_foldingStartIncluded = userDate->foldingStartIncluded();
|
m_foldingStartIncluded = userDate->foldingStartIncluded();
|
||||||
m_foldingEndIncluded = userDate->foldingEndIncluded();
|
m_foldingEndIncluded = userDate->foldingEndIncluded();
|
||||||
@@ -83,28 +84,27 @@ public:
|
|||||||
{
|
{
|
||||||
block.setUserState(m_userState);
|
block.setUserState(m_userState);
|
||||||
|
|
||||||
if (m_hasBlockUserData) {
|
if (!m_hasBlockUserData)
|
||||||
TextBlockUserData *data = TextDocumentLayout::userData(block);
|
return;
|
||||||
data->setExpectedRawStringSuffix(m_expectedRawStringSuffix);
|
|
||||||
data->setFolded(m_folded);
|
|
||||||
data->setFoldingIndent(m_foldingIndent);
|
|
||||||
data->setFoldingStartIncluded(m_foldingStartIncluded);
|
|
||||||
data->setFoldingEndIncluded(m_foldingEndIncluded);
|
|
||||||
|
|
||||||
if (m_ifdefedOut)
|
TextBlockUserData *data = TextDocumentLayout::userData(block);
|
||||||
data->setIfdefedOut();
|
data->setExpectedRawStringSuffix(m_expectedRawStringSuffix);
|
||||||
else
|
data->setFoldingIndent(m_foldingIndent);
|
||||||
data->clearIfdefedOut();
|
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;
|
int m_blockNumber;
|
||||||
bool m_hasBlockUserData = false;
|
bool m_hasBlockUserData = false;
|
||||||
|
|
||||||
int m_foldingIndent : 16;
|
int m_foldingIndent : 16;
|
||||||
uint m_folded : 1;
|
|
||||||
uint m_ifdefedOut : 1;
|
uint m_ifdefedOut : 1;
|
||||||
uint m_foldingStartIncluded : 1;
|
uint m_foldingStartIncluded : 1;
|
||||||
uint m_foldingEndIncluded : 1;
|
uint m_foldingEndIncluded : 1;
|
||||||
|
@@ -100,7 +100,6 @@ public:
|
|||||||
|
|
||||||
SyntaxHighlighter *m_highlighter = nullptr;
|
SyntaxHighlighter *m_highlighter = nullptr;
|
||||||
QTextDocument *m_document = nullptr;
|
QTextDocument *m_document = nullptr;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void resultsReady(const QList<SyntaxHighlighter::Result> &result);
|
void resultsReady(const QList<SyntaxHighlighter::Result> &result);
|
||||||
|
|
||||||
@@ -130,6 +129,8 @@ SyntaxHighlighterRunner::SyntaxHighlighterRunner(SyntaxHighlighter *highlighter,
|
|||||||
&QTextDocument::contentsChange,
|
&QTextDocument::contentsChange,
|
||||||
this,
|
this,
|
||||||
&SyntaxHighlighterRunner::changeDocument);
|
&SyntaxHighlighterRunner::changeDocument);
|
||||||
|
|
||||||
|
m_foldValidator.setup(qobject_cast<TextDocumentLayout *>(document->documentLayout()));
|
||||||
} else {
|
} else {
|
||||||
connect(d,
|
connect(d,
|
||||||
&SyntaxHighlighterRunnerPrivate::resultsReady,
|
&SyntaxHighlighterRunnerPrivate::resultsReady,
|
||||||
@@ -169,7 +170,12 @@ void SyntaxHighlighterRunner::applyFormatRanges(const QList<SyntaxHighlighter::R
|
|||||||
|
|
||||||
for (const SyntaxHighlighter::Result &result : results) {
|
for (const SyntaxHighlighter::Result &result : results) {
|
||||||
m_syntaxInfoUpdated = result.m_state;
|
m_syntaxInfoUpdated = result.m_state;
|
||||||
|
if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Start) {
|
||||||
|
m_foldValidator.reset();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Done) {
|
if (m_syntaxInfoUpdated == SyntaxHighlighter::State::Done) {
|
||||||
|
m_foldValidator.finalize();
|
||||||
emit highlightingFinished();
|
emit highlightingFinished();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -181,12 +187,11 @@ void SyntaxHighlighterRunner::applyFormatRanges(const QList<SyntaxHighlighter::R
|
|||||||
result.copyToBlock(docBlock);
|
result.copyToBlock(docBlock);
|
||||||
|
|
||||||
if (result.m_formatRanges != docBlock.layout()->formats()) {
|
if (result.m_formatRanges != docBlock.layout()->formats()) {
|
||||||
TextDocumentLayout::FoldValidator foldValidator;
|
|
||||||
foldValidator.setup(qobject_cast<TextDocumentLayout *>(m_document->documentLayout()));
|
|
||||||
docBlock.layout()->setFormats(result.m_formatRanges);
|
docBlock.layout()->setFormats(result.m_formatRanges);
|
||||||
m_document->markContentsDirty(docBlock.position(), docBlock.length());
|
m_document->markContentsDirty(docBlock.position(), docBlock.length());
|
||||||
foldValidator.process(docBlock);
|
|
||||||
}
|
}
|
||||||
|
if (m_syntaxInfoUpdated != SyntaxHighlighter::State::Extras)
|
||||||
|
m_foldValidator.process(docBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -56,6 +56,7 @@ private:
|
|||||||
bool m_useGenericHighlighter = false;
|
bool m_useGenericHighlighter = false;
|
||||||
QString m_definitionName;
|
QString m_definitionName;
|
||||||
std::optional<QThread> m_thread;
|
std::optional<QThread> m_thread;
|
||||||
|
TextDocumentLayout::FoldValidator m_foldValidator;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TextEditor
|
} // namespace TextEditor
|
||||||
|
@@ -3492,13 +3492,36 @@ QByteArray TextEditorWidget::saveState() const
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TextEditorWidget::singleShotAfterHighlightingDone(std::function<void()> &&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)
|
void TextEditorWidget::restoreState(const QByteArray &state)
|
||||||
{
|
{
|
||||||
|
const auto callFoldLicenseHeader = [this] {
|
||||||
|
auto callFold = [this] {
|
||||||
|
if (d->m_displaySettings.m_autoFoldFirstComment)
|
||||||
|
d->foldLicenseHeader();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!singleShotAfterHighlightingDone(callFold))
|
||||||
|
callFold();
|
||||||
|
};
|
||||||
|
|
||||||
if (state.isEmpty()) {
|
if (state.isEmpty()) {
|
||||||
if (d->m_displaySettings.m_autoFoldFirstComment)
|
callFoldLicenseHeader();
|
||||||
d->foldLicenseHeader();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int version;
|
int version;
|
||||||
int vval;
|
int vval;
|
||||||
int hval;
|
int hval;
|
||||||
@@ -3514,24 +3537,27 @@ void TextEditorWidget::restoreState(const QByteArray &state)
|
|||||||
if (version >= 1) {
|
if (version >= 1) {
|
||||||
QList<int> collapsedBlocks;
|
QList<int> collapsedBlocks;
|
||||||
stream >> collapsedBlocks;
|
stream >> collapsedBlocks;
|
||||||
QTextDocument *doc = document();
|
auto foldingRestore = [this, collapsedBlocks] {
|
||||||
bool layoutChanged = false;
|
QTextDocument *doc = document();
|
||||||
for (const int blockNumber : std::as_const(collapsedBlocks)) {
|
bool layoutChanged = false;
|
||||||
QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
|
for (const int blockNumber : std::as_const(collapsedBlocks)) {
|
||||||
if (block.isValid()) {
|
QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
|
||||||
TextDocumentLayout::doFoldOrUnfold(block, false);
|
if (block.isValid()) {
|
||||||
layoutChanged = true;
|
TextDocumentLayout::doFoldOrUnfold(block, false);
|
||||||
|
layoutChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (layoutChanged) {
|
||||||
if (layoutChanged) {
|
auto documentLayout = qobject_cast<TextDocumentLayout *>(doc->documentLayout());
|
||||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
QTC_ASSERT(documentLayout, return);
|
||||||
QTC_ASSERT(documentLayout, return );
|
documentLayout->requestUpdate();
|
||||||
documentLayout->requestUpdate();
|
documentLayout->emitDocumentSizeChanged();
|
||||||
documentLayout->emitDocumentSizeChanged();
|
}
|
||||||
}
|
};
|
||||||
|
if (!singleShotAfterHighlightingDone(foldingRestore))
|
||||||
|
foldingRestore();
|
||||||
} else {
|
} else {
|
||||||
if (d->m_displaySettings.m_autoFoldFirstComment)
|
callFoldLicenseHeader();
|
||||||
d->foldLicenseHeader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
|
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
|
||||||
@@ -6642,6 +6668,9 @@ void TextEditorWidget::ensureCursorVisible()
|
|||||||
|
|
||||||
void TextEditorWidget::ensureBlockIsUnfolded(QTextBlock block)
|
void TextEditorWidget::ensureBlockIsUnfolded(QTextBlock block)
|
||||||
{
|
{
|
||||||
|
if (singleShotAfterHighlightingDone([this, block] { ensureBlockIsUnfolded(block); }))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!block.isVisible()) {
|
if (!block.isVisible()) {
|
||||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
||||||
QTC_ASSERT(documentLayout, return);
|
QTC_ASSERT(documentLayout, return);
|
||||||
@@ -8215,6 +8244,9 @@ void TextEditorWidget::foldCurrentBlock()
|
|||||||
|
|
||||||
void TextEditorWidget::fold(const QTextBlock &block)
|
void TextEditorWidget::fold(const QTextBlock &block)
|
||||||
{
|
{
|
||||||
|
if (singleShotAfterHighlightingDone([this, block] { fold(block); }))
|
||||||
|
return;
|
||||||
|
|
||||||
QTextDocument *doc = document();
|
QTextDocument *doc = document();
|
||||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
||||||
QTC_ASSERT(documentLayout, return);
|
QTC_ASSERT(documentLayout, return);
|
||||||
@@ -8235,6 +8267,9 @@ void TextEditorWidget::fold(const QTextBlock &block)
|
|||||||
|
|
||||||
void TextEditorWidget::unfold(const QTextBlock &block)
|
void TextEditorWidget::unfold(const QTextBlock &block)
|
||||||
{
|
{
|
||||||
|
if (singleShotAfterHighlightingDone([this, block] { unfold(block); }))
|
||||||
|
return;
|
||||||
|
|
||||||
QTextDocument *doc = document();
|
QTextDocument *doc = document();
|
||||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
||||||
QTC_ASSERT(documentLayout, return);
|
QTC_ASSERT(documentLayout, return);
|
||||||
@@ -8254,6 +8289,9 @@ void TextEditorWidget::unfoldCurrentBlock()
|
|||||||
|
|
||||||
void TextEditorWidget::unfoldAll()
|
void TextEditorWidget::unfoldAll()
|
||||||
{
|
{
|
||||||
|
if (singleShotAfterHighlightingDone([this] { unfoldAll(); }))
|
||||||
|
return;
|
||||||
|
|
||||||
QTextDocument *doc = document();
|
QTextDocument *doc = document();
|
||||||
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
||||||
QTC_ASSERT(documentLayout, return);
|
QTC_ASSERT(documentLayout, return);
|
||||||
|
@@ -645,6 +645,7 @@ private:
|
|||||||
friend class Internal::TextEditorOverlay;
|
friend class Internal::TextEditorOverlay;
|
||||||
friend class RefactorOverlay;
|
friend class RefactorOverlay;
|
||||||
|
|
||||||
|
bool singleShotAfterHighlightingDone(std::function<void()> &&f);
|
||||||
void updateVisualWrapColumn();
|
void updateVisualWrapColumn();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 106 B |
Before Width: | Height: | Size: 148 B |
BIN
src/plugins/welcome/images/link.png
Normal file
After Width: | Height: | Size: 186 B |
BIN
src/plugins/welcome/images/link@2x.png
Normal file
After Width: | Height: | Size: 236 B |
Before Width: | Height: | Size: 106 B After Width: | Height: | Size: 175 B |
Before Width: | Height: | Size: 112 B After Width: | Height: | Size: 225 B |
Before Width: | Height: | Size: 118 B After Width: | Height: | Size: 192 B |
Before Width: | Height: | Size: 161 B After Width: | Height: | Size: 255 B |