Merge remote-tracking branch 'origin/5.12' into 5.13
Conflicts: qmake/generators/makefile.cpp src/plugins/platforms/cocoa/qcocoaintegration.h src/plugins/platforms/cocoa/qcocoaintegration.mm Done-With: Jörg Bornemann <joerg.bornemann@qt.io> Change-Id: I5a61e161784cc6f947abe370aab8f2971a9cbe78
This commit is contained in:
commit
9967a011ea
@ -704,6 +704,7 @@
|
||||
},
|
||||
"optimize_debug": {
|
||||
"label": "Optimize debug build",
|
||||
"autoDetect": "!features.developer-build",
|
||||
"condition": "!config.msvc && !config.clang && (features.debug || features.debug_and_release) && tests.optimize_debug",
|
||||
"output": [ "privateConfig" ]
|
||||
},
|
||||
|
@ -20,7 +20,7 @@
|
||||
# output check that "EGLFS GBM .......... yes" is present, otherwise
|
||||
# eglfs will not be functional.
|
||||
#
|
||||
# ./configure -release -opengl es2 -device linux-rpi3-vc4-g++ \
|
||||
# ./configure -release -opengl es2 -device linux-rasp-pi3-vc4-g++ \
|
||||
# -device-option CROSS_COMPILE=~/raspbian/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf- \
|
||||
# -sysroot ~/raspbian/sysroot \
|
||||
# -prefix /usr/local/qt5pi -extprefix ~/raspbian/qt5pi -hostprefix ~/raspbian/qt5 \
|
||||
|
@ -37,8 +37,10 @@ intel_icl {
|
||||
|
||||
QMAKE_DIR_REPLACE_SANE = PRECOMPILED_DIR OBJECTS_DIR MOC_DIR RCC_DIR UI_DIR
|
||||
|
||||
load(qt_prefix_build_check)
|
||||
|
||||
# force_independent can be set externally. prefix_build not.
|
||||
!exists($$[QT_HOST_DATA]/.qmake.cache): \
|
||||
qtIsPrefixBuild($$[QT_HOST_DATA]): \
|
||||
CONFIG += prefix_build force_independent
|
||||
|
||||
!build_pass:!isEmpty(_QMAKE_SUPER_CACHE_):force_independent {
|
||||
|
@ -2510,17 +2510,19 @@ logn("Configure summary:")
|
||||
logn()
|
||||
qtConfPrintReport()
|
||||
|
||||
load(qt_prefix_build_check)
|
||||
|
||||
# final notes for the user
|
||||
logn()
|
||||
logn("Qt is now configured for building. Just run '$$QMAKE_MAKE_NAME'.")
|
||||
pfx = $$[QT_INSTALL_PREFIX]
|
||||
exists($$pfx/.qmake.cache) {
|
||||
qtIsPrefixBuild($$pfx) {
|
||||
logn("Once everything is built, you must run '$$QMAKE_MAKE_NAME install'.")
|
||||
logn("Qt will be installed into '$$system_path($$pfx)'.")
|
||||
} else {
|
||||
logn("Once everything is built, Qt is installed.")
|
||||
logn("You should NOT run '$$QMAKE_MAKE_NAME install'.")
|
||||
logn("Note that this build cannot be deployed to other machines or devices.")
|
||||
} else {
|
||||
logn("Once everything is built, you must run '$$QMAKE_MAKE_NAME install'.")
|
||||
logn("Qt will be installed into '$$system_path($$pfx)'.")
|
||||
}
|
||||
logn()
|
||||
logn("Prior to reconfiguration, make sure you remove any leftovers from")
|
||||
|
@ -82,6 +82,11 @@ header_module {
|
||||
CONFIG += force_qt # Needed for the headers_clean tests.
|
||||
!lib_bundle: \
|
||||
CONFIG += qt_no_install_library
|
||||
|
||||
# Allow creation of .prl, .la and .pc files.
|
||||
target.path = $$[QT_INSTALL_LIBS]
|
||||
target.CONFIG += dummy_install
|
||||
INSTALLS += target
|
||||
} else {
|
||||
TEMPLATE = lib
|
||||
}
|
||||
|
21
mkspecs/features/qt_prefix_build_check.prf
Normal file
21
mkspecs/features/qt_prefix_build_check.prf
Normal file
@ -0,0 +1,21 @@
|
||||
#
|
||||
# W A R N I N G
|
||||
# -------------
|
||||
#
|
||||
# This file is not part of the Qt API. It exists purely as an
|
||||
# implementation detail. It may change from version to version
|
||||
# without notice, or even be removed.
|
||||
#
|
||||
# We mean it.
|
||||
#
|
||||
|
||||
defineTest(qtIsPrefixBuild) {
|
||||
prefixdir = $$1
|
||||
# qtbase non-prefix build?
|
||||
exists($$prefixdir/.qmake.cache): \
|
||||
return(false)
|
||||
# top-level non-prefix build?
|
||||
contains(prefixdir, .*/qtbase):exists($$dirname(prefixdir)/.qmake.super): \
|
||||
return(false)
|
||||
return(true)
|
||||
}
|
@ -518,7 +518,6 @@ bool
|
||||
ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
|
||||
{
|
||||
ProStringList tmp;
|
||||
bool did_preprocess = false;
|
||||
|
||||
//HEADER
|
||||
const int pbVersion = pbuilderVersion();
|
||||
@ -736,7 +735,6 @@ ProjectBuilderMakefileGenerator::writeMakeParts(QTextStream &t)
|
||||
QFile mkf(mkfile);
|
||||
if(mkf.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
writingUnixMakefileGenerator = true;
|
||||
did_preprocess = true;
|
||||
debug_msg(1, "pbuilder: Creating file: %s", mkfile.toLatin1().constData());
|
||||
QTextStream mkt(&mkf);
|
||||
writeHeader(mkt);
|
||||
|
@ -900,7 +900,7 @@ MakefileGenerator::processPrlFile(QString &file, bool baseOnly)
|
||||
|
||||
bool
|
||||
MakefileGenerator::processPrlFileBase(QString &origFile, const QStringRef &origName,
|
||||
const QStringRef &fixedBase, int slashOff)
|
||||
const QStringRef &fixedBase, int /*slashOff*/)
|
||||
{
|
||||
return processPrlFileCore(origFile, origName, fixedBase + Option::prl_ext);
|
||||
}
|
||||
@ -1120,7 +1120,8 @@ MakefileGenerator::writePrlFile()
|
||||
&& project->values("QMAKE_FAILED_REQUIREMENTS").isEmpty()
|
||||
&& project->isActiveConfig("create_prl")
|
||||
&& (project->first("TEMPLATE") == "lib"
|
||||
|| project->first("TEMPLATE") == "vclib")
|
||||
|| project->first("TEMPLATE") == "vclib"
|
||||
|| project->first("TEMPLATE") == "aux")
|
||||
&& (!project->isActiveConfig("plugin") || project->isActiveConfig("static"))) { //write prl file
|
||||
QString local_prl = prlFileName();
|
||||
QString prl = fileFixify(local_prl);
|
||||
@ -2400,8 +2401,15 @@ MakefileGenerator::findSubDirsSubTargets() const
|
||||
st->profile = file;
|
||||
}
|
||||
} else {
|
||||
if(!file.isEmpty() && !project->isActiveConfig("subdir_first_pro"))
|
||||
st->profile = file.section(Option::dir_sep, -1) + Option::pro_ext;
|
||||
if (!file.isEmpty() && !project->isActiveConfig("subdir_first_pro")) {
|
||||
const QString baseName = file.section(Option::dir_sep, -1);
|
||||
if (baseName.isEmpty()) {
|
||||
warn_msg(WarnLogic, "Ignoring invalid SUBDIRS entry %s",
|
||||
subdirs[subdir].toLatin1().constData());
|
||||
continue;
|
||||
}
|
||||
st->profile = baseName + Option::pro_ext;
|
||||
}
|
||||
st->in_directory = file;
|
||||
}
|
||||
while(st->in_directory.endsWith(Option::dir_sep))
|
||||
@ -3352,42 +3360,44 @@ MakefileGenerator::writePkgConfigFile()
|
||||
if (!version.isEmpty())
|
||||
t << "Version: " << version << endl;
|
||||
|
||||
// libs
|
||||
t << "Libs: ";
|
||||
QString pkgConfiglibName;
|
||||
if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")) {
|
||||
if (libDir != QLatin1String("/Library/Frameworks"))
|
||||
t << "-F${libdir} ";
|
||||
ProString bundle;
|
||||
if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
|
||||
bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
|
||||
else
|
||||
bundle = project->first("TARGET");
|
||||
int suffix = bundle.lastIndexOf(".framework");
|
||||
if (suffix != -1)
|
||||
bundle = bundle.left(suffix);
|
||||
t << "-framework ";
|
||||
pkgConfiglibName = bundle.toQString();
|
||||
} else {
|
||||
if (!project->values("QMAKE_DEFAULT_LIBDIRS").contains(libDir))
|
||||
t << "-L${libdir} ";
|
||||
pkgConfiglibName = "-l" + project->first("QMAKE_ORIG_TARGET");
|
||||
if (project->isActiveConfig("shared"))
|
||||
pkgConfiglibName += project->first("TARGET_VERSION_EXT").toQString();
|
||||
}
|
||||
t << shellQuote(pkgConfiglibName) << " \n";
|
||||
if (project->first("TEMPLATE") == "lib") {
|
||||
// libs
|
||||
t << "Libs: ";
|
||||
QString pkgConfiglibName;
|
||||
if (target_mode == TARG_MAC_MODE && project->isActiveConfig("lib_bundle")) {
|
||||
if (libDir != QLatin1String("/Library/Frameworks"))
|
||||
t << "-F${libdir} ";
|
||||
ProString bundle;
|
||||
if (!project->isEmpty("QMAKE_FRAMEWORK_BUNDLE_NAME"))
|
||||
bundle = project->first("QMAKE_FRAMEWORK_BUNDLE_NAME");
|
||||
else
|
||||
bundle = project->first("TARGET");
|
||||
int suffix = bundle.lastIndexOf(".framework");
|
||||
if (suffix != -1)
|
||||
bundle = bundle.left(suffix);
|
||||
t << "-framework ";
|
||||
pkgConfiglibName = bundle.toQString();
|
||||
} else {
|
||||
if (!project->values("QMAKE_DEFAULT_LIBDIRS").contains(libDir))
|
||||
t << "-L${libdir} ";
|
||||
pkgConfiglibName = "-l" + project->first("QMAKE_ORIG_TARGET");
|
||||
if (project->isActiveConfig("shared"))
|
||||
pkgConfiglibName += project->first("TARGET_VERSION_EXT").toQString();
|
||||
}
|
||||
t << shellQuote(pkgConfiglibName) << " \n";
|
||||
|
||||
if (project->isActiveConfig("staticlib")) {
|
||||
ProStringList libs;
|
||||
libs << "LIBS"; // FIXME: this should not be conditional on staticlib
|
||||
libs << "LIBS_PRIVATE";
|
||||
libs << "QMAKE_LIBS"; // FIXME: this should not be conditional on staticlib
|
||||
libs << "QMAKE_LIBS_PRIVATE";
|
||||
libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread?
|
||||
t << "Libs.private:";
|
||||
for (ProStringList::ConstIterator it = libs.cbegin(); it != libs.cend(); ++it)
|
||||
t << ' ' << fixLibFlags((*it).toKey()).join(' ');
|
||||
t << endl;
|
||||
if (project->isActiveConfig("staticlib")) {
|
||||
ProStringList libs;
|
||||
libs << "LIBS"; // FIXME: this should not be conditional on staticlib
|
||||
libs << "LIBS_PRIVATE";
|
||||
libs << "QMAKE_LIBS"; // FIXME: this should not be conditional on staticlib
|
||||
libs << "QMAKE_LIBS_PRIVATE";
|
||||
libs << "QMAKE_LFLAGS_THREAD"; //not sure about this one, but what about things like -pthread?
|
||||
t << "Libs.private:";
|
||||
for (ProStringList::ConstIterator it = libs.cbegin(); it != libs.cend(); ++it)
|
||||
t << ' ' << fixLibFlags((*it).toKey()).join(' ');
|
||||
t << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// flags
|
||||
@ -3430,19 +3440,23 @@ QString MakefileGenerator::installMetaFile(const ProKey &replace_rule, const QSt
|
||||
|| project->isActiveConfig("no_sed_meta_install")) {
|
||||
ret += "-$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst);
|
||||
} else {
|
||||
ret += "-$(SED)";
|
||||
QString sedargs;
|
||||
const ProStringList &replace_rules = project->values(replace_rule);
|
||||
for (int r = 0; r < replace_rules.size(); ++r) {
|
||||
const ProString match = project->first(ProKey(replace_rules.at(r) + ".match")),
|
||||
replace = project->first(ProKey(replace_rules.at(r) + ".replace"));
|
||||
if (!match.isEmpty() /*&& match != replace*/) {
|
||||
ret += " -e " + shellQuote("s," + match + "," + replace + ",g");
|
||||
sedargs += " -e " + shellQuote("s," + match + "," + replace + ",g");
|
||||
if (isWindowsShell() && project->first(ProKey(replace_rules.at(r) + ".CONFIG")).contains("path"))
|
||||
ret += " -e " + shellQuote("s," + windowsifyPath(match.toQString())
|
||||
sedargs += " -e " + shellQuote("s," + windowsifyPath(match.toQString())
|
||||
+ "," + windowsifyPath(replace.toQString()) + ",gi");
|
||||
}
|
||||
}
|
||||
ret += ' ' + escapeFilePath(src) + " > " + escapeFilePath(dst);
|
||||
if (sedargs.isEmpty()) {
|
||||
ret += "-$(INSTALL_FILE) " + escapeFilePath(src) + ' ' + escapeFilePath(dst);
|
||||
} else {
|
||||
ret += "-$(SED) " + sedargs + ' ' + escapeFilePath(src) + " > " + escapeFilePath(dst);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -815,7 +815,7 @@ bool QMakeSourceFileInfo::findDeps(SourceFile *file)
|
||||
break;
|
||||
}
|
||||
cpp_state = InCode;
|
||||
// ... and fall through to handle buffer[x] as such.
|
||||
Q_FALLTHROUGH(); // to handle buffer[x] as such.
|
||||
case InCode:
|
||||
// matching quotes (string literals and character literals)
|
||||
if (buffer[x] == '\'' || buffer[x] == '"') {
|
||||
|
@ -43,7 +43,7 @@ protected:
|
||||
void init() override;
|
||||
bool writeMakefile(QTextStream &) override;
|
||||
|
||||
QString escapeFilePath(const QString &path) const override { Q_ASSERT(false); return QString(); }
|
||||
QString escapeFilePath(const QString &) const override { Q_ASSERT(false); return QString(); }
|
||||
|
||||
public:
|
||||
ProjectGenerator();
|
||||
|
@ -726,7 +726,7 @@ UnixMakefileGenerator::defaultInstall(const QString &t)
|
||||
}
|
||||
}
|
||||
}
|
||||
if(project->first("TEMPLATE") == "lib") {
|
||||
if (isAux || project->first("TEMPLATE") == "lib") {
|
||||
QStringList types;
|
||||
types << "prl" << "libtool" << "pkgconfig";
|
||||
for(int i = 0; i < types.size(); ++i) {
|
||||
|
@ -48,12 +48,15 @@ void
|
||||
UnixMakefileGenerator::writePrlFile(QTextStream &t)
|
||||
{
|
||||
MakefileGenerator::writePrlFile(t);
|
||||
const ProString tmplt = project->first("TEMPLATE");
|
||||
if (tmplt != "lib" && tmplt != "aux")
|
||||
return;
|
||||
// libtool support
|
||||
if(project->isActiveConfig("create_libtool") && project->first("TEMPLATE") == "lib") { //write .la
|
||||
if (project->isActiveConfig("create_libtool")) {
|
||||
writeLibtoolFile();
|
||||
}
|
||||
// pkg-config support
|
||||
if(project->isActiveConfig("create_pc") && project->first("TEMPLATE") == "lib")
|
||||
if (project->isActiveConfig("create_pc"))
|
||||
writePkgConfigFile();
|
||||
}
|
||||
|
||||
@ -1199,7 +1202,8 @@ void UnixMakefileGenerator::init2()
|
||||
project->values("QMAKE_FRAMEWORK_VERSION").append(project->first("VER_MAJ"));
|
||||
|
||||
if (project->first("TEMPLATE") == "aux") {
|
||||
// nothing
|
||||
project->values("PRL_TARGET") =
|
||||
project->values("TARGET").first().prepend(project->first("QMAKE_PREFIX_STATICLIB"));
|
||||
} else if (!project->values("QMAKE_APP_FLAG").isEmpty()) {
|
||||
if(!project->isEmpty("QMAKE_BUNDLE")) {
|
||||
ProString bundle_loc = project->first("QMAKE_BUNDLE_LOCATION");
|
||||
|
@ -1990,6 +1990,7 @@ bool VCMIDLTool::parseOption(const char* option)
|
||||
break;
|
||||
case 0x5eb7af2: // /header filename
|
||||
offset = 5;
|
||||
Q_FALLTHROUGH();
|
||||
case 0x0000358: // /h filename
|
||||
HeaderFileName = option + offset + 3;
|
||||
break;
|
||||
|
@ -1521,6 +1521,18 @@ void VcprojGenerator::initDistributionFiles()
|
||||
vcProject.DistributionFiles.Config = &(vcProject.Configuration);
|
||||
}
|
||||
|
||||
QString VcprojGenerator::extraCompilerName(const ProString &extraCompiler,
|
||||
const QStringList &inputs,
|
||||
const QStringList &outputs)
|
||||
{
|
||||
QString name = project->values(ProKey(extraCompiler + ".name")).join(' ');
|
||||
if (name.isEmpty())
|
||||
name = extraCompiler.toQString();
|
||||
else
|
||||
name = replaceExtraCompilerVariables(name, inputs, outputs, NoShell);
|
||||
return name;
|
||||
}
|
||||
|
||||
void VcprojGenerator::initExtraCompilerOutputs()
|
||||
{
|
||||
ProStringList otherFilters;
|
||||
@ -1538,13 +1550,16 @@ void VcprojGenerator::initExtraCompilerOutputs()
|
||||
<< "YACCSOURCES";
|
||||
const ProStringList &quc = project->values("QMAKE_EXTRA_COMPILERS");
|
||||
for (ProStringList::ConstIterator it = quc.begin(); it != quc.end(); ++it) {
|
||||
ProString extracompilerName = project->first(ProKey(*it + ".name"));
|
||||
if (extracompilerName.isEmpty())
|
||||
extracompilerName = (*it);
|
||||
const ProStringList &inputVars = project->values(ProKey(*it + ".input"));
|
||||
ProStringList inputFiles;
|
||||
for (auto var : inputVars)
|
||||
inputFiles.append(project->values(var.toKey()));
|
||||
const ProStringList &outputs = project->values(ProKey(*it + ".output"));
|
||||
|
||||
// Create an extra compiler filter and add the files
|
||||
VCFilter extraCompile;
|
||||
extraCompile.Name = extracompilerName.toQString();
|
||||
extraCompile.Name = extraCompilerName(it->toQString(), inputFiles.toQStringList(),
|
||||
outputs.toQStringList());
|
||||
extraCompile.ParseFiles = _False;
|
||||
extraCompile.Filter = "";
|
||||
extraCompile.Guid = QString(_GUIDExtraCompilerFiles) + "-" + (*it);
|
||||
@ -1557,14 +1572,14 @@ void VcprojGenerator::initExtraCompilerOutputs()
|
||||
if (!outputVar.isEmpty() && otherFilters.contains(outputVar))
|
||||
continue;
|
||||
|
||||
QString tmp_out = project->first(ProKey(*it + ".output")).toQString();
|
||||
QString tmp_out = project->first(outputs.first().toKey()).toQString();
|
||||
if (project->values(ProKey(*it + ".CONFIG")).indexOf("combine") != -1) {
|
||||
// Combined output, only one file result
|
||||
extraCompile.addFile(Option::fixPathToTargetOS(
|
||||
replaceExtraCompilerVariables(tmp_out, QString(), QString(), NoShell), false));
|
||||
} else {
|
||||
// One output file per input
|
||||
const ProStringList &tmp_in = project->values(project->first(ProKey(*it + ".input")).toKey());
|
||||
const ProStringList &tmp_in = project->values(inputVars.first().toKey());
|
||||
for (int i = 0; i < tmp_in.count(); ++i) {
|
||||
const QString &filename = tmp_in.at(i).toQString();
|
||||
if (extraCompilerSources.contains(filename) && !otherFiltersContain(filename))
|
||||
@ -1577,7 +1592,6 @@ void VcprojGenerator::initExtraCompilerOutputs()
|
||||
// build steps there. So, we turn it around and add it to the input files instead,
|
||||
// provided that the input file variable is not handled already (those in otherFilters
|
||||
// are handled, so we avoid them).
|
||||
const ProStringList &inputVars = project->values(ProKey(*it + ".input"));
|
||||
for (const ProString &inputVar : inputVars) {
|
||||
if (!otherFilters.contains(inputVar)) {
|
||||
const ProStringList &tmp_in = project->values(inputVar.toKey());
|
||||
|
@ -74,6 +74,8 @@ protected:
|
||||
bool doDepends() const override { return false; } // Never necessary
|
||||
using Win32MakefileGenerator::replaceExtraCompilerVariables;
|
||||
QString replaceExtraCompilerVariables(const QString &, const QStringList &, const QStringList &, ReplaceFor) override;
|
||||
QString extraCompilerName(const ProString &extraCompiler, const QStringList &inputs,
|
||||
const QStringList &outputs);
|
||||
bool supportsMetaBuild() override { return true; }
|
||||
bool supportsMergedBuilds() override { return true; }
|
||||
bool mergeBuildProject(MakefileGenerator *other) override;
|
||||
|
@ -671,7 +671,7 @@ void Win32MakefileGenerator::writeObjectsPart(QTextStream &t)
|
||||
t << "OBJECTS = " << valList(escapeDependencyPaths(project->values("OBJECTS"))) << endl;
|
||||
}
|
||||
|
||||
void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &t)
|
||||
void Win32MakefileGenerator::writeImplicitRulesPart(QTextStream &)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1128,35 +1128,48 @@ bool QMakeEvaluator::prepareProject(const QString &inDir)
|
||||
}
|
||||
superdir = qdfi.path();
|
||||
}
|
||||
QString sdir = inDir;
|
||||
QString dir = m_outputDir;
|
||||
forever {
|
||||
conffile = sdir + QLatin1String("/.qmake.conf");
|
||||
if (!m_vfs->exists(conffile, flags))
|
||||
conffile.clear();
|
||||
cachefile = dir + QLatin1String("/.qmake.cache");
|
||||
if (!m_vfs->exists(cachefile, flags))
|
||||
cachefile.clear();
|
||||
if (!conffile.isEmpty() || !cachefile.isEmpty()) {
|
||||
if (dir != sdir)
|
||||
m_sourceRoot = sdir;
|
||||
if (!cachefile.isEmpty()) {
|
||||
m_buildRoot = dir;
|
||||
break;
|
||||
}
|
||||
if (dir == superdir)
|
||||
goto no_cache;
|
||||
QFileInfo qsdfi(sdir);
|
||||
QFileInfo qdfi(dir);
|
||||
if (qsdfi.isRoot() || qdfi.isRoot())
|
||||
goto no_cache;
|
||||
sdir = qsdfi.path();
|
||||
if (qdfi.isRoot()) {
|
||||
cachefile.clear();
|
||||
break;
|
||||
}
|
||||
dir = qdfi.path();
|
||||
}
|
||||
QString sdir = inDir;
|
||||
forever {
|
||||
conffile = sdir + QLatin1String("/.qmake.conf");
|
||||
if (!m_vfs->exists(conffile, flags))
|
||||
conffile.clear();
|
||||
if (!conffile.isEmpty()) {
|
||||
if (sdir != m_buildRoot)
|
||||
m_sourceRoot = sdir;
|
||||
break;
|
||||
}
|
||||
QFileInfo qsdfi(sdir);
|
||||
if (qsdfi.isRoot()) {
|
||||
conffile.clear();
|
||||
break;
|
||||
}
|
||||
sdir = qsdfi.path();
|
||||
}
|
||||
} else {
|
||||
m_buildRoot = QFileInfo(cachefile).path();
|
||||
}
|
||||
m_conffile = QDir::cleanPath(conffile);
|
||||
m_cachefile = QDir::cleanPath(cachefile);
|
||||
if (!conffile.isEmpty())
|
||||
m_conffile = QDir::cleanPath(conffile);
|
||||
if (!cachefile.isEmpty())
|
||||
m_cachefile = QDir::cleanPath(cachefile);
|
||||
}
|
||||
no_cache:
|
||||
|
||||
|
@ -296,6 +296,9 @@ endfunction()
|
||||
# qt5_add_big_resources(outfiles inputfile ... )
|
||||
|
||||
function(QT5_ADD_BIG_RESOURCES outfiles )
|
||||
if (CMAKE_VERSION VERSION_LESS 3.9)
|
||||
message(FATAL_ERROR, "qt5_add_big_resources requires CMake 3.9 or newer")
|
||||
endif()
|
||||
|
||||
set(options)
|
||||
set(oneValueArgs)
|
||||
@ -326,6 +329,8 @@ function(QT5_ADD_BIG_RESOURCES outfiles )
|
||||
set_target_properties(rcc_object_${outfilename} PROPERTIES AUTOMOC OFF)
|
||||
set_target_properties(rcc_object_${outfilename} PROPERTIES AUTOUIC OFF)
|
||||
add_dependencies(rcc_object_${outfilename} big_resources_${outfilename})
|
||||
# The modification of TARGET_OBJECTS needs the following change in cmake
|
||||
# https://gitlab.kitware.com/cmake/cmake/commit/93c89bc75ceee599ba7c08b8fe1ac5104942054f
|
||||
add_custom_command(OUTPUT ${outfile}
|
||||
COMMAND ${Qt5Core_RCC_EXECUTABLE}
|
||||
ARGS ${rcc_options} --name ${outfilename} --pass 2 --temp $<TARGET_OBJECTS:rcc_object_${outfilename}> --output ${outfile} ${infile}
|
||||
|
@ -131,6 +131,8 @@ files (\c .o, \c .obj) files instead of C++ source code. This allows to
|
||||
embed bigger resources, where compiling to C++ sources and then to
|
||||
binaries would be too time consuming or memory intensive.
|
||||
|
||||
Note that this macro is only available if using \c{CMake} 3.9 or later.
|
||||
|
||||
\section1 Arguments
|
||||
|
||||
You can set additional \c{OPTIONS} that should be added to the \c{rcc} calls.
|
||||
|
@ -432,7 +432,24 @@ void QLibraryInfo::reload()
|
||||
{
|
||||
QLibraryInfoPrivate::reload();
|
||||
}
|
||||
#endif
|
||||
|
||||
void QLibraryInfo::sysrootify(QString *path)
|
||||
{
|
||||
if (!QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool())
|
||||
return;
|
||||
|
||||
const QString sysroot = rawLocation(SysrootPath, FinalPaths);
|
||||
if (sysroot.isEmpty())
|
||||
return;
|
||||
|
||||
if (path->length() > 2 && path->at(1) == QLatin1Char(':')
|
||||
&& (path->at(2) == QLatin1Char('/') || path->at(2) == QLatin1Char('\\'))) {
|
||||
path->replace(0, 2, sysroot); // Strip out the drive on Windows targets
|
||||
} else {
|
||||
path->prepend(sysroot);
|
||||
}
|
||||
}
|
||||
#endif // QT_BUILD_QMAKE
|
||||
|
||||
/*!
|
||||
Returns the location specified by \a loc.
|
||||
@ -444,18 +461,8 @@ QLibraryInfo::location(LibraryLocation loc)
|
||||
QString ret = rawLocation(loc, FinalPaths);
|
||||
|
||||
// Automatically prepend the sysroot to target paths
|
||||
if (loc < SysrootPath || loc > LastHostPath) {
|
||||
QString sysroot = rawLocation(SysrootPath, FinalPaths);
|
||||
if (!sysroot.isEmpty()
|
||||
&& QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool()) {
|
||||
if (ret.length() > 2 && ret.at(1) == QLatin1Char(':')
|
||||
&& (ret.at(2) == QLatin1Char('/') || ret.at(2) == QLatin1Char('\\'))) {
|
||||
ret.replace(0, 2, sysroot); // Strip out the drive on Windows targets
|
||||
} else {
|
||||
ret.prepend(sysroot);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loc < SysrootPath || loc > LastHostPath)
|
||||
sysrootify(&ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -598,6 +605,8 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group)
|
||||
} else {
|
||||
// we make any other path absolute to the prefix directory
|
||||
baseDir = rawLocation(PrefixPath, group);
|
||||
if (group == EffectivePaths)
|
||||
sysrootify(&baseDir);
|
||||
}
|
||||
#else
|
||||
if (loc == PrefixPath) {
|
||||
|
@ -107,6 +107,7 @@ public:
|
||||
enum PathGroup { FinalPaths, EffectivePaths, EffectiveSourcePaths, DevicePaths };
|
||||
static QString rawLocation(LibraryLocation, PathGroup);
|
||||
static void reload();
|
||||
static void sysrootify(QString *path);
|
||||
#endif
|
||||
|
||||
static QStringList platformPluginArguments(const QString &platformName);
|
||||
|
@ -228,7 +228,18 @@ QUrl QFileSelector::select(const QUrl &filePath) const
|
||||
QString selectedPath = d->select(equivalentPath);
|
||||
ret.setPath(selectedPath.remove(0, scheme.size()));
|
||||
} else {
|
||||
// we need to store the original query and fragment, since toLocalFile() will strip it off
|
||||
QString frag;
|
||||
if (ret.hasFragment())
|
||||
frag = ret.fragment();
|
||||
QString query;
|
||||
if (ret.hasQuery())
|
||||
query= ret.query();
|
||||
ret = QUrl::fromLocalFile(d->select(ret.toLocalFile()));
|
||||
if (!frag.isNull())
|
||||
ret.setFragment(frag);
|
||||
if (!query.isNull())
|
||||
ret.setQuery(query);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -295,13 +295,13 @@ QT_MAC_WEAK_IMPORT(_os_activity_current);
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
#if defined( __OBJC__)
|
||||
class QMacScopedObserver
|
||||
class QMacNotificationObserver
|
||||
{
|
||||
public:
|
||||
QMacScopedObserver() {}
|
||||
QMacNotificationObserver() {}
|
||||
|
||||
template<typename Functor>
|
||||
QMacScopedObserver(id object, NSNotificationName name, Functor callback) {
|
||||
QMacNotificationObserver(id object, NSNotificationName name, Functor callback) {
|
||||
observer = [[NSNotificationCenter defaultCenter] addObserverForName:name
|
||||
object:object queue:nil usingBlock:^(NSNotification *) {
|
||||
callback();
|
||||
@ -309,13 +309,13 @@ public:
|
||||
];
|
||||
}
|
||||
|
||||
QMacScopedObserver(const QMacScopedObserver& other) = delete;
|
||||
QMacScopedObserver(QMacScopedObserver&& other) : observer(other.observer) {
|
||||
QMacNotificationObserver(const QMacNotificationObserver& other) = delete;
|
||||
QMacNotificationObserver(QMacNotificationObserver&& other) : observer(other.observer) {
|
||||
other.observer = nil;
|
||||
}
|
||||
|
||||
QMacScopedObserver &operator=(const QMacScopedObserver& other) = delete;
|
||||
QMacScopedObserver &operator=(QMacScopedObserver&& other) {
|
||||
QMacNotificationObserver &operator=(const QMacNotificationObserver& other) = delete;
|
||||
QMacNotificationObserver &operator=(QMacNotificationObserver&& other) {
|
||||
if (this != &other) {
|
||||
remove();
|
||||
observer = other.observer;
|
||||
@ -329,7 +329,7 @@ public:
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:observer];
|
||||
observer = nil;
|
||||
}
|
||||
~QMacScopedObserver() { remove(); }
|
||||
~QMacNotificationObserver() { remove(); }
|
||||
|
||||
private:
|
||||
id observer = nil;
|
||||
|
@ -329,19 +329,17 @@ static int fromOffsetString(const QStringRef &offsetString, bool *valid) Q_DECL_
|
||||
\brief The QDate class provides date functions.
|
||||
|
||||
|
||||
A QDate object encodes a calendar date, i.e. year, month, and day numbers,
|
||||
in the proleptic Gregorian calendar by default. It can read the current date
|
||||
from the system clock. It provides functions for comparing dates, and for
|
||||
manipulating dates. For example, it is possible to add and subtract days,
|
||||
months, and years to dates.
|
||||
A QDate object represents a particular date. This can be expressed as a
|
||||
calendar date, i.e. year, month, and day numbers, in the proleptic Gregorian
|
||||
calendar.
|
||||
|
||||
A QDate object is typically created by giving the year, month, and day
|
||||
numbers explicitly. Note that QDate interprets two digit years as presented,
|
||||
i.e., as years 0 through 99, without adding any offset. A QDate can also be
|
||||
constructed with the static function currentDate(), which creates a QDate
|
||||
object containing the system clock's date. An explicit date can also be set
|
||||
using setDate(). The fromString() function returns a QDate given a string
|
||||
and a date format which is used to interpret the date within the string.
|
||||
numbers explicitly. Note that QDate interprets year numbers less than 100 as
|
||||
presented, i.e., as years 1 through 99, without adding any offset. The
|
||||
static function currentDate() creates a QDate object containing the date
|
||||
read from the system clock. An explicit date can also be set using
|
||||
setDate(). The fromString() function returns a QDate given a string and a
|
||||
date format which is used to interpret the date within the string.
|
||||
|
||||
The year(), month(), and day() functions provide access to the
|
||||
year, month, and day numbers. Also, dayOfWeek() and dayOfYear()
|
||||
@ -375,7 +373,7 @@ static int fromOffsetString(const QStringRef &offsetString, bool *valid) Q_DECL_
|
||||
every day in a contiguous range, with 24 November 4714 BCE in the Gregorian
|
||||
calendar being Julian Day 0 (1 January 4713 BCE in the Julian calendar).
|
||||
As well as being an efficient and accurate way of storing an absolute date,
|
||||
it is suitable for converting a Date into other calendar systems such as
|
||||
it is suitable for converting a date into other calendar systems such as
|
||||
Hebrew, Islamic or Chinese. The Julian Day number can be obtained using
|
||||
QDate::toJulianDay() and can be set using QDate::fromJulianDay().
|
||||
|
||||
@ -1437,12 +1435,10 @@ bool QDate::isLeapYear(int y)
|
||||
Unlike QDateTime, QTime knows nothing about time zones or
|
||||
daylight-saving time (DST).
|
||||
|
||||
A QTime object is typically created either by giving the number
|
||||
of hours, minutes, seconds, and milliseconds explicitly, or by
|
||||
using the static function currentTime(), which creates a QTime
|
||||
object that contains the system's local time. Note that the
|
||||
accuracy depends on the accuracy of the underlying operating
|
||||
system; not all systems provide 1-millisecond accuracy.
|
||||
A QTime object is typically created either by giving the number of hours,
|
||||
minutes, seconds, and milliseconds explicitly, or by using the static
|
||||
function currentTime(), which creates a QTime object that represents the
|
||||
system's local time.
|
||||
|
||||
The hour(), minute(), second(), and msec() functions provide
|
||||
access to the number of hours, minutes, seconds, and milliseconds
|
||||
@ -1902,6 +1898,12 @@ int QTime::msecsTo(const QTime &t) const
|
||||
|
||||
Note that the accuracy depends on the accuracy of the underlying
|
||||
operating system; not all systems provide 1-millisecond accuracy.
|
||||
|
||||
Furthermore, currentTime() only increases within each day; it shall drop by
|
||||
24 hours each time midnight passes; and, beside this, changes in it may not
|
||||
correspond to elapsed time, if a daylight-saving transition intervenes.
|
||||
|
||||
\sa QDateTime::currentDateTime(), QDateTime::curentDateTimeUtc()
|
||||
*/
|
||||
|
||||
#if QT_CONFIG(datestring)
|
||||
@ -3020,15 +3022,30 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
|
||||
provides functions for comparing datetimes and for manipulating a
|
||||
datetime by adding a number of seconds, days, months, or years.
|
||||
|
||||
A QDateTime object is typically created either by giving a date
|
||||
and time explicitly in the constructor, or by using the static
|
||||
function currentDateTime() that returns a QDateTime object set
|
||||
to the system clock's time. The date and time can be changed with
|
||||
setDate() and setTime(). A datetime can also be set using the
|
||||
setTime_t() function that takes a POSIX-standard "number of
|
||||
seconds since 00:00:00 on January 1, 1970" value. The fromString()
|
||||
function returns a QDateTime, given a string and a date format
|
||||
used to interpret the date within the string.
|
||||
QDateTime can describe datetimes with respect to \l{Qt::LocalTime}{local
|
||||
time}, to \l{Qt::UTC}{UTC}, to a specified \l{{Qt::OffsetFromUTC}{offset
|
||||
from UTC} or to a specified \l{{Qt::TimeZone}{time zone}, in conjunction
|
||||
with the QTimeZone class. For example, a time zone of "Europe/Berlin" will
|
||||
apply the daylight-saving rules as used in Germany since 1970. In contrast,
|
||||
an offset from UTC of +3600 seconds is one hour ahead of UTC (usually
|
||||
written in ISO standard notation as "UTC+01:00"), with no daylight-saving
|
||||
offset or changes. When using either local time or a specified time zone,
|
||||
time-zone transitions such as the starts and ends of daylight-saving time
|
||||
(DST) are taken into account. The choice of system used to represent a
|
||||
datetime is described as its "timespec".
|
||||
|
||||
A QDateTime object is typically created either by giving a date and time
|
||||
explicitly in the constructor, or by using a static function such as
|
||||
currentDateTime() or fromMSecsSinceEpoch(). The date and time can be changed
|
||||
with setDate() and setTime(). A datetime can also be set using the
|
||||
setMSecsSinceEpoch() function that takes the time, in milliseconds, since
|
||||
00:00:00 on January 1, 1970. The fromString() function returns a QDateTime,
|
||||
given a string and a date format used to interpret the date within the
|
||||
string.
|
||||
|
||||
QDateTime::currentDateTime() returns a QDateTime that expresses the current
|
||||
time with respect to local time. QDateTime::currentDateTimeUtc() returns a
|
||||
QDateTime that expresses the current time with respect to UTC.
|
||||
|
||||
The date() and time() functions provide access to the date and
|
||||
time parts of the datetime. The same information is provided in
|
||||
@ -3039,18 +3056,20 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
|
||||
later.
|
||||
|
||||
You can increment (or decrement) a datetime by a given number of
|
||||
milliseconds using addMSecs(), seconds using addSecs(), or days
|
||||
using addDays(). Similarly, you can use addMonths() and addYears().
|
||||
The daysTo() function returns the number of days between two datetimes,
|
||||
secsTo() returns the number of seconds between two datetimes, and
|
||||
msecsTo() returns the number of milliseconds between two datetimes.
|
||||
milliseconds using addMSecs(), seconds using addSecs(), or days using
|
||||
addDays(). Similarly, you can use addMonths() and addYears(). The daysTo()
|
||||
function returns the number of days between two datetimes, secsTo() returns
|
||||
the number of seconds between two datetimes, and msecsTo() returns the
|
||||
number of milliseconds between two datetimes. These operations are aware of
|
||||
daylight-saving time (DST) and other time-zone transitions, where
|
||||
applicable.
|
||||
|
||||
QDateTime can store datetimes as \l{Qt::LocalTime}{local time} or
|
||||
as \l{Qt::UTC}{UTC}. QDateTime::currentDateTime() returns a
|
||||
QDateTime expressed as local time; use toUTC() to convert it to
|
||||
UTC. You can also use timeSpec() to find out if a QDateTime
|
||||
object stores a UTC time or a local time. Operations such as
|
||||
addSecs() and secsTo() are aware of daylight-saving time (DST).
|
||||
Use toTimeSpec() to express a datetime in local time or UTC,
|
||||
toOffsetFromUtc() to express in terms of an offset from UTC, or toTimeZone()
|
||||
to express it with respect to a general time zone. You can use timeSpec() to
|
||||
find out what time-spec a QDateTime object stores its time relative to. When
|
||||
that is Qt::TimeZone, you can use timeZone() to find out which zone it is
|
||||
using.
|
||||
|
||||
\note QDateTime does not account for leap seconds.
|
||||
|
||||
@ -3064,67 +3083,55 @@ inline qint64 QDateTimePrivate::zoneMSecsToEpochMSecs(qint64 zoneMSecs, const QT
|
||||
|
||||
\section2 Range of Valid Dates
|
||||
|
||||
The range of valid values able to be stored in QDateTime is dependent on
|
||||
the internal storage implementation. QDateTime is currently stored in a
|
||||
qint64 as a serial msecs value encoding the date and time. This restricts
|
||||
the date range to about +/- 292 million years, compared to the QDate range
|
||||
of +/- 2 billion years. Care must be taken when creating a QDateTime with
|
||||
extreme values that you do not overflow the storage. The exact range of
|
||||
supported values varies depending on the Qt::TimeSpec and time zone.
|
||||
The range of values that QDateTime can represent is dependent on the
|
||||
internal storage implementation. QDateTime is currently stored in a qint64
|
||||
as a serial msecs value encoding the date and time. This restricts the date
|
||||
range to about +/- 292 million years, compared to the QDate range of +/- 2
|
||||
billion years. Care must be taken when creating a QDateTime with extreme
|
||||
values that you do not overflow the storage. The exact range of supported
|
||||
values varies depending on the Qt::TimeSpec and time zone.
|
||||
|
||||
\section2 Use of System Timezone
|
||||
\section2 Use of Timezones
|
||||
|
||||
QDateTime uses the system's time zone information to determine the
|
||||
offset of local time from UTC. If the system is not configured
|
||||
correctly or not up-to-date, QDateTime will give wrong results as
|
||||
well.
|
||||
QDateTime uses the system's time zone information to determine the current
|
||||
local time zone and its offset from UTC. If the system is not configured
|
||||
correctly or not up-to-date, QDateTime will give wrong results.
|
||||
|
||||
QDateTime likewise uses system-provided information to determine the offsets
|
||||
of other timezones from UTC. If this information is incomplete or out of
|
||||
date, QDateTime will give wrong results. See the QTimeZone documentation for
|
||||
more details.
|
||||
|
||||
On modern Unix systems, this means QDateTime usually has accurate
|
||||
information about historical transitions (including DST, see below) whenever
|
||||
possible. On Windows, where the system doesn't support historical timezone
|
||||
data, historical accuracy is not maintained with respect to timezone
|
||||
transitions, notably including DST.
|
||||
|
||||
\section2 Daylight-Saving Time (DST)
|
||||
|
||||
QDateTime takes into account the system's time zone information
|
||||
when dealing with DST. On modern Unix systems, this means it
|
||||
applies the correct historical DST data whenever possible. On
|
||||
Windows, where the system doesn't support historical DST data,
|
||||
historical accuracy is not maintained with respect to DST.
|
||||
QDateTime takes into account transitions between Standard Time and
|
||||
Daylight-Saving Time. For example, if the transition is at 2am and the clock
|
||||
goes forward to 3am, then there is a "missing" hour from 02:00:00 to
|
||||
02:59:59.999 which QDateTime considers to be invalid. Any date arithmetic
|
||||
performed will take this missing hour into account and return a valid
|
||||
result. For example, adding one minute to 01:59:59 will get 03:00:00.
|
||||
|
||||
The range of valid dates taking DST into account is 1970-01-01 to
|
||||
the present, and rules are in place for handling DST correctly
|
||||
until 2037-12-31, but these could change. For dates falling
|
||||
outside that range, QDateTime makes a \e{best guess} using the
|
||||
rules for year 1970 or 2037, but we can't guarantee accuracy. This
|
||||
means QDateTime doesn't take into account changes in a locale's
|
||||
time zone before 1970, even if the system's time zone database
|
||||
supports that information.
|
||||
The range of valid dates taking DST into account is 1970-01-01 to the
|
||||
present, and rules are in place for handling DST correctly until 2037-12-31,
|
||||
but these could change. For dates falling outside that range, QDateTime
|
||||
makes a \e{best guess} using the rules for year 1970 or 2037, but we can't
|
||||
guarantee accuracy. This means QDateTime doesn't take into account changes
|
||||
in a time zone before 1970, even if the system's time zone database provides
|
||||
that information.
|
||||
|
||||
QDateTime takes into consideration the Standard Time to Daylight-Saving Time
|
||||
transition. For example if the transition is at 2am and the clock goes
|
||||
forward to 3am, then there is a "missing" hour from 02:00:00 to 02:59:59.999
|
||||
which QDateTime considers to be invalid. Any date maths performed
|
||||
will take this missing hour into account and return a valid result.
|
||||
\section2 Offsets From UTC
|
||||
|
||||
\section2 Offset From UTC
|
||||
|
||||
A Qt::TimeSpec of Qt::OffsetFromUTC is also supported. This allows you
|
||||
to define a QDateTime relative to UTC at a fixed offset of a given number
|
||||
of seconds from UTC. For example, an offset of +3600 seconds is one hour
|
||||
ahead of UTC and is usually written in ISO standard notation as
|
||||
"UTC+01:00". Daylight-Saving Time never applies with this TimeSpec.
|
||||
|
||||
There is no explicit size restriction to the offset seconds, but there is
|
||||
an implicit limit imposed when using the toString() and fromString()
|
||||
methods which use a format of [+|-]hh:mm, effectively limiting the range
|
||||
to +/- 99 hours and 59 minutes and whole minutes only. Note that currently
|
||||
no time zone lies outside the range of +/- 14 hours.
|
||||
|
||||
\section2 Time Zone Support
|
||||
|
||||
A Qt::TimeSpec of Qt::TimeZone is also supported in conjunction with the
|
||||
QTimeZone class. This allows you to define a datetime in a named time zone
|
||||
adhering to a consistent set of daylight-saving transition rules. For
|
||||
example a time zone of "Europe/Berlin" will apply the daylight-saving
|
||||
rules as used in Germany since 1970. Note that the transition rules
|
||||
applied depend on the platform support. See the QTimeZone documentation
|
||||
for more details.
|
||||
There is no explicit size restriction on an offset from UTC, but there is an
|
||||
implicit limit imposed when using the toString() and fromString() methods
|
||||
which use a [+|-]hh:mm format, effectively limiting the range to +/- 99
|
||||
hours and 59 minutes and whole minutes only. Note that currently no time
|
||||
zone lies outside the range of +/- 14 hours.
|
||||
|
||||
\sa QDate, QTime, QDateTimeEdit, QTimeZone
|
||||
*/
|
||||
@ -4247,7 +4254,7 @@ qint64 QDateTime::msecsTo(const QDateTime &other) const
|
||||
Example:
|
||||
\snippet code/src_corelib_tools_qdatetime.cpp 16
|
||||
|
||||
\sa timeSpec(), toTimeZone(), toUTC(), toLocalTime()
|
||||
\sa timeSpec(), toTimeZone(), toOffsetFromUtc()
|
||||
*/
|
||||
|
||||
QDateTime QDateTime::toTimeSpec(Qt::TimeSpec spec) const
|
||||
|
@ -825,7 +825,7 @@ static QString wc2rx(const QString &wc_str, const bool enableEscaping)
|
||||
if (wc[i] == QLatin1Char('^'))
|
||||
rx += wc[i++];
|
||||
if (i < wclen) {
|
||||
if (rx[i] == QLatin1Char(']'))
|
||||
if (wc[i] == QLatin1Char(']'))
|
||||
rx += wc[i++];
|
||||
while (i < wclen && wc[i] != QLatin1Char(']')) {
|
||||
if (wc[i] == QLatin1Char('\\'))
|
||||
|
@ -51,6 +51,12 @@
|
||||
#include "qlocale_tools_p.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#if !defined(Q_OS_INTEGRITY)
|
||||
#include <sys/param.h> // to use MAXSYMLINKS constant
|
||||
#endif
|
||||
#include <unistd.h> // to use _SC_SYMLOOP_MAX constant
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -1045,6 +1051,27 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
|
||||
return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData();
|
||||
}
|
||||
|
||||
static long getSymloopMax()
|
||||
{
|
||||
#if defined(SYMLOOP_MAX)
|
||||
return SYMLOOP_MAX; // if defined, at runtime it can only be greater than this, so this is a safe bet
|
||||
#else
|
||||
errno = 0;
|
||||
long result = sysconf(_SC_SYMLOOP_MAX);
|
||||
if (result >= 0)
|
||||
return result;
|
||||
// result is -1, meaning either error or no limit
|
||||
Q_ASSERT(!errno); // ... but it can't be an error, POSIX mandates _SC_SYMLOOP_MAX
|
||||
|
||||
// therefore we can make up our own limit
|
||||
# if defined(MAXSYMLINKS)
|
||||
return MAXSYMLINKS;
|
||||
# else
|
||||
return 8;
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO Could cache the value and monitor the required files for any changes
|
||||
QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
|
||||
{
|
||||
@ -1062,12 +1089,18 @@ QByteArray QTzTimeZonePrivate::systemTimeZoneId() const
|
||||
|
||||
// On most distros /etc/localtime is a symlink to a real file so extract name from the path
|
||||
if (ianaId.isEmpty()) {
|
||||
const QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime"));
|
||||
if (!path.isEmpty()) {
|
||||
const QLatin1String zoneinfo("/zoneinfo/");
|
||||
QString path = QFile::symLinkTarget(QStringLiteral("/etc/localtime"));
|
||||
int index = -1;
|
||||
long iteration = getSymloopMax();
|
||||
// Symlink may point to another symlink etc. before being under zoneinfo/
|
||||
// We stop on the first path under /zoneinfo/, even if it is itself a
|
||||
// symlink, like America/Montreal pointing to America/Toronto
|
||||
while (iteration-- > 0 && !path.isEmpty() && (index = path.indexOf(zoneinfo)) < 0)
|
||||
path = QFile::symLinkTarget(path);
|
||||
if (index >= 0) {
|
||||
// /etc/localtime is a symlink to the current TZ file, so extract from path
|
||||
int index = path.indexOf(QLatin1String("/zoneinfo/"));
|
||||
if (index != -1)
|
||||
ianaId = path.mid(index + 10).toUtf8();
|
||||
ianaId = path.mid(index + zoneinfo.size()).toUtf8();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1008,6 +1008,8 @@ QDataStream &operator<<(QDataStream &s, const QPalette &p)
|
||||
max = QPalette::HighlightedText + 1;
|
||||
else if (s.version() <= QDataStream::Qt_4_3)
|
||||
max = QPalette::AlternateBase + 1;
|
||||
else if (s.version() <= QDataStream::Qt_5_11)
|
||||
max = QPalette::ToolTipText + 1;
|
||||
for (int r = 0; r < max; r++)
|
||||
s << p.d->br[grp][r];
|
||||
}
|
||||
@ -1048,6 +1050,9 @@ QDataStream &operator>>(QDataStream &s, QPalette &p)
|
||||
} else if (s.version() <= QDataStream::Qt_4_3) {
|
||||
p = QPalette();
|
||||
max = QPalette::AlternateBase + 1;
|
||||
} else if (s.version() <= QDataStream::Qt_5_11) {
|
||||
p = QPalette();
|
||||
max = QPalette::ToolTipText + 1;
|
||||
}
|
||||
|
||||
QBrush tmp;
|
||||
|
@ -218,6 +218,12 @@ QWindow::~QWindow()
|
||||
QGuiApplicationPrivate::window_list.removeAll(this);
|
||||
if (!QGuiApplicationPrivate::is_app_closing)
|
||||
QGuiApplicationPrivate::instance()->modalWindowList.removeOne(this);
|
||||
|
||||
// focus_window is normally cleared in destroy(), but the window may in
|
||||
// some cases end up becoming the focus window again. Clear it again
|
||||
// here as a workaround. See QTBUG-75326.
|
||||
if (QGuiApplicationPrivate::focus_window == this)
|
||||
QGuiApplicationPrivate::focus_window = 0;
|
||||
}
|
||||
|
||||
void QWindowPrivate::init(QScreen *targetScreen)
|
||||
|
@ -71,6 +71,24 @@
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static inline bool isValidCoord(qreal c)
|
||||
{
|
||||
if (sizeof(qreal) >= sizeof(double))
|
||||
return qIsFinite(c) && fabs(c) < 1e128;
|
||||
else
|
||||
return qIsFinite(c) && fabsf(float(c)) < 1e16f;
|
||||
}
|
||||
|
||||
static bool hasValidCoords(QPointF p)
|
||||
{
|
||||
return isValidCoord(p.x()) && isValidCoord(p.y());
|
||||
}
|
||||
|
||||
static bool hasValidCoords(QRectF r)
|
||||
{
|
||||
return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
|
||||
}
|
||||
|
||||
struct QPainterPathPrivateDeleter
|
||||
{
|
||||
static inline void cleanup(QPainterPathPrivate *d)
|
||||
@ -724,9 +742,9 @@ void QPainterPath::moveTo(const QPointF &p)
|
||||
printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
|
||||
#endif
|
||||
|
||||
if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
|
||||
if (!hasValidCoords(p)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::moveTo: Adding point where x or y is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -774,9 +792,9 @@ void QPainterPath::lineTo(const QPointF &p)
|
||||
printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
|
||||
#endif
|
||||
|
||||
if (!qt_is_finite(p.x()) || !qt_is_finite(p.y())) {
|
||||
if (!hasValidCoords(p)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::lineTo: Adding point where x or y is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -833,10 +851,9 @@ void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &
|
||||
c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
|
||||
#endif
|
||||
|
||||
if (!qt_is_finite(c1.x()) || !qt_is_finite(c1.y()) || !qt_is_finite(c2.x()) || !qt_is_finite(c2.y())
|
||||
|| !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
|
||||
if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -890,9 +907,9 @@ void QPainterPath::quadTo(const QPointF &c, const QPointF &e)
|
||||
c.x(), c.y(), e.x(), e.y());
|
||||
#endif
|
||||
|
||||
if (!qt_is_finite(c.x()) || !qt_is_finite(c.y()) || !qt_is_finite(e.x()) || !qt_is_finite(e.y())) {
|
||||
if (!hasValidCoords(c) || !hasValidCoords(e)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::quadTo: Adding point where x or y is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -961,10 +978,9 @@ void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength
|
||||
rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
|
||||
#endif
|
||||
|
||||
if ((!qt_is_finite(rect.x()) && !qt_is_finite(rect.y())) || !qt_is_finite(rect.width()) || !qt_is_finite(rect.height())
|
||||
|| !qt_is_finite(startAngle) || !qt_is_finite(sweepLength)) {
|
||||
if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -1067,9 +1083,9 @@ QPointF QPainterPath::currentPosition() const
|
||||
*/
|
||||
void QPainterPath::addRect(const QRectF &r)
|
||||
{
|
||||
if (!qt_is_finite(r.x()) || !qt_is_finite(r.y()) || !qt_is_finite(r.width()) || !qt_is_finite(r.height())) {
|
||||
if (!hasValidCoords(r)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -1147,10 +1163,9 @@ void QPainterPath::addPolygon(const QPolygonF &polygon)
|
||||
*/
|
||||
void QPainterPath::addEllipse(const QRectF &boundingRect)
|
||||
{
|
||||
if (!qt_is_finite(boundingRect.x()) || !qt_is_finite(boundingRect.y())
|
||||
|| !qt_is_finite(boundingRect.width()) || !qt_is_finite(boundingRect.height())) {
|
||||
if (!hasValidCoords(boundingRect)) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN or Inf, ignoring call");
|
||||
qWarning("QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@ -2501,6 +2516,7 @@ QDataStream &operator<<(QDataStream &s, const QPainterPath &p)
|
||||
*/
|
||||
QDataStream &operator>>(QDataStream &s, QPainterPath &p)
|
||||
{
|
||||
bool errorDetected = false;
|
||||
int size;
|
||||
s >> size;
|
||||
|
||||
@ -2519,10 +2535,11 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
|
||||
s >> x;
|
||||
s >> y;
|
||||
Q_ASSERT(type >= 0 && type <= 3);
|
||||
if (!qt_is_finite(x) || !qt_is_finite(y)) {
|
||||
if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
|
||||
#ifndef QT_NO_DEBUG
|
||||
qWarning("QDataStream::operator>>: NaN or Inf element found in path, skipping it");
|
||||
qWarning("QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
|
||||
#endif
|
||||
errorDetected = true;
|
||||
continue;
|
||||
}
|
||||
QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
|
||||
@ -2535,6 +2552,8 @@ QDataStream &operator>>(QDataStream &s, QPainterPath &p)
|
||||
p.d_func()->fillRule = Qt::FillRule(fillRule);
|
||||
p.d_func()->dirtyBounds = true;
|
||||
p.d_func()->dirtyControlBounds = true;
|
||||
if (errorDetected)
|
||||
p = QPainterPath(); // Better than to return path with possibly corrupt datastructure, which would likely cause crash
|
||||
return s;
|
||||
}
|
||||
#endif // QT_NO_DATASTREAM
|
||||
|
@ -80,7 +80,7 @@ private:
|
||||
NSOpenGLContext *m_shareContext = nil;
|
||||
QSurfaceFormat m_format;
|
||||
bool m_didCheckForSoftwareContext = false;
|
||||
QVarLengthArray<QMacScopedObserver, 3> m_updateObservers;
|
||||
QVarLengthArray<QMacNotificationObserver, 3> m_updateObservers;
|
||||
QAtomicInt m_needsUpdate = false;
|
||||
};
|
||||
|
||||
|
@ -404,13 +404,13 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
|
||||
m_updateObservers.clear();
|
||||
|
||||
if (view.layer) {
|
||||
m_updateObservers.append(QMacScopedObserver(view, NSViewFrameDidChangeNotification, updateCallback));
|
||||
m_updateObservers.append(QMacScopedObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
|
||||
m_updateObservers.append(QMacNotificationObserver(view, NSViewFrameDidChangeNotification, updateCallback));
|
||||
m_updateObservers.append(QMacNotificationObserver(view.window, NSWindowDidChangeScreenNotification, updateCallback));
|
||||
} else {
|
||||
m_updateObservers.append(QMacScopedObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
|
||||
m_updateObservers.append(QMacNotificationObserver(view, NSViewGlobalFrameDidChangeNotification, updateCallback));
|
||||
}
|
||||
|
||||
m_updateObservers.append(QMacScopedObserver([NSApplication sharedApplication],
|
||||
m_updateObservers.append(QMacNotificationObserver([NSApplication sharedApplication],
|
||||
NSApplicationDidChangeScreenParametersNotification, updateCallback));
|
||||
|
||||
// If any of the observers fire at this point it's fine. We check the
|
||||
|
@ -297,13 +297,12 @@ Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum)
|
||||
*/
|
||||
Qt::MouseButton cocoaButton2QtButton(NSEvent *event)
|
||||
{
|
||||
switch (event.type) {
|
||||
case NSEventTypeMouseMoved:
|
||||
if (cocoaEvent2QtMouseEvent(event) == QEvent::MouseMove)
|
||||
return Qt::NoButton;
|
||||
|
||||
switch (event.type) {
|
||||
case NSEventTypeRightMouseUp:
|
||||
case NSEventTypeRightMouseDown:
|
||||
case NSEventTypeRightMouseDragged:
|
||||
return Qt::RightButton;
|
||||
|
||||
default:
|
||||
|
@ -45,6 +45,8 @@
|
||||
|
||||
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver));
|
||||
|
||||
#include <QtCore/private/qcore_mac_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QPalette;
|
||||
@ -82,6 +84,7 @@ public:
|
||||
|
||||
private:
|
||||
mutable QPalette *m_systemPalette;
|
||||
QMacNotificationObserver m_systemColorObserver;
|
||||
mutable QHash<QPlatformTheme::Palette, QPalette*> m_palettes;
|
||||
mutable QHash<QPlatformTheme::Font, QFont*> m_fonts;
|
||||
QT_MANGLE_NAMESPACE(QCocoaThemeAppAppearanceObserver) *m_appearanceObserver;
|
||||
|
@ -133,10 +133,10 @@ QCocoaTheme::QCocoaTheme()
|
||||
m_appearanceObserver = [[QCocoaThemeAppAppearanceObserver alloc] initWithTheme:this];
|
||||
#endif
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:NSSystemColorsDidChangeNotification
|
||||
object:nil queue:nil usingBlock:^(NSNotification *) {
|
||||
m_systemColorObserver = QMacNotificationObserver(nil,
|
||||
NSSystemColorsDidChangeNotification, [this] {
|
||||
handleSystemThemeChange();
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
QCocoaTheme::~QCocoaTheme()
|
||||
|
@ -54,7 +54,7 @@ QCocoaVulkanInstance::~QCocoaVulkanInstance()
|
||||
|
||||
void QCocoaVulkanInstance::createOrAdoptInstance()
|
||||
{
|
||||
initInstance(m_instance, QByteArrayList());
|
||||
initInstance(m_instance, QByteArrayList() << QByteArrayLiteral("VK_MVK_macos_surface"));
|
||||
}
|
||||
|
||||
VkSurfaceKHR *QCocoaVulkanInstance::createSurface(QWindow *window)
|
||||
|
@ -277,20 +277,19 @@
|
||||
nativeDrag->setLastMouseEvent(theEvent, self);
|
||||
|
||||
const auto modifiers = [QNSView convertKeyModifiers:theEvent.modifierFlags];
|
||||
const auto buttons = currentlyPressedMouseButtons();
|
||||
auto button = cocoaButton2QtButton(theEvent);
|
||||
if (button == Qt::LeftButton && m_sendUpAsRightButton)
|
||||
button = Qt::RightButton;
|
||||
const auto eventType = cocoaEvent2QtMouseEvent(theEvent);
|
||||
|
||||
if (eventType == QEvent::MouseMove)
|
||||
qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << buttons;
|
||||
qCDebug(lcQpaMouse) << eventType << "at" << qtWindowPoint << "with" << m_buttons;
|
||||
else
|
||||
qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << buttons;
|
||||
qCInfo(lcQpaMouse) << eventType << "of" << button << "at" << qtWindowPoint << "with" << m_buttons;
|
||||
|
||||
QWindowSystemInterface::handleMouseEvent(targetView->m_platformWindow->window(),
|
||||
timestamp, qtWindowPoint, qtScreenPoint,
|
||||
buttons, button, eventType, modifiers);
|
||||
m_buttons, button, eventType, modifiers);
|
||||
}
|
||||
|
||||
- (bool)handleMouseDownEvent:(NSEvent *)theEvent
|
||||
|
@ -69,12 +69,15 @@ inline D2D1_RECT_F to_d2d_rect_f(const QRectF &qrect)
|
||||
|
||||
inline D2D1_SIZE_U to_d2d_size_u(const QSizeF &qsize)
|
||||
{
|
||||
return D2D1::SizeU(qsize.width(), qsize.height());
|
||||
|
||||
return D2D1::SizeU(UINT32(qRound(qsize.width())),
|
||||
UINT32(qRound(qsize.height())));
|
||||
}
|
||||
|
||||
inline D2D1_SIZE_U to_d2d_size_u(const QSize &qsize)
|
||||
{
|
||||
return D2D1::SizeU(qsize.width(), qsize.height());
|
||||
return D2D1::SizeU(UINT32(qsize.width()),
|
||||
UINT32(qsize.height()));
|
||||
}
|
||||
|
||||
inline D2D1_POINT_2F to_d2d_point_2f(const QPointF &qpoint)
|
||||
|
@ -749,6 +749,12 @@ QWindowsWindow *QWindowsContext::findPlatformWindowAt(HWND parent,
|
||||
QWindowsWindow *result = nullptr;
|
||||
const POINT screenPoint = { screenPointIn.x(), screenPointIn.y() };
|
||||
while (findPlatformWindowHelper(screenPoint, cwex_flags, this, &parent, &result)) {}
|
||||
// QTBUG-40815: ChildWindowFromPointEx() can hit on special windows from
|
||||
// screen recorder applications like ScreenToGif. Fall back to WindowFromPoint().
|
||||
if (result == nullptr) {
|
||||
if (const HWND window = WindowFromPoint(screenPoint))
|
||||
result = findPlatformWindow(window);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -925,7 +931,7 @@ bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void
|
||||
bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
|
||||
const QPlatformScreen *screen)
|
||||
{
|
||||
return systemParametersInfo(action, param, out, screen ? screen->logicalDpi().first : 0);
|
||||
return systemParametersInfo(action, param, out, screen ? unsigned(screen->logicalDpi().first) : 0u);
|
||||
}
|
||||
|
||||
bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
|
||||
@ -944,7 +950,8 @@ bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi)
|
||||
bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
|
||||
const QPlatformScreen *screen)
|
||||
{
|
||||
return nonClientMetrics(ncm, screen ? screen->logicalDpi().first : 0);
|
||||
const int dpi = screen ? qRound(screen->logicalDpi().first) : 0;
|
||||
return nonClientMetrics(ncm, unsigned(dpi));
|
||||
}
|
||||
|
||||
bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win)
|
||||
|
@ -269,7 +269,10 @@ static Qt::MouseButtons queryMouseButtons()
|
||||
|
||||
static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos)
|
||||
{
|
||||
QWindow *currentWindowUnderPointer = QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT);
|
||||
QWindowsWindow *platformWindow = static_cast<QWindowsWindow *>(window->handle());
|
||||
|
||||
QWindow *currentWindowUnderPointer = platformWindow->hasMouseCapture() ?
|
||||
QWindowsScreen::windowAt(globalPos, CWP_SKIPINVISIBLE | CWP_SKIPTRANSPARENT) : window;
|
||||
|
||||
while (currentWindowUnderPointer && currentWindowUnderPointer->flags() & Qt::WindowTransparentForInput)
|
||||
currentWindowUnderPointer = currentWindowUnderPointer->parent();
|
||||
|
@ -91,7 +91,7 @@ static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data)
|
||||
} else {
|
||||
if (const HDC hdc = CreateDC(info.szDevice, nullptr, nullptr, nullptr)) {
|
||||
const QDpi dpi = monitorDPI(hMonitor);
|
||||
data->dpi = dpi.first ? dpi : deviceDPI(hdc);
|
||||
data->dpi = dpi.first > 0 ? dpi : deviceDPI(hdc);
|
||||
data->depth = GetDeviceCaps(hdc, BITSPIXEL);
|
||||
data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32;
|
||||
data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE));
|
||||
|
@ -851,10 +851,12 @@ static QSize toNativeSizeConstrained(QSize dip, const QWindow *w)
|
||||
{
|
||||
if (QHighDpiScaling::isActive()) {
|
||||
const qreal factor = QHighDpiScaling::factor(w);
|
||||
if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
|
||||
dip.rwidth() *= factor;
|
||||
if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
|
||||
dip.rheight() *= factor;
|
||||
if (!qFuzzyCompare(factor, qreal(1))) {
|
||||
if (dip.width() > 0 && dip.width() < QWINDOWSIZE_MAX)
|
||||
dip.setWidth(qRound(qreal(dip.width()) * factor));
|
||||
if (dip.height() > 0 && dip.height() < QWINDOWSIZE_MAX)
|
||||
dip.setHeight(qRound(qreal(dip.height()) * factor));
|
||||
}
|
||||
}
|
||||
return dip;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ void setVariantBool(bool value, VARIANT *variant)
|
||||
void setVariantDouble(double value, VARIANT *variant)
|
||||
{
|
||||
variant->vt = VT_R8;
|
||||
variant->boolVal = value;
|
||||
variant->dblVal = value;
|
||||
}
|
||||
|
||||
BSTR bStrFromQString(const QString &value)
|
||||
|
@ -161,18 +161,6 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(NotificationReceiver);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)scrollBarStyleDidChange:(NSNotification *)notification
|
||||
{
|
||||
Q_UNUSED(notification);
|
||||
|
||||
// purge destroyed scroll bars:
|
||||
QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>());
|
||||
|
||||
QEvent event(QEvent::StyleChange);
|
||||
for (const auto &o : QMacStylePrivate::scrollBars)
|
||||
QCoreApplication::sendEvent(o, &event);
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
|
||||
change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
|
||||
{
|
||||
@ -2095,11 +2083,18 @@ QMacStyle::QMacStyle()
|
||||
Q_D(QMacStyle);
|
||||
QMacAutoReleasePool pool;
|
||||
|
||||
static QMacNotificationObserver scrollbarStyleObserver(nil,
|
||||
NSPreferredScrollerStyleDidChangeNotification, []() {
|
||||
// Purge destroyed scroll bars
|
||||
QMacStylePrivate::scrollBars.removeAll(QPointer<QObject>());
|
||||
|
||||
QEvent event(QEvent::StyleChange);
|
||||
for (const auto &o : QMacStylePrivate::scrollBars)
|
||||
QCoreApplication::sendEvent(o, &event);
|
||||
});
|
||||
|
||||
d->receiver = [[NotificationReceiver alloc] initWithPrivateStyle:d];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:d->receiver
|
||||
selector:@selector(scrollBarStyleDidChange:)
|
||||
name:NSPreferredScrollerStyleDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave) {
|
||||
[NSApplication.sharedApplication addObserver:d->receiver forKeyPath:@"effectiveAppearance"
|
||||
@ -2113,7 +2108,6 @@ QMacStyle::~QMacStyle()
|
||||
Q_D(QMacStyle);
|
||||
QMacAutoReleasePool pool;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:d->receiver];
|
||||
#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
|
||||
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::MacOSMojave)
|
||||
[NSApplication.sharedApplication removeObserver:d->receiver forKeyPath:@"effectiveAppearance"];
|
||||
@ -3152,7 +3146,9 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
|
||||
theStroker.setCapStyle(Qt::FlatCap);
|
||||
theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
|
||||
path = theStroker.createStroke(path);
|
||||
p->fillPath(path, QColor(0, 0, 0, 119));
|
||||
const auto dark = qt_mac_applicationIsInDarkMode() ? opt->palette.dark().color().darker()
|
||||
: QColor(0, 0, 0, 119);
|
||||
p->fillPath(path, dark);
|
||||
}
|
||||
break;
|
||||
case PE_FrameWindow:
|
||||
|
@ -146,7 +146,10 @@ static const int maxSizeSection = 1048575; // since section size is in a bitfiel
|
||||
Not all \l{Qt::}{ItemDataRole}s will have an effect on a
|
||||
QHeaderView. If you need to draw other roles, you can subclass
|
||||
QHeaderView and reimplement \l{QHeaderView::}{paintEvent()}.
|
||||
QHeaderView respects the following item data roles:
|
||||
QHeaderView respects the following item data roles, unless they are
|
||||
in conflict with the style (which can happen for styles that follow
|
||||
the desktop theme):
|
||||
|
||||
\l{Qt::}{TextAlignmentRole}, \l{Qt::}{DisplayRole},
|
||||
\l{Qt::}{FontRole}, \l{Qt::}{DecorationRole},
|
||||
\l{Qt::}{ForegroundRole}, and \l{Qt::}{BackgroundRole}.
|
||||
|
@ -2769,8 +2769,16 @@ void QFusionStyle::drawComplexControl(ComplexControl control, const QStyleOption
|
||||
buttonOption.state &= ~State_MouseOver;
|
||||
}
|
||||
|
||||
if (comboBox->frame)
|
||||
if (comboBox->frame) {
|
||||
cachePainter.save();
|
||||
cachePainter.setRenderHint(QPainter::Antialiasing, true);
|
||||
cachePainter.translate(0.5, 0.5);
|
||||
cachePainter.setPen(Qt::NoPen);
|
||||
cachePainter.setBrush(buttonOption.palette.base());
|
||||
cachePainter.drawRoundedRect(rect.adjusted(0, 0, -1, -1), 2, 2);
|
||||
cachePainter.restore();
|
||||
proxy()->drawPrimitive(PE_FrameLineEdit, &buttonOption, &cachePainter, widget);
|
||||
}
|
||||
|
||||
// Draw button clipped
|
||||
cachePainter.save();
|
||||
|
@ -1957,6 +1957,7 @@ void QPlainTextEdit::paintEvent(QPaintEvent *e)
|
||||
}
|
||||
|
||||
QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
|
||||
painter.setPen(context.palette.text().color());
|
||||
|
||||
while (block.isValid()) {
|
||||
|
||||
|
@ -61,6 +61,9 @@
|
||||
#include "private/qtextdocument_p.h"
|
||||
#include "qtextlist.h"
|
||||
#include "private/qwidgettextcontrol_p.h"
|
||||
#if QT_CONFIG(style_stylesheet)
|
||||
# include "private/qstylesheetstyle_p.h"
|
||||
#endif
|
||||
#if QT_CONFIG(graphicsview)
|
||||
#include "qgraphicssceneevent.h"
|
||||
#endif
|
||||
@ -3198,6 +3201,15 @@ QAbstractTextDocumentLayout::PaintContext QWidgetTextControl::getPaintContext(QW
|
||||
|
||||
ctx.selections = d->extraSelections;
|
||||
ctx.palette = d->palette;
|
||||
#if QT_CONFIG(style_stylesheet)
|
||||
if (widget) {
|
||||
if (auto cssStyle = qt_styleSheet(widget->style())) {
|
||||
QStyleOption option;
|
||||
option.initFrom(widget);
|
||||
cssStyle->styleSheetPalette(widget, &option, &ctx.palette);
|
||||
}
|
||||
}
|
||||
#endif // style_stylesheet
|
||||
if (d->cursorOn && d->isEnabled) {
|
||||
if (d->hideCursor)
|
||||
ctx.cursorPosition = -1;
|
||||
|
@ -146,7 +146,12 @@ endif()
|
||||
|
||||
expect_pass(test_interface_link_libraries)
|
||||
expect_pass(test_moc_macro_target)
|
||||
expect_pass(test_add_big_resource)
|
||||
|
||||
if (NOT CMAKE_VERSION VERSION_LESS 3.9)
|
||||
# The modification of TARGET_OBJECTS needs the following change in cmake
|
||||
# https://gitlab.kitware.com/cmake/cmake/commit/93c89bc75ceee599ba7c08b8fe1ac5104942054f
|
||||
expect_pass(test_add_big_resource)
|
||||
endif()
|
||||
|
||||
if (NOT CMAKE_VERSION VERSION_LESS 3.8)
|
||||
# With earlier CMake versions, this test would simply run moc multiple times and lead to:
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
project(test_add_big_resource)
|
||||
|
||||
|
@ -172,9 +172,9 @@ void tst_qfloat16::qNan()
|
||||
QVERIFY(qIsInf(-inf));
|
||||
QVERIFY(qIsInf(2.f*inf));
|
||||
QVERIFY(qIsInf(inf*2.f));
|
||||
// QTBUG-75812: QEMU's over-optimized arm64 flakily fails 1/inf == 0 :-(
|
||||
// QTBUG-75812: QEMU/arm64 compiler over-optimizes, so flakily fails 1/inf == 0 :-(
|
||||
if (qfloat16(9.785e-4f) == qfloat16(9.794e-4f))
|
||||
QCOMPARE(qfloat16(1.f/inf), qfloat16(0.f));
|
||||
QCOMPARE(qfloat16(1.f) / inf, qfloat16(0.f));
|
||||
#ifdef Q_CC_INTEL
|
||||
QEXPECT_FAIL("", "ICC optimizes zero * anything to zero", Continue);
|
||||
#endif
|
||||
|
@ -205,15 +205,23 @@ void tst_QFileSelector::urlConvenience_data()
|
||||
|
||||
QString test("/test");// '/' is here so dir string can also be selector string
|
||||
QString custom1("custom1");
|
||||
QString testWithQueryAndFragment("/test?query#Fragment");
|
||||
|
||||
QTest::newRow("qrc") << QUrl("qrc:///extras/test") << (QStringList() << custom1)
|
||||
<< QUrl(QString("qrc:///extras/") + QLatin1Char(selectorIndicator) + custom1 + test);
|
||||
QTest::newRow("qrc with query and fragment") << QUrl(QString::fromLatin1("qrc:///extras%1").arg(testWithQueryAndFragment)) << (QStringList() << custom1)
|
||||
<< QUrl(QString("qrc:///extras/") + QLatin1Char(selectorIndicator) + custom1 + testWithQueryAndFragment);
|
||||
|
||||
QString fileBasePath = QFINDTESTDATA("extras/test");
|
||||
QString fileSelectedPath = QFINDTESTDATA(QString("extras/") + QLatin1Char(selectorIndicator)
|
||||
+ custom1 + QString("/test"));
|
||||
QTest::newRow("file") << QUrl::fromLocalFile(fileBasePath) << (QStringList() << custom1)
|
||||
<< QUrl::fromLocalFile(fileSelectedPath);
|
||||
// do not strip off the query and fragment
|
||||
QString strUrlWithFragment = QString("file://") + testWithQueryAndFragment;
|
||||
QTest::newRow("file with query and fragment") << QUrl(strUrlWithFragment) << (QStringList()) << QUrl(strUrlWithFragment);
|
||||
strUrlWithFragment = QString("file:") + testWithQueryAndFragment;
|
||||
QTest::newRow("file with query and fragment too") << QUrl(strUrlWithFragment) << (QStringList()) << QUrl(strUrlWithFragment);
|
||||
|
||||
// http://qt-project.org/images/qtdn/sprites-combined-latest.png is chosen as a representative real world URL
|
||||
// But note that this test is checking that http urls are NOT selected so it shouldn't be checked
|
||||
|
@ -183,6 +183,7 @@ private slots:
|
||||
|
||||
void floatingPointPrecision();
|
||||
|
||||
void compatibility_Qt5();
|
||||
void compatibility_Qt3();
|
||||
void compatibility_Qt2();
|
||||
|
||||
@ -269,17 +270,17 @@ static int NColorRoles[] = {
|
||||
QPalette::HighlightedText + 1, // Qt_4_0, Qt_4_1
|
||||
QPalette::HighlightedText + 1, // Qt_4_2
|
||||
QPalette::AlternateBase + 1, // Qt_4_3
|
||||
QPalette::PlaceholderText + 1, // Qt_4_4
|
||||
QPalette::PlaceholderText + 1, // Qt_4_5
|
||||
QPalette::PlaceholderText + 1, // Qt_4_6
|
||||
QPalette::PlaceholderText + 1, // Qt_5_0
|
||||
QPalette::PlaceholderText + 1, // Qt_5_1
|
||||
QPalette::PlaceholderText + 1, // Qt_5_2
|
||||
QPalette::PlaceholderText + 1, // Qt_5_3
|
||||
QPalette::PlaceholderText + 1, // Qt_5_4
|
||||
QPalette::PlaceholderText + 1, // Qt_5_5
|
||||
QPalette::PlaceholderText + 1, // Qt_5_6
|
||||
0 // add the correct value for Qt_5_7 here later
|
||||
QPalette::ToolTipText + 1, // Qt_4_4
|
||||
QPalette::ToolTipText + 1, // Qt_4_5
|
||||
QPalette::ToolTipText + 1, // Qt_4_6, Qt_4_7, Qt_4_8, Qt_4_9
|
||||
QPalette::ToolTipText + 1, // Qt_5_0
|
||||
QPalette::ToolTipText + 1, // Qt_5_1
|
||||
QPalette::ToolTipText + 1, // Qt_5_2, Qt_5_3
|
||||
QPalette::ToolTipText + 1, // Qt_5_4, Qt_5_5
|
||||
QPalette::ToolTipText + 1, // Qt_5_6, Qt_5_7, Qt_5_8, Qt_5_9, Qt_5_10, Qt_5_11
|
||||
QPalette::PlaceholderText + 1, // Qt_5_12
|
||||
QPalette::PlaceholderText + 1, // Qt_5_13
|
||||
0 // add the correct value for Qt_5_14 here later
|
||||
};
|
||||
|
||||
// Testing get/set functions
|
||||
@ -3243,6 +3244,37 @@ void tst_QDataStream::streamRealDataTypes()
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDataStream::compatibility_Qt5()
|
||||
{
|
||||
QLinearGradient gradient(QPointF(0,0), QPointF(1,1));
|
||||
gradient.setColorAt(0, Qt::red);
|
||||
gradient.setColorAt(1, Qt::blue);
|
||||
|
||||
QBrush brush(gradient);
|
||||
QPalette palette;
|
||||
palette.setBrush(QPalette::Button, brush);
|
||||
palette.setColor(QPalette::Light, Qt::green);
|
||||
|
||||
QByteArray stream;
|
||||
{
|
||||
QDataStream out(&stream, QIODevice::WriteOnly);
|
||||
out.setVersion(QDataStream::Qt_5_7);
|
||||
out << palette;
|
||||
out << brush;
|
||||
}
|
||||
QBrush in_brush;
|
||||
QPalette in_palette;
|
||||
{
|
||||
QDataStream in(stream);
|
||||
in.setVersion(QDataStream::Qt_5_7);
|
||||
in >> in_palette;
|
||||
in >> in_brush;
|
||||
}
|
||||
QCOMPARE(in_brush.style(), Qt::LinearGradientPattern);
|
||||
QCOMPARE(in_palette.brush(QPalette::Button).style(), Qt::LinearGradientPattern);
|
||||
QCOMPARE(in_palette.color(QPalette::Light), QColor(Qt::green));
|
||||
}
|
||||
|
||||
void tst_QDataStream::compatibility_Qt3()
|
||||
{
|
||||
QByteArray ba("hello");
|
||||
|
@ -834,6 +834,13 @@ void tst_QRegExp::testEscapingWildcard_data(){
|
||||
QTest::newRow("a true '\\' in input") << "\\Qt;" << "\\Qt;" << true;
|
||||
QTest::newRow("two true '\\' in input") << "\\\\Qt;" << "\\\\Qt;" << true;
|
||||
QTest::newRow("a '\\' at the end") << "\\\\Qt;\\" << "\\\\Qt;\\" << true;
|
||||
|
||||
QTest::newRow("[]\\] matches ]") << "[]\\]" << "]" << true;
|
||||
QTest::newRow("[]\\] matches \\") << "[]\\]" << "\\" << true;
|
||||
QTest::newRow("[]\\] does not match [") << "[]\\]" << "[" << false;
|
||||
QTest::newRow("[]\\]a matches ]a") << "[]\\]a" << "]a" << true;
|
||||
QTest::newRow("[]\\]a matches \\a") << "[]\\]a" << "\\a" << true;
|
||||
QTest::newRow("[]\\]a does not match [a") << "[]\\]a" << "[a" << false;
|
||||
}
|
||||
|
||||
void tst_QRegExp::testEscapingWildcard(){
|
||||
|
109
tests/auto/shared/highdpi.h
Normal file
109
tests/auto/shared/highdpi.h
Normal file
@ -0,0 +1,109 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef HIGHDPI_H
|
||||
#define HIGHDPI_H
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qpoint.h>
|
||||
#include <QtCore/qrect.h>
|
||||
#include <QtCore/qsize.h>
|
||||
|
||||
// Helpers for comparing geometries a that may go through scaling in the
|
||||
// platform plugin with fuzz (pass rounded-down device pixel ratios or
|
||||
// scaling factors). Error message for use with QVERIFY2() are also provided.
|
||||
|
||||
class HighDpi
|
||||
{
|
||||
public:
|
||||
HighDpi() = delete;
|
||||
HighDpi(const HighDpi &) = delete;
|
||||
HighDpi &operator=(const HighDpi &) = delete;
|
||||
HighDpi(HighDpi &&) = delete;
|
||||
HighDpi &operator=(HighDpi &&) = delete;
|
||||
~HighDpi() = delete;
|
||||
|
||||
static int manhattanDelta(const QPoint &p1, const QPoint p2)
|
||||
{
|
||||
return (p1 - p2).manhattanLength();
|
||||
}
|
||||
|
||||
static bool fuzzyCompare(const QPoint &p1, const QPoint p2, int fuzz)
|
||||
{
|
||||
return manhattanDelta(p1, p2) <= fuzz;
|
||||
}
|
||||
|
||||
static QByteArray msgPointMismatch(const QPoint &p1, const QPoint p2)
|
||||
{
|
||||
return QByteArray::number(p1.x()) + ',' + QByteArray::number(p1.y())
|
||||
+ " != " + QByteArray::number(p2.x()) + ',' + QByteArray::number(p2.y())
|
||||
+ ", manhattanLength=" + QByteArray::number(manhattanDelta(p1, p2));
|
||||
}
|
||||
|
||||
// Compare a size that may go through scaling in the platform plugin with fuzz.
|
||||
|
||||
static inline int manhattanDelta(const QSize &s1, const QSize &s2)
|
||||
{
|
||||
return qAbs(s1.width() - s2.width()) + qAbs(s1.height() - s2.height());
|
||||
}
|
||||
|
||||
static inline bool fuzzyCompare(const QSize &s1, const QSize &s2, int fuzz)
|
||||
{
|
||||
return manhattanDelta(s1, s2) <= fuzz;
|
||||
}
|
||||
|
||||
static QByteArray msgSizeMismatch(const QSize &s1, const QSize &s2)
|
||||
{
|
||||
return QByteArray::number(s1.width()) + 'x' + QByteArray::number(s1.height())
|
||||
+ " != " + QByteArray::number(s2.width()) + 'x' + QByteArray::number(s2.height())
|
||||
+ ", manhattanLength=" + QByteArray::number(manhattanDelta(s1, s2));
|
||||
}
|
||||
|
||||
// Compare a geometry that may go through scaling in the platform plugin with fuzz.
|
||||
|
||||
static inline bool fuzzyCompare(const QRect &r1, const QRect &r2, int fuzz)
|
||||
{
|
||||
return manhattanDelta(r1.topLeft(), r2.topLeft()) <= fuzz
|
||||
&& manhattanDelta(r1.size(), r2.size()) <= fuzz;
|
||||
}
|
||||
|
||||
static QByteArray msgRectMismatch(const QRect &r1, const QRect &r2)
|
||||
{
|
||||
return formatRect(r1) + " != " + formatRect(r2);
|
||||
}
|
||||
|
||||
private:
|
||||
static QByteArray formatRect(const QRect &r)
|
||||
{
|
||||
return QByteArray::number(r.width()) + 'x' + QByteArray::number(r.height())
|
||||
+ (r.left() < 0 ? '-' : '+') + QByteArray::number(r.left())
|
||||
+ (r.top() < 0 ? '-' : '+') + QByteArray::number(r.top());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // HIGHDPI_H
|
@ -26,6 +26,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "../../../shared/highdpi.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
@ -388,8 +389,10 @@ void tst_QDialog::toolDialogPosition()
|
||||
dialog.move(QPoint(100,100));
|
||||
const QPoint beforeShowPosition = dialog.pos();
|
||||
dialog.show();
|
||||
const int fuzz = int(dialog.devicePixelRatioF());
|
||||
const QPoint afterShowPosition = dialog.pos();
|
||||
QCOMPARE(afterShowPosition, beforeShowPosition);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(afterShowPosition, beforeShowPosition, fuzz),
|
||||
HighDpi::msgPointMismatch(afterShowPosition, beforeShowPosition).constData());
|
||||
}
|
||||
|
||||
class Dialog : public QDialog
|
||||
|
@ -2551,7 +2551,7 @@ void tst_QWizard::task183550_stretchFactor()
|
||||
page2->enableVerticalExpansion();
|
||||
wizard.next();
|
||||
QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page2));
|
||||
QVERIFY(page2->treeWidgetHeight() > page2->treeWidgetSizeHintHeight());
|
||||
QVERIFY(page2->treeWidgetHeight() >= page2->treeWidgetSizeHintHeight());
|
||||
|
||||
wizard.back();
|
||||
QCOMPARE(wizard.currentPage(), static_cast<QWizardPage*>(page1));
|
||||
|
@ -26,6 +26,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "../../../shared/highdpi.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
@ -51,6 +52,8 @@
|
||||
#include <QPlainTextEdit>
|
||||
#include <QDialog>
|
||||
|
||||
#include <qscreen.h>
|
||||
|
||||
#include <QtWidgets/private/qabstractitemdelegate_p.h>
|
||||
|
||||
Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint)
|
||||
@ -223,8 +226,8 @@ private slots:
|
||||
void dateTextForRole_data();
|
||||
void dateTextForRole();
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
private:
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
struct RoleDelegate : public QItemDelegate
|
||||
{
|
||||
QString textForRole(Qt::ItemDataRole role, const QVariant &value, const QLocale &locale)
|
||||
@ -234,6 +237,8 @@ private:
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
const int m_fuzz = int(QGuiApplication::primaryScreen()->devicePixelRatio());
|
||||
};
|
||||
|
||||
|
||||
@ -286,8 +291,8 @@ void tst_QItemDelegate::textRectangle()
|
||||
QFont font;
|
||||
TestItemDelegate delegate;
|
||||
QRect result = delegate.textRectangle(0, rect, font, text);
|
||||
|
||||
QCOMPARE(result, expected);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(result, expected, m_fuzz),
|
||||
HighDpi::msgRectMismatch(result, expected).constData());
|
||||
}
|
||||
|
||||
void tst_QItemDelegate::sizeHint_data()
|
||||
|
@ -26,6 +26,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "../../../shared/highdpi.h"
|
||||
|
||||
#include <qboxlayout.h>
|
||||
#include <qapplication.h>
|
||||
@ -140,19 +141,6 @@ static QByteArray msgComparisonFailed(T v1, const char *op, T v2)
|
||||
return s.toLocal8Bit();
|
||||
}
|
||||
|
||||
// Compare a window position that may go through scaling in the platform plugin with fuzz.
|
||||
static inline bool qFuzzyCompareWindowPosition(const QPoint &p1, const QPoint p2, int fuzz)
|
||||
{
|
||||
return (p1 - p2).manhattanLength() <= fuzz;
|
||||
}
|
||||
|
||||
static QString msgPointMismatch(const QPoint &p1, const QPoint p2)
|
||||
{
|
||||
QString result;
|
||||
QDebug(&result) << p1 << "!=" << p2 << ", manhattanLength=" << (p1 - p2).manhattanLength();
|
||||
return result;
|
||||
}
|
||||
|
||||
class tst_QWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -418,6 +406,7 @@ private:
|
||||
QPoint m_safeCursorPos;
|
||||
const bool m_windowsAnimationsEnabled;
|
||||
QTouchDevice *m_touchScreen;
|
||||
const int m_fuzz;
|
||||
};
|
||||
|
||||
bool tst_QWidget::ensureScreenSize(int width, int height)
|
||||
@ -576,6 +565,7 @@ tst_QWidget::tst_QWidget()
|
||||
, m_safeCursorPos(0, 0)
|
||||
, m_windowsAnimationsEnabled(windowsAnimationsEnabled())
|
||||
, m_touchScreen(QTest::createTouchDevice())
|
||||
, m_fuzz(int(QGuiApplication::primaryScreen()->devicePixelRatio()))
|
||||
{
|
||||
if (m_windowsAnimationsEnabled) // Disable animations which can interfere with screen grabbing in moveChild(), showAndMoveChild()
|
||||
setWindowsAnimationsEnabled(false);
|
||||
@ -2049,10 +2039,9 @@ void tst_QWidget::windowState()
|
||||
|
||||
widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
|
||||
QTest::qWait(100);
|
||||
const int fuzz = int(QHighDpiScaling::factor(widget1.windowHandle()));
|
||||
QVERIFY(!(widget1.windowState() & Qt::WindowMaximized));
|
||||
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
|
||||
qPrintable(msgPointMismatch(widget1.pos(), pos)));
|
||||
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
|
||||
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
|
||||
|
||||
widget1.setWindowState(Qt::WindowMinimized);
|
||||
@ -2073,8 +2062,8 @@ void tst_QWidget::windowState()
|
||||
widget1.setWindowState(widget1.windowState() ^ Qt::WindowMaximized);
|
||||
QTest::qWait(100);
|
||||
QVERIFY(!(widget1.windowState() & (Qt::WindowMinimized|Qt::WindowMaximized)));
|
||||
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
|
||||
qPrintable(msgPointMismatch(widget1.pos(), pos)));
|
||||
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
|
||||
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
|
||||
|
||||
widget1.setWindowState(Qt::WindowFullScreen);
|
||||
@ -2095,8 +2084,8 @@ void tst_QWidget::windowState()
|
||||
widget1.setWindowState(Qt::WindowNoState);
|
||||
QTest::qWait(100);
|
||||
VERIFY_STATE(Qt::WindowNoState);
|
||||
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
|
||||
qPrintable(msgPointMismatch(widget1.pos(), pos)));
|
||||
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
|
||||
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
|
||||
|
||||
widget1.setWindowState(Qt::WindowFullScreen);
|
||||
@ -2129,8 +2118,8 @@ void tst_QWidget::windowState()
|
||||
QVERIFY(!(widget1.windowState() & stateMask));
|
||||
QCOMPARE(widget1.windowHandle()->windowState(), Qt::WindowNoState);
|
||||
|
||||
QTRY_VERIFY2(qFuzzyCompareWindowPosition(widget1.pos(), pos, fuzz),
|
||||
qPrintable(msgPointMismatch(widget1.pos(), pos)));
|
||||
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget1.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget1.pos(), pos)));
|
||||
QTRY_COMPARE(widget1.size(), size);
|
||||
}
|
||||
|
||||
@ -2323,7 +2312,7 @@ void tst_QWidget::resizeEvent()
|
||||
{
|
||||
QWidget wParent;
|
||||
wParent.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
||||
wParent.resize(200, 200);
|
||||
wParent.resize(m_testWidgetSize);
|
||||
ResizeWidget wChild(&wParent);
|
||||
wParent.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&wParent));
|
||||
@ -2341,7 +2330,7 @@ void tst_QWidget::resizeEvent()
|
||||
{
|
||||
ResizeWidget wTopLevel;
|
||||
wTopLevel.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
||||
wTopLevel.resize(200, 200);
|
||||
wTopLevel.resize(m_testWidgetSize);
|
||||
wTopLevel.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&wTopLevel));
|
||||
if (m_platform == QStringLiteral("winrt"))
|
||||
@ -2379,17 +2368,20 @@ void tst_QWidget::showMinimized()
|
||||
#ifdef Q_OS_WINRT
|
||||
QEXPECT_FAIL("", "Winrt does not support move and resize", Abort);
|
||||
#endif
|
||||
QCOMPARE(plain.pos(), pos);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
|
||||
|
||||
plain.showNormal();
|
||||
QVERIFY(!plain.isMinimized());
|
||||
QVERIFY(plain.isVisible());
|
||||
QCOMPARE(plain.pos(), pos);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
|
||||
|
||||
plain.showMinimized();
|
||||
QVERIFY(plain.isMinimized());
|
||||
QVERIFY(plain.isVisible());
|
||||
QCOMPARE(plain.pos(), pos);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(plain.pos(), pos, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(plain.pos(), pos)));
|
||||
|
||||
plain.hide();
|
||||
QVERIFY(plain.isMinimized());
|
||||
@ -2781,7 +2773,9 @@ void tst_QWidget::setGeometry()
|
||||
tlw.setWindowTitle(QLatin1String(QTest::currentTestFunction()));
|
||||
QWidget child(&tlw);
|
||||
|
||||
QRect tr(100,100,200,200);
|
||||
const QPoint topLeft = QGuiApplication::primaryScreen()->availableGeometry().topLeft();
|
||||
const QSize initialSize = 2 * m_testWidgetSize;
|
||||
QRect tr(topLeft + QPoint(100,100), initialSize);
|
||||
QRect cr(50,50,50,50);
|
||||
tlw.setGeometry(tr);
|
||||
child.setGeometry(cr);
|
||||
@ -2792,8 +2786,7 @@ void tst_QWidget::setGeometry()
|
||||
QCOMPARE(child.geometry(), cr);
|
||||
|
||||
tlw.setParent(nullptr, Qt::Window|Qt::FramelessWindowHint);
|
||||
tr = QRect(0,0,100,100);
|
||||
tr.moveTopLeft(QGuiApplication::primaryScreen()->availableGeometry().topLeft());
|
||||
tr = QRect(topLeft, initialSize / 2);
|
||||
tlw.setGeometry(tr);
|
||||
QCOMPARE(tlw.geometry(), tr);
|
||||
tlw.showNormal();
|
||||
@ -3265,7 +3258,8 @@ void tst_QWidget::saveRestoreGeometry()
|
||||
|
||||
if (m_platform == QStringLiteral("winrt"))
|
||||
QEXPECT_FAIL("", "WinRT does not support move/resize", Abort);
|
||||
QTRY_COMPARE(widget.pos(), position);
|
||||
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
|
||||
QCOMPARE(widget.size(), size);
|
||||
savedGeometry = widget.saveGeometry();
|
||||
}
|
||||
@ -3293,10 +3287,12 @@ void tst_QWidget::saveRestoreGeometry()
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&widget));
|
||||
QApplication::processEvents();
|
||||
|
||||
QTRY_COMPARE(widget.pos(), position);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
|
||||
QCOMPARE(widget.size(), size);
|
||||
widget.show();
|
||||
QCOMPARE(widget.pos(), position);
|
||||
QVERIFY2(HighDpi::fuzzyCompare(widget.pos(), position, m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget.pos(), position)));
|
||||
QCOMPARE(widget.size(), size);
|
||||
}
|
||||
|
||||
@ -3410,6 +3406,9 @@ void tst_QWidget::restoreVersion1Geometry()
|
||||
QFETCH(QSize, expectedSize);
|
||||
QFETCH(QRect, expectedNormalGeometry);
|
||||
|
||||
if (m_platform == QLatin1String("windows") && QGuiApplication::primaryScreen()->geometry().width() > 2000)
|
||||
QSKIP("Skipping due to minimum decorated window size on Windows");
|
||||
|
||||
// WindowActive is uninteresting for this test
|
||||
const Qt::WindowStates WindowStateMask = Qt::WindowFullScreen | Qt::WindowMaximized | Qt::WindowMinimized;
|
||||
|
||||
@ -4990,7 +4989,8 @@ void tst_QWidget::windowMoveResize()
|
||||
widget.showNormal();
|
||||
|
||||
QTest::qWait(10);
|
||||
QTRY_COMPARE(widget.pos(), rect.topLeft());
|
||||
QTRY_VERIFY2(HighDpi::fuzzyCompare(widget.pos(), rect.topLeft(), m_fuzz),
|
||||
qPrintable(HighDpi::msgPointMismatch(widget.pos(), rect.topLeft())));
|
||||
// Windows: Minimum size of decorated windows.
|
||||
const bool expectResizeFail = (!windowFlags && (rect.width() < 160 || rect.height() < 40))
|
||||
&& m_platform == QStringLiteral("windows");
|
||||
|
@ -27,6 +27,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "debugproxystyle.h"
|
||||
#include "eventfilter.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
@ -73,7 +74,7 @@ QDebug operator<<(QDebug debug, const QStyleOption *option)
|
||||
debug << ", state=" << option->state;
|
||||
#if QT_VERSION >= 0x050000
|
||||
if (option->styleObject && !option->styleObject->isWidgetType())
|
||||
debug << ", styleObject=" << option->styleObject;
|
||||
debug << ", styleObject=" << QtDiag::formatQObject(option->styleObject);
|
||||
#endif
|
||||
debug << ')';
|
||||
return debug;
|
||||
@ -97,19 +98,19 @@ DebugProxyStyle::DebugProxyStyle(QStyle *style) : QProxyStyle(style)
|
||||
|
||||
void DebugProxyStyle::drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
||||
{
|
||||
qDebug() << __FUNCTION__ << "element=" << element << option << widget;
|
||||
qDebug() << __FUNCTION__ << "element=" << element << option << QtDiag::formatQObject(widget);
|
||||
QProxyStyle::drawPrimitive( element, option, painter, widget);
|
||||
}
|
||||
|
||||
void DebugProxyStyle::drawControl(QStyle::ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const
|
||||
{
|
||||
qDebug() << __FUNCTION__ << "element=" << element << option << widget;
|
||||
qDebug() << __FUNCTION__ << "element=" << element << option << QtDiag::formatQObject(widget);
|
||||
QProxyStyle::drawControl(element, option, painter, widget);
|
||||
}
|
||||
|
||||
void DebugProxyStyle::drawComplexControl(QStyle::ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const
|
||||
{
|
||||
qDebug() << __FUNCTION__ << "control=" << control << option << widget;
|
||||
qDebug() << __FUNCTION__ << "control=" << control << option << QtDiag::formatQObject(widget);
|
||||
QProxyStyle::drawComplexControl(control, option, painter, widget);
|
||||
}
|
||||
|
||||
@ -122,21 +123,24 @@ void DebugProxyStyle::drawItemPixmap(QPainter *painter, const QRect &rect, int a
|
||||
QSize DebugProxyStyle::sizeFromContents(QStyle::ContentsType type, const QStyleOption *option, const QSize &size, const QWidget *widget) const
|
||||
{
|
||||
const QSize result = QProxyStyle::sizeFromContents(type, option, size, widget);
|
||||
qDebug() << __FUNCTION__ << size << "type=" << type << option << widget << "returns" << result;
|
||||
qDebug() << __FUNCTION__ << size << "type=" << type << option
|
||||
<< QtDiag::formatQObject(widget) << "returns" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
QRect DebugProxyStyle::subElementRect(QStyle::SubElement element, const QStyleOption *option, const QWidget *widget) const
|
||||
{
|
||||
const QRect result = QProxyStyle::subElementRect(element, option, widget);
|
||||
qDebug() << __FUNCTION__ << "element=" << element << option << widget << "returns" << result;
|
||||
qDebug() << __FUNCTION__ << "element=" << element << option
|
||||
<< QtDiag::formatQObject(widget) << "returns" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
QRect DebugProxyStyle::subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget) const
|
||||
{
|
||||
const QRect result = QProxyStyle::subControlRect(cc, opt, sc, widget);
|
||||
qDebug() << __FUNCTION__ << "cc=" << cc << "sc=" << sc << opt << widget << "returns" << result;
|
||||
qDebug() << __FUNCTION__ << "cc=" << cc << "sc=" << sc << opt
|
||||
<< QtDiag::formatQObject(widget) << "returns" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -159,7 +163,7 @@ int DebugProxyStyle::styleHint(StyleHint hint, const QStyleOption *option, const
|
||||
QStyleHintReturn *returnData) const
|
||||
{
|
||||
const int result = QProxyStyle::styleHint(hint, option, widget, returnData);
|
||||
qDebug() << __FUNCTION__ << hint << option << widget << "returnData="
|
||||
qDebug() << __FUNCTION__ << hint << option << QtDiag::formatQObject(widget) << "returnData="
|
||||
<< returnData << "returns" << result;
|
||||
return result;
|
||||
}
|
||||
@ -167,7 +171,8 @@ int DebugProxyStyle::styleHint(StyleHint hint, const QStyleOption *option, const
|
||||
int DebugProxyStyle::pixelMetric(QStyle::PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
|
||||
{
|
||||
const int result = QProxyStyle::pixelMetric(metric, option, widget);
|
||||
qDebug() << __FUNCTION__ << "metric=" << metric << option << widget << "returns" << result;
|
||||
qDebug() << __FUNCTION__ << "metric=" << metric << option
|
||||
<< QtDiag::formatQObject(widget) << "returns" << result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ static inline bool matchesType(const QObject *o, EventFilter::ObjectTypes types)
|
||||
return types & EventFilter::OtherType;
|
||||
}
|
||||
|
||||
static void formatObject(const QObject *o, QDebug debug)
|
||||
void EventFilter::formatObject(const QObject *o, QDebug debug)
|
||||
{
|
||||
if (o) {
|
||||
debug << o->metaObject()->className();
|
||||
@ -166,32 +166,30 @@ static void formatObject(const QObject *o, QDebug debug)
|
||||
}
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug d, const formatQObject &fo)
|
||||
{
|
||||
EventFilter::formatObject(fo.m_object, d);
|
||||
return d;
|
||||
}
|
||||
|
||||
static void formatApplicationState(QDebug debug)
|
||||
{
|
||||
#if defined(HAVE_APPLICATION)
|
||||
if (const QWidget *mw = QApplication::activeModalWidget()) {
|
||||
debug << "\n QApplication::activeModalWidget = ";
|
||||
formatObject(mw, debug);
|
||||
}
|
||||
if (const QWidget *pw = QApplication::activePopupWidget()) {
|
||||
debug << "\n QApplication::activePopupWidget = ";
|
||||
formatObject(pw, debug);
|
||||
}
|
||||
debug << "\n QApplication::activeWindow = ";
|
||||
formatObject(QApplication::activeWindow(), debug);
|
||||
if (const QWidget *mw = QApplication::activeModalWidget())
|
||||
debug << "\n QApplication::activeModalWidget = " << formatQObject(mw);
|
||||
if (const QWidget *pw = QApplication::activePopupWidget())
|
||||
debug << "\n QApplication::activePopupWidget = " << formatQObject(pw);
|
||||
debug << "\n QApplication::activeWindow = " << formatQObject(QApplication::activeWindow());
|
||||
#endif // HAVE_APPLICATION
|
||||
#if defined(HAVE_GUI_APPLICATION)
|
||||
if (const QWindow *mw = QGuiApplication::modalWindow()) {
|
||||
debug << "\n QGuiApplication::modalWindow = ";
|
||||
formatObject(mw, debug);
|
||||
debug << "\n QGuiApplication::modalWindow = " << formatQObject(mw);
|
||||
}
|
||||
const QObject *focusObject = QGuiApplication::focusObject();
|
||||
const QObject *focusWindow = QGuiApplication::focusWindow();
|
||||
debug << "\n QGuiApplication::focusObject = ";
|
||||
formatObject(focusObject, debug);
|
||||
debug << "\n QGuiApplication::focusObject = " << formatQObject(focusObject);
|
||||
if (focusWindow && focusWindow != focusObject)
|
||||
debug << "\n QGuiApplication::focusWindow = ";
|
||||
formatObject(focusWindow, debug);
|
||||
debug << "\n QGuiApplication::focusWindow = " << formatQObject(focusWindow);
|
||||
#endif // HAVE_GUI_APPLICATION
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QList>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QDebug)
|
||||
|
||||
namespace QtDiag {
|
||||
|
||||
// Event filter that can for example be installed on QApplication
|
||||
@ -74,6 +76,8 @@ public:
|
||||
ObjectTypes objectTypes() const { return m_objectTypes; }
|
||||
void setObjectTypes(ObjectTypes objectTypes) { m_objectTypes = objectTypes; }
|
||||
|
||||
static void formatObject(const QObject *o, QDebug debug);
|
||||
|
||||
private:
|
||||
void init(EventCategories eventCategories);
|
||||
|
||||
@ -84,6 +88,15 @@ private:
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(EventFilter::EventCategories)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(EventFilter::ObjectTypes)
|
||||
|
||||
struct formatQObject
|
||||
{
|
||||
explicit formatQObject(const QObject *o) : m_object(o) {}
|
||||
|
||||
const QObject *m_object;
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug d, const formatQObject &fo);
|
||||
|
||||
} // namespace QtDiag
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user