Merge remote-tracking branch 'origin/5.12' into dev

Change-Id: I0fe623517af28e408b642c879efd59f633ab63ac
This commit is contained in:
Qt Forward Merge Bot 2018-12-13 01:00:10 +01:00
commit ad4f359f80
30 changed files with 318 additions and 103 deletions

1
dist/changes-5.12.0 vendored
View File

@ -48,6 +48,7 @@ information about a particular change.
- The minimal required version of libxcb is now 1.9. Bundled xcb sources
were updated to libxcb 1.9.1 built with xcb-proto 1.8.
- [QTBUG-67654] Updated CLDR to version 33.1
- [QTBUG-71144] Updated CLDR to version 34
- [QTBUG-66561][QTBUG-70008] double-conversion got updated to upstream
version 3.1.1.
- libjpeg-turbo was updated to version 2.0.0

View File

@ -110,7 +110,7 @@
# Backwards compatibility
for (bundle_data, QMAKE_BUNDLE_DATA) {
for (bundle_file, $${bundle_data}.files) {
!contains(bundle_file, .*\.xcassets$): next()
!contains(bundle_file, .*\\.xcassets$): next()
warning("*.xcassets in QMAKE_BUNDLE_DATA is deprecated. Use QMAKE_ASSET_CATALOGS instead.")
!exists($$absolute_path($$bundle_file/AppIcon.appiconset, $$_PRO_FILE_PWD_)): next()

View File

@ -21,7 +21,7 @@ contains(TEMPLATE, .*app) {
!isEmpty($$list($$(QT_MAC_SDK_NO_VERSION_CHECK))): \
CONFIG += sdk_no_version_check
QMAKE_MAC_SDK_MAJOR_MINOR_VERSION = $$replace(QMAKE_MAC_SDK_VERSION, "(\d+)(\.\d+)(\.\d+)?", \1\2)
QMAKE_MAC_SDK_MAJOR_MINOR_VERSION = $$replace(QMAKE_MAC_SDK_VERSION, "(\\d+)(\\.\\d+)(\\.\\d+)?", \\1\\2)
!sdk_no_version_check:!versionAtMost(QMAKE_MAC_SDK_MAJOR_MINOR_VERSION, $$QT_MAC_SDK_VERSION_MAX) {
warning("Qt has only been tested with version $$QT_MAC_SDK_VERSION_MAX"\

View File

@ -38,7 +38,7 @@ if(gcc|intel_icl|msvc):!rim_qcc:!uikit:!no_moc_predefs:if(!macos|count(QMAKE_APP
} else: error("Oops, I messed up")
moc_predefs.output = $$MOC_DIR/moc_predefs.h
moc_predefs.input = MOC_PREDEF_FILE
silent: moc_predefs.commands = @echo generating $$moc_predefs.output$$escape_expand(\n\t)@$$moc_predefs.commands
silent: moc_predefs.commands = @echo generating $$moc_predefs.output$$escape_expand(\\n\\t)@$$moc_predefs.commands
QMAKE_EXTRA_COMPILERS += moc_predefs
MOC_PREDEF_FILE = $$[QT_HOST_DATA/src]/mkspecs/features/data/dummy.cpp
}

View File

@ -89,6 +89,14 @@ clang {
greaterThan(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS_WARN_ON += -Wshift-overflow=2 -Wduplicated-cond
# GCC 7 has a lot of false positives relating to this, so disable completely
greaterThan(QT_GCC_MAJOR_VERSION, 6): QMAKE_CXXFLAGS_WARN_ON += -Wno-stringop-overflow
# GCC 9 has a lot of false positives relating to this, so disable completely
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-copy
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-redundant-move
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-format-overflow
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-init-list-lifetime
}
warnings_are_errors:warning_clean {
@ -127,7 +135,14 @@ warnings_are_errors:warning_clean {
# GCC 7 includes -Wimplicit-fallthrough in -Wextra, but Qt is not yet free of implicit fallthroughs.
greaterThan(QT_GCC_MAJOR_VERSION, 6): QMAKE_CXXFLAGS_WARN_ON += -Wno-error=implicit-fallthrough
# GCC 9 has a lot of false positives relating to this, so disable completely
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-deprecated-copy
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-redundant-move
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-format-overflow
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): QMAKE_CXXFLAGS_WARN_ON += -Wno-init-list-lifetime
# Work-around for bug https://code.google.com/p/android/issues/detail?id=58135
android: QMAKE_CXXFLAGS_WARN_ON += -Wno-error=literal-suffix
}

View File

@ -378,6 +378,7 @@ defineReplace(qtConfFindInPathList) {
defineReplace(qtConfFindInPath) {
ensurePathEnv()
equals(QMAKE_HOST.os, Windows):!contains(1, .*\\.exe): 1 = $${1}.exe
return($$qtConfFindInPathList($$1, $$2 $$QMAKE_PATH_ENV))
}
@ -1196,9 +1197,9 @@ defineTest(qtConfTest_files) {
for(i, $${1}.files._KEYS_) {
f = $$eval($${1}.files.$${i})
qtLog("Searching for file $${f}.")
contains(f, ".*\.h") {
contains(f, ".*\\.h") {
file = $$qtConfFindInPathList($$f, $$EXTRA_INCLUDEPATH $$QMAKE_DEFAULT_INCDIRS)
} else: contains(f, ".*\.(lib|so|a)") {
} else: contains(f, ".*\\.(lib|so|a)") {
file = $$qtConfFindInPathList($$f, $$EXTRA_LIBDIR $$QMAKE_DEFAULT_LIBDIRS)
} else {
# assume we're looking for an executable
@ -1399,7 +1400,7 @@ defineReplace(qtConfEvaluate) {
1 ~= s/$$escape_expand(\\n) */ /g
expr = $${1}
expr ~= s/&&/ && /g
expr ~= s/\|\|/ || /g
expr ~= s/\\|\\|/ || /g
expr ~= s/!/ ! /g
expr ~= s/\\(/ ( /g
expr ~= s/\\)/ ) /g
@ -1422,7 +1423,7 @@ defineReplace(qtConfEvaluateSingleExpression) {
} else: contains(e, "^'.*'$") {
# quoted literals
result = $$replace(e, "^'(.*)'$", "\\1")
} else: contains(e, "^tests\..*") {
} else: contains(e, "^tests\\..*") {
!qt_conf_tests_allowed: \
error("Expression '$${1}' refers to a test, which is not allowed at this stage of configuring.")
test = $$section(e, ".", 1, 1)
@ -1433,7 +1434,7 @@ defineReplace(qtConfEvaluateSingleExpression) {
error("Unknown test object $${test} in expression '$${1}'.")
qtRunSingleTest($$test)
result = $$eval($${currentConfig}.tests.$${test}.$${var})
} else: contains(e, "^libs\..*") {
} else: contains(e, "^libs\\..*") {
!qt_conf_tests_allowed: \
error("Expression '$${1}' refers to a library, which is not allowed at this stage of configuring.")
lib = $$section(e, ".", 1, 1)
@ -1446,7 +1447,7 @@ defineReplace(qtConfEvaluateSingleExpression) {
!defined($${currentConfig}.libraries.$${lib}.$${var}, var): \
var = sources.$$eval($${currentConfig}.libraries.$${lib}.source).$$var
result = $$eval($${currentConfig}.libraries.$${lib}.$${var})
} else: contains(e, "^features\..*") {
} else: contains(e, "^features\\..*") {
feature = $$section(e, ".", 1, 1)
var = $$section(e, ".", 2, -1)
isEmpty(var): \
@ -1470,33 +1471,33 @@ defineReplace(qtConfEvaluateSingleExpression) {
!qtConfCheckFeature($$feature): \
error("Expression '$$1' is accessing non-emitted feature $${feature}.")
result = $$eval($${currentConfig}.features.$${feature}.$${var})
} else: contains(e, "^config\..*") {
var = $$replace(e, "^config\.", "")
} else: contains(e, "^config\\..*") {
var = $$replace(e, "^config\\.", "")
result = false
contains(CONFIG, $$var): result = true
} else: contains(e, "^module\..*") {
var = $$replace(e, "^module\.", "")
} else: contains(e, "^module\\..*") {
var = $$replace(e, "^module\\.", "")
result = false
qtConfHaveModule($$var): result = true
} else: contains(e, "^arch\..*") {
var = $$replace(e, "^arch\.", "")
} else: contains(e, "^arch\\..*") {
var = $$replace(e, "^arch\\.", "")
result = false
isEmpty(QT_ARCH): \
qtConfCheckFeature(architecture)
contains(QT_ARCH, $$var): result = true
} else: contains(e, "^subarch\..*") {
var = $$replace(e, "^subarch\.", "")
} else: contains(e, "^subarch\\..*") {
var = $$replace(e, "^subarch\\.", "")
result = false
isEmpty(QT_ARCH): \
qtConfCheckFeature(architecture)
contains(QT_CPU_FEATURES.$$QT_ARCH, $$var): result = true
} else: contains(e, "^input\..*") {
} else: contains(e, "^input\\..*") {
result = $$eval(config.$$e)
} else: contains(e, "^var\..*") {
var = $$replace(e, "^var\.", "")
} else: contains(e, "^var\\..*") {
var = $$replace(e, "^var\\.", "")
result = $$eval($$var)
} else: contains(e, "^call\..*") {
call = $$replace(e, "^call\.", "qtConfFunc_")
} else: contains(e, "^call\\..*") {
call = $$replace(e, "^call\\.", "qtConfFunc_")
!defined($$call, replace): \
error("Call $$call referenced in expression '$${1}' does not exist")
eval(result = \$\$"$$call"())

View File

@ -18,7 +18,7 @@ isEmpty(qtver.value): error("No version for documentation specified.")
qtmver.name = QT_VER
qtmver.value = $$replace(qtver.value, ^(\\d+\\.\\d+).*$, \\1)
qtvertag.name = QT_VERSION_TAG
qtvertag.value = $$replace(qtver.value, \.,)
qtvertag.value = $$replace(qtver.value, \\.,)
qtdocs.name = QT_INSTALL_DOCS
qtdocs.value = $$[QT_INSTALL_DOCS/src]
builddir.name = BUILDDIR

View File

@ -239,6 +239,14 @@ headersclean:!internal_module {
gcc_ver = $${QT_GCC_MAJOR_VERSION}.$${QT_GCC_MINOR_VERSION}
versionAtLeast(gcc_ver, 4.5): hcleanFLAGS += -Wdouble-promotion
versionAtLeast(gcc_ver, 4.9): hcleanFLAGS += -Wfloat-conversion
# GCC 9 has a lot of false positives relating to this, so disable completely
greaterThan(QT_GCC_MAJOR_VERSION, 8): hcleanFLAGS += -Wno-deprecated-copy
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): hcleanFLAGS += -Wno-redundant-move
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): hcleanFLAGS += -Wno-format-overflow
# GCC 9 introduced this
greaterThan(QT_GCC_MAJOR_VERSION, 8): hcleanFLAGS += -Wno-init-list-lifetime
c++11 {
# only enabled for actual c++11 builds due to

View File

@ -1,7 +1,7 @@
msvc {
# -MD becomes -MT, -MDd becomes -MTd
QMAKE_CFLAGS ~= s,^-MD(d?)$,-MT\1,g
QMAKE_CXXFLAGS ~= s,^-MD(d?)$,-MT\1,g
QMAKE_CFLAGS ~= s,^-MD(d?)$,-MT\\1,g
QMAKE_CXXFLAGS ~= s,^-MD(d?)$,-MT\\1,g
} else: mingw {
QMAKE_LFLAGS += -static
}

View File

@ -3428,7 +3428,7 @@
\snippet code/doc_src_qmake-manual.pro 72
Like \l {qmake-cat}{$$cat()}, the \a mode argument takes \c blob, \c lines,
\c true, and \false as value. However, the legacy word splitting rules
\c true, and \c false as value. However, the legacy word splitting rules
(i.e. empty or \c true, and \c false) differ subtly.
If you pass \c stsvar, the command's exit status will be stored in that

View File

@ -14,6 +14,10 @@
#include "libANGLE/histogram_macros.h"
#include "third_party/trace_event/trace_event.h"
#ifndef QT_D3DCOMPILER_DLL
#define QT_D3DCOMPILER_DLL D3DCOMPILER_DLL
#endif
#if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
namespace
{
@ -130,6 +134,27 @@ gl::Error HLSLCompiler::ensureInitialized()
}
#endif // ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES
// Load the compiler DLL specified by the environment, or default to QT_D3DCOMPILER_DLL
const wchar_t *defaultCompiler = _wgetenv(L"QT_D3DCOMPILER_DLL");
if (!defaultCompiler)
defaultCompiler = QT_D3DCOMPILER_DLL;
const wchar_t *compilerDlls[] = {
defaultCompiler,
L"d3dcompiler_47.dll",
L"d3dcompiler_46.dll",
L"d3dcompiler_43.dll",
0
};
// Load the first available known compiler DLL
for (int i = 0; compilerDlls[i]; ++i)
{
mD3DCompilerModule = LoadLibrary(compilerDlls[i]);
if (mD3DCompilerModule)
break;
}
if (!mD3DCompilerModule)
{
// Load the version of the D3DCompiler DLL associated with the Direct3D version ANGLE was built with.

View File

@ -0,0 +1,59 @@
From dff9676c60c51fa7af0749e1cb54305f112183e3 Mon Sep 17 00:00:00 2001
From: Oliver Wolff <oliver.wolff@qt.io>
Date: Mon, 10 Dec 2018 08:33:14 +0100
Subject: [PATCH] ANGLE: Dynamically load D3D compiler from a list
If the default compiler cannot be found, load it from a list of DLL names,
including a non-versioned proxy DLL provided by Qt. On Desktop Windows,
the default compiler can also be specified by an environment variable,
QT_D3DCOMPILER_DLL.
---
src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp b/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp
index b38765070..5d47308d6 100644
--- a/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp
+++ b/src/3rdparty/angle/src/libANGLE/renderer/d3d/HLSLCompiler.cpp
@@ -14,6 +14,10 @@
#include "libANGLE/histogram_macros.h"
#include "third_party/trace_event/trace_event.h"
+#ifndef QT_D3DCOMPILER_DLL
+#define QT_D3DCOMPILER_DLL D3DCOMPILER_DLL
+#endif
+
#if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED
namespace
{
@@ -130,6 +134,27 @@ gl::Error HLSLCompiler::ensureInitialized()
}
#endif // ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES
+ // Load the compiler DLL specified by the environment, or default to QT_D3DCOMPILER_DLL
+ const wchar_t *defaultCompiler = _wgetenv(L"QT_D3DCOMPILER_DLL");
+ if (!defaultCompiler)
+ defaultCompiler = QT_D3DCOMPILER_DLL;
+
+ const wchar_t *compilerDlls[] = {
+ defaultCompiler,
+ L"d3dcompiler_47.dll",
+ L"d3dcompiler_46.dll",
+ L"d3dcompiler_43.dll",
+ 0
+ };
+
+ // Load the first available known compiler DLL
+ for (int i = 0; compilerDlls[i]; ++i)
+ {
+ mD3DCompilerModule = LoadLibrary(compilerDlls[i]);
+ if (mD3DCompilerModule)
+ break;
+ }
+
if (!mD3DCompilerModule)
{
// Load the version of the D3DCompiler DLL associated with the Direct3D version ANGLE was built with.
--
2.15.0.windows.1

View File

@ -179,7 +179,7 @@ QString QWindowsLocalCodec::convertToUnicodeCharByChar(const char *chars, int le
#else
QString s;
size_t size = mbstowcs(NULL, mb, length);
if (size < 0) {
if (size == size_t(-1)) {
Q_ASSERT("Error in CE TextCodec");
return QString();
}

View File

@ -79,7 +79,6 @@ QT_BEGIN_NAMESPACE
QCollator::QCollator(const QLocale &locale)
: d(new QCollatorPrivate(locale))
{
d->init();
}
/*!
@ -323,6 +322,8 @@ bool QCollator::ignorePunctuation() const
methods directly. But if the string is compared repeatedly (e.g. when sorting
a whole list of strings), it's usually faster to create the sort keys for each
string and then sort using the keys.
\note Not supported with the C (a.k.a. POSIX) locale on Darwin.
*/
/*!

View File

@ -55,6 +55,8 @@ QT_BEGIN_NAMESPACE
void QCollatorPrivate::init()
{
cleanup();
if (isC())
return;
UErrorCode status = U_ZERO_ERROR;
QByteArray name = QLocalePrivate::get(locale)->bcp47Name('_');
@ -140,6 +142,8 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const
{
if (d->dirty)
d->init();
if (d->isC())
return QCollatorSortKey(new QCollatorSortKeyPrivate(string.toUtf8()));
if (d->collator) {
QByteArray result(16 + string.size() + (string.size() >> 2), Qt::Uninitialized);

View File

@ -55,6 +55,15 @@ QT_BEGIN_NAMESPACE
void QCollatorPrivate::init()
{
cleanup();
/*
LocaleRefFromLocaleString() will accept "POSIX" as the locale name, but
the locale it produces (named "pos") doesn't implement the [A-Z] < [a-z]
behavior we expect of the C locale. We can use QStringView to get round
that for collation, but this leaves no way to do a sort key.
*/
if (isC())
return;
LocaleRef localeRef;
int rc = LocaleRefFromLocaleString(QLocalePrivate::get(locale)->bcp47Name().constData(), &localeRef);
if (rc != 0)
@ -92,6 +101,8 @@ int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) con
{
if (d->dirty)
d->init();
if (!d->collator)
return QStringView(s1, len1).compare(QStringView(s2, len2), caseSensitivity());
SInt32 result;
Boolean equivalent;
@ -104,6 +115,7 @@ int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) con
return 0;
return result < 0 ? -1 : 1;
}
int QCollator::compare(const QString &str1, const QString &str2) const
{
return compare(str1.constData(), str1.size(), str2.constData(), str2.size());
@ -118,6 +130,11 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const
{
if (d->dirty)
d->init();
if (!d->collator) {
// What should (or even *can*) we do here ? (See init()'s comment.)
qWarning("QCollator doesn't support sort keys for the C locale on Darwin");
return QCollatorSortKey(nullptr);
}
//Documentation recommends having it 5 times as big as the input
QVector<UCCollationValue> ret(string.size() * 5);
@ -136,6 +153,9 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const
int QCollatorSortKey::compare(const QCollatorSortKey &key) const
{
if (!d.data())
return 0;
SInt32 order;
UCCompareCollationKeys(d->m_key.data(), d->m_key.size(),
key.d->m_key.data(), key.d->m_key.size(),

View File

@ -110,6 +110,7 @@ public:
QCollatorPrivate(const QLocale &locale) : locale(locale) {}
~QCollatorPrivate() { cleanup(); }
bool isC() { return locale.language() == QLocale::C; }
void clear() {
cleanup();

View File

@ -48,10 +48,12 @@ QT_BEGIN_NAMESPACE
void QCollatorPrivate::init()
{
if (locale != QLocale())
qWarning("Only default locale supported with the posix collation implementation");
if (caseSensitivity != Qt::CaseSensitive)
qWarning("Case insensitive sorting unsupported in the posix collation implementation");
if (!isC()) {
if (locale != QLocale())
qWarning("Only C and default locale supported with the posix collation implementation");
if (caseSensitivity != Qt::CaseSensitive)
qWarning("Case insensitive sorting unsupported in the posix collation implementation");
}
if (numericMode)
qWarning("Numeric mode unsupported in the posix collation implementation");
if (ignorePunctuation)
@ -73,14 +75,16 @@ static void stringToWCharArray(QVarLengthArray<wchar_t> &ret, const QString &str
int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) const
{
QVarLengthArray<wchar_t> array1, array2;
stringToWCharArray(array1, QString(s1, len1));
stringToWCharArray(array2, QString(s2, len2));
return std::wcscoll(array1.constData(), array2.constData());
return compare(QString::fromRawData(s1, len1), QString::fromRawData(s2, len2));
}
int QCollator::compare(const QString &s1, const QString &s2) const
{
if (d->isC())
return s1.compare(s2, caseSensitivity());
if (d->dirty)
d->init();
QVarLengthArray<wchar_t> array1, array2;
stringToWCharArray(array1, s1);
stringToWCharArray(array2, s2);
@ -89,10 +93,7 @@ int QCollator::compare(const QString &s1, const QString &s2) const
int QCollator::compare(const QStringRef &s1, const QStringRef &s2) const
{
if (d->dirty)
d->init();
return compare(s1.constData(), s1.size(), s2.constData(), s2.size());
return compare(s1.toString(), s2.toString());
}
QCollatorSortKey QCollator::sortKey(const QString &string) const
@ -102,14 +103,18 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const
QVarLengthArray<wchar_t> original;
stringToWCharArray(original, string);
QVector<wchar_t> result(string.size());
size_t size = std::wcsxfrm(result.data(), original.constData(), string.size());
if (size > uint(result.size())) {
QVector<wchar_t> result(original.size());
if (d->isC()) {
std::copy(original.cbegin(), original.cend(), result.begin());
} else {
size_t size = std::wcsxfrm(result.data(), original.constData(), string.size());
if (size > uint(result.size())) {
result.resize(size+1);
size = std::wcsxfrm(result.data(), original.constData(), string.size());
}
result.resize(size+1);
size = std::wcsxfrm(result.data(), original.constData(), string.size());
result[size] = 0;
}
result.resize(size+1);
result[size] = 0;
return QCollatorSortKey(new QCollatorSortKeyPrivate(std::move(result)));
}

View File

@ -60,6 +60,8 @@ extern LCID qt_inIsoNametoLCID(const char *name);
void QCollatorPrivate::init()
{
collator = 0;
if (isC())
return;
#ifndef USE_COMPARESTRINGEX
localeID = qt_inIsoNametoLCID(QLocalePrivate::get(locale)->bcp47Name().constData());
@ -86,6 +88,9 @@ void QCollatorPrivate::cleanup()
int QCollator::compare(const QChar *s1, int len1, const QChar *s2, int len2) const
{
if (d->isC())
return QString::compare_helper(s1, len1, s2, len2, d->caseSensitivity);
if (d->dirty)
d->init();
@ -119,6 +124,8 @@ QCollatorSortKey QCollator::sortKey(const QString &string) const
{
if (d->dirty)
d->init();
if (d->isC())
return QCollatorSortKey(new QCollatorSortKeyPrivate(string));
#ifndef USE_COMPARESTRINGEX
int size = LCMapStringW(d->localeID, LCMAP_SORTKEY | d->collator,

View File

@ -102,7 +102,7 @@
}
bool event(QEvent *e) {
if (e->type == QEvent::UpdateRequest)
if (e->type() == QEvent::UpdateRequest)
render();
return QWindow::event(e);
}

View File

@ -422,8 +422,11 @@ static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int
if (info.bmiHeader.biHeight > 0) // Force top-down
info.bmiHeader.biHeight = -info.bmiHeader.biHeight;
info.bmiHeader.biCompression = BI_RGB; // Extract using no compression (can be BI_BITFIELD)
if (forceQuads)
size_t allocSize = info.bmiHeader.biSizeImage;
if (forceQuads) {
info.bmiHeader.biBitCount = 32;
allocSize = info.bmiHeader.biWidth * qAbs(info.bmiHeader.biHeight) * 4;
}
const QImage::Format imageFormat = imageFromWinHBITMAP_Format(info.bmiHeader, hbitmapFormat);
if (imageFormat == QImage::Format_Invalid) {
@ -431,7 +434,7 @@ static QImage imageFromWinHBITMAP_GetDiBits(HBITMAP bitmap, bool forceQuads, int
return QImage();
}
QScopedPointer<uchar> data(new uchar[info.bmiHeader.biSizeImage]);
QScopedArrayPointer<uchar> data(new uchar[allocSize]);
if (!GetDIBits(displayDc, bitmap, 0, qAbs(info.bmiHeader.biHeight), data.data(), &info, DIB_RGB_COLORS)) {
qErrnoWarning("%s: GetDIBits() failed to get data.", __FUNCTION__);
return QImage();

View File

@ -997,6 +997,10 @@ void QRasterPaintEnginePrivate::drawImage(const QPointF &pt,
{
if (alpha == 0 || !clip.isValid())
return;
if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom()))
return;
if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top()))
return;
Q_ASSERT(img.depth() >= 8);
@ -1063,6 +1067,10 @@ void QRasterPaintEnginePrivate::blitImage(const QPointF &pt,
{
if (!clip.isValid())
return;
if (pt.x() > qreal(clip.right()) || pt.y() > qreal(clip.bottom()))
return;
if ((pt.x() + img.width()) < qreal(clip.left()) || (pt.y() + img.height()) < qreal(clip.top()))
return;
Q_ASSERT(img.depth() >= 8);

View File

@ -45,6 +45,7 @@
#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
#if defined(Q_OS_MAC)
#include <qpa/qplatformfontdatabase.h>
#include <QtFontDatabaseSupport/private/qcoretextfontdatabase_p.h>
#else
#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
#endif
@ -67,6 +68,8 @@
QT_BEGIN_NAMESPACE
class QCoreTextFontEngine;
template <typename BaseEventDispatcher>
class QOffscreenEventDispatcher : public BaseEventDispatcher
{
@ -101,7 +104,7 @@ QOffscreenIntegration::QOffscreenIntegration()
{
#if defined(Q_OS_UNIX)
#if defined(Q_OS_MAC)
m_fontDatabase.reset(new QPlatformFontDatabase());
m_fontDatabase.reset(new QCoreTextFontDatabaseEngineFactory<QCoreTextFontEngine>);
#else
m_fontDatabase.reset(new QGenericUnixFontDatabase());
#endif

View File

@ -2866,7 +2866,11 @@ void QFileDialogPrivate::init(const QUrl &directory, const QString &nameFilter,
q->setFileMode(QFileDialog::AnyFile);
if (!nameFilter.isEmpty())
q->setNameFilter(nameFilter);
// QTBUG-70798, prevent the default blocking the restore logic.
const bool dontStoreDir = !directory.isValid() && !lastVisitedDir()->isValid();
q->setDirectoryUrl(workingDirectory(directory));
if (dontStoreDir)
lastVisitedDir()->clear();
if (directory.isLocalFile())
q->selectFile(initialSelection(directory));
else

View File

@ -1064,8 +1064,10 @@ QStyle *QApplication::style()
if (!QApplicationPrivate::styleOverride.isEmpty()) {
const QString style = QApplicationPrivate::styleOverride.toLower();
app_style = QStyleFactory::create(style);
if (!app_style)
qWarning("QApplication: invalid style override passed, ignoring it.");
if (Q_UNLIKELY(!app_style)) {
qWarning("QApplication: invalid style override passed, ignoring it.\n"
" Available styles: %s", qPrintable(QStyleFactory::keys().join(QLatin1String(", "))));
}
}
if (!app_style)
app_style = QStyleFactory::create(QApplicationPrivate::desktopStyleKey());

View File

@ -93,7 +93,7 @@ void tst_QCollator::compare_data()
QTest::addColumn<int>("caseInsensitiveResult");
QTest::addColumn<bool>("numericMode");
QTest::addColumn<bool>("ignorePunctuation");
QTest::addColumn<int>("punctuationResult");
QTest::addColumn<int>("punctuationResult"); // Test ignores punctuation *and case*
/*
It's hard to test English, because it's treated differently
@ -169,8 +169,12 @@ void tst_QCollator::compare_data()
QTest::newRow("french6") << QString("fr_FR") << QString("Test 9") << QString("Test_19") << -1 << -1 << true << true << -1;
QTest::newRow("french7") << QString("fr_FR") << QString("test_19") << QString("test 19") << 1 << 1 << true << false << 1;
QTest::newRow("french8") << QString("fr_FR") << QString("test.19") << QString("test,19") << 1 << 1 << true << true << 0;
}
// C locale: case sensitive [A-Z] < [a-z] but case insensitive [Aa] < [Bb] <...< [Zz]
const QString C = QStringLiteral("C");
QTest::newRow("C:ABBA:AaaA") << C << QStringLiteral("ABBA") << QStringLiteral("AaaA") << -1 << 1 << false << false << 1;
QTest::newRow("C:AZa:aAZ") << C << QStringLiteral("AZa") << QStringLiteral("aAZ") << -1 << 1 << false << false << 1;
}
void tst_QCollator::compare()
{
@ -184,6 +188,10 @@ void tst_QCollator::compare()
QFETCH(int, punctuationResult);
QCollator collator(locale);
// Need to canonicalize sign to -1, 0 or 1, as .compare() can produce any -ve for <, any +ve for >.
auto asSign = [](int compared) {
return compared < 0 ? -1 : compared > 0 ? 1 : 0;
};
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
if (collator.locale() != QLocale())
@ -193,12 +201,12 @@ void tst_QCollator::compare()
if (numericMode)
collator.setNumericMode(true);
QCOMPARE(collator.compare(s1, s2), result);
QCOMPARE(asSign(collator.compare(s1, s2)), result);
collator.setCaseSensitivity(Qt::CaseInsensitive);
QCOMPARE(collator.compare(s1, s2), caseInsensitiveResult);
QCOMPARE(asSign(collator.compare(s1, s2)), caseInsensitiveResult);
#if !QT_CONFIG(iconv)
collator.setIgnorePunctuation(ignorePunctuation);
QCOMPARE(collator.compare(s1, s2), punctuationResult);
QCOMPARE(asSign(collator.compare(s1, s2)), punctuationResult);
#endif
}

View File

@ -233,6 +233,7 @@ private slots:
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
void toWinHBITMAP_data();
void toWinHBITMAP();
void fromMonoHBITMAP();
#endif // Q_OS_WIN && !Q_OS_WINRT
private:
@ -3635,6 +3636,19 @@ void tst_QImage::toWinHBITMAP()
DeleteDC(bitmapDc);
ReleaseDC(0, displayDc);
}
void tst_QImage::fromMonoHBITMAP() // QTBUG-72343, corruption for mono bitmaps
{
enum : int { width = 32, height = 32, size = width * height / 8 }; // 32x32 mono bitmap
char bitmapData[size];
memset(bitmapData, 0, size);
const HBITMAP hbitmap = CreateBitmap(width, height, /* planes */ 1, /* bitcount */ 1, bitmapData);
const QImage image = qt_imageFromWinHBITMAP(hbitmap);
QCOMPARE(image.size(), QSize(width, height));
QCOMPARE(image.scanLine(0)[0], 0u);
DeleteObject(hbitmap);
}
#endif // Q_OS_WIN && !Q_OS_WINRT
QTEST_GUILESS_MAIN(tst_QImage)

View File

@ -299,6 +299,8 @@ private slots:
void fillPolygon();
void drawImageAtPointF();
private:
void fillData();
void setPenColor(QPainter& p);
@ -5292,6 +5294,20 @@ void tst_QPainter::fillPolygon()
}
}
void tst_QPainter::drawImageAtPointF()
{
// Just test we do not crash
QImage image1(10, 10, QImage::Format_RGB32);
QImage image2(200, 200, QImage::Format_RGB32);
QPainter paint(&image2);
paint.setClipRect(97, 46, 14, 14);
paint.setCompositionMode(QPainter::CompositionMode_Source);
paint.drawImage(QPointF(96, std::numeric_limits<int>::max()), image1);
paint.drawImage(QPointF(std::numeric_limits<int>::min(), 48), image1);
paint.end();
}
QTEST_MAIN(tst_QPainter)
#include "tst_qpainter.moc"

View File

@ -210,9 +210,9 @@ void tst_QMessageBox::button()
{
QMessageBox msgBox;
msgBox.addButton("retry", QMessageBox::DestructiveRole);
QVERIFY(msgBox.button(QMessageBox::Ok) == 0); // not added yet
QVERIFY(msgBox.button(QMessageBox::Ok) == nullptr); // not added yet
QPushButton *b1 = msgBox.addButton(QMessageBox::Ok);
QCOMPARE(msgBox.button(QMessageBox::Ok), (QAbstractButton *)b1); // just added
QCOMPARE(msgBox.button(QMessageBox::Ok), static_cast<QAbstractButton *>(b1)); // just added
QCOMPARE(msgBox.standardButton(b1), QMessageBox::Ok);
msgBox.addButton(QMessageBox::Cancel);
QCOMPARE(msgBox.standardButtons(), QMessageBox::Ok | QMessageBox::Cancel);
@ -220,12 +220,12 @@ void tst_QMessageBox::button()
// remove the cancel, should not exist anymore
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
QVERIFY(!msgBox.button(QMessageBox::Cancel));
QVERIFY(msgBox.button(QMessageBox::Yes) != 0);
QVERIFY(msgBox.button(QMessageBox::Yes) != nullptr);
// should not crash
QPushButton *b4 = new QPushButton;
msgBox.addButton(b4, QMessageBox::DestructiveRole);
msgBox.addButton(0, QMessageBox::ActionRole);
msgBox.addButton(nullptr, QMessageBox::ActionRole);
}
void tst_QMessageBox::defaultButton()
@ -237,7 +237,7 @@ void tst_QMessageBox::defaultButton()
QVERIFY(!msgBox.defaultButton());
QPushButton pushButton;
msgBox.setDefaultButton(&pushButton);
QVERIFY(msgBox.defaultButton() == 0); // we have not added it yet
QVERIFY(msgBox.defaultButton() == nullptr); // we have not added it yet
QPushButton *retryButton = msgBox.addButton(QMessageBox::Retry);
msgBox.setDefaultButton(retryButton);
QCOMPARE(msgBox.defaultButton(), retryButton);
@ -248,11 +248,11 @@ void tst_QMessageBox::defaultButton()
closeHelper.start(Qt::Key_Enter, &msgBox);
msgBox.exec();
QCOMPARE(msgBox.clickedButton(), (QAbstractButton *)retryButton);
QCOMPARE(msgBox.clickedButton(), static_cast<QAbstractButton *>(retryButton));
QAbstractButton *okButton = msgBox.button(QMessageBox::Ok);
msgBox.setDefaultButton(QMessageBox::Ok);
QCOMPARE(msgBox.defaultButton(), (QPushButton *)okButton);
QCOMPARE(msgBox.defaultButton(), static_cast<QPushButton *>(okButton));
closeHelper.start(Qt::Key_Enter, &msgBox);
msgBox.exec();
QCOMPARE(msgBox.clickedButton(), okButton);
@ -260,7 +260,7 @@ void tst_QMessageBox::defaultButton()
QCOMPARE(msgBox.defaultButton(), okButton);
msgBox.removeButton(okButton);
delete okButton;
okButton = 0;
okButton = nullptr;
QVERIFY(!msgBox.defaultButton());
msgBox.setDefaultButton(QMessageBox::Ok);
QVERIFY(!msgBox.defaultButton());
@ -287,7 +287,7 @@ void tst_QMessageBox::escapeButton()
QVERIFY(msgBox.clickedButton() == msgBox.button(QMessageBox::Cancel)); // auto detected (cancel)
msgBox.setEscapeButton(retryButton);
QCOMPARE(msgBox.escapeButton(), (QAbstractButton *)retryButton);
QCOMPARE(msgBox.escapeButton(), static_cast<QAbstractButton *>(retryButton));
// with escape
closeHelper.start(Qt::Key_Escape, &msgBox);
@ -297,7 +297,7 @@ void tst_QMessageBox::escapeButton()
// with close
closeHelper.start(ExecCloseHelper::CloseWindow, &msgBox);
msgBox.exec();
QCOMPARE(msgBox.clickedButton(), (QAbstractButton *)retryButton);
QCOMPARE(msgBox.clickedButton(), static_cast<QAbstractButton *>(retryButton));
QAbstractButton *okButton = msgBox.button(QMessageBox::Ok);
msgBox.setEscapeButton(QMessageBox::Ok);
@ -309,7 +309,7 @@ void tst_QMessageBox::escapeButton()
QCOMPARE(msgBox.escapeButton(), okButton);
msgBox.removeButton(okButton);
delete okButton;
okButton = 0;
okButton = nullptr;
QVERIFY(!msgBox.escapeButton());
msgBox.setEscapeButton(QMessageBox::Ok);
QVERIFY(!msgBox.escapeButton());
@ -353,28 +353,28 @@ void tst_QMessageBox::statics()
ExecCloseHelper closeHelper;
for (int i = 0; i < 4; i++) {
closeHelper.start(Qt::Key_Escape);
QMessageBox::StandardButton sb = (*statics[i])(0, "caption",
QMessageBox::StandardButton sb = (*statics[i])(nullptr, "caption",
"text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help | QMessageBox::Cancel,
QMessageBox::NoButton);
QCOMPARE(sb, QMessageBox::Cancel);
QVERIFY(closeHelper.done());
closeHelper.start(ExecCloseHelper::CloseWindow);
sb = (*statics[i])(0, "caption",
sb = (*statics[i])(nullptr, "caption",
"text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help | QMessageBox::Cancel,
QMessageBox::NoButton);
QCOMPARE(sb, QMessageBox::Cancel);
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Enter);
sb = (*statics[i])(0, "caption",
sb = (*statics[i])(nullptr, "caption",
"text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help,
QMessageBox::Yes);
QCOMPARE(sb, QMessageBox::Yes);
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Enter);
sb = (*statics[i])(0, "caption",
sb = (*statics[i])(nullptr, "caption",
"text", QMessageBox::Yes | QMessageBox::No | QMessageBox::Help,
QMessageBox::No);
QCOMPARE(sb, QMessageBox::No);
@ -400,7 +400,7 @@ void tst_QMessageBox::about()
{
ExecCloseHelper closeHelper;
closeHelper.start(Qt::Key_Escape);
QMessageBox::about(0, "Caption", "This is an auto test");
QMessageBox::about(nullptr, "Caption", "This is an auto test");
// On Mac, about and aboutQt are not modal, so we need to
// explicitly run the event loop
#ifdef Q_OS_MAC
@ -412,7 +412,7 @@ void tst_QMessageBox::about()
const int keyToSend = Qt::Key_Enter;
closeHelper.start(keyToSend);
QMessageBox::aboutQt(0, "Caption");
QMessageBox::aboutQt(nullptr, "Caption");
#ifdef Q_OS_MAC
QTRY_VERIFY(closeHelper.done());
#else
@ -427,7 +427,7 @@ void tst_QMessageBox::staticSourceCompat()
// source compat tests for < 4.2
ExecCloseHelper closeHelper;
closeHelper.start(Qt::Key_Enter);
ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes, QMessageBox::No);
ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes, QMessageBox::No);
int expectedButton = int(QMessageBox::Yes);
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
const int dialogButtonBoxLayout = theme->themeHint(QPlatformTheme::DialogButtonBoxLayout).toInt();
@ -439,39 +439,39 @@ void tst_QMessageBox::staticSourceCompat()
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Enter);
ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No);
ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No);
QCOMPARE(ret, int(QMessageBox::Yes));
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Enter);
ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes, QMessageBox::No | QMessageBox::Default);
ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes, QMessageBox::No | QMessageBox::Default);
QCOMPARE(ret, int(QMessageBox::No));
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Enter);
ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape);
ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes | QMessageBox::Default, QMessageBox::No | QMessageBox::Escape);
QCOMPARE(ret, int(QMessageBox::Yes));
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Enter);
ret = QMessageBox::information(0, "title", "text", QMessageBox::Yes | QMessageBox::Escape, QMessageBox::No | QMessageBox::Default);
ret = QMessageBox::information(nullptr, "title", "text", QMessageBox::Yes | QMessageBox::Escape, QMessageBox::No | QMessageBox::Default);
QCOMPARE(ret, int(QMessageBox::No));
QVERIFY(closeHelper.done());
// the button text versions
closeHelper.start(Qt::Key_Enter);
ret = QMessageBox::information(0, "title", "text", "Yes", "No", QString(), 1);
ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 1);
QCOMPARE(ret, 1);
QVERIFY(closeHelper.done());
if (0) { // don't run these tests since the dialog won't close!
closeHelper.start(Qt::Key_Escape);
ret = QMessageBox::information(0, "title", "text", "Yes", "No", QString(), 1);
ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 1);
QCOMPARE(ret, -1);
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Escape);
ret = QMessageBox::information(0, "title", "text", "Yes", "No", QString(), 0, 1);
ret = QMessageBox::information(nullptr, "title", "text", "Yes", "No", QString(), 0, 1);
QCOMPARE(ret, 1);
QVERIFY(closeHelper.done());
}
@ -618,9 +618,8 @@ void tst_QMessageBox::detailsButtonText()
QDialogButtonBox* bb = box.findChild<QDialogButtonBox*>("qt_msgbox_buttonbox");
QVERIFY(bb); //get the detail button
QList<QAbstractButton *> list = bb->buttons();
QAbstractButton* btn = NULL;
foreach(btn, list) {
auto list = bb->buttons();
for (auto btn : list) {
if (btn && (btn->inherits("QPushButton"))) {
if (btn->text().remove(QLatin1Char('&')) != QMessageBox::tr("OK")
&& btn->text() != QMessageBox::tr("Show Details...")) {
@ -640,12 +639,12 @@ void tst_QMessageBox::expandDetails_QTBUG_32473()
QDialogButtonBox* bb = box.findChild<QDialogButtonBox*>("qt_msgbox_buttonbox");
QVERIFY(bb);
QList<QAbstractButton *> list = bb->buttons();
QAbstractButton* moreButton = NULL;
foreach (QAbstractButton* btn, list)
if (btn && bb->buttonRole(btn) == QDialogButtonBox::ActionRole)
moreButton = btn;
QVERIFY(moreButton);
auto list = bb->buttons();
auto it = std::find_if(list.rbegin(), list.rend(), [&](QAbstractButton *btn) {
return btn && bb->buttonRole(btn) == QDialogButtonBox::ActionRole; });
QVERIFY(it != list.rend());
auto moreButton = *it;
QVERIFY(QTest::qWaitForWindowExposed(&box));
QRect geom = box.geometry();
box.resized = false;
@ -667,19 +666,19 @@ void tst_QMessageBox::incorrectDefaultButton()
closeHelper.start(Qt::Key_Escape);
//Do not crash here
QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
QMessageBox::question( 0, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save );
QMessageBox::question(nullptr, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save);
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Escape);
QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
QMessageBox::question( 0, "", "I've been hit!",QFlag(QMessageBox::Ok | QMessageBox::Cancel),QMessageBox::Save );
QMessageBox::question(nullptr, "", "I've been hit!",QFlag(QMessageBox::Ok | QMessageBox::Cancel),QMessageBox::Save);
QVERIFY(closeHelper.done());
closeHelper.start(Qt::Key_Escape);
QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
QTest::ignoreMessage(QtWarningMsg, "QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
//do not crash here -> call old function of QMessageBox in this case
QMessageBox::question( 0, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save | QMessageBox::Cancel,QMessageBox::Ok);
QMessageBox::question(nullptr, "", "I've been hit!",QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Save | QMessageBox::Cancel,QMessageBox::Ok);
QVERIFY(closeHelper.done());
}

View File

@ -101,7 +101,9 @@ public:
const int ascent = fontMetrics().ascent();
p.setPen(Qt::magenta);
QPen metricsPen(Qt::magenta, 1.0);
metricsPen.setCosmetic(true);
p.setPen(metricsPen);
p.drawLine(QPoint(0, ascent), QPoint(width(), ascent));
p.end();
@ -152,13 +154,22 @@ public:
if (font().styleStrategy() & QFont::NoAntialias)
CGContextSetShouldAntialias(ctx, false);
// Retain count already tracked by QMacCGContext above
NSGraphicsContext.currentContext = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES];
[text().toNSString() drawAtPoint:CGPointZero withAttributes:@{
NSFontAttributeName : (NSFont *)fontEngine->handle(),
NSForegroundColorAttributeName : nsColor
}];
NSGraphicsContext.currentContext = nil;
// Flip to what CT expects
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -height());
// Set up baseline
CGContextSetTextPosition(ctx, 0, height() - fontMetrics().ascent());
auto *attributedString = [[NSAttributedString alloc] initWithString:text().toNSString()
attributes:@{
NSFontAttributeName : (NSFont *)fontEngine->handle(),
NSForegroundColorAttributeName : nsColor
}
];
QCFType<CTLineRef> line = CTLineCreateWithAttributedString(CFAttributedStringRef([attributedString autorelease]));
CTLineDraw(line, ctx);
#endif
}