diff options
110 files changed, 3812 insertions, 1178 deletions
diff --git a/.gitignore b/.gitignore index 7c37e4daff..c14d237ee3 100644 --- a/.gitignore +++ b/.gitignore @@ -100,6 +100,7 @@ tests/auto/qlibrary/libmylib.so* tests/auto/qresourceengine/runtime_resource.rcc tools/qtestlib/chart/chart* tools/qtestlib/updater/updater* +tools/qmleasing/qmleasing tools/qmltestrunner/qmltestrunner tools/activeqt/testcon/testcon.tlb translations/*.qm diff --git a/.qmake.conf b/.qmake.conf index d170e6d272..28e9cfd9ba 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) CONFIG += qt_example_installs CONFIG += warning_clean -MODULE_VERSION = 5.3.1 +MODULE_VERSION = 5.4.0 diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 5dc4332d69..1f28d8009e 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -446,7 +446,7 @@ void QQuickFolderListModel::setFolder(const QUrl &folder) /*! - \qmlproperty url QQuickFolderListModel::rootFolder + \qmlproperty url FolderListModel::rootFolder When the rootFolder is set, then this folder will be threated as the root in the file system, so that diff --git a/src/plugins/accessible/accessible.pro b/src/plugins/accessible/accessible.pro deleted file mode 100644 index b97d323a08..0000000000 --- a/src/plugins/accessible/accessible.pro +++ /dev/null @@ -1,2 +0,0 @@ -TEMPLATE = subdirs -qtHaveModule(quick): SUBDIRS += quick diff --git a/src/plugins/accessible/quick/accessible.json b/src/plugins/accessible/quick/accessible.json deleted file mode 100644 index b21218f19c..0000000000 --- a/src/plugins/accessible/quick/accessible.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "Keys": [ "QQuickWindow", "QQuickItem" ] -} diff --git a/src/plugins/accessible/quick/quick.pro b/src/plugins/accessible/quick/quick.pro deleted file mode 100644 index 115f9bebad..0000000000 --- a/src/plugins/accessible/quick/quick.pro +++ /dev/null @@ -1,23 +0,0 @@ -TARGET = qtaccessiblequick - -PLUGIN_TYPE = accessible -PLUGIN_EXTENDS = quick -PLUGIN_CLASS_NAME = AccessibleQuickFactory -load(qt_plugin) - -include ($$PWD/../shared/qaccessiblebase.pri) - -QT += core-private gui-private qml-private quick-private - -#DEFINES+=Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION - -SOURCES += \ - main.cpp \ - qaccessiblequickview.cpp \ - qaccessiblequickitem.cpp - -HEADERS += \ - qaccessiblequickview.h \ - qaccessiblequickitem.h - -OTHERFILES += accessible.json diff --git a/src/plugins/accessible/shared/qaccessiblebase.pri b/src/plugins/accessible/shared/qaccessiblebase.pri deleted file mode 100644 index 827df0f132..0000000000 --- a/src/plugins/accessible/shared/qaccessiblebase.pri +++ /dev/null @@ -1,3 +0,0 @@ -INCLUDEPATH += $$PWD -SOURCES += $$PWD/qqmlaccessible.cpp -HEADERS += $$PWD/qqmlaccessible.h diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 9ef8c7ab72..664a457608 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,5 +1,2 @@ TEMPLATE = subdirs SUBDIRS += qmltooling -contains(QT_CONFIG, accessibility) { - SUBDIRS += accessible -} diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 1110b3f29d..ede150c67b 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -2155,7 +2155,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa #endif // QT_NO_DATESTRING case QVariant::Point: { bool ok = false; - QQmlStringConverters::pointFFromString(binding->valueAsString(&qmlUnit->header), &ok).toPoint(); + QQmlStringConverters::pointFFromString(binding->valueAsString(&qmlUnit->header), &ok); if (!ok) { recordError(binding->valueLocation, tr("Invalid property assignment: point expected")); return false; @@ -2173,7 +2173,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa break; case QVariant::Size: { bool ok = false; - QQmlStringConverters::sizeFFromString(binding->valueAsString(&qmlUnit->header), &ok).toSize(); + QQmlStringConverters::sizeFFromString(binding->valueAsString(&qmlUnit->header), &ok); if (!ok) { recordError(binding->valueLocation, tr("Invalid property assignment: size expected")); return false; @@ -2191,7 +2191,7 @@ bool QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache *propertyCa break; case QVariant::Rect: { bool ok = false; - QQmlStringConverters::rectFFromString(binding->valueAsString(&qmlUnit->header), &ok).toRect(); + QQmlStringConverters::rectFFromString(binding->valueAsString(&qmlUnit->header), &ok); if (!ok) { recordError(binding->valueLocation, tr("Invalid property assignment: rect expected")); return false; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 429688090c..c055cffd0d 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -226,7 +226,7 @@ void IRDecoder::visitMove(IR::Move *s) // For anything else...: Q_UNIMPLEMENTED(); - s->dump(qout, IR::Stmt::MIR); + IRPrinter(&qout).print(s); qout << endl; Q_ASSERT(!"TODO"); } @@ -402,7 +402,7 @@ void IRDecoder::callBuiltin(IR::Call *call, IR::Temp *result) } Q_UNIMPLEMENTED(); - call->dump(qout); qout << endl; + IRPrinter(&qout).print(call); qout << endl; Q_ASSERT(!"TODO!"); Q_UNREACHABLE(); } diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 5d30d6e3b9..501758145a 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -45,6 +45,8 @@ #ifndef V4_BOOTSTRAP #include <private/qqmlpropertycache_p.h> #endif + +#include <QtCore/QBuffer> #include <QtCore/qtextstream.h> #include <QtCore/qdebug.h> #include <QtCore/qset.h> @@ -275,104 +277,6 @@ struct RemoveSharedExpressions: IR::StmtVisitor, IR::ExprVisitor } }; -static QString dumpStart(const Expr *e) { - if (e->type == UnknownType) -// return QStringLiteral("**UNKNOWN**"); - return QString(); - - QString result = typeName(e->type); -#ifndef V4_BOOTSTRAP - const Temp *temp = const_cast<Expr*>(e)->asTemp(); - if (e->type == QObjectType && temp && temp->memberResolver.isQObjectResolver) { - result += QLatin1Char('<'); - result += QString::fromUtf8(static_cast<QQmlPropertyCache*>(temp->memberResolver.data)->className()); - result += QLatin1Char('>'); - } -#endif - result += QLatin1Char('{'); - return result; -} - -static const char *dumpEnd(const Expr *e) { - if (e->type == UnknownType) - return ""; - else - return "}"; -} - -void Const::dump(QTextStream &out) const -{ - if (type != UndefinedType && type != NullType) - out << dumpStart(this); - switch (type) { - case QV4::IR::UndefinedType: - out << "undefined"; - break; - case QV4::IR::NullType: - out << "null"; - break; - case QV4::IR::BoolType: - out << (value ? "true" : "false"); - break; - case QV4::IR::MissingType: - out << "missing"; - break; - default: - if (int(value) == 0 && int(value) == value) { - if (isNegative(value)) - out << "-0"; - else - out << "0"; - } else { - out << QString::number(value, 'g', 16); - } - break; - } - if (type != UndefinedType && type != NullType) - out << dumpEnd(this); -} - -void String::dump(QTextStream &out) const -{ - out << '"' << escape(*value) << '"'; -} - -QString String::escape(const QString &s) -{ - QString r; - for (int i = 0; i < s.length(); ++i) { - const QChar ch = s.at(i); - if (ch == QLatin1Char('\n')) - r += QStringLiteral("\\n"); - else if (ch == QLatin1Char('\r')) - r += QStringLiteral("\\r"); - else if (ch == QLatin1Char('\\')) - r += QStringLiteral("\\\\"); - else if (ch == QLatin1Char('"')) - r += QStringLiteral("\\\""); - else if (ch == QLatin1Char('\'')) - r += QStringLiteral("\\'"); - else - r += ch; - } - return r; -} - -void RegExp::dump(QTextStream &out) const -{ - char f[3]; - int i = 0; - if (flags & RegExp_Global) - f[i++] = 'g'; - if (flags & RegExp_IgnoreCase) - f[i++] = 'i'; - if (flags & RegExp_Multiline) - f[i++] = 'm'; - f[i] = 0; - - out << '/' << *value << '/' << f; -} - void Name::initGlobal(const QString *id, quint32 line, quint32 column) { this->id = id; @@ -453,33 +357,6 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_(###FIXME)"; }; -void Name::dump(QTextStream &out) const -{ - if (id) - out << *id; - else - out << builtin_to_string(builtin); -} - -void Temp::dump(QTextStream &out) const -{ - out << dumpStart(this); - switch (kind) { - case Formal: out << '#' << index; break; - case ScopedFormal: out << '#' << index - << '@' << scope; break; - case Local: out << '$' << index; break; - case ScopedLocal: out << '$' << index - << '@' << scope; break; - case VirtualRegister: out << '%' << index; break; - case PhysicalRegister: out << (type == DoubleType ? "fp" : "r") - << index; break; - case StackSlot: out << '&' << index; break; - default: out << "INVALID"; - } - out << dumpEnd(this); -} - bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW { if (t1.kind < t2.kind) return true; @@ -489,151 +366,6 @@ bool operator<(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW return t1.scope < t2.scope; } -void Closure::dump(QTextStream &out) const -{ - QString name = functionName ? *functionName : QString(); - if (name.isEmpty()) - name.sprintf("%x", value); - out << "closure(" << name << ')'; -} - -void Convert::dump(QTextStream &out) const -{ - out << dumpStart(this); - out << "convert("; - expr->dump(out); - out << ')' << dumpEnd(this); -} - -void Unop::dump(QTextStream &out) const -{ - out << dumpStart(this) << opname(op); - expr->dump(out); - out << dumpEnd(this); -} - -void Binop::dump(QTextStream &out) const -{ - out << dumpStart(this); - left->dump(out); - out << ' ' << opname(op) << ' '; - right->dump(out); - out << dumpEnd(this); -} - -void Call::dump(QTextStream &out) const -{ - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void New::dump(QTextStream &out) const -{ - out << "new "; - base->dump(out); - out << '('; - for (ExprList *it = args; it; it = it->next) { - if (it != args) - out << ", "; - it->expr->dump(out); - } - out << ')'; -} - -void Subscript::dump(QTextStream &out) const -{ - base->dump(out); - out << '['; - index->dump(out); - out << ']'; -} - -void Member::dump(QTextStream &out) const -{ - if (kind != MemberOfEnum && attachedPropertiesIdOrEnumValue != 0 && !base->asTemp()) - out << "[[attached property from " << attachedPropertiesIdOrEnumValue << "]]"; - else - base->dump(out); - out << '.' << *name; -#ifndef V4_BOOTSTRAP - if (property) - out << " (meta-property " << property->coreIndex << " <" << QMetaType::typeName(property->propType) << ">)"; -#endif -} - -void Exp::dump(QTextStream &out, Mode) -{ - out << "(void) "; - expr->dump(out); - out << ';'; -} - -void Move::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - - target->dump(out); - out << ' '; - if (swap) - out << "<=> "; - else - out << "= "; -// if (source->type != target->type) -// out << typeName(source->type) << "_to_" << typeName(target->type) << '('; - source->dump(out); -// if (source->type != target->type) -// out << ')'; - out << ';'; -} - -void Jump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "goto " << 'L' << target->index() << ';'; -} - -void CJump::dump(QTextStream &out, Mode mode) -{ - Q_UNUSED(mode); - out << "if ("; - cond->dump(out); - if (mode == HIR) - out << ") goto " << 'L' << iftrue->index() << "; else goto " << 'L' << iffalse->index() << ';'; - else - out << ") goto " << 'L' << iftrue->index() << ";"; -} - -void Ret::dump(QTextStream &out, Mode) -{ - out << "return"; - if (expr) { - out << ' '; - expr->dump(out); - } - out << ';'; -} - -void Phi::dump(QTextStream &out, Stmt::Mode mode) -{ - Q_UNUSED(mode); - - targetTemp->dump(out); - out << " = phi("; - for (int i = 0, ei = d->incoming.size(); i < ei; ++i) { - if (i > 0) - out << ", "; - if (d->incoming[i]) - d->incoming[i]->dump(out); - } - out << ");"; -} - Function *Module::newFunction(const QString &name, Function *outer) { Function *f = new Function(this, outer, name); @@ -734,21 +466,6 @@ int Function::liveBasicBlocksCount() const return count; } -void Function::dump(QTextStream &out, Stmt::Mode mode) -{ - QString n = name ? *name : QString(); - if (n.isEmpty()) - n.sprintf("%p", this); - out << "function " << n << "() {" << endl; - foreach (const QString *formal, formals) - out << "\treceive " << *formal << ';' << endl; - foreach (const QString *local, locals) - out << "\tlocal " << *local << ';' << endl; - foreach (BasicBlock *bb, basicBlocks()) - bb->dump(out, mode); - out << '}' << endl; -} - void Function::removeSharedExpressions() { RemoveSharedExpressions removeSharedExpressions; @@ -1028,23 +745,6 @@ Stmt *BasicBlock::RET(Temp *expr) return s; } -void BasicBlock::dump(QTextStream &out, Stmt::Mode mode) -{ - out << 'L' << index() << ':'; - if (catchBlock) - out << " (catchBlock L" << catchBlock->index() << ")"; - out << endl; - foreach (Stmt *s, statements()) { - out << '\t'; - s->dump(out, mode); - - if (s->location.isValid()) - out << " // line: " << s->location.startLine << " ; column: " << s->location.startColumn; - - out << endl; - } -} - void BasicBlock::setStatements(const QVector<Stmt *> &newStatements) { Q_ASSERT(!isRemoved()); @@ -1189,6 +889,387 @@ void CloneExpr::visitMember(Member *e) cloned = block->MEMBER(clonedBase, e->name, e->property, e->kind, e->attachedPropertiesIdOrEnumValue); } +IRPrinter::IRPrinter(QTextStream *out) + : out(out) + , printElse(true) +{ +} + +IRPrinter::~IRPrinter() +{ +} + +void IRPrinter::print(Stmt *s) +{ + s->accept(this); +} + +void IRPrinter::print(const Expr &e) +{ + const_cast<Expr *>(&e)->accept(this); +} + +void IRPrinter::print(Expr *e) +{ + e->accept(this); +} + +void IRPrinter::print(Function *f) +{ + QString n = f->name ? *f->name : QString(); + if (n.isEmpty()) + n.sprintf("%p", f); + *out << "function " << n << '('; + + for (int i = 0; i < f->formals.size(); ++i) { + if (i != 0) + *out << ", "; + *out << *f->formals.at(i); + } + *out << ')' << endl + << '{' << endl; + + foreach (const QString *local, f->locals) + *out << " var " << *local << ';' << endl; + + foreach (BasicBlock *bb, f->basicBlocks()) + if (!bb->isRemoved()) + print(bb); + *out << '}' << endl; +} + +void IRPrinter::print(BasicBlock *bb) +{ + bool prevPrintElse = false; + std::swap(printElse, prevPrintElse); + printBlockStart(bb); + + foreach (Stmt *s, bb->statements()) { + QByteArray str; + QBuffer buf(&str); + buf.open(QIODevice::WriteOnly); + QTextStream os(&buf); + QTextStream *prevOut = &os; + std::swap(out, prevOut); + if (s->id > 0) + *out << s->id << ": "; + s->accept(this); + if (s->location.isValid()) { + out->flush(); + for (int i = 58 - str.length(); i > 0; --i) + *out << ' '; + *out << " // line: " << s->location.startLine << " column: " << s->location.startColumn; + } + + out->flush(); + std::swap(out, prevOut); + + *out << " " << str; + *out << endl; + + if (s->asCJump()) { + *out << " else goto L" << s->asCJump()->iffalse->index() << ";" << endl; + } + } + + std::swap(printElse, prevPrintElse); +} + +void IRPrinter::visitExp(Exp *s) +{ + *out << "(void) "; + s->expr->accept(this); + *out << ';'; +} + +void IRPrinter::visitMove(Move *s) +{ + s->target->accept(this); + *out << ' '; + if (s->swap) + *out << "<=> "; + else + *out << "= "; + s->source->accept(this); + *out << ';'; +} + +void IRPrinter::visitJump(Jump *s) +{ + *out << "goto L" << s->target->index() << ';'; +} + +void IRPrinter::visitCJump(CJump *s) +{ + *out << "if ("; + s->cond->accept(this); + *out << ") goto L" << s->iftrue->index() << ';'; + if (printElse) + *out << " else goto L" << s->iffalse->index() << ';'; +} + +void IRPrinter::visitRet(Ret *s) +{ + *out << "return"; + if (s->expr) { + *out << ' '; + s->expr->accept(this); + } + *out << ';'; +} + +void IRPrinter::visitPhi(Phi *s) +{ + s->targetTemp->accept(this); + *out << " = phi("; + for (int i = 0, ei = s->d->incoming.size(); i < ei; ++i) { + if (i > 0) + *out << ", "; + if (s->d->incoming[i]) + s->d->incoming[i]->accept(this); + } + *out << ");"; +} + +void IRPrinter::visitConst(Const *e) +{ + if (e->type != UndefinedType && e->type != NullType) + *out << dumpStart(e); + switch (e->type) { + case QV4::IR::UndefinedType: + *out << "undefined"; + break; + case QV4::IR::NullType: + *out << "null"; + break; + case QV4::IR::BoolType: + *out << (e->value ? "true" : "false"); + break; + case QV4::IR::MissingType: + *out << "missing"; + break; + default: + if (int(e->value) == 0 && int(e->value) == e->value) { + if (isNegative(e->value)) + *out << "-0"; + else + *out << "0"; + } else { + *out << QString::number(e->value, 'g', 16); + } + break; + } + if (e->type != UndefinedType && e->type != NullType) + *out << dumpEnd(e); +} + +void IRPrinter::visitString(String *e) +{ + *out << '"' << escape(*e->value) << '"'; +} + +void IRPrinter::visitRegExp(RegExp *e) +{ + char f[3]; + int i = 0; + if (e->flags & RegExp::RegExp_Global) + f[i++] = 'g'; + if (e->flags & RegExp::RegExp_IgnoreCase) + f[i++] = 'i'; + if (e->flags & RegExp::RegExp_Multiline) + f[i++] = 'm'; + f[i] = 0; + + *out << '/' << *e->value << '/' << f; +} + +void IRPrinter::visitName(Name *e) +{ + if (e->id) + *out << *e->id; + else + *out << builtin_to_string(e->builtin); +} + +void IRPrinter::visitTemp(Temp *e) +{ + *out << dumpStart(e); + switch (e->kind) { + case Temp::Formal: *out << '#' << e->index; break; + case Temp::ScopedFormal: *out << '#' << e->index + << '@' << e->scope; break; + case Temp::Local: *out << '$' << e->index; break; + case Temp::ScopedLocal: *out << '$' << e->index + << '@' << e->scope; break; + case Temp::VirtualRegister: *out << '%' << e->index; break; + case Temp::PhysicalRegister: *out << (e->type == DoubleType ? "fp" : "r") + << e->index; break; + case Temp::StackSlot: *out << '&' << e->index; break; + default: *out << "INVALID"; + } + *out << dumpEnd(e); +} + +void IRPrinter::visitClosure(Closure *e) +{ + QString name = e->functionName ? *e->functionName : QString(); + if (name.isEmpty()) + name.sprintf("%x", e->value); + *out << "closure(" << name << ')'; +} + +void IRPrinter::visitConvert(Convert *e) +{ + *out << dumpStart(e); + *out << "convert("; + e->expr->accept(this); + *out << ')' << dumpEnd(e); +} + +void IRPrinter::visitUnop(Unop *e) +{ + *out << dumpStart(e) << opname(e->op); + e->expr->accept(this); + *out << dumpEnd(e); +} + +void IRPrinter::visitBinop(Binop *e) +{ + *out << dumpStart(e); + e->left->accept(this); + *out << ' ' << opname(e->op) << ' '; + e->right->accept(this); + *out << dumpEnd(e); +} + +void IRPrinter::visitCall(Call *e) +{ + e->base->accept(this); + *out << '('; + for (ExprList *it = e->args; it; it = it->next) { + if (it != e->args) + *out << ", "; + it->expr->accept(this); + } + *out << ')'; +} + +void IRPrinter::visitNew(New *e) +{ + *out << "new "; + e->base->accept(this); + *out << '('; + for (ExprList *it = e->args; it; it = it->next) { + if (it != e->args) + *out << ", "; + it->expr->accept(this); + } + *out << ')'; +} + +void IRPrinter::visitSubscript(Subscript *e) +{ + e->base->accept(this); + *out << '['; + e->index->accept(this); + *out << ']'; +} + +void IRPrinter::visitMember(Member *e) +{ + if (e->kind != Member::MemberOfEnum + && e->attachedPropertiesIdOrEnumValue != 0 && !e->base->asTemp()) + *out << "[[attached property from " << e->attachedPropertiesIdOrEnumValue << "]]"; + else + e->base->accept(this); + *out << '.' << *e->name; +#ifndef V4_BOOTSTRAP + if (e->property) + *out << " (meta-property " << e->property->coreIndex + << " <" << QMetaType::typeName(e->property->propType) + << ">)"; +#endif +} + +QString IRPrinter::escape(const QString &s) +{ + QString r; + for (int i = 0; i < s.length(); ++i) { + const QChar ch = s.at(i); + if (ch == QLatin1Char('\n')) + r += QStringLiteral("\\n"); + else if (ch == QLatin1Char('\r')) + r += QStringLiteral("\\r"); + else if (ch == QLatin1Char('\\')) + r += QStringLiteral("\\\\"); + else if (ch == QLatin1Char('"')) + r += QStringLiteral("\\\""); + else if (ch == QLatin1Char('\'')) + r += QStringLiteral("\\'"); + else + r += ch; + } + return r; +} + +QString IRPrinter::dumpStart(const Expr *e) +{ + if (e->type == UnknownType) + return QString(); + + QString result = typeName(e->type); +#ifndef V4_BOOTSTRAP + const Temp *temp = const_cast<Expr*>(e)->asTemp(); + if (e->type == QObjectType && temp && temp->memberResolver.isQObjectResolver) { + result += QLatin1Char('<'); + result += QString::fromUtf8(static_cast<QQmlPropertyCache*>(temp->memberResolver.data)->className()); + result += QLatin1Char('>'); + } +#endif + result += QLatin1Char('{'); + return result; +} + +const char *IRPrinter::dumpEnd(const Expr *e) +{ + if (e->type == UnknownType) + return ""; + else + return "}"; +} + +void IRPrinter::printBlockStart(BasicBlock *bb) +{ + if (bb->isRemoved()) { + *out << "(block has been removed)"; + return; + } + + QByteArray str; + str.append('L'); + str.append(QByteArray::number(bb->index())); + str.append(':'); + if (bb->catchBlock) { + str.append(" (exception handler L"); + str.append(QByteArray::number(bb->catchBlock->index())); + str.append(')'); + } + for (int i = 66 - str.length(); i; --i) + str.append(' '); + *out << str; + + *out << "// predecessor blocks:"; + foreach (BasicBlock *in, bb->in) + *out << " L" << in->index(); + if (bb->in.isEmpty()) + *out << " (none)"; + if (BasicBlock *container = bb->containingGroup()) + *out << "; container block: L" << container->index(); + if (bb->isGroupStart()) + *out << "; group start"; + *out << endl; +} + } // end of namespace IR } // end of namespace QV4 diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 9eff90dd30..2940fd50f4 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -268,7 +268,6 @@ struct Q_AUTOTEST_EXPORT Expr { virtual New *asNew() { return 0; } virtual Subscript *asSubscript() { return 0; } virtual Member *asMember() { return 0; } - virtual void dump(QTextStream &out) const = 0; }; struct ExprList { @@ -295,8 +294,6 @@ struct Const: Expr { virtual void accept(ExprVisitor *v) { v->visitConst(this); } virtual Const *asConst() { return this; } - - virtual void dump(QTextStream &out) const; }; struct String: Expr { @@ -309,9 +306,6 @@ struct String: Expr { virtual void accept(ExprVisitor *v) { v->visitString(this); } virtual String *asString() { return this; } - - virtual void dump(QTextStream &out) const; - static QString escape(const QString &s); }; struct RegExp: Expr { @@ -333,8 +327,6 @@ struct RegExp: Expr { virtual void accept(ExprVisitor *v) { v->visitRegExp(this); } virtual RegExp *asRegExp() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Name: Expr { @@ -376,8 +368,6 @@ struct Name: Expr { virtual void accept(ExprVisitor *v) { v->visitName(this); } virtual bool isLValue() { return true; } virtual Name *asName() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Q_AUTOTEST_EXPORT Temp: Expr { @@ -415,8 +405,6 @@ struct Q_AUTOTEST_EXPORT Temp: Expr { virtual void accept(ExprVisitor *v) { v->visitTemp(this); } virtual bool isLValue() { return !isReadOnly; } virtual Temp *asTemp() { return this; } - - virtual void dump(QTextStream &out) const; }; inline bool operator==(const Temp &t1, const Temp &t2) Q_DECL_NOTHROW @@ -442,8 +430,6 @@ struct Closure: Expr { virtual void accept(ExprVisitor *v) { v->visitClosure(this); } virtual Closure *asClosure() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Convert: Expr { @@ -457,8 +443,6 @@ struct Convert: Expr { virtual void accept(ExprVisitor *v) { v->visitConvert(this); } virtual Convert *asConvert() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Unop: Expr { @@ -473,8 +457,6 @@ struct Unop: Expr { virtual void accept(ExprVisitor *v) { v->visitUnop(this); } virtual Unop *asUnop() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Binop: Expr { @@ -491,8 +473,6 @@ struct Binop: Expr { virtual void accept(ExprVisitor *v) { v->visitBinop(this); } virtual Binop *asBinop() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Call: Expr { @@ -513,8 +493,6 @@ struct Call: Expr { virtual void accept(ExprVisitor *v) { v->visitCall(this); } virtual Call *asCall() { return this; } - - virtual void dump(QTextStream &out) const; }; struct New: Expr { @@ -535,8 +513,6 @@ struct New: Expr { virtual void accept(ExprVisitor *v) { v->visitNew(this); } virtual New *asNew() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Subscript: Expr { @@ -552,8 +528,6 @@ struct Subscript: Expr { virtual void accept(ExprVisitor *v) { v->visitSubscript(this); } virtual bool isLValue() { return true; } virtual Subscript *asSubscript() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Member: Expr { @@ -605,16 +579,9 @@ struct Member: Expr { virtual void accept(ExprVisitor *v) { v->visitMember(this); } virtual bool isLValue() { return true; } virtual Member *asMember() { return this; } - - virtual void dump(QTextStream &out) const; }; struct Stmt { - enum Mode { - HIR, - MIR - }; - struct Data { QVector<Expr *> incoming; // used by Phi nodes }; @@ -641,7 +608,6 @@ struct Stmt { virtual CJump *asCJump() { return 0; } virtual Ret *asRet() { return 0; } virtual Phi *asPhi() { return 0; } - virtual void dump(QTextStream &out, Mode mode = HIR) = 0; private: // For memory management in BasicBlock friend struct BasicBlock; @@ -662,7 +628,6 @@ struct Exp: Stmt { virtual void accept(StmtVisitor *v) { v->visitExp(this); } virtual Exp *asExp() { return this; } - virtual void dump(QTextStream &out, Mode); }; struct Move: Stmt { @@ -680,7 +645,6 @@ struct Move: Stmt { virtual void accept(StmtVisitor *v) { v->visitMove(this); } virtual Move *asMove() { return this; } - virtual void dump(QTextStream &out, Mode mode = HIR); }; struct Jump: Stmt { @@ -695,8 +659,6 @@ struct Jump: Stmt { virtual void accept(StmtVisitor *v) { v->visitJump(this); } virtual Jump *asJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); }; struct CJump: Stmt { @@ -715,8 +677,6 @@ struct CJump: Stmt { virtual void accept(StmtVisitor *v) { v->visitCJump(this); } virtual CJump *asCJump() { return this; } - - virtual void dump(QTextStream &out, Mode mode); }; struct Ret: Stmt { @@ -731,8 +691,6 @@ struct Ret: Stmt { virtual void accept(StmtVisitor *v) { v->visitRet(this); } virtual Ret *asRet() { return this; } - - virtual void dump(QTextStream &out, Mode); }; struct Phi: Stmt { @@ -740,8 +698,6 @@ struct Phi: Stmt { virtual void accept(StmtVisitor *v) { v->visitPhi(this); } virtual Phi *asPhi() { return this; } - - virtual void dump(QTextStream &out, Mode mode); }; struct Q_QML_PRIVATE_EXPORT Module { @@ -871,8 +827,6 @@ public: Stmt *CJUMP(Expr *cond, BasicBlock *iftrue, BasicBlock *iffalse); Stmt *RET(Temp *expr); - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); - BasicBlock *containingGroup() const { Q_ASSERT(!isRemoved()); @@ -1024,8 +978,6 @@ struct Function { int liveBasicBlocksCount() const; - void dump(QTextStream &out, Stmt::Mode mode = Stmt::HIR); - void removeSharedExpressions(); int indexOfArgument(const QStringRef &string) const; @@ -1116,6 +1068,52 @@ private: IR::Expr *cloned; }; +class IRPrinter: public StmtVisitor, public ExprVisitor +{ +public: + IRPrinter(QTextStream *out); + virtual ~IRPrinter(); + + void print(Stmt *s); + void print(Expr *e); + void print(const Expr &e); + + virtual void print(Function *f); + virtual void print(BasicBlock *bb); + + virtual void visitExp(Exp *s); + virtual void visitMove(Move *s); + virtual void visitJump(Jump *s); + virtual void visitCJump(CJump *s); + virtual void visitRet(Ret *s); + virtual void visitPhi(Phi *s); + + virtual void visitConst(Const *e); + virtual void visitString(String *e); + virtual void visitRegExp(RegExp *e); + virtual void visitName(Name *e); + virtual void visitTemp(Temp *e); + virtual void visitClosure(Closure *e); + virtual void visitConvert(Convert *e); + virtual void visitUnop(Unop *e); + virtual void visitBinop(Binop *e); + virtual void visitCall(Call *e); + virtual void visitNew(New *e); + virtual void visitSubscript(Subscript *e); + virtual void visitMember(Member *e); + + static QString escape(const QString &s); + +protected: + QString dumpStart(const Expr *e); + const char *dumpEnd(const Expr *e); + void printBlockStart(BasicBlock *bb); + +protected: + QTextStream *out; + bool printElse; +}; + } // end of namespace IR } // end of namespace QV4 diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 97114b9507..48c68533ba 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -50,7 +50,6 @@ #include <QtCore/QCoreApplication> #include <QtCore/QStringList> #include <QtCore/QSet> -#include <QtCore/QBuffer> #include <QtCore/QLinkedList> #include <QtCore/QStack> #include <qv4runtime_p.h> @@ -75,96 +74,8 @@ Q_GLOBAL_STATIC_WITH_ARGS(QTextStream, qout, (stderr, QIODevice::WriteOnly)); void showMeTheCode(IR::Function *function) { static bool showCode = !qgetenv("QV4_SHOW_IR").isNull(); - if (showCode) { - QVector<Stmt *> code; - QHash<Stmt *, BasicBlock *> leader; - - foreach (BasicBlock *block, function->basicBlocks()) { - if (block->isRemoved() || block->isEmpty()) - continue; - leader.insert(block->statements().first(), block); - foreach (Stmt *s, block->statements()) { - code.append(s); - } - } - - QString name; - if (function->name && !function->name->isEmpty()) - name = *function->name; - else - name.sprintf("%p", function); - - qout << "function " << name << "("; - for (int i = 0; i < function->formals.size(); ++i) { - if (i != 0) - qout << ", "; - qout << *function->formals.at(i); - } - qout << ")" << endl - << "{" << endl; - - foreach (const QString *local, function->locals) { - qout << " var " << *local << ';' << endl; - } - - for (int i = 0; i < code.size(); ++i) { - Stmt *s = code.at(i); - Q_ASSERT(s); - - if (BasicBlock *bb = leader.value(s)) { - qout << endl; - QByteArray str; - str.append('L'); - str.append(QByteArray::number(bb->index())); - str.append(':'); - if (bb->catchBlock) { - str.append(" (exception handler L"); - str.append(QByteArray::number(bb->catchBlock->index())); - str.append(')'); - } - for (int i = 66 - str.length(); i; --i) - str.append(' '); - qout << str; - qout << "// predecessor blocks:"; - foreach (BasicBlock *in, bb->in) - qout << " L" << in->index(); - if (bb->in.isEmpty()) - qout << "(none)"; - if (BasicBlock *container = bb->containingGroup()) - qout << "; container block: L" << container->index(); - if (bb->isGroupStart()) - qout << "; group start"; - qout << endl; - } - Stmt *n = (i + 1) < code.size() ? code.at(i + 1) : 0; - - QByteArray str; - QBuffer buf(&str); - buf.open(QIODevice::WriteOnly); - QTextStream out(&buf); - if (s->id > 0) - out << s->id << ": "; - s->dump(out, Stmt::MIR); - if (s->location.isValid()) { - out.flush(); - for (int i = 58 - str.length(); i > 0; --i) - out << ' '; - out << " // line: " << s->location.startLine << " column: " << s->location.startColumn; - } - - out.flush(); - - qout << " " << str; - qout << endl; - - if (n && s->asCJump()) { - qout << " else goto L" << s->asCJump()->iffalse->index() << ";" << endl; - } - } - - qout << "}" << endl - << endl; - } + if (showCode) + IRPrinter(&qout).print(function); } class ProcessedBlocks @@ -1395,14 +1306,15 @@ public: void dump() const { + IRPrinter printer(&qout); foreach (const UntypedTemp &var, _defUses.keys()) { const DefUse &du = _defUses[var]; - var.temp.dump(qout); + printer.print(const_cast<Temp *>(&var.temp)); qout<<" -> defined in block "<<du.blockOfStatement->index()<<", statement: "; - du.defStmt->dump(qout); + printer.print(du.defStmt); qout<<endl<<" uses:"<<endl; foreach (Stmt *s, du.uses) { - qout<<" ";s->dump(qout);qout<<endl; + qout<<" ";printer.print(s);qout<<endl; } } } @@ -3581,13 +3493,14 @@ public: qout << endl; } + IRPrinter printer(&qout); for (int i = 0, ei = _liveIn.size(); i != ei; ++i) { qout << "L" << i <<" live-in: "; QList<Temp> live = QList<Temp>::fromSet(_liveIn.at(i)); std::sort(live.begin(), live.end()); for (int i = 0; i < live.size(); ++i) { if (i > 0) qout << ", "; - live[i].dump(qout); + printer.print(&live[i]); } qout << endl; } @@ -3778,7 +3691,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart) } void LifeTimeInterval::dump(QTextStream &out) const { - _temp.dump(out); + IRPrinter(&out).print(const_cast<Temp *>(&_temp)); out << ": ends at " << _end << " with ranges "; if (_ranges.isEmpty()) out << "(none)"; diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index b5765cd589..a66eab005f 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -44,9 +44,9 @@ #include <algorithm> -//#define DEBUG_REGALLOC - namespace { +enum { DebugRegAlloc = 0 }; + struct Use { enum RegisterFlag { MustHaveRegister = 0, CouldHaveRegister = 1 }; unsigned flag : 1; @@ -143,16 +143,19 @@ public: _hints[t].append(hint); } -#ifdef DEBUG_REGALLOC void dump() const { + if (!DebugRegAlloc) + return; + QTextStream qout(stdout, QIODevice::WriteOnly); + IRPrinter printer(&qout); qout << "RegAllocInfo:" << endl << "Defs/uses:" << endl; QList<Temp> temps = _defs.keys(); std::sort(temps.begin(), temps.end()); foreach (const Temp &t, temps) { - t.dump(qout); + printer.print(t); qout << " def at " << _defs[t].defStmt << " (" << (_defs[t].canHaveReg ? "can" : "can NOT") << " have a register, and " @@ -181,17 +184,16 @@ public: std::sort(hinted.begin(), hinted.end()); foreach (const Temp &t, hinted) { qout << "\t"; - t.dump(qout); + printer.print(t); qout << ": "; QList<Temp> hints = _hints[t]; for (int i = 0; i < hints.size(); ++i) { if (i > 0) qout << ", "; - hints[i].dump(qout); + printer.print(hints[i]); } qout << endl; } } -#endif // DEBUG_REGALLOC protected: // IRDecoder virtual void callBuiltinInvalid(IR::Name *, IR::ExprList *, IR::Temp *) {} @@ -747,25 +749,25 @@ private: cleanOldIntervals(); _liveAtEnd[bb] = _intervalForTemp.values(); -#ifdef DEBUG_REGALLOC - QTextStream os(stdout, QIODevice::WriteOnly); - os << "Intervals live at the start of L" << bb->index << ":" << endl; - if (_liveAtStart[bb].isEmpty()) - os << "\t(none)" << endl; - foreach (const LifeTimeInterval *i, _liveAtStart[bb]) { - os << "\t"; - i->dump(os); - os << endl; - } - os << "Intervals live at the end of L" << bb->index << ":" << endl; - if (_liveAtEnd[bb].isEmpty()) - os << "\t(none)" << endl; - foreach (const LifeTimeInterval *i, _liveAtEnd[bb]) { - os << "\t"; - i->dump(os); - os << endl; + if (DebugRegAlloc) { + QTextStream os(stdout, QIODevice::WriteOnly); + os << "Intervals live at the start of L" << bb->index() << ":" << endl; + if (_liveAtStart[bb].isEmpty()) + os << "\t(none)" << endl; + foreach (const LifeTimeInterval *i, _liveAtStart[bb]) { + os << "\t"; + i->dump(os); + os << endl; + } + os << "Intervals live at the end of L" << bb->index() << ":" << endl; + if (_liveAtEnd[bb].isEmpty()) + os << "\t(none)" << endl; + foreach (const LifeTimeInterval *i, _liveAtEnd[bb]) { + os << "\t"; + i->dump(os); + os << endl; + } } -#endif bb->setStatements(newStatements); } @@ -842,10 +844,10 @@ private: void resolveEdge(BasicBlock *predecessor, BasicBlock *successor) { -#ifdef DEBUG_REGALLOC - Optimizer::showMeTheCode(_function); - qDebug() << "Resolving edge" << predecessor->index << "->" << successor->index; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + Optimizer::showMeTheCode(_function); + qDebug() << "Resolving edge" << predecessor->index() << "->" << successor->index(); + } MoveMapping mapping; @@ -972,9 +974,8 @@ private: } mapping.order(); -#ifdef DEBUG_REGALLOC - mapping.dump(); -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + mapping.dump(); bool insertIntoPredecessor = successor->in.size() > 1; mapping.insertMoves(insertIntoPredecessor ? predecessor : successor, _function, @@ -1091,9 +1092,8 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) _assignedSpillSlots.reserve(function->tempCount); _activeSpillSlots.resize(function->tempCount); -#ifdef DEBUG_REGALLOC - qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** Running regalloc for function" << (function->name ? qPrintable(*function->name) : "NO NAME") << "***"; _unhandled = opt.lifeTimeIntervals(); _handled.reserve(_unhandled.size()); @@ -1101,8 +1101,7 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) _info.reset(new RegAllocInfo); _info->collect(function); -#ifdef DEBUG_REGALLOC - { + if (DebugRegAlloc) { QTextStream qout(stdout, QIODevice::WriteOnly); qout << "Ranges:" << endl; QVector<LifeTimeInterval> intervals = _unhandled; @@ -1111,9 +1110,8 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) r.dump(qout); qout << endl; } + _info->dump(); } - _info->dump(); -#endif // DEBUG_REGALLOC prepareRanges(); @@ -1121,9 +1119,8 @@ void RegisterAllocator::run(IR::Function *function, const Optimizer &opt) linearScan(); -#ifdef DEBUG_REGALLOC - dump(); -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + dump(); std::sort(_handled.begin(), _handled.end(), LifeTimeInterval::lessThan); ResolutionPhase(_handled, function, _info.data(), _assignedSpillSlots, _normalRegisters, _fpRegisters).run(); @@ -1246,10 +1243,9 @@ void RegisterAllocator::linearScan() } else { assignSpillSlot(current.temp(), current.start(), current.end()); _inactive += current; -#ifdef DEBUG_REGALLOC - qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current.temp()] - << "for %" << current.temp().index << "as it cannot be loaded in a register"; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** allocating stack slot" << _assignedSpillSlots[current.temp()] + << "for %" << current.temp().index << "as it cannot be loaded in a register"; } } @@ -1350,24 +1346,21 @@ void RegisterAllocator::tryAllocateFreeReg(LifeTimeInterval ¤t, const int if (freeUntilPos_reg == 0) { // no register available without spilling -#ifdef DEBUG_REGALLOC - qDebug() << "*** no register available for %" << current.temp().index; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** no register available for %" << current.temp().index; return; } else if (current.end() < freeUntilPos_reg) { // register available for the whole interval -#ifdef DEBUG_REGALLOC - qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** allocating register" << reg << "for the whole interval of %" << current.temp().index; current.setReg(reg); _lastAssignedRegister.insert(current.temp(), reg); } else { // register available for the first part of the interval current.setReg(reg); _lastAssignedRegister.insert(current.temp(), reg); -#ifdef DEBUG_REGALLOC - qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) + qDebug() << "*** allocating register" << reg << "for the first part of interval of %" << current.temp().index; split(current, freeUntilPos_reg, true); } } @@ -1427,19 +1420,19 @@ void RegisterAllocator::allocateBlockedReg(LifeTimeInterval ¤t, const int if (current.start() > nextUsePos_reg) { // all other intervals are used before current, so it is best to spill current itself -#ifdef DEBUG_REGALLOC - QTextStream out(stderr, QIODevice::WriteOnly); - out << "*** splitting current for range ";current.dump(out);out<<endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "*** splitting current for range ";current.dump(out);out<<endl; + } Q_ASSERT(!_info->useMustHaveReg(current.temp(), position)); split(current, position + 1, true); _inactive.append(current); } else { // spill intervals that currently block reg -#ifdef DEBUG_REGALLOC - QTextStream out(stderr, QIODevice::WriteOnly); - out << "*** spilling intervals that block reg "<<reg<<" for interval ";current.dump(out);out<<endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "*** spilling intervals that block reg "<<reg<<" for interval ";current.dump(out);out<<endl; + } current.setReg(reg); _lastAssignedRegister.insert(current.temp(), reg); LifeTimeInterval *nextUse = nextUseRangeForReg[reg]; @@ -1463,9 +1456,10 @@ void RegisterAllocator::allocateBlockedReg(LifeTimeInterval ¤t, const int : _fixedRegisterRanges.at(reg); int ni = nextIntersection(current, fixedRegRange, position); if (ni != -1) { -#ifdef DEBUG_REGALLOC - out << "***-- current range intersects with a fixed reg use at "<<ni<<", so splitting it."<<endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***-- current range intersects with a fixed reg use at "<<ni<<", so splitting it."<<endl; + } split(current, ni, true); } } @@ -1544,18 +1538,19 @@ void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, { // TODO: check if we can always skip the optional register uses Q_ASSERT(!current.isFixedInterval()); -#ifdef DEBUG_REGALLOC - QTextStream out(stderr, QIODevice::WriteOnly); - out << "***** split request for range ";current.dump(out);out<<" before position "<<beforePosition<<" and skipOptionalRegisterUses = "<<skipOptionalRegisterUses<<endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***** split request for range ";current.dump(out);out<<" before position "<<beforePosition<<" and skipOptionalRegisterUses = "<<skipOptionalRegisterUses<<endl; + } assignSpillSlot(current.temp(), current.start(), current.end()); const int defPosition = _info->def(current.temp()); if (beforePosition < defPosition) { -#ifdef DEBUG_REGALLOC - out << "***** split before position is before or at definition, so not splitting."<<endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***** split before position is before or at definition, so not splitting."<<endl; + } return; } @@ -1581,14 +1576,13 @@ void RegisterAllocator::split(LifeTimeInterval ¤t, int beforePosition, Q_ASSERT(lastUse < beforePosition); -#ifdef DEBUG_REGALLOC - out << "***** last use = "<<lastUse<<", nextUse = " << nextUse<<endl; -#endif // DEBUG_REGALLOC LifeTimeInterval newInterval = current.split(lastUse, nextUse); -#ifdef DEBUG_REGALLOC - out << "***** new interval: "; newInterval.dump(out); out << endl; - out << "***** preceding interval: "; current.dump(out); out << endl; -#endif // DEBUG_REGALLOC + if (DebugRegAlloc) { + QTextStream out(stderr, QIODevice::WriteOnly); + out << "***** last use = "<<lastUse<<", nextUse = " << nextUse<<endl; + out << "***** new interval: "; newInterval.dump(out); out << endl; + out << "***** preceding interval: "; current.dump(out); out << endl; + } if (newInterval.isValid()) { if (current.reg() != LifeTimeInterval::Invalid) _info->addHint(current.temp(), current.reg()); @@ -1634,7 +1628,8 @@ void RegisterAllocator::assignSpillSlot(const Temp &t, int startPos, int endPos) void RegisterAllocator::dump() const { -#ifdef DEBUG_REGALLOC + if (!DebugRegAlloc) + return; QTextStream qout(stdout, QIODevice::WriteOnly); { @@ -1648,6 +1643,7 @@ void RegisterAllocator::dump() const } { + IRPrinter printer(&qout); qout << "Spill slots:" << endl; QList<Temp> temps = _assignedSpillSlots.keys(); if (temps.isEmpty()) @@ -1655,9 +1651,8 @@ void RegisterAllocator::dump() const std::sort(temps.begin(), temps.end()); foreach (const Temp &t, temps) { qout << "\t"; - t.dump(qout); + printer.print(t); qout << " -> " << _assignedSpillSlots[t] << endl; } } -#endif // DEBUG_REGALLOC } diff --git a/src/quick/accessible/accessible.pri b/src/quick/accessible/accessible.pri new file mode 100644 index 0000000000..88ff747488 --- /dev/null +++ b/src/quick/accessible/accessible.pri @@ -0,0 +1,16 @@ + +QT += core-private gui-private qml-private + +#DEFINES+=Q_ACCESSIBLE_QUICK_ITEM_ENABLE_DEBUG_DESCRIPTION + +SOURCES += \ + $$PWD/qqmlaccessible.cpp \ + $$PWD/qaccessiblequickview.cpp \ + $$PWD/qaccessiblequickitem.cpp \ + $$PWD/qquickaccessiblefactory.cpp \ + +HEADERS += \ + $$PWD/qqmlaccessible_p.h \ + $$PWD/qaccessiblequickview_p.h \ + $$PWD/qaccessiblequickitem_p.h \ + $$PWD/qquickaccessiblefactory_p.h \ diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.cpp b/src/quick/accessible/qaccessiblequickitem.cpp index 164144c052..c8b2d9ec3e 100644 --- a/src/plugins/accessible/quick/qaccessiblequickitem.cpp +++ b/src/quick/accessible/qaccessiblequickitem.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -39,7 +39,7 @@ ** ****************************************************************************/ -#include "qaccessiblequickitem.h" +#include "qaccessiblequickitem_p.h" #include <QtGui/qtextdocument.h> diff --git a/src/plugins/accessible/quick/qaccessiblequickitem.h b/src/quick/accessible/qaccessiblequickitem_p.h index b486720c68..d1facf2199 100644 --- a/src/plugins/accessible/quick/qaccessiblequickitem.h +++ b/src/quick/accessible/qaccessiblequickitem_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -44,7 +44,7 @@ #include <QtQuick/QQuickItem> #include <QtQuick/QQuickView> -#include "qqmlaccessible.h" +#include <QtQuick/private/qqmlaccessible_p.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/accessible/quick/qaccessiblequickview.cpp b/src/quick/accessible/qaccessiblequickview.cpp index 1240b2ef4c..05e37d6240 100644 --- a/src/plugins/accessible/quick/qaccessiblequickview.cpp +++ b/src/quick/accessible/qaccessiblequickview.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -39,15 +39,15 @@ ** ****************************************************************************/ -#include "qaccessiblequickview.h" +#include "qaccessiblequickview_p.h" #include <QtGui/qguiapplication.h> #include <QtQuick/qquickitem.h> #include <QtQuick/private/qquickitem_p.h> -#include "qaccessiblequickitem.h" -#include "qqmlaccessible.h" +#include "qaccessiblequickitem_p.h" +#include "qqmlaccessible_p.h" #ifndef QT_NO_ACCESSIBILITY diff --git a/src/plugins/accessible/quick/qaccessiblequickview.h b/src/quick/accessible/qaccessiblequickview_p.h index 41c34c5432..f14d4c9584 100644 --- a/src/plugins/accessible/quick/qaccessiblequickview.h +++ b/src/quick/accessible/qaccessiblequickview_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage diff --git a/src/plugins/accessible/shared/qqmlaccessible.cpp b/src/quick/accessible/qqmlaccessible.cpp index ecf4e56acf..abe94537a9 100644 --- a/src/plugins/accessible/shared/qqmlaccessible.cpp +++ b/src/quick/accessible/qqmlaccessible.cpp @@ -40,7 +40,7 @@ ****************************************************************************/ #include <qnamespace.h> -#include "qqmlaccessible.h" +#include "qqmlaccessible_p.h" #ifndef QT_NO_ACCESSIBILITY @@ -148,8 +148,8 @@ void QQmlAccessible::doAction(const QString &actionName) { // Look for and call the accessible[actionName]Action() function on the item. // This allows for overriding the default action handling. - const QByteArray functionName = "accessible" + actionName.toLatin1() + "Action"; - if (object()->metaObject()->indexOfMethod(functionName + "()") != -1) { + const QByteArray functionName = QByteArrayLiteral("accessible") + actionName.toLatin1() + QByteArrayLiteral("Action"); + if (object()->metaObject()->indexOfMethod(QByteArray(functionName + QByteArrayLiteral("()"))) != -1) { QMetaObject::invokeMethod(object(), functionName); return; } diff --git a/src/plugins/accessible/shared/qqmlaccessible.h b/src/quick/accessible/qqmlaccessible_p.h index b6da016b2d..b6da016b2d 100644 --- a/src/plugins/accessible/shared/qqmlaccessible.h +++ b/src/quick/accessible/qqmlaccessible_p.h diff --git a/src/plugins/accessible/quick/main.cpp b/src/quick/accessible/qquickaccessiblefactory.cpp index 6c7be155ce..d0e7f0f5e8 100644 --- a/src/plugins/accessible/quick/main.cpp +++ b/src/quick/accessible/qquickaccessiblefactory.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -39,50 +39,16 @@ ** ****************************************************************************/ +#include "qquickaccessiblefactory_p.h" -#include "qqmlaccessible.h" -#include "qaccessiblequickview.h" -#include "qaccessiblequickitem.h" - -#include <QtQuick/QQuickWindow> -#include <QtQuick/QQuickItem> +#include "qaccessiblequickview_p.h" +#include "qaccessiblequickitem_p.h" #include <QtQuick/private/qquickitem_p.h> -#include <QtQuick/private/qquickaccessibleattached_p.h> - -#include <qaccessibleplugin.h> -#include <qvariant.h> -#include <qplugin.h> -#include <qaccessible.h> - -#ifndef QT_NO_ACCESSIBILITY QT_BEGIN_NAMESPACE +#ifndef QT_NO_ACCESSIBILITY -class AccessibleQuickFactory : public QAccessiblePlugin -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QAccessibleFactoryInterface" FILE "accessible.json") - -public: - AccessibleQuickFactory(); - - QStringList keys() const; - QAccessibleInterface *create(const QString &classname, QObject *object); -}; - -AccessibleQuickFactory::AccessibleQuickFactory() -{ -} - -QStringList AccessibleQuickFactory::keys() const -{ - QStringList list; - list << QLatin1String("QQuickWindow"); - list << QLatin1String("QQuickItem"); - return list; -} - -QAccessibleInterface *AccessibleQuickFactory::create(const QString &classname, QObject *object) +QAccessibleInterface *qQuickAccessibleFactory(const QString &classname, QObject *object) { if (classname == QLatin1String("QQuickWindow")) { return new QAccessibleQuickWindow(qobject_cast<QQuickWindow *>(object)); @@ -98,8 +64,5 @@ QAccessibleInterface *AccessibleQuickFactory::create(const QString &classname, Q return 0; } +#endif QT_END_NAMESPACE - -#include "main.moc" - -#endif // QT_NO_ACCESSIBILITY diff --git a/src/quick/accessible/qquickaccessiblefactory_p.h b/src/quick/accessible/qquickaccessiblefactory_p.h new file mode 100644 index 0000000000..792364b7fa --- /dev/null +++ b/src/quick/accessible/qquickaccessiblefactory_p.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKACCESSIBLEFACTORY_H +#define QQUICKACCESSIBLEFACTORY_H + +#include <QtGui/qaccessible.h> + +QT_BEGIN_NAMESPACE +#ifndef QT_NO_ACCESSIBILITY + +QAccessibleInterface *qQuickAccessibleFactory(const QString &classname, QObject *object); + +#endif +QT_END_NAMESPACE + +#endif diff --git a/src/quick/doc/snippets/qml/itemGrab.qml b/src/quick/doc/snippets/qml/itemGrab.qml new file mode 100644 index 0000000000..4ceaea6133 --- /dev/null +++ b/src/quick/doc/snippets/qml/itemGrab.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 + +Item { + width: 320 + height: 480 + +//! [grab-source] +Rectangle { + id: source + width: 100 + height: 100 + gradient: Gradient { + GradientStop { position: 0; color: "steelblue" } + GradientStop { position: 1; color: "black" } + } +} +//! [grab-source] + +//! [grab-image-target] +Image { + id: image +} +//! [grab-image-target] + Timer { + repeat: false + running: true + interval: 1000 + onTriggered: { +//! [grab-to-file] + + // ... + source.grabToImage(function(result) { + result.save("something.png"); + }); +//! [grab-to-file] + +//! [grab-to-cache] + + // ... + source.grabToImage(function(result) { + image.source = result.url; + }, + Qt.size(50, 50)); +//! [grab-to-cache] + } + } +} diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 7fc9bea032..3f9de28c9d 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -322,7 +322,8 @@ set of Particle System types for Qt Quick 2 /*! \qmlmodule QtQuick 2.2 -\brief The Provides graphical primitives for use in QML. +\title Qt Quick QML Types +\brief This module provides graphical primitives for use in QML. The \l{Qt Quick} module provides graphical primitive types. They can be used with the following import \code diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index cb378b424b..add909d0cb 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -75,7 +75,8 @@ HEADERS += \ $$PWD/qquickitemviewtransition_p.h \ $$PWD/qquickscreen_p.h \ $$PWD/qquickwindowmodule_p.h \ - $$PWD/qquickframebufferobject.h + $$PWD/qquickframebufferobject.h \ + $$PWD/qquickitemgrabresult.h SOURCES += \ $$PWD/qquickevents.cpp \ @@ -128,7 +129,8 @@ SOURCES += \ $$PWD/qquickitemviewtransition.cpp \ $$PWD/qquickwindowmodule.cpp \ $$PWD/qquickscreen.cpp \ - $$PWD/qquickframebufferobject.cpp + $$PWD/qquickframebufferobject.cpp \ + $$PWD/qquickitemgrabresult.cpp SOURCES += \ $$PWD/qquickshadereffect.cpp \ diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index bfe957e943..533f1cabed 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -498,7 +498,6 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode() return 0; m_sheetSize = QSizeF(image.size()); m_material->texture = window()->createTextureFromImage(image); - m_material->texture->setFiltering(QSGTexture::Linear); m_spriteEngine->start(0); m_material->animT = 0; m_material->animX1 = m_spriteEngine->spriteX() / m_sheetSize.width(); @@ -677,6 +676,7 @@ void QQuickAnimatedSprite::prepareNextFrame() m_material->animW = w; m_material->animH = h; m_material->animT = m_interpolate ? progress : 0.0; + m_material->texture->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); } QT_END_NAMESPACE diff --git a/src/quick/items/qquickdrag.cpp b/src/quick/items/qquickdrag.cpp index 6c36032d3c..7e5b357e9c 100644 --- a/src/quick/items/qquickdrag.cpp +++ b/src/quick/items/qquickdrag.cpp @@ -791,7 +791,7 @@ void QQuickDragAttached::startDrag(QQmlV4Function *args) QQuickDrag::QQuickDrag(QObject *parent) : QObject(parent), _target(0), _axis(XAndYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), _active(false), _filterChildren(false), - _threshold(qApp->styleHints()->startDragDistance()) + _smoothed(true), _threshold(qApp->styleHints()->startDragDistance()) { } @@ -885,6 +885,18 @@ void QQuickDrag::setYmax(qreal m) emit maximumYChanged(); } +bool QQuickDrag::smoothed() const +{ + return _smoothed; +} + +void QQuickDrag::setSmoothed(bool smooth) +{ + if (_smoothed != smooth) { + _smoothed = smooth; + emit smoothedChanged(); + } +} qreal QQuickDrag::threshold() const { diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h index d9021d0f6d..eebe07469a 100644 --- a/src/quick/items/qquickdrag_p.h +++ b/src/quick/items/qquickdrag_p.h @@ -157,6 +157,7 @@ class Q_AUTOTEST_EXPORT QQuickDrag : public QObject Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged) Q_PROPERTY(bool active READ active NOTIFY activeChanged) Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged) + Q_PROPERTY(bool smoothed READ smoothed WRITE setSmoothed NOTIFY smoothedChanged) // Note, threshold was added in QtQuick 2.2 but REVISION is not supported (or needed) for grouped // properties See QTBUG-33179 Q_PROPERTY(qreal threshold READ threshold WRITE setThreshold NOTIFY thresholdChanged RESET resetThreshold) @@ -185,6 +186,9 @@ public: qreal ymax() const; void setYmax(qreal); + bool smoothed() const; + void setSmoothed(bool smooth); + qreal threshold() const; void setThreshold(qreal); void resetThreshold(); @@ -206,6 +210,7 @@ Q_SIGNALS: void maximumYChanged(); void activeChanged(); void filterChildrenChanged(); + void smoothedChanged(); void thresholdChanged(); private: @@ -217,6 +222,7 @@ private: qreal _ymax; bool _active : 1; bool _filterChildren: 1; + bool _smoothed : 1; qreal _threshold; Q_DISABLE_COPY(QQuickDrag) }; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index ef2eac4b61..da8ca9fd5d 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -59,6 +59,7 @@ #include <QtCore/qcoreevent.h> #include <QtCore/qnumeric.h> #include <QtGui/qpa/qplatformtheme.h> +#include <QtCore/qloggingcategory.h> #include <private/qqmlglobal_p.h> #include <private/qqmlengine_p.h> @@ -87,25 +88,24 @@ QT_BEGIN_NAMESPACE static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty(); #endif -#ifdef FOCUS_DEBUG -void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1); -void printFocusTree(QQuickItem *item, QQuickItem *scope, int depth) -{ - qWarning() - << QByteArray(depth, '\t').constData() - << (scope && QQuickItemPrivate::get(scope)->subFocusItem == item ? '*' : ' ') - << item->hasFocus() - << item->hasActiveFocus() - << item->isFocusScope() - << item; - foreach (QQuickItem *child, item->childItems()) { - printFocusTree( - child, - item->isFocusScope() || !scope ? item : scope, - item->isFocusScope() || !scope ? depth + 1 : depth); +void debugFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1) +{ + if (DBG_FOCUS().isEnabled(QtDebugMsg)) { + qCDebug(DBG_FOCUS) + << QByteArray(depth, '\t').constData() + << (scope && QQuickItemPrivate::get(scope)->subFocusItem == item ? '*' : ' ') + << item->hasFocus() + << item->hasActiveFocus() + << item->isFocusScope() + << item; + foreach (QQuickItem *child, item->childItems()) { + debugFocusTree( + child, + item->isFocusScope() || !scope ? item : scope, + item->isFocusScope() || !scope ? depth + 1 : depth); + } } } -#endif static void QQuickItem_parentNotifier(QObject *o, qintptr, QQmlNotifier **n) { @@ -657,13 +657,13 @@ void QQuickKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post) break; case Qt::Key_Tab: if (d->tab) { - setFocusNavigation(d->tab, "tab"); + setFocusNavigation(d->tab, "tab", Qt::TabFocusReason); event->accept(); } break; case Qt::Key_Backtab: if (d->backtab) { - setFocusNavigation(d->backtab, "backtab"); + setFocusNavigation(d->backtab, "backtab", Qt::BacktabFocusReason); event->accept(); } break; @@ -725,14 +725,15 @@ void QQuickKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post) if (!event->isAccepted()) QQuickItemKeyFilter::keyReleased(event, post); } -void QQuickKeyNavigationAttached::setFocusNavigation(QQuickItem *currentItem, const char *dir) +void QQuickKeyNavigationAttached::setFocusNavigation(QQuickItem *currentItem, const char *dir, + Qt::FocusReason reason) { QQuickItem *initialItem = currentItem; bool isNextItem = false; do { isNextItem = false; if (currentItem->isVisible() && currentItem->isEnabled()) { - currentItem->forceActiveFocus(Qt::OtherFocusReason); + currentItem->forceActiveFocus(reason); } else { QObject *attached = qmlAttachedPropertiesObject<QQuickKeyNavigationAttached>(currentItem, false); @@ -2261,14 +2262,10 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo if (current == startItem && from == firstFromItem) { // wrapped around, avoid endless loops if (originalItem == contentItem) { -#ifdef FOCUS_DEBUG - qDebug() << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem"; -#endif + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return contentItem"; return item->window()->contentItem(); } else { -#ifdef FOCUS_DEBUG - qDebug() << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return " << startItem; -#endif + qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: looped, return " << startItem; return startItem; } } @@ -2507,6 +2504,12 @@ void QQuickItem::stackAfter(const QQuickItem *sibling) */ /*! + \qmlproperty Window QtQuick::Item::window + \since 5.4 + This property holds the window in which the item is rendered. + */ + +/*! Returns the window in which this item is rendered. The item does not have a window until it has been assigned into a scene. The @@ -5390,6 +5393,17 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec emit q->enabledChanged(); } +bool QQuickItemPrivate::isTransparentForPositioner() const +{ + return extra.isAllocated() && extra.value().transparentForPositioner; +} + +void QQuickItemPrivate::setTransparentForPositioner(bool transparent) +{ + extra.value().transparentForPositioner = transparent; +} + + QString QQuickItemPrivate::dirtyToString() const { #define DIRTY_TO_STRING(value) if (dirtyAttributes & value) { \ @@ -5818,10 +5832,6 @@ qreal QQuickItem::y() const } /*! - \property QQuickItem::pos - \internal - */ -/*! \internal */ QPointF QQuickItem::position() const @@ -7249,6 +7259,7 @@ void QQuickItemLayer::activate() { Q_ASSERT(!m_effectSource); m_effectSource = new QQuickShaderEffectSource(); + QQuickItemPrivate::get(m_effectSource)->setTransparentForPositioner(true); QQuickItem *parentItem = m_item->parentItem(); if (parentItem) { @@ -7314,6 +7325,7 @@ void QQuickItemLayer::activateEffect() } m_effect->setVisible(m_item->isVisible()); m_effect->setProperty(m_name, qVariantFromValue<QObject *>(m_effectSource)); + QQuickItemPrivate::get(m_effect)->setTransparentForPositioner(true); m_effectComponent->completeCreate(); } @@ -7630,7 +7642,8 @@ QQuickItemPrivate::ExtraData::ExtraData() #endif effectRefCount(0), hideRefCount(0), opacityNode(0), clipNode(0), rootNode(0), beforePaintNode(0), - acceptedMouseButtons(0), origin(QQuickItem::Center) + acceptedMouseButtons(0), origin(QQuickItem::Center), + transparentForPositioner(false) { } diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 2b08cc2598..91940c7cf0 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -52,6 +52,7 @@ #include <QtGui/qfont.h> #include <QtGui/qaccessible.h> + QT_BEGIN_NAMESPACE class QQuickItem; @@ -92,6 +93,7 @@ class QTouchEvent; class QSGNode; class QSGTransformNode; class QSGTextureProvider; +class QQuickItemGrabResult; class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus { @@ -145,6 +147,8 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged) Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged) + Q_PROPERTY(QQuickWindow *window READ window NOTIFY windowChanged REVISION 2) + Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickItemLayer *layer READ layer DESIGNABLE false CONSTANT FINAL) Q_ENUMS(TransformOrigin) @@ -308,6 +312,10 @@ public: bool keepTouchGrab() const; void setKeepTouchGrab(bool); + // implemented in qquickitemgrabresult.cpp + Q_REVISION(2) Q_INVOKABLE bool grabToImage(const QJSValue &callback, const QSize &targetSize = QSize()); + QSharedPointer<QQuickItemGrabResult> grabToImage(const QSize &targetSize = QSize()); + Q_INVOKABLE virtual bool contains(const QPointF &point) const; QTransform itemTransform(QQuickItem *, bool *) const; diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 96cb9e8843..dbd4d51941 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -368,6 +368,7 @@ public: Qt::MouseButtons acceptedMouseButtons; QQuickItem::TransformOrigin origin:5; + uint transparentForPositioner : 1; QObjectList resourcesList; }; @@ -545,6 +546,9 @@ public: void deliverInputMethodEvent(QInputMethodEvent *); #endif + bool isTransparentForPositioner() const; + void setTransparentForPositioner(bool trans); + bool calcEffectiveVisible() const; bool setEffectiveVisibleRecur(bool); bool calcEffectiveEnable() const; @@ -686,7 +690,8 @@ Q_SIGNALS: private: virtual void keyPressed(QKeyEvent *event, bool post); virtual void keyReleased(QKeyEvent *event, bool post); - void setFocusNavigation(QQuickItem *currentItem, const char *dir); + void setFocusNavigation(QQuickItem *currentItem, const char *dir, + Qt::FocusReason reason = Qt::OtherFocusReason); }; class QQuickLayoutMirroringAttached : public QObject diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp new file mode 100644 index 0000000000..93b9261c44 --- /dev/null +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -0,0 +1,399 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitemgrabresult.h" + +#include "qquickwindow.h" +#include "qquickitem.h" +#include "qquickshadereffectsource_p.h" + +#include <QtQml/QQmlEngine> + +#include <private/qquickpixmapcache_p.h> +#include <private/qquickitem_p.h> +#include <private/qsgcontext_p.h> + +QT_BEGIN_NAMESPACE + +const QEvent::Type Event_Grab_Completed = static_cast<QEvent::Type>(QEvent::User + 1); + +class QQuickItemGrabResultPrivate : public QObjectPrivate +{ +public: + QQuickItemGrabResultPrivate() + : cacheEntry(0) + , texture(0) + { + } + + ~QQuickItemGrabResultPrivate() + { + delete cacheEntry; + } + + void ensureImageInCache() const { + if (url.isEmpty() && !image.isNull()) { + url.setScheme(QStringLiteral("ItemGrabber")); + url.setPath(QVariant::fromValue(item.data()).toString()); + static uint counter = 0; + url.setFragment(QString::number(++counter)); + cacheEntry = new QQuickPixmap(url, image); + } + } + + static QQuickItemGrabResult *create(QQuickItem *item, const QSize &size); + + QImage image; + + mutable QUrl url; + mutable QQuickPixmap *cacheEntry; + + QQmlEngine *qmlEngine; + QJSValue callback; + + QPointer<QQuickItem> item; + QPointer<QQuickWindow> window; + QQuickShaderEffectTexture *texture; + QSizeF itemSize; + QSize textureSize; +}; + +/*! + * \qmlproperty url QtQuick::ItemGrabResult::url + * + * This property holds a URL which can be used in conjunction with + * URL based image consumers, such as the QtQuick::Image type. + * + * The URL is valid while there is a reference in QML or JavaScript + * to the ItemGrabResult or while the image the URL references is + * actively used. + * + * The URL does not represent a valid file or location to read it from, it + * is primarily a key to access images through Qt Quick's image-based types. + */ + +/*! + * \property QQuickItemGrabResult::url + * + * This property holds a URL which can be used in conjunction with + * URL based image consumers, such as the QtQuick::Image type. + * + * The URL is valid until the QQuickItemGrabResult object is deleted. + * + * The URL does not represent a valid file or location to read it from, it + * is primarily a key to access images through Qt Quick's image-based types. + */ + +/*! + * \qmlproperty variant QtQuick::ItemGrabResult::image + * + * This property holds the pixel results from a grab in the + * form of a QImage. + */ + +/*! + * \property QQuickItemGrabResult::image + * + * This property holds the pixel results from a grab. + * + * If the grab is not yet complete or if it failed, + * an empty image is returned. + */ + +/*! + \class QQuickItemGrabResult + \inmodule QtQuick + \brief The QQuickItemGrabResult contains the result from QQuickItem::grabToImage(). + + \sa QQuickItem::grabToImage() + */ + +/*! + * \fn void QQuickItemGrabResult::ready() + * + * This signal is emitted when the grab has completed. + */ + +/*! + * \qmltype ItemGrabResult + * \instantiates QQuickItemGrabResult + * \inherits QtObject + * \inqmlmodule QtQuick + * \ingroup qtquick-visual + * \brief Contains the results from a call to Item::grabToImage(). + * + * The ItemGrabResult is a small container used to encapsulate + * the results from Item::grabToImage(). + * + * \sa Item::grabToImage() + */ + +QQuickItemGrabResult::QQuickItemGrabResult(QObject *parent) + : QObject(*new QQuickItemGrabResultPrivate, parent) +{ +} + +/*! + * \qmlmethod bool QtQuick::ItemGrabResult::saveToFile(fileName) + * + * Saves the grab result as an image to \a fileName. Returns true + * if successful; otherwise returns false. + */ + +/*! + * Saves the grab result as an image to \a fileName. Returns true + * if successful; otherwise returns false. + */ +bool QQuickItemGrabResult::saveToFile(const QString &fileName) +{ + Q_D(QQuickItemGrabResult); + return d->image.save(fileName); +} + +QUrl QQuickItemGrabResult::url() const +{ + Q_D(const QQuickItemGrabResult); + d->ensureImageInCache(); + return d->url; +} + +QImage QQuickItemGrabResult::image() const +{ + Q_D(const QQuickItemGrabResult); + return d->image; +} + +/*! + * \internal + */ +bool QQuickItemGrabResult::event(QEvent *e) +{ + Q_D(QQuickItemGrabResult); + if (e->type() == Event_Grab_Completed) { + // JS callback + if (d->qmlEngine && d->callback.isCallable()) + d->callback.call(QJSValueList() << d->qmlEngine->newQObject(this)); + else + Q_EMIT ready(); + return true; + } + return QObject::event(e); +} + +void QQuickItemGrabResult::setup() +{ + Q_D(QQuickItemGrabResult); + if (!d->item) { + disconnect(d->window.data(), &QQuickWindow::beforeSynchronizing, this, &QQuickItemGrabResult::setup); + disconnect(d->window.data(), &QQuickWindow::afterRendering, this, &QQuickItemGrabResult::render); + QCoreApplication::postEvent(this, new QEvent(Event_Grab_Completed)); + return; + } + + d->texture = new QQuickShaderEffectTexture(d->item); + d->texture->setItem(QQuickItemPrivate::get(d->item)->itemNode()); + d->itemSize = QSizeF(d->item->width(), d->item->height()); +} + +void QQuickItemGrabResult::render() +{ + Q_D(QQuickItemGrabResult); + if (!d->texture) + return; + + d->texture->setRect(QRectF(0, d->itemSize.height(), d->itemSize.width(), -d->itemSize.height())); + QSGContext *sg = QSGRenderContext::from(QOpenGLContext::currentContext())->sceneGraphContext(); + const QSize minSize = sg->minimumFBOSize(); + d->texture->setSize(QSize(qMax(minSize.width(), d->textureSize.width()), + qMax(minSize.height(), d->textureSize.height()))); + d->texture->scheduleUpdate(); + d->texture->updateTexture(); + d->image = d->texture->toImage(); + + delete d->texture; + d->texture = 0; + + disconnect(d->window.data(), &QQuickWindow::beforeSynchronizing, this, &QQuickItemGrabResult::setup); + disconnect(d->window.data(), &QQuickWindow::afterRendering, this, &QQuickItemGrabResult::render); + QCoreApplication::postEvent(this, new QEvent(Event_Grab_Completed)); +} + +QQuickItemGrabResult *QQuickItemGrabResultPrivate::create(QQuickItem *item, const QSize &targetSize) +{ + QSize size = targetSize; + if (size.isEmpty()) + size = QSize(item->width(), item->height()); + + if (size.width() < 1 || size.height() < 1) { + qWarning("Item::grabToImage: item has invalid dimensions"); + return 0; + } + + if (!item->window()) { + qWarning("Item::grabToImage: item is not attached to a window"); + return 0; + } + + if (!item->window()->isVisible()) { + qWarning("Item::grabToImage: item's window is not visible"); + return 0; + } + + QQuickItemGrabResult *result = new QQuickItemGrabResult(); + QQuickItemGrabResultPrivate *d = result->d_func(); + d->item = item; + d->window = item->window(); + d->textureSize = size; + + QQuickItemPrivate::get(item)->refFromEffectItem(false); + + // trigger sync & render + item->window()->update(); + + return result; +} + +/*! + * Grabs the item into an in-memory image. + * + * The grab happens asynchronously and the signal QQuickItemGrabResult::ready() + * is emitted when the grab has been completed. + * + * Use \a targetSize to specify the size of the target image. By default, the + * result will have the same size as item. + * + * If the grab could not be initiated, the function returns a \c null. + * + * \note This function will render the item to an offscreen surface and + * copy that surface from the GPU's memory into the CPU's memory, which can + * be quite costly. For "live" preview, use \l {QtQuick::Item::layer.enabled} {layers} + * or ShaderEffectSource. + * + * \sa QQuickWindow::grabWindow() + */ +QSharedPointer<QQuickItemGrabResult> QQuickItem::grabToImage(const QSize &targetSize) +{ + QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, targetSize); + if (!result) + return QSharedPointer<QQuickItemGrabResult>(); + + connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection); + connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection); + + return QSharedPointer<QQuickItemGrabResult>(result); +} + +/*! + * \qmlmethod bool QtQuick::Item::grabToImage(callback, targetSize) + * + * Grabs the item into an in-memory image. + * + * The grab happens asynchronously and the JavaScript function \a callback is + * invoked when the grab is completed. + * + * Use \a targetSize to specify the size of the target image. By default, the result + * will have the same size as the item. + * + * If the grab could not be initiated, the function returns \c false. + * + * The following snippet shows how to grab an item and store the results to + * a file. + * + * \snippet qml/itemGrab.qml grab-source + * \snippet qml/itemGrab.qml grab-to-file + * + * The following snippet shows how to grab an item and use the results in + * another image element. + * + * \snippet qml/itemGrab.qml grab-image-target + * \snippet qml/itemGrab.qml grab-to-cache + * + * \note This function will render the item to an offscreen surface and + * copy that surface from the GPU's memory into the CPU's memory, which can + * be quite costly. For "live" preview, use \l {QtQuick::Item::layer.enabled} {layers} + * or ShaderEffectSource. + */ + +/*! + * \internal + * Only visible from QML. + */ +bool QQuickItem::grabToImage(const QJSValue &callback, const QSize &targetSize) +{ + QQmlEngine *engine = qmlEngine(this); + if (!engine) { + qWarning("Item::grabToImage: no QML Engine"); + return false; + } + + if (!callback.isCallable()) { + qWarning("Item::grabToImage: 'callback' is not a function"); + return false; + } + + QSize size = targetSize; + if (size.isEmpty()) + size = QSize(width(), height()); + + if (size.width() < 1 || size.height() < 1) { + qWarning("Item::grabToImage: item has invalid dimensions"); + return false; + } + + if (!window()) { + qWarning("Item::grabToImage: item is not attached to a window"); + return false; + } + + QQuickItemGrabResult *result = QQuickItemGrabResultPrivate::create(this, size); + if (!result) + return false; + + connect(window(), &QQuickWindow::beforeSynchronizing, result, &QQuickItemGrabResult::setup, Qt::DirectConnection); + connect(window(), &QQuickWindow::afterRendering, result, &QQuickItemGrabResult::render, Qt::DirectConnection); + + QQuickItemGrabResultPrivate *d = result->d_func(); + d->qmlEngine = engine; + d->callback = callback; + return true; +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquickitemgrabresult.h b/src/quick/items/qquickitemgrabresult.h new file mode 100644 index 0000000000..5297002bb8 --- /dev/null +++ b/src/quick/items/qquickitemgrabresult.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKITEMGRABRESULT_H +#define QQUICKITEMGRABRESULT_H + +#include <QtCore/QObject> +#include <QtCore/QSize> +#include <QtCore/QUrl> +#include <QtGui/QImage> +#include <QtQml/QJSValue> +#include <QtQuick/qtquickglobal.h> + +QT_BEGIN_NAMESPACE + +class QImage; + +class QQuickItemGrabResultPrivate; + +class Q_QUICK_EXPORT QQuickItemGrabResult : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickItemGrabResult) + + Q_PROPERTY(QImage image READ image CONSTANT) + Q_PROPERTY(QUrl url READ url CONSTANT) +public: + QImage image() const; + QUrl url() const; + + Q_INVOKABLE bool saveToFile(const QString &fileName); + +protected: + bool event(QEvent *); + +Q_SIGNALS: + void ready(); + +private Q_SLOTS: + void setup(); + void render(); + +private: + friend class QQuickItem; + + QQuickItemGrabResult(QObject *parent = 0); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 10c1780799..2f20dd763b 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -43,6 +43,7 @@ #include "qquickitem.h" #include "qquickitem_p.h" +#include "qquickitemgrabresult.h" #include "qquickevents_p_p.h" #include "qquickrectangle_p.h" #include "qquickfocusscope_p.h" @@ -183,8 +184,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickTextEdit,1>(uri,2,1,"TextEdit"); qmlRegisterType<QQuickTextInput>(uri,major,minor,"TextInput"); qmlRegisterType<QQuickTextInput,2>(uri,2,2,"TextInput"); + qmlRegisterType<QQuickTextInput,3>(uri,2,4,"TextInput"); qmlRegisterType<QQuickViewSection>(uri,major,minor,"ViewSection"); + qmlRegisterType<QQuickItemGrabResult>(); qmlRegisterType<QQuickItemLayer>(); qmlRegisterType<QQuickAnchors>(); qmlRegisterType<QQuickKeyEvent>(); @@ -265,6 +268,8 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickText, 3>(uri, 2, 3, "Text"); qmlRegisterType<QQuickTextEdit, 3>(uri, 2, 3, "TextEdit"); qmlRegisterType<QQuickImage, 1>(uri, 2, 3,"Image"); + + qmlRegisterType<QQuickItem, 2>(uri, 2, 4, "Item"); } static void initResources() diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index ba4f1c53ba..ec13fa5a6b 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -1378,6 +1378,8 @@ void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF & // position all subsequent items if (visibleItems.count() && item == visibleItems.first()->item) { FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first()); + if (listItem->transitionScheduledOrRunning()) + return; if (orient == QQuickListView::Vertical) { const qreal oldItemEndPosition = verticalLayoutDirection == QQuickItemView::BottomToTop ? -oldGeometry.y() : oldGeometry.y() + oldGeometry.height(); qreal diff = newGeometry.height() - oldGeometry.height(); diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index baa48291e2..01d1305260 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -483,6 +483,10 @@ qreal QQuickMouseArea::mouseY() const \qmlproperty bool QtQuick::MouseArea::enabled This property holds whether the item accepts mouse events. + \note Due to historical reasons, this property is not equivalent to + Item.enabled. It only affects mouse events, and its effect does not + propagate to child items. + By default, this property is true. */ bool QQuickMouseArea::isEnabled() const @@ -719,7 +723,9 @@ void QQuickMouseArea::mouseMoveEvent(QMouseEvent *event) || QQuickWindowPrivate::dragOverThreshold(dragPos.y() - startPos.y(), Qt::YAxis, event, d->drag->threshold()))) { setKeepMouseGrab(true); d->stealMouse = true; - d->startScene = event->windowPos(); + + if (d->drag->smoothed()) + d->startScene = event->windowPos(); } d->moved = true; @@ -1246,6 +1252,10 @@ void QQuickMouseArea::setCursorShape(Qt::CursorShape shape) start. By default this is bound to a platform dependent value. This property was added in Qt Quick 2.2. + If \c drag.smoothed is \c true, the target will be moved only after the drag operation has + started. If set to \c false, the target will be moved straight to the current mouse position. + By default, this property is \c true. This property was added in Qt Quick 2.4 + \snippet qml/mousearea/mouseareadragfilter.qml dragfilter */ diff --git a/src/quick/items/qquickpincharea.cpp b/src/quick/items/qquickpincharea.cpp index 1f00e6a74d..234f78c380 100644 --- a/src/quick/items/qquickpincharea.cpp +++ b/src/quick/items/qquickpincharea.cpp @@ -320,7 +320,6 @@ void QQuickPinchArea::touchEvent(QTouchEvent *event) // it's always going to accept the touches, and that means the item underneath // will not get them (unless the PA's parent is doing parent filtering, // as the Flickable does, for example). - switch (event->type()) { case QEvent::TouchBegin: case QEvent::TouchUpdate: @@ -333,18 +332,52 @@ void QQuickPinchArea::touchEvent(QTouchEvent *event) updatePinch(); break; case QEvent::TouchEnd: - d->touchPoints.clear(); - updatePinch(); + clearPinch(); break; default: QQuickItem::event(event); } } +void QQuickPinchArea::clearPinch() +{ + Q_D(QQuickPinchArea); + + d->touchPoints.clear(); + if (d->inPinch) { + d->inPinch = false; + QPointF pinchCenter = mapFromScene(d->sceneLastCenter); + QQuickPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation); + pe.setStartCenter(d->pinchStartCenter); + pe.setPreviousCenter(pinchCenter); + pe.setPreviousAngle(d->pinchLastAngle); + pe.setPreviousScale(d->pinchLastScale); + pe.setStartPoint1(mapFromScene(d->sceneStartPoint1)); + pe.setStartPoint2(mapFromScene(d->sceneStartPoint2)); + pe.setPoint1(mapFromScene(d->lastPoint1)); + pe.setPoint2(mapFromScene(d->lastPoint2)); + emit pinchFinished(&pe); + if (d->pinch && d->pinch->target()) + d->pinch->setActive(false); + } + d->pinchStartDist = 0; + d->pinchActivated = false; + d->initPinch = false; + d->pinchRejected = false; + d->stealMouse = false; + d->id1 = -1; + QQuickWindow *win = window(); + if (win && win->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); +} + void QQuickPinchArea::updatePinch() { Q_D(QQuickPinchArea); + QQuickWindow *win = window(); + if (d->touchPoints.count() < 2) { setKeepMouseGrab(false); QQuickWindow *c = window(); @@ -379,6 +412,7 @@ void QQuickPinchArea::updatePinch() QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0); QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0); + QRectF bounds = clipRect(); // Pinch is not started unless there are exactly two touch points // AND one or more of the points has just now been pressed (wasn't pressed already) @@ -391,6 +425,13 @@ void QQuickPinchArea::updatePinch() d->sceneStartPoint2 = touchPoint2.scenePos(); d->pinchActivated = true; d->initPinch = true; + + int touchMouseId = QQuickWindowPrivate::get(win)->touchMouseId; + if (touchPoint1.id() == touchMouseId || touchPoint2.id() == touchMouseId) { + if (win && win->mouseGrabberItem() != this) { + grabMouse(); + } + } } if (d->pinchActivated && !d->pinchRejected) { const int dragThreshold = qApp->styleHints()->startDragDistance(); @@ -449,10 +490,12 @@ void QQuickPinchArea::updatePinch() if (pe.accepted()) { d->inPinch = true; d->stealMouse = true; - QQuickWindow *c = window(); - if (c && c->mouseGrabberItem() != this) + if (win && win->mouseGrabberItem() != this) grabMouse(); setKeepMouseGrab(true); + grabTouchPoints(QVector<int>() << touchPoint1.id() << touchPoint2.id()); + d->inPinch = true; + d->stealMouse = true; if (d->pinch && d->pinch->target()) { d->pinchStartPos = pinch()->target()->position(); d->pinchStartScale = d->pinch->target()->scale(); @@ -528,22 +571,19 @@ bool QQuickPinchArea::childMouseEventFilter(QQuickItem *i, QEvent *e) return QQuickItem::childMouseEventFilter(i, e); switch (e->type()) { case QEvent::TouchBegin: + clearPinch(); // fall through case QEvent::TouchUpdate: { - QTouchEvent *touch = static_cast<QTouchEvent*>(e); - if (touch->touchPoints().count() > 1) { - touchEvent(touch); - } else { - d->touchPoints.clear(); - for (int i = 0; i < touch->touchPoints().count(); ++i) - if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) - d->touchPoints << touch->touchPoints().at(i); - updatePinch(); - } + QTouchEvent *touch = static_cast<QTouchEvent*>(e); + d->touchPoints.clear(); + for (int i = 0; i < touch->touchPoints().count(); ++i) + if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased)) + d->touchPoints << touch->touchPoints().at(i); + updatePinch(); } + e->setAccepted(d->inPinch); return d->inPinch; case QEvent::TouchEnd: - d->touchPoints.clear(); - updatePinch(); + clearPinch(); break; default: break; diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h index 81bdbda3a1..7f2a379a8f 100644 --- a/src/quick/items/qquickpincharea_p.h +++ b/src/quick/items/qquickpincharea_p.h @@ -289,6 +289,7 @@ protected: #endif private: + void clearPinch(); void updatePinch(); void handlePress(); void handleRelease(); diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp index 0ef871be43..6236f0047b 100644 --- a/src/quick/items/qquickpositioners.cpp +++ b/src/quick/items/qquickpositioners.cpp @@ -305,6 +305,8 @@ void QQuickBasePositioner::prePositioning() for (int ii = 0; ii < children.count(); ++ii) { QQuickItem *child = children.at(ii); + if (QQuickItemPrivate::get(child)->isTransparentForPositioner()) + continue; QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child); PositionedItem posItem(child); int wIdx = oldItems.find(posItem); @@ -1038,7 +1040,7 @@ void QQuickRow::reportConflictingAnchors() \image gridLayout_example.png - If an item within a Column is not \l {Item::}{visible}, or if it has a width or + If an item within a Grid is not \l {Item::}{visible}, or if it has a width or height of 0, the item will not be laid out and it will not be visible within the column. Also, since a Grid automatically positions its children, a child item within a Grid should not set its \l {Item::x}{x} or \l {Item::y}{y} positions diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index e2a26836cf..55736c33c3 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -140,6 +140,9 @@ void QQuickRenderControl::polishItems() return; QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); + cd->flushDelayedTouchEvent(); + if (!d->window) + return; cd->polishItems(); } diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index ebeff599f4..44cafe347e 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -710,9 +710,15 @@ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item) if (window()) d->derefWindow(); } - m_sourceItem = item; - if (item) { + if (window() == item->window()) { + m_sourceItem = item; + } else { + qWarning("ShaderEffectSource: sourceItem and ShaderEffectSource must both be children of the same window."); + m_sourceItem = 0; + } + + if (m_sourceItem) { QQuickItemPrivate *d = QQuickItemPrivate::get(item); // 'item' needs a window to get a scene graph node. It usually gets one through its // parent, but if the source item is "inline" rather than a reference -- i.e. diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 597025b796..883d0c744d 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -881,6 +881,8 @@ void QQuickTextInput::setFocusOnPress(bool b) Whether the TextInput should scroll when the text is longer than the width. By default this is set to true. + + \sa ensureVisible() */ bool QQuickTextInput::autoScroll() const { @@ -1721,33 +1723,24 @@ void QQuickTextInput::geometryChanged(const QRectF &newGeometry, QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry); } -void QQuickTextInputPrivate::updateHorizontalScroll() +void QQuickTextInputPrivate::ensureVisible(int position, int preeditCursor, int preeditLength) { Q_Q(QQuickTextInput); -#ifndef QT_NO_IM - QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor); - const int preeditLength = m_textLayout.preeditAreaText().length(); -#else - QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor); -#endif + QTextLine textLine = m_textLayout.lineForTextPosition(position + preeditCursor); const qreal width = qMax<qreal>(0, q->width()); qreal cix = 0; qreal widthUsed = 0; - if (currentLine.isValid()) { -#ifndef QT_NO_IM - cix = currentLine.cursorToX(m_cursor + preeditLength); -#else - cix = currentLine.cursorToX(m_cursor); -#endif + if (textLine.isValid()) { + cix = textLine.cursorToX(position + preeditLength); const qreal cursorWidth = cix >= 0 ? cix : width - cix; - widthUsed = qMax(currentLine.naturalTextWidth(), cursorWidth); + widthUsed = qMax(textLine.naturalTextWidth(), cursorWidth); } int previousScroll = hscroll; - if (!autoScroll || widthUsed <= width || m_echoMode == QQuickTextInput::NoEcho) { + if (widthUsed <= width) { hscroll = 0; } else { - Q_ASSERT(currentLine.isValid()); + Q_ASSERT(textLine.isValid()); if (cix - hscroll >= width) { // text doesn't fit, cursor is to the right of br (scroll right) hscroll = cix - width; @@ -1767,7 +1760,7 @@ void QQuickTextInputPrivate::updateHorizontalScroll() if (preeditLength > 0) { // check to ensure long pre-edit text doesn't push the cursor // off to the left - cix = currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1)); + cix = textLine.cursorToX(position + qMax(0, preeditCursor - 1)); if (cix < hscroll) hscroll = cix; } @@ -1777,6 +1770,20 @@ void QQuickTextInputPrivate::updateHorizontalScroll() textLayoutDirty = true; } +void QQuickTextInputPrivate::updateHorizontalScroll() +{ + if (autoScroll && m_echoMode != QQuickTextInput::NoEcho) { +#ifndef QT_NO_IM + const int preeditLength = m_textLayout.preeditAreaText().length(); + ensureVisible(m_cursor, m_preeditCursor, preeditLength); +#else + ensureVisible(m_cursor); +#endif + } else { + hscroll = 0; + } +} + void QQuickTextInputPrivate::updateVerticalScroll() { Q_Q(QQuickTextInput); @@ -2063,9 +2070,8 @@ void QQuickTextInput::insert(int position, const QString &text) { Q_D(QQuickTextInput); if (d->m_echoMode == QQuickTextInput::Password) { - int delay = qGuiApp->styleHints()->passwordMaskDelay(); - if (delay > 0) - d->m_passwordEchoTimer.start(delay, this); + if (d->m_passwordMaskDelay > 0) + d->m_passwordEchoTimer.start(d->m_passwordMaskDelay, this); } if (position < 0 || position > d->m_text.length()) return; @@ -2245,6 +2251,34 @@ void QQuickTextInput::setPasswordCharacter(const QString &str) } /*! + \qmlproperty int QtQuick::TextInput::passwordMaskDelay + \since 5.4 + + Sets the delay before visible character is masked with password character, in milliseconds. + + The reset method will be called by assigning undefined. +*/ +int QQuickTextInput::passwordMaskDelay() const +{ + Q_D(const QQuickTextInput); + return d->m_passwordMaskDelay; +} + +void QQuickTextInput::setPasswordMaskDelay(int delay) +{ + Q_D(QQuickTextInput); + if (d->m_passwordMaskDelay != delay) { + d->m_passwordMaskDelay = delay; + emit passwordMaskDelayChanged(delay); + } +} + +void QQuickTextInput::resetPasswordMaskDelay() +{ + setPasswordMaskDelay(qGuiApp->styleHints()->passwordMaskDelay()); +} + +/*! \qmlproperty string QtQuick::TextInput::displayText This is the text displayed in the TextInput. @@ -2607,14 +2641,16 @@ void QQuickTextInputPrivate::init() } } -void QQuickTextInput::updateCursorRectangle() +void QQuickTextInput::updateCursorRectangle(bool scroll) { Q_D(QQuickTextInput); if (!isComponentComplete()) return; - d->updateHorizontalScroll(); - d->updateVerticalScroll(); + if (scroll) { + d->updateHorizontalScroll(); + d->updateVerticalScroll(); + } d->updateType = QQuickTextInputPrivate::UpdatePaintNode; update(); emit cursorRectangleChanged(); @@ -3484,9 +3520,8 @@ void QQuickTextInputPrivate::internalInsert(const QString &s) { Q_Q(QQuickTextInput); if (m_echoMode == QQuickTextInput::Password) { - int delay = qGuiApp->styleHints()->passwordMaskDelay(); - if (delay > 0) - m_passwordEchoTimer.start(delay, q); + if (m_passwordMaskDelay > 0) + m_passwordEchoTimer.start(m_passwordMaskDelay, q); } Q_ASSERT(!hasSelectedText()); // insert(), processInputMethodEvent() call removeSelectedText() first. if (m_maskData) { @@ -4376,5 +4411,21 @@ void QQuickTextInputPrivate::deleteEndOfLine() finishChange(priorState); } +/*! + \qmlmethod QtQuick::TextInput::ensureVisible(int position) + \since 5.4 + + Scrolls the contents of the text input so that the specified character + \a position is visible inside the boundaries of the text input. + + \sa autoScroll +*/ +void QQuickTextInput::ensureVisible(int position) +{ + Q_D(QQuickTextInput); + d->ensureVisible(position); + updateCursorRectangle(false); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 042859df17..66cabb9cfe 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -94,6 +94,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextInput : public QQuickImplicitSizeItem Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged) Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged) Q_PROPERTY(QString passwordCharacter READ passwordCharacter WRITE setPasswordCharacter NOTIFY passwordCharacterChanged) + Q_PROPERTY(int passwordMaskDelay READ passwordMaskDelay WRITE setPasswordMaskDelay RESET resetPasswordMaskDelay NOTIFY passwordMaskDelayChanged REVISION 3) Q_PROPERTY(QString displayText READ displayText NOTIFY displayTextChanged) Q_PROPERTY(bool autoScroll READ autoScroll WRITE setAutoScroll NOTIFY autoScrollChanged) Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) @@ -225,6 +226,10 @@ public: QString passwordCharacter() const; void setPasswordCharacter(const QString &str); + int passwordMaskDelay() const; + void setPasswordMaskDelay(int delay); + void resetPasswordMaskDelay(); + QString displayText() const; QQmlComponent* cursorDelegate() const; @@ -296,6 +301,7 @@ Q_SIGNALS: void inputMaskChanged(const QString &inputMask); void echoModeChanged(EchoMode echoMode); void passwordCharacterChanged(); + Q_REVISION(3) void passwordMaskDelayChanged(int delay); void displayTextChanged(); void activeFocusOnPressChanged(bool activeFocusOnPress); void autoScrollChanged(bool autoScroll); @@ -349,11 +355,12 @@ public Q_SLOTS: void redo(); void insert(int position, const QString &text); void remove(int start, int end); + Q_REVISION(3) void ensureVisible(int position); private Q_SLOTS: void selectionChanged(); void createCursor(); - void updateCursorRectangle(); + void updateCursorRectangle(bool scroll = true); void q_canPasteChanged(); void q_updateAlignment(); void triggerPreprocess(); diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 21bd1bd6d7..facc6356a1 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -111,6 +111,7 @@ public: , mouseSelectionMode(QQuickTextInput::SelectCharacters) , m_layoutDirection(Qt::LayoutDirectionAuto) , m_passwordCharacter(qApp->styleHints()->passwordMaskCharacter()) + , m_passwordMaskDelay(qApp->styleHints()->passwordMaskDelay()) , focusOnPress(true) , cursorVisible(false) , cursorPending(false) @@ -147,6 +148,7 @@ public: void init(); void startCreatingCursor(); + void ensureVisible(int position, int preeditCursor = 0, int preeditLength = 0); void updateHorizontalScroll(); void updateVerticalScroll(); bool determineHorizontalAlignment(); @@ -250,6 +252,7 @@ public: QChar m_blank; QChar m_passwordCharacter; + int m_passwordMaskDelay; bool focusOnPress:1; bool cursorVisible:1; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index e78f9141a8..a95c13f161 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -75,9 +75,16 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch"); +Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse"); +Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus"); +Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty"); + extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); -bool QQuickWindowPrivate::defaultAlphaBuffer(0); +bool QQuickWindowPrivate::defaultAlphaBuffer = false; +bool QQuickWindowPrivate::defaultFormatInitialized = false; +QSurfaceFormat QQuickWindowPrivate::defaultFormat; void QQuickWindowPrivate::updateFocusItemTransform() { @@ -103,9 +110,9 @@ public: // Allow incubation for 1/3 of a frame. m_incubation_time = qMax(1, int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3); - m_animation_driver = m_renderLoop->animationDriver(); - if (m_animation_driver) { - connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); + QAnimationDriver *animationDriver = m_renderLoop->animationDriver(); + if (animationDriver) { + connect(animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped())); connect(m_renderLoop, SIGNAL(timeToIncubate()), this, SLOT(incubate())); } } @@ -151,7 +158,6 @@ protected: private: QSGRenderLoop *m_renderLoop; int m_incubation_time; - QAnimationDriver *m_animation_driver; int m_timer; }; @@ -189,16 +195,6 @@ thus the first item that has focus will get it (assuming the scope doesn't alrea have a scope focused item), and the other items will have their focus cleared. */ - -// #define FOCUS_DEBUG -// #define MOUSE_DEBUG -// #define TOUCH_DEBUG -// #define DIRTY_DEBUG - -#ifdef FOCUS_DEBUG -void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1); -#endif - QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() : transformNode(0) { @@ -404,12 +400,14 @@ QQuickWindowPrivate::QQuickWindowPrivate() , renderer(0) , windowManager(0) , renderControl(0) + , touchRecursionGuard(0) , clearColor(Qt::white) , clearBeforeRendering(true) , persistentGLContext(true) , persistentSceneGraph(true) , lastWheelEventAccepted(false) , componentCompleted(true) + , lastFocusReason(Qt::OtherFocusReason) , renderTarget(0) , renderTargetId(0) , incubationController(0) @@ -458,11 +456,13 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) } q->setSurfaceType(QWindow::OpenGLSurface); - q->setFormat(sg->defaultSurfaceFormat()); + q->setFormat(q->defaultFormat()); animationController = new QQuickAnimatorController(); animationController->m_window = q; + delayedTouch = 0; + QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection); QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection); QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection); @@ -706,14 +706,12 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q Q_ASSERT(item); Q_ASSERT(scope || item == contentItem); -#ifdef FOCUS_DEBUG - qWarning() << "QQuickWindowPrivate::setFocusInScope():"; - qWarning() << " scope:" << (QObject *)scope; + qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::setFocusInScope():"; + qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope; if (scope) - qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem; - qWarning() << " item:" << (QObject *)item; - qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; -#endif + qCDebug(DBG_FOCUS) << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem; + qCDebug(DBG_FOCUS) << " item:" << (QObject *)item; + qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem; QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -721,6 +719,8 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q QQuickItem *currentActiveFocusItem = activeFocusItem; QQuickItem *newActiveFocusItem = 0; + lastFocusReason = reason; + QVarLengthArray<QQuickItem *, 20> changed; // Does this change the active focus? @@ -809,12 +809,10 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Q_ASSERT(item); Q_ASSERT(scope || item == contentItem); -#ifdef FOCUS_DEBUG - qWarning() << "QQuickWindowPrivate::clearFocusInScope():"; - qWarning() << " scope:" << (QObject *)scope; - qWarning() << " item:" << (QObject *)item; - qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; -#endif + qCDebug(DBG_FOCUS) << "QQuickWindowPrivate::clearFocusInScope():"; + qCDebug(DBG_FOCUS) << " scope:" << (QObject *)scope; + qCDebug(DBG_FOCUS) << " item:" << (QObject *)item; + qCDebug(DBG_FOCUS) << " activeFocusItem:" << (QObject *)activeFocusItem; QQuickItemPrivate *scopePrivate = 0; if (scope) { @@ -827,6 +825,8 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, QQuickItem *oldActiveFocusItem = 0; QQuickItem *newActiveFocusItem = 0; + lastFocusReason = reason; + QVarLengthArray<QQuickItem *, 20> changed; Q_ASSERT(item == contentItem || item == scopePrivate->subFocusItem); @@ -1483,10 +1483,7 @@ void QQuickWindow::mousePressEvent(QMouseEvent *event) return; } -#ifdef MOUSE_DEBUG - qWarning() << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons(); -#endif - + qCDebug(DBG_MOUSE) << "QQuickWindow::mousePressEvent()" << event->localPos() << event->button() << event->buttons(); d->deliverMouseEvent(event); } @@ -1500,9 +1497,7 @@ void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) return; } -#ifdef MOUSE_DEBUG - qWarning() << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons(); -#endif + qCDebug(DBG_MOUSE) << "QQuickWindow::mouseReleaseEvent()" << event->localPos() << event->button() << event->buttons(); if (!d->mouseGrabberItem) { QWindow::mouseReleaseEvent(event); @@ -1524,9 +1519,7 @@ void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) return; } -#ifdef MOUSE_DEBUG - qWarning() << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons(); -#endif + qCDebug(DBG_MOUSE) << "QQuickWindow::mouseDoubleClickEvent()" << event->localPos() << event->button() << event->buttons(); if (!d->mouseGrabberItem && (event->buttons() & event->button()) == event->buttons()) { if (d->deliverInitialMousePressEvent(d->contentItem, event)) @@ -1565,9 +1558,7 @@ void QQuickWindow::mouseMoveEvent(QMouseEvent *event) return; } -#ifdef MOUSE_DEBUG - qWarning() << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons(); -#endif + qCDebug(DBG_MOUSE) << "QQuickWindow::mouseMoveEvent()" << event->localPos() << event->button() << event->buttons(); #ifndef QT_NO_CURSOR d->updateCursor(event->windowPos()); @@ -1706,9 +1697,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event void QQuickWindow::wheelEvent(QWheelEvent *event) { Q_D(QQuickWindow); -#ifdef MOUSE_DEBUG - qWarning() << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta(); -#endif + qCDebug(DBG_MOUSE) << "QQuickWindow::wheelEvent()" << event->pixelDelta() << event->angleDelta(); //if the actual wheel event was accepted, accept the compatibility wheel event and return early if (d->lastWheelEventAccepted && event->angleDelta().isNull() && event->phase() == Qt::ScrollUpdate) @@ -1723,9 +1712,7 @@ void QQuickWindow::wheelEvent(QWheelEvent *event) bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) { -#ifdef TOUCH_DEBUG - qWarning("touchCancelEvent"); -#endif + qCDebug(DBG_TOUCH) << event; Q_Q(QQuickWindow); // A TouchCancel event will typically not contain any points. // Deliver it to all items that have active touches. @@ -1744,18 +1731,104 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event) return true; } +static bool qquickwindow_no_touch_compression = qEnvironmentVariableIsSet("QML_NO_TOUCH_COMPRESSION"); + // check what kind of touch we have (begin/update) and // call deliverTouchPoints to actually dispatch the points -bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) -{ -#ifdef TOUCH_DEBUG - if (event->type() == QEvent::TouchBegin) - qWarning() << "touchBeginEvent"; - else if (event->type() == QEvent::TouchUpdate) - qWarning() << "touchUpdateEvent points"; - else if (event->type() == QEvent::TouchEnd) - qWarning("touchEndEvent"); -#endif +void QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) +{ + qCDebug(DBG_TOUCH) << event; + Q_Q(QQuickWindow); + + if (qquickwindow_no_touch_compression || touchRecursionGuard) { + reallyDeliverTouchEvent(event); + return; + } + + Qt::TouchPointStates states = event->touchPointStates(); + if (((states & (Qt::TouchPointMoved | Qt::TouchPointStationary)) != 0) + && ((states & (Qt::TouchPointPressed | Qt::TouchPointReleased)) == 0)) { + // we can only compress something that isn't a press or release + if (!delayedTouch) { + delayedTouch = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); + delayedTouch->setTimestamp(event->timestamp()); + if (windowManager) + windowManager->maybeUpdate(q); + return; + } else { + // check if this looks like the last touch event + if (delayedTouch->type() == event->type() && + delayedTouch->device() == event->device() && + delayedTouch->modifiers() == event->modifiers() && + delayedTouch->touchPoints().count() == event->touchPoints().count()) + { + // possible match.. is it really the same? + bool mismatch = false; + + QList<QTouchEvent::TouchPoint> tpts = event->touchPoints(); + Qt::TouchPointStates states; + for (int i = 0; i < event->touchPoints().count(); ++i) { + const QTouchEvent::TouchPoint &tp = tpts.at(i); + const QTouchEvent::TouchPoint &tp2 = delayedTouch->touchPoints().at(i); + if (tp.id() != tp2.id()) { + mismatch = true; + break; + } + + if (tp2.state() == Qt::TouchPointMoved && tp.state() == Qt::TouchPointStationary) + tpts[i].setState(Qt::TouchPointMoved); + + states |= tpts.at(i).state(); + } + + // same touch event? then merge if so + if (!mismatch) { + delayedTouch->setTouchPoints(tpts); + delayedTouch->setTimestamp(event->timestamp()); + return; + } + } + + // otherwise; we need to deliver the delayed event first, and + // then delay this one.. + reallyDeliverTouchEvent(delayedTouch); + delete delayedTouch; + delayedTouch = new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints()); + delayedTouch->setTimestamp(event->timestamp()); + return; + } + } else { + if (delayedTouch) { + // deliver the delayed touch first + reallyDeliverTouchEvent(delayedTouch); + delete delayedTouch; + delayedTouch = 0; + } + reallyDeliverTouchEvent(event); + } +} + +void QQuickWindowPrivate::flushDelayedTouchEvent() +{ + if (delayedTouch) { + reallyDeliverTouchEvent(delayedTouch); + delete delayedTouch; + delayedTouch = 0; + + // To flush pending meta-calls triggered from the recently flushed touch events. + // This is safe because flushDelayedEvent is only called from eventhandlers. + QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + } +} + +void QQuickWindowPrivate::reallyDeliverTouchEvent(QTouchEvent *event) +{ + qCDebug(DBG_TOUCH) << " - delivering" << event; + + // If users spin the eventloop as a result of touch delivery, we disable + // touch compression and send events directly. This is because we consider + // the usecase a bit evil, but we at least don't want to lose events. + ++touchRecursionGuard; // List of all items that received an event before // When we have TouchBegin this is and will stay empty @@ -1805,7 +1878,7 @@ bool QQuickWindowPrivate::deliverTouchEvent(QTouchEvent *event) Q_ASSERT(itemForTouchPointId.isEmpty()); } - return event->isAccepted(); + --touchRecursionGuard; } // This function recurses and sends the events to the individual items @@ -2462,9 +2535,7 @@ void QQuickWindowPrivate::cleanupNodesOnShutdown() void QQuickWindowPrivate::updateDirtyNodes() { -#ifdef DIRTY_DEBUG - qWarning() << "QQuickWindowPrivate::updateDirtyNodes():"; -#endif + qCDebug(DBG_DIRTY) << "QQuickWindowPrivate::updateDirtyNodes():"; cleanupNodes(); @@ -2477,9 +2548,7 @@ void QQuickWindowPrivate::updateDirtyNodes() QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); itemPriv->removeFromDirtyList(); -#ifdef DIRTY_DEBUG - qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); -#endif + qCDebug(DBG_DIRTY) << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); updateDirtyNode(item); } } @@ -2816,7 +2885,7 @@ QOpenGLContext *QQuickWindow::openglContext() const \internal \since 5.1 - \inmodule QtQuick.Window + \inmodule QtQuick \brief Notification that a \l QQuickWindow is about to be closed */ @@ -3445,6 +3514,130 @@ void QQuickWindow::resetOpenGLState() } /*! + * \brief QQuickWindow::setDefaultFormat + * \since 5.4 + * @brief Sets the global default surface format that is used for all new QQuickWindow instances. + * + * While it is possible to specify a QSurfaceFormat for every QQuickWindow by + * calling the member function setFormat(), windows may also be created from + * QML by using the Window and ApplicationWindow elements. In this case there + * is no C++ code involved in the creation of the window instance, yet + * applications may still wish to set certain surface format values, for + * example to request a given OpenGL version or profile. Such applications can + * call this static functions in main(). \a format will be used for all Quick + * windows created afterwards. + * + * \note The default value for the default format is not necessarily a + * default-constructed QSurfaceFormat. It may already have depth, stencil and alpha + * buffer sizes set. Unless there is a need to change all these sizes, the format should + * first be queried via defaultFormat() and the changes should be applied to that, + * instead of merely starting with default-constructed QSurfaceFormat. + * + * \sa setFormat(), format(), defaultFormat() + */ +void QQuickWindow::setDefaultFormat(const QSurfaceFormat &format) +{ + QQuickWindowPrivate::defaultFormatInitialized = true; + QQuickWindowPrivate::defaultFormat = format; +} + +/*! + * \brief QQuickWindow::defaultFormat + * \since 5.4 + * \return The global default surface format that is used for all QQuickWindow instances. + * \note This function requires a QGuiApplication or QApplication instance. + */ +QSurfaceFormat QQuickWindow::defaultFormat() +{ + if (!QQuickWindowPrivate::defaultFormatInitialized) { + QQuickWindowPrivate::defaultFormatInitialized = true; + QQuickWindowPrivate::defaultFormat = QSGRenderLoop::instance()->sceneGraphContext()->defaultSurfaceFormat(); + } + return QQuickWindowPrivate::defaultFormat; +} + +/*! + * \brief QQuickWindow::glslVersion + * \since 5.4 + * \return The OpenGL Shading Language version for this window. + * + * QML components that need to be usable on different platforms and environments may need + * to deal with different OpenGL versions if they include ShaderEffect items. The source + * code for a given shader may not be compatible with an OpenGL context that targets a + * different OpenGL version or profile, hence it might be necessary to provide multiple + * versions of the shader. This property helps in deciding which shader source should be + * chosen. + * + * The value corresponds to GLSL version declarations, for example an OpenGL 4.2 core + * profile context will result in the value \e{420 core}, while an OpenGL ES 3.0 context + * gives \e{300 es}. For OpenGL (ES) 2 the value will be an empty string since the + * corresponding shading language does not use version declarations. + * + * \note The value does not necessarily indicate that the shader source must target that + * specific version. For example, compatibility profiles and ES 3.x all allow using + * OpenGL 2 style shaders. The most important for reusable components is to check for + * core profiles since these do not accept shaders with the old syntax. + * + * \sa setFormat(), glslIsCoreProfile() + */ +QString QQuickWindow::glslVersion() const +{ + QString ver; + QOpenGLContext *ctx = openglContext(); + if (ctx) { + const QSurfaceFormat fmt = ctx->format(); + if (fmt.renderableType() == QSurfaceFormat::OpenGLES + && fmt.majorVersion() >= 3) { + ver += QLatin1Char(fmt.majorVersion() + '0'); + ver += QLatin1Char(fmt.minorVersion() + '0'); + ver += QLatin1String("0 es"); + } else if (fmt.renderableType() == QSurfaceFormat::OpenGL + && fmt.majorVersion() >= 3) { + if (fmt.version() == qMakePair(3, 0)) { + ver = QStringLiteral("130"); + } else if (fmt.version() == qMakePair(3, 1)) { + ver = QStringLiteral("140"); + } else if (fmt.version() == qMakePair(3, 2)) { + ver = QStringLiteral("150"); + } else { + ver += QLatin1Char(fmt.majorVersion() + '0'); + ver += QLatin1Char(fmt.minorVersion() + '0'); + ver += QLatin1Char('0'); + } + if (fmt.version() >= qMakePair(3, 2)) { + if (fmt.profile() == QSurfaceFormat::CoreProfile) + ver += QStringLiteral(" core"); + else if (fmt.profile() == QSurfaceFormat::CompatibilityProfile) + ver += QStringLiteral(" compatibility"); + } + } + } + return ver; +} + +/*! + * \brief QQuickWindow::glslIsCoreProfile + * \since 5.4 + * \return True if the window is rendering using OpenGL core profile. + * + * This is convenience function to check if the window's OpenGL context is a core profile + * context. It is more efficient to perform the check via this function than parsing the + * string returned from glslVersion(). + * + * Resusable QML components will typically use this function in bindings in order to + * choose between core and non core profile compatible shader sources. + * + * To retrieve more information about the shading language, use glslVersion(). + * + * \sa glslVersion() + */ +bool QQuickWindow::glslIsCoreProfile() const +{ + QOpenGLContext *ctx = openglContext(); + return ctx ? ctx->format().profile() == QSurfaceFormat::CoreProfile : false; +} + +/*! \qmlproperty string Window::title The window's title in the windowing system. diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 2572f31375..1a4adb0785 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -67,6 +67,8 @@ class Q_QUICK_EXPORT QQuickWindow : public QWindow Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT) Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged REVISION 1) + Q_PROPERTY(QString glslVersion READ glslVersion CONSTANT REVISION 2) + Q_PROPERTY(bool glslIsCoreProfile READ glslIsCoreProfile CONSTANT REVISION 2) Q_CLASSINFO("DefaultProperty", "data") Q_DECLARE_PRIVATE(QQuickWindow) public: @@ -136,6 +138,12 @@ public: QOpenGLContext *openglContext() const; + static void setDefaultFormat(const QSurfaceFormat &format); + static QSurfaceFormat defaultFormat(); + + QString glslVersion() const; + bool glslIsCoreProfile() const; + Q_SIGNALS: void frameSwapped(); Q_REVISION(2) void openglContextCreated(QOpenGLContext *context); diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index 421651b483..8faaf6489b 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -141,8 +141,10 @@ public: #endif bool deliverTouchPoints(QQuickItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *); - bool deliverTouchEvent(QTouchEvent *); + void deliverTouchEvent(QTouchEvent *); + void reallyDeliverTouchEvent(QTouchEvent *); bool deliverTouchCancelEvent(QTouchEvent *); + void flushDelayedTouchEvent(); bool deliverHoverEvent(QQuickItem *, const QPointF &scenePos, const QPointF &lastScenePos, Qt::KeyboardModifiers modifiers, bool &accepted); bool deliverMatchingPointsToItem(QQuickItem *item, QTouchEvent *event, QSet<int> *acceptedNewPoints, const QSet<int> &matchingNewPoints, const QList<QTouchEvent::TouchPoint> &matchingPoints); QTouchEvent *touchEventForItemBounds(QQuickItem *target, const QTouchEvent &originalEvent); @@ -210,6 +212,8 @@ public: QSGRenderLoop *windowManager; QQuickRenderControl *renderControl; QQuickAnimatorController *animationController; + QTouchEvent *delayedTouch; + int touchRecursionGuard; QColor clearColor; @@ -223,6 +227,8 @@ public: uint lastWheelEventAccepted : 1; bool componentCompleted : 1; + Qt::FocusReason lastFocusReason; + QOpenGLFramebufferObject *renderTarget; uint renderTargetId; QSize renderTargetSize; @@ -247,6 +253,9 @@ public: QString *untranslatedMessage, bool isEs); + static bool defaultFormatInitialized; + static QSurfaceFormat defaultFormat; + private: static void cleanupNodesOnShutdown(QQuickItem *); }; diff --git a/src/quick/qtquick2.cpp b/src/quick/qtquick2.cpp index 9d2a0b0f75..fe24c2512a 100644 --- a/src/quick/qtquick2.cpp +++ b/src/quick/qtquick2.cpp @@ -44,6 +44,7 @@ #include <private/qquickutilmodule_p.h> #include <private/qquickvaluetypes_p.h> #include <private/qquickitemsmodule_p.h> +#include <private/qquickaccessiblefactory_p.h> #include <private/qqmlenginedebugservice_p.h> #include <private/qqmldebugstatesdelegate_p.h> @@ -190,6 +191,10 @@ void QQmlQtQuick2Module::defineModule() QQuickValueTypes::registerValueTypes(); +#ifndef QT_NO_ACCESSIBILITY + QAccessible::installFactory(&qQuickAccessibleFactory); +#endif + if (QQmlDebugService::isDebuggingEnabled()) { QQmlEngineDebugService::instance()->setStatesDelegate( new QQmlQtQuick2DebugStatesDelegate); diff --git a/src/quick/qtquickglobal_p.h b/src/quick/qtquickglobal_p.h index f67a08c218..cd4cb61ebb 100644 --- a/src/quick/qtquickglobal_p.h +++ b/src/quick/qtquickglobal_p.h @@ -42,6 +42,8 @@ #ifndef QTQUICKGLOBAL_P_H #define QTQUICKGLOBAL_P_H +#include <QtCore/qloggingcategory.h> + // // W A R N I N G // ------------- @@ -61,6 +63,11 @@ QT_BEGIN_NAMESPACE void QQuick_initializeProviders(); +Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH) +Q_DECLARE_LOGGING_CATEGORY(DBG_MOUSE) +Q_DECLARE_LOGGING_CATEGORY(DBG_FOCUS) +Q_DECLARE_LOGGING_CATEGORY(DBG_DIRTY) + QT_END_NAMESPACE #endif // QTQUICKGLOBAL_P_H diff --git a/src/quick/quick.pro b/src/quick/quick.pro index 38e743cc5c..6e08662e61 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -29,6 +29,9 @@ include(util/util.pri) include(scenegraph/scenegraph.pri) include(items/items.pri) include(designer/designer.pri) +contains(QT_CONFIG, accessibility) { + include(accessible/accessible.pri) +} HEADERS += \ qtquickglobal.h \ diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index e8f803f2a9..c9115f35fc 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -606,11 +606,6 @@ void Element::computeBounds() BatchCompatibility Batch::isMaterialCompatible(Element *e) const { - // If material has changed between opaque and translucent, it is not compatible - QSGMaterial *m = e->node->activeMaterial(); - if (isOpaque != ((m->flags() & QSGMaterial::Blending) == 0)) - return BatchBreaksOnBlending; - Element *n = first; // Skip to the first node other than e which has not been removed while (n && (n == e || n->removed)) @@ -621,6 +616,7 @@ BatchCompatibility Batch::isMaterialCompatible(Element *e) const if (!n) return BatchIsCompatible; + QSGMaterial *m = e->node->activeMaterial(); QSGMaterial *nm = n->node->activeMaterial(); return (nm->type() == m->type() && nm->compare(m) == 0) ? BatchIsCompatible @@ -838,10 +834,12 @@ static void qsg_wipeBatch(Batch *batch, QOpenGLFunctions *funcs) Renderer::~Renderer() { - // Clean up batches and buffers - for (int i=0; i<m_opaqueBatches.size(); ++i) qsg_wipeBatch(m_opaqueBatches.at(i), this); - for (int i=0; i<m_alphaBatches.size(); ++i) qsg_wipeBatch(m_alphaBatches.at(i), this); - for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this); + if (QOpenGLContext::currentContext()) { + // Clean up batches and buffers + for (int i=0; i<m_opaqueBatches.size(); ++i) qsg_wipeBatch(m_opaqueBatches.at(i), this); + for (int i=0; i<m_alphaBatches.size(); ++i) qsg_wipeBatch(m_alphaBatches.at(i), this); + for (int i=0; i<m_batchPool.size(); ++i) qsg_wipeBatch(m_batchPool.at(i), this); + } // The shadowtree qDeleteAll(m_nodes.values()); @@ -1185,11 +1183,12 @@ void Renderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) if (state & QSGNode::DirtyMaterial && node->type() == QSGNode::GeometryNodeType) { Element *e = shadowNode->element(); if (e) { - if (e->batch) { - BatchCompatibility compat = e->batch->isMaterialCompatible(e); - if (compat == BatchBreaksOnBlending) - m_rebuild |= Renderer::FullRebuild; - else if (compat == BatchBreaksOnCompare) + bool blended = hasMaterialWithBlending(static_cast<QSGGeometryNode *>(node)); + if (e->isMaterialBlended != blended) { + m_rebuild |= Renderer::FullRebuild; + e->isMaterialBlended = blended; + } else if (e->batch) { + if (e->batch->isMaterialCompatible(e) == BatchBreaksOnCompare) invalidateBatchAndOverlappingRenderOrders(e->batch); } else { m_rebuild |= Renderer::BuildBatches; @@ -1464,7 +1463,7 @@ void Renderer::prepareOpaqueBatches() { for (int i=m_opaqueRenderList.size() - 1; i >= 0; --i) { Element *ei = m_opaqueRenderList.at(i); - if (!ei || ei->batch) + if (!ei || ei->batch || ei->node->geometry()->vertexCount() == 0) continue; Batch *batch = newBatch(); batch->first = ei; @@ -1486,7 +1485,7 @@ void Renderer::prepareOpaqueBatches() continue; if (ej->root != ei->root) break; - if (ej->batch) + if (ej->batch || ej->node->geometry()->vertexCount() == 0) continue; QSGGeometryNode *gnj = ej->node; @@ -1557,6 +1556,9 @@ void Renderer::prepareAlphaBatches() continue; } + if (ei->node->geometry()->vertexCount() == 0) + continue; + Batch *batch = newBatch(); batch->first = ei; batch->root = ei->root; @@ -1583,6 +1585,8 @@ void Renderer::prepareAlphaBatches() continue; QSGGeometryNode *gnj = ej->node; + if (gnj->geometry()->vertexCount() == 0) + continue; if (gni->clipList() == gnj->clipList() && gni->geometry()->drawingMode() == gnj->geometry()->drawingMode() @@ -2473,11 +2477,14 @@ void Renderer::renderRenderNode(Batch *batch) QSGNode *xform = e->renderNode->parent(); QMatrix4x4 matrix; - while (xform != rootNode()) { + QSGNode *root = rootNode(); + if (e->root) { + matrix = qsg_matrixForRoot(e->root); + root = e->root->sgNode; + } + while (xform != root) { if (xform->type() == QSGNode::TransformNodeType) { - matrix = static_cast<QSGTransformNode *>(xform)->combinedMatrix(); - if (e->root) - matrix = qsg_matrixForRoot(e->root) * matrix; + matrix = matrix * static_cast<QSGTransformNode *>(xform)->combinedMatrix(); break; } xform = xform->parent(); diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 44b7b8740d..89a33cb8c4 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -67,6 +67,12 @@ class Updater; class Renderer; class ShaderManager; +inline bool hasMaterialWithBlending(QSGGeometryNode *n) +{ + return (n->opaqueMaterial() ? n->opaqueMaterial()->flags() & QSGMaterial::Blending + : n->material()->flags() & QSGMaterial::Blending); +} + struct Pt { float x, y; @@ -163,6 +169,7 @@ struct Element { , removed(false) , orphaned(false) , isRenderNode(false) + , isMaterialBlended(n ? hasMaterialWithBlending(n) : false) { } @@ -187,6 +194,7 @@ struct Element { uint removed : 1; uint orphaned : 1; uint isRenderNode : 1; + uint isMaterialBlended : 1; }; struct RenderNodeElement : public Element { @@ -233,7 +241,6 @@ struct DrawSet enum BatchCompatibility { - BatchBreaksOnBlending, BatchBreaksOnCompare, BatchIsCompatible }; diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index e76aa1bbb5..38de4a5c39 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -352,6 +352,12 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (!current) return; + if (!data.grabOnly) { + cd->flushDelayedTouchEvent(); + // Event delivery/processing triggered the window to be deleted or stop rendering. + if (!m_windows.contains(window)) + return; + } cd->polishItems(); emit window->afterAnimating(); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 0aa30280e5..1ba54ea19e 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQuick module of the Qt Toolkit. @@ -50,6 +51,8 @@ #include <QtGui/QScreen> #include <QtGui/QOffscreenSurface> +#include <qpa/qwindowsysteminterface.h> + #include <QtQuick/QQuickWindow> #include <private/qquickwindow_p.h> @@ -154,35 +157,25 @@ extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_ // RL: Render Loop // RT: Render Thread -// Passed from the RL to the RT when a window is rendeirng on screen -// and should be added to the render loop. -const QEvent::Type WM_Expose = QEvent::Type(QEvent::User + 1); - // Passed from the RL to the RT when a window is removed obscured and // should be removed from the render loop. -const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 2); - -// Passed from the RL to itself to initiate a polishAndSync() call. -//const QEvent::Type WM_LockAndSync = QEvent::Type(QEvent::User + 3); // not used for now +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 1); // Passed from the RL to RT when GUI has been locked, waiting for sync // (updatePaintNode()) -const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 4); +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 2); // Passed by the RT to itself to trigger another render pass. This is // typically a result of QQuickWindow::update(). -const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 5); +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 3); // Passed by the RL to the RT to free up maybe release SG and GL contexts // if no windows are rendering. -const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 7); +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 4); // Passed by the RL to the RT when a QQuickWindow::grabWindow() is // called. -const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 9); - -// Passed by RL to RT when polish fails and we need to reset the expose sycle. -const QEvent::Type WM_ResetExposeCycle = QEvent::Type(QEvent::User + 10); +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 5); template <typename T> T *windowFor(const QList<T> list, QQuickWindow *window) { @@ -215,11 +208,12 @@ public: QOffscreenSurface *fallbackSurface; }; -class WMExposeEvent : public WMWindowEvent +class WMSyncEvent : public WMWindowEvent { public: - WMExposeEvent(QQuickWindow *c) : WMWindowEvent(c, WM_Expose), size(c->size()) { } + WMSyncEvent(QQuickWindow *c, bool inExpose) : WMWindowEvent(c, WM_RequestSync), size(c->size()), syncInExpose(inExpose) { } QSize size; + bool syncInExpose; }; @@ -285,7 +279,6 @@ public: , pendingUpdate(0) , sleeping(false) , syncResultedInChanges(false) - , exposeCycle(NoExpose) , active(false) , window(0) , stopEventProcessing(false) @@ -309,7 +302,7 @@ public: void run(); void syncAndRender(); - void sync(); + void sync(bool inExpose); void requestRepaint() { @@ -332,13 +325,8 @@ public slots: public: enum UpdateRequest { SyncRequest = 0x01, - RepaintRequest = 0x02 - }; - - enum ExposeCycle { - NoExpose, - ExposePendingSync, - ExposePendingSwap + RepaintRequest = 0x02, + ExposeRequest = 0x04 | RepaintRequest | SyncRequest }; QSGThreadedRenderLoop *wm; @@ -350,7 +338,6 @@ public: uint pendingUpdate; bool sleeping; bool syncResultedInChanges; - ExposeCycle exposeCycle; volatile bool active; @@ -373,21 +360,6 @@ bool QSGRenderThread::event(QEvent *e) { switch ((int) e->type()) { - case WM_Expose: { - QSG_RT_DEBUG("WM_Expose"); - WMExposeEvent *se = static_cast<WMExposeEvent *>(e); - Q_ASSERT(!window || window == se->window); - windowSize = se->size; - window = se->window; - Q_ASSERT(exposeCycle == NoExpose); - exposeCycle = ExposePendingSync; - return true; } - - case WM_ResetExposeCycle: - QSG_RT_DEBUG("WM_ResetExposeCycle"); - exposeCycle = NoExpose; - return true; - case WM_Obscure: { QSG_RT_DEBUG("WM_Obscure"); @@ -397,6 +369,7 @@ bool QSGRenderThread::event(QEvent *e) if (window) { QQuickWindowPrivate::get(window)->fireAboutToStop(); QSG_RT_DEBUG(" - removed window..."); + gl->doneCurrent(); window = 0; } waitCondition.wakeOne(); @@ -404,34 +377,38 @@ bool QSGRenderThread::event(QEvent *e) return true; } - case WM_RequestSync: + case WM_RequestSync: { QSG_RT_DEBUG("WM_RequestSync"); + WMSyncEvent *se = static_cast<WMSyncEvent *>(e); if (sleeping) stopEventProcessing = true; - if (window) - pendingUpdate |= SyncRequest; - if (exposeCycle == ExposePendingSync) { - pendingUpdate |= RepaintRequest; - exposeCycle = ExposePendingSwap; + window = se->window; + windowSize = se->size; + + pendingUpdate |= SyncRequest; + if (se->syncInExpose) { + QSG_RT_DEBUG(" - triggered from expose"); + pendingUpdate |= ExposeRequest; } - return true; + return true; } case WM_TryRelease: { QSG_RT_DEBUG("WM_TryRelease"); mutex.lock(); - wm->m_locked = true; + wm->m_lockedForSync = true; WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e); if (!window || wme->inDestructor) { QSG_RT_DEBUG(" - setting exit flag and invalidating GL"); invalidateOpenGL(wme->window, wme->inDestructor, wme->fallbackSurface); active = gl; + Q_ASSERT_X(!wme->inDestructor || !active, "QSGRenderThread::invalidateOpenGL()", "Thread's active state is not set to false when shutting down"); if (sleeping) stopEventProcessing = true; } else { QSG_RT_DEBUG(" - not releasing anything because we have active windows..."); } waitCondition.wakeOne(); - wm->m_locked = false; + wm->m_lockedForSync = false; mutex.unlock(); return true; } @@ -489,11 +466,8 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, bool wipeGL = inDestructor || (wipeSG && !window->isPersistentOpenGLContext()); bool current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window)); - if (!current) { -#ifndef QT_NO_DEBUG - qWarning() << "Scene Graph failed to acquire GL context during cleanup"; -#endif - return; + if (Q_UNLIKELY(!current)) { + QSG_RT_DEBUG(" - cleanup without an OpenGL context"); } // The canvas nodes must be cleaned up regardless if we are in the destructor.. @@ -502,14 +476,16 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, dd->cleanupNodesOnShutdown(); } else { QSG_RT_DEBUG(" - persistent SG, avoiding cleanup"); - gl->doneCurrent(); + if (current) + gl->doneCurrent(); return; } sgrc->invalidate(); QCoreApplication::processEvents(); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - gl->doneCurrent(); + if (current) + gl->doneCurrent(); QSG_RT_DEBUG(" - invalidated scenegraph.."); if (wipeGL) { @@ -525,12 +501,12 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, Enters the mutex lock to make sure GUI is blocking and performs sync, then wakes GUI. */ -void QSGRenderThread::sync() +void QSGRenderThread::sync(bool inExpose) { QSG_RT_DEBUG("sync()"); mutex.lock(); - Q_ASSERT_X(wm->m_locked, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); + Q_ASSERT_X(wm->m_lockedForSync, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); bool current = false; if (windowSize.width() > 0 && windowSize.height() > 0) @@ -557,11 +533,13 @@ void QSGRenderThread::sync() QSG_RT_DEBUG(" - window has bad size, waiting..."); } - waitCondition.wakeOne(); - mutex.unlock(); + if (!inExpose) { + QSG_RT_DEBUG(" - sync complete, waking GUI"); + waitCondition.wakeOne(); + mutex.unlock(); + } } - void QSGRenderThread::syncAndRender() { #ifndef QSG_NO_RENDER_TIMING @@ -578,16 +556,15 @@ void QSGRenderThread::syncAndRender() syncResultedInChanges = false; - bool repaintRequested = pendingUpdate & RepaintRequest; - bool syncRequested = pendingUpdate & SyncRequest; + uint pending = pendingUpdate; pendingUpdate = 0; - if (syncRequested) { + if (pending & SyncRequest) { QSG_RT_DEBUG(" - update pending, doing sync"); - sync(); + sync(pending == ExposeRequest); } - if (!syncResultedInChanges && !(repaintRequested)) { + if (!syncResultedInChanges && ((pending & RepaintRequest) == 0)) { QSG_RT_DEBUG(" - no changes, rendering aborted"); int waitTime = vsyncDelta - (int) waitTimer.elapsed(); if (waitTime > 0) @@ -631,13 +608,11 @@ void QSGRenderThread::syncAndRender() // that to avoid blocking the GUI thread in the case where it // has started rendering with a bad window, causing makeCurrent to // fail or if the window has a bad size. - mutex.lock(); - if (exposeCycle == ExposePendingSwap) { + if (pending == ExposeRequest) { QSG_RT_DEBUG(" - waking GUI after expose"); - exposeCycle = NoExpose; waitCondition.wakeOne(); + mutex.unlock(); } - mutex.unlock(); #ifndef QSG_NO_RENDER_TIMING if (qsg_render_timing) @@ -824,49 +799,14 @@ void QSGThreadedRenderLoop::startOrStopAnimationTimer() } /* - Adds this window to the list of tracked windows in this window - manager. show() does not trigger rendering to start, that happens - in expose. - */ - -void QSGThreadedRenderLoop::show(QQuickWindow *window) -{ - QSG_GUI_DEBUG(window, "show()"); - - if (Window *w = windowFor(m_windows, window)) { - /* Safeguard ourselves against misbehaving platform plugins. - * - * When being shown, the window should not be exposed as the - * platform plugin is only told to show after we send the show - * event. If we are already shown at this time and we don't have - * an active rendering thread we don't trust the plugin to send - * us another expose event, so make this explicit call to - * handleExposure. - * - * REF: QTCREATORBUG-10699 - */ - if (window->isExposed() && (!w->thread || !w->thread->window)) - handleExposure(w); - return; - } - - QSG_GUI_DEBUG(window, " - now tracking new window"); - - Window win; - win.window = window; - win.actualWindowFormat = window->format(); - win.thread = new QSGRenderThread(this, QQuickWindowPrivate::get(window)->context); - win.timerId = 0; - win.updateDuringSync = false; - m_windows << win; -} - - - -/* Removes this window from the list of tracked windowes in this window manager. hide() will trigger obscure, which in turn will stop rendering. + + This function will be called during QWindow::close() which will + also destroy the QPlatformWindow so it is important that this + triggers handleObscurity() and that rendering for that window + is fully done and over with by the time this function exits. */ void QSGThreadedRenderLoop::hide(QQuickWindow *window) @@ -889,17 +829,21 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window) { QSG_GUI_DEBUG(window, "windowDestroyed()"); - if (window->isVisible()) - hide(window); - releaseResources(window, true); + Window *w = windowFor(m_windows, window); + if (!w) + return; + + handleObscurity(w); + releaseResources(w, true); + + QSGRenderThread *thread = w->thread; + while (thread->isRunning()) + QThread::yieldCurrentThread(); + Q_ASSERT(thread->thread() == QThread::currentThread()); + delete thread; for (int i=0; i<m_windows.size(); ++i) { if (m_windows.at(i).window == window) { - QSGRenderThread *thread = m_windows.at(i).thread; - while (thread->isRunning()) - QThread::yieldCurrentThread(); - Q_ASSERT(thread->thread() == QThread::currentThread()); - delete thread; m_windows.removeAt(i); break; } @@ -912,14 +856,12 @@ void QSGThreadedRenderLoop::windowDestroyed(QQuickWindow *window) void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window) { QSG_GUI_DEBUG(window, "exposureChanged()"); - Window *w = windowFor(m_windows, window); - if (!w) - return; - if (window->isExposed()) { - handleExposure(w); + handleExposure(window); } else { - handleObscurity(w); + Window *w = windowFor(m_windows, window); + if (w) + handleObscurity(w); } } @@ -927,9 +869,22 @@ void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window) Will post an event to the render thread that this window should start to render. */ -void QSGThreadedRenderLoop::handleExposure(Window *w) +void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) { - QSG_GUI_DEBUG(w->window, "handleExposure"); + QSG_GUI_DEBUG(window, "handleExposure"); + + Window *w = windowFor(m_windows, window); + if (!w) { + QSG_GUI_DEBUG(window, " - adding window to list"); + Window win; + win.window = window; + win.actualWindowFormat = window->format(); + win.thread = new QSGRenderThread(this, QQuickWindowPrivate::get(window)->context); + win.timerId = 0; + win.updateDuringSync = false; + m_windows << win; + w = &m_windows.last(); + } if (w->window->width() <= 0 || w->window->height() <= 0 || !w->window->geometry().intersects(w->window->screen()->availableGeometry())) { @@ -982,20 +937,7 @@ void QSGThreadedRenderLoop::handleExposure(Window *w) QSG_GUI_DEBUG(w->window, " - render thread already running"); } - w->thread->postEvent(new WMExposeEvent(w->window)); - bool synced = polishAndSync(w); - - if (synced) { - w->thread->mutex.lock(); - if (w->thread->exposeCycle != QSGRenderThread::NoExpose) { - QSG_GUI_DEBUG(w->window, " - waiting for swap to complete..."); - w->thread->waitCondition.wait(&w->thread->mutex); - } - Q_ASSERT(w->thread->exposeCycle == QSGRenderThread::NoExpose); - w->thread->mutex.unlock(); - } else { - w->thread->postEvent(new QEvent(WM_ResetExposeCycle)); - } + polishAndSync(w, true); QSG_GUI_DEBUG(w->window, " - handleExposure completed..."); startOrStopAnimationTimer(); @@ -1017,7 +959,6 @@ void QSGThreadedRenderLoop::handleObscurity(Window *w) w->thread->waitCondition.wait(&w->thread->mutex); w->thread->mutex.unlock(); } - startOrStopAnimationTimer(); } @@ -1038,7 +979,7 @@ void QSGThreadedRenderLoop::maybeUpdate(Window *w) if (!QCoreApplication::instance()) return; - Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_locked, + Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_lockedForSync, "QQuickItem::update()", "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); @@ -1081,21 +1022,24 @@ void QSGThreadedRenderLoop::update(QQuickWindow *window) } +void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window) +{ + Window *w = windowFor(m_windows, window); + if (w) + releaseResources(w, false); +} /*! * Release resources will post an event to the render thread to * free up the SG and GL resources and exists the render thread. */ -void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestructor) +void QSGThreadedRenderLoop::releaseResources(Window *w, bool inDestructor) { - QSG_GUI_DEBUG(window, "releaseResources requested..."); - - Window *w = windowFor(m_windows, window); - if (!w) - return; + QSG_GUI_DEBUG(w->window, "releaseResources requested..."); w->thread->mutex.lock(); if (w->thread->isRunning() && w->thread->active) { + QQuickWindow *window = w->window; // The platform window might have been destroyed before // hide/release/windowDestroyed is called, so we need to have a @@ -1105,16 +1049,15 @@ void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestru // create it here and pass it on to QSGRenderThread::invalidateGL() QOffscreenSurface *fallback = 0; if (!window->handle()) { - QSG_GUI_DEBUG(w->window, " - using fallback surface"); + QSG_GUI_DEBUG(window, " - using fallback surface"); fallback = new QOffscreenSurface(); fallback->setFormat(w->actualWindowFormat); fallback->create(); } - QSG_GUI_DEBUG(w->window, " - posting release request to render thread"); + QSG_GUI_DEBUG(window, " - posting release request to render thread"); w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback)); w->thread->waitCondition.wait(&w->thread->mutex); - delete fallback; } w->thread->mutex.unlock(); @@ -1124,15 +1067,35 @@ void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestru /* Calls polish on all items, then requests synchronization with the render thread * and blocks until that is complete. Returns false if it aborted; otherwise true. */ -bool QSGThreadedRenderLoop::polishAndSync(Window *w) +void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) { QSG_GUI_DEBUG(w->window, "polishAndSync()"); - if (!w->window->isExposed() || !w->window->isVisible() || w->window->size().isEmpty()) { + QQuickWindow *window = w->window; + if (!window->isExposed() || !window->isVisible() || window->size().isEmpty()) { QSG_GUI_DEBUG(w->window, " - not exposed, aborting..."); killTimer(w->timerId); w->timerId = 0; - return false; + return; + } + + // Flush pending touch events. + // First we force flushing of the windowing system events, so that we're + // working with the latest possible data. This can trigger event processing + // which in turn can stop rendering this window, so verify that before + // proceeding. Then we flush the touch event and as that also does event + // processing, verify again that we still are active and rendering. + QWindowSystemInterface::flushWindowSystemEvents(); + w = windowFor(m_windows, window); + if (w) { + QQuickWindowPrivate::get(window)->flushDelayedTouchEvent(); + w = windowFor(m_windows, window); + } + if (!w) { + QSG_GUI_DEBUG(w->window, " - removed after event flushing.."); + killTimer(w->timerId); + w->timerId = 0; + return; } @@ -1146,7 +1109,7 @@ bool QSGThreadedRenderLoop::polishAndSync(Window *w) timer.start(); #endif - QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); d->polishItems(); #ifndef QSG_NO_RENDER_TIMING @@ -1156,22 +1119,22 @@ bool QSGThreadedRenderLoop::polishAndSync(Window *w) w->updateDuringSync = false; - emit w->window->afterAnimating(); + emit window->afterAnimating(); - QSG_GUI_DEBUG(w->window, " - lock for sync..."); + QSG_GUI_DEBUG(window, " - lock for sync..."); w->thread->mutex.lock(); - m_locked = true; - w->thread->postEvent(new QEvent(WM_RequestSync)); + m_lockedForSync = true; + w->thread->postEvent(new WMSyncEvent(window, inExpose)); - QSG_GUI_DEBUG(w->window, " - wait for sync..."); + QSG_GUI_DEBUG(window, " - wait for sync..."); #ifndef QSG_NO_RENDER_TIMING if (profileFrames) waitTime = timer.nsecsElapsed(); #endif w->thread->waitCondition.wait(&w->thread->mutex); - m_locked = false; + m_lockedForSync = false; w->thread->mutex.unlock(); - QSG_GUI_DEBUG(w->window, " - unlocked after sync..."); + QSG_GUI_DEBUG(window, " - unlocked after sync..."); #ifndef QSG_NO_RENDER_TIMING if (profileFrames) @@ -1182,9 +1145,9 @@ bool QSGThreadedRenderLoop::polishAndSync(Window *w) w->timerId = 0; if (m_animation_timer == 0 && m_animation_driver->isRunning()) { - QSG_GUI_DEBUG(w->window, " - animations advancing"); + QSG_GUI_DEBUG(window, " - animations advancing"); m_animation_driver->advance(); - QSG_GUI_DEBUG(w->window, " - animations done"); + QSG_GUI_DEBUG(window, " - animations done"); // We need to trigger another sync to keep animations running... maybePostPolishRequest(w); emit timeToIncubate(); @@ -1196,7 +1159,7 @@ bool QSGThreadedRenderLoop::polishAndSync(Window *w) #ifndef QSG_NO_RENDER_TIMING if (qsg_render_timing) qDebug(" - Gui Thread: window=%p, polish=%d, lock=%d, block/sync=%d -- animations=%d", - w->window, + window, int(polishTime/1000000), int((waitTime - polishTime)/1000000), int((syncTime - waitTime)/1000000), @@ -1208,8 +1171,17 @@ bool QSGThreadedRenderLoop::polishAndSync(Window *w) syncTime - waitTime, timer.nsecsElapsed() - syncTime)); #endif +} - return true; +QSGThreadedRenderLoop::Window *QSGThreadedRenderLoop::windowForTimer(int timerId) const +{ + for (int i=0; i<m_windows.size(); ++i) { + if (m_windows.at(i).timerId == timerId) { + return const_cast<Window *>(&m_windows.at(i)); + break; + } + } + return 0; } bool QSGThreadedRenderLoop::event(QEvent *e) @@ -1224,13 +1196,7 @@ bool QSGThreadedRenderLoop::event(QEvent *e) emit timeToIncubate(); } else { QSG_GUI_DEBUG((void *) 0, "QEvent::Timer -> Polish & Sync"); - Window *w = 0; - for (int i=0; i<m_windows.size(); ++i) { - if (m_windows.at(i).timerId == te->timerId()) { - w = const_cast<Window *>(&m_windows.at(i)); - break; - } - } + Window *w = windowForTimer(te->timerId()); if (w) polishAndSync(w); } diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index 82ab2cdaa0..b86b3c73a4 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -58,8 +58,8 @@ class QSGThreadedRenderLoop : public QSGRenderLoop public: QSGThreadedRenderLoop(); - void show(QQuickWindow *window); - void hide(QQuickWindow *window); + void show(QQuickWindow *) {} + void hide(QQuickWindow *); void windowDestroyed(QQuickWindow *window); void exposureChanged(QQuickWindow *window); @@ -73,7 +73,7 @@ public: QAnimationDriver *animationDriver() const; - void releaseResources(QQuickWindow *window) { releaseResources(window, false); } + void releaseResources(QQuickWindow *window); bool event(QEvent *); @@ -94,8 +94,9 @@ private: friend class QSGRenderThread; - void releaseResources(QQuickWindow *window, bool inDestructor); + void releaseResources(Window *window, bool inDestructor); bool checkAndResetForceUpdate(QQuickWindow *window); + Window *windowForTimer(int timerId) const; bool anyoneShowing() const; void initialize(); @@ -103,10 +104,10 @@ private: void startOrStopAnimationTimer(); void maybePostPolishRequest(Window *w); void waitForReleaseComplete(); - bool polishAndSync(Window *w); + void polishAndSync(Window *w, bool inExpose = false); void maybeUpdate(Window *window); - void handleExposure(Window *w); + void handleExposure(QQuickWindow *w); void handleObscurity(Window *w); @@ -117,7 +118,7 @@ private: int m_animation_timer; int m_exhaust_delay; - bool m_locked; + bool m_lockedForSync; }; diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index e20f9cdb9e..dc12d00490 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -442,6 +442,11 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) if (!m_gl->makeCurrent(window)) return; + d->flushDelayedTouchEvent(); + // Event delivery or processing has caused the window to stop rendering. + if (!windowData(window)) + return; + QSG_RENDER_TIMING_SAMPLE(time_start); RLDEBUG(" - polishing"); diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index e37344b21c..1ff7d11162 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -191,11 +191,9 @@ Atlas::~Atlas() void Atlas::invalidate() { - Q_ASSERT(QOpenGLContext::currentContext()); - if (m_texture_id) { + if (m_texture_id && QOpenGLContext::currentContext()) glDeleteTextures(1, &m_texture_id); - m_texture_id = 0; - } + m_texture_id = 0; } Texture *Atlas::create(const QImage &image) diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp index 4512577f23..bbf115fa2a 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.cpp +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.cpp @@ -52,10 +52,12 @@ public: : QSGGeometryNodePrivate() , m_texCoordMode(QSGSimpleTextureNode::NoTransform) , isAtlasTexture(false) + , ownsTexture(false) {} QSGSimpleTextureNode::TextureCoordinatesTransformMode m_texCoordMode; uint isAtlasTexture : 1; + uint ownsTexture : 1; }; static void qsgsimpletexturenode_update(QSGGeometry *g, @@ -113,6 +115,16 @@ QSGSimpleTextureNode::QSGSimpleTextureNode() } /*! + Destroys the texture node + */ +QSGSimpleTextureNode::~QSGSimpleTextureNode() +{ + Q_D(QSGSimpleTextureNode); + if (d->ownsTexture) + delete m_material.texture(); +} + +/*! Sets the filtering to be used for this texture node to \a filtering. For smooth scaling, use QSGTexture::Linear; for normal scaling, use @@ -170,6 +182,10 @@ QRectF QSGSimpleTextureNode::rect() const /*! Sets the texture of this texture node to \a texture. + Use setOwnsTexture() to set whether the node should take + ownership of the texture. By default, the node does not + take ownership. + \warning A texture node must have a texture before being added to the scenegraph to be rendered. */ @@ -246,4 +262,26 @@ QSGSimpleTextureNode::TextureCoordinatesTransformMode QSGSimpleTextureNode::text return d->m_texCoordMode; } +/*! + Sets whether the node takes ownership of the texture to \a owns. + + By default, the node does not take ownership of the texture. + + \sa setTexture() + */ +void QSGSimpleTextureNode::setOwnsTexture(bool owns) +{ + Q_D(QSGSimpleTextureNode); + d->ownsTexture = owns; +} + +/*! + Returns \c true if the node takes ownership of the texture; otherwise returns \c false. + */ +bool QSGSimpleTextureNode::ownsTexture() const +{ + Q_D(const QSGSimpleTextureNode); + return d->ownsTexture; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.h b/src/quick/scenegraph/util/qsgsimpletexturenode.h index 0c0b58442c..d971f00326 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.h +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.h @@ -54,6 +54,7 @@ class Q_QUICK_EXPORT QSGSimpleTextureNode : public QSGGeometryNode { public: QSGSimpleTextureNode(); + ~QSGSimpleTextureNode(); void setRect(const QRectF &rect); inline void setRect(qreal x, qreal y, qreal w, qreal h) { setRect(QRectF(x, y, w, h)); } @@ -75,6 +76,9 @@ public: void setTextureCoordinatesTransform(TextureCoordinatesTransformMode mode); TextureCoordinatesTransformMode textureCoordinatesTransform() const; + void setOwnsTexture(bool owns); + bool ownsTexture() const; + private: QSGGeometry m_geometry; QSGOpaqueTextureMaterial m_opaque_material; diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index a7f9174219..b90ca47fd1 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -508,6 +508,7 @@ void QQuickPixmapReader::processJobs() replies.remove(reply); reply->close(); } + Q_QUICK_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(job->url)); // deleteLater, since not owned by this thread job->deleteLater(); } @@ -664,7 +665,11 @@ void QQuickPixmapReader::cancel(QQuickPixmapReply *reply) // XXX if (threadObject) threadObject->processJobs(); } else { - jobs.removeAll(reply); + // If loading was started (reply removed from jobs) but the reply was never processed + // (otherwise it would have deleted itself) we need to profile an error. + if (jobs.removeAll(reply) == 0) { + Q_QUICK_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(reply->url)); + } delete reply; } mutex.unlock(); @@ -898,7 +903,9 @@ bool QQuickPixmapReply::event(QEvent *event) data->textureFactory = de->textureFactory; data->implicitSize = de->implicitSize; Q_QUICK_PROFILE(pixmapLoadingFinished(data->url, - data->requestSize.width() > 0 ? data->requestSize : data->implicitSize)); + data->textureFactory != 0 && data->textureFactory->textureSize().isValid() ? + data->textureFactory->textureSize() : + (data->requestSize.isValid() ? data->requestSize : data->implicitSize))); } else { Q_QUICK_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(data->url)); data->errorString = de->errorString; @@ -907,6 +914,8 @@ bool QQuickPixmapReply::event(QEvent *event) data->reply = 0; emit finished(); + } else { + Q_QUICK_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingError>(url)); } delete this; @@ -975,10 +984,10 @@ void QQuickPixmapData::removeFromCache() { if (inCache) { QQuickPixmapKey key = { &url, &requestSize }; - Q_QUICK_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>( - url, pixmapStore()->m_cache.count())); pixmapStore()->m_cache.remove(key); inCache = false; + Q_QUICK_PROFILE(pixmapCountChanged<QQuickProfiler::PixmapCacheCountChanged>( + url, pixmapStore()->m_cache.count())); } } @@ -1075,6 +1084,12 @@ QQuickPixmap::QQuickPixmap(QQmlEngine *engine, const QUrl &url, const QSize &siz load(engine, url, size); } +QQuickPixmap::QQuickPixmap(const QUrl &url, const QImage &image) +{ + d = new QQuickPixmapData(this, url, new QQuickDefaultTextureFactory(image), image.size(), QSize()); + d->addToCache(); +} + QQuickPixmap::~QQuickPixmap() { if (d) { @@ -1242,8 +1257,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques Q_QUICK_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); d = createPixmapDataSync(this, engine, url, requestSize, &ok); if (ok) { - Q_QUICK_PROFILE(pixmapLoadingFinished(url, - d->requestSize.width() > 0 ? d->requestSize : d->implicitSize)); + Q_QUICK_PROFILE(pixmapLoadingFinished(url, QSize(width(), height()))); if (options & QQuickPixmap::Cache) d->addToCache(); return; diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index aa1761e896..6ab1ff6482 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -78,6 +78,7 @@ public: QQuickPixmap(); QQuickPixmap(QQmlEngine *, const QUrl &); QQuickPixmap(QQmlEngine *, const QUrl &, const QSize &); + QQuickPixmap(const QUrl &, const QImage &image); ~QQuickPixmap(); enum Status { Null, Ready, Error, Loading }; diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp index 610d80d559..022ba8c440 100644 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp +++ b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp @@ -84,7 +84,8 @@ QQmlEngineDebugClient::QQmlEngineDebugClient( QQmlDebugConnection *connection) : QQmlDebugClient(QLatin1String("QmlDebugger"), connection), m_nextId(0), - m_valid(false) + m_valid(false), + m_connection(connection) { } @@ -467,6 +468,9 @@ void QQmlEngineDebugClient::messageReceived(const QByteArray &data) { m_valid = false; QDataStream ds(data); + ds.setVersion(m_connection->dataStreamVersion()); + + int queryId; QByteArray type; ds >> type >> queryId; diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h index 1d4b95a9e3..2712692389 100644 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h +++ b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.h @@ -242,6 +242,8 @@ private: QmlDebugObjectReference m_object; QList<QmlDebugObjectReference> m_objects; QVariant m_exprResult; + + QQmlDebugConnection *m_connection; }; #endif // QQMLENGINEDEBUGCLIENT_H diff --git a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp index dbf28a5471..d5a5f10634 100644 --- a/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp +++ b/tests/auto/qml/qqmlcomponent/tst_qqmlcomponent.cpp @@ -267,8 +267,8 @@ void tst_qqmlcomponent::qmlCreateParentReference() void tst_qqmlcomponent::async() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine); @@ -287,8 +287,8 @@ void tst_qqmlcomponent::async() void tst_qqmlcomponent::asyncHierarchy() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); // ensure that the item hierarchy is compiled correctly. diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a1e36b42e6..770d6b8197 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -4171,8 +4171,8 @@ void tst_qqmlecmascript::importScripts() QFETCH(QStringList, propertyNames); QFETCH(QVariantList, propertyValues); - TestHTTPServer server(8111); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(8111), qPrintable(server.errorString())); server.serveDirectory(dataDirectory() + "/remote"); QStringList importPathList = engine.importPathList(); @@ -5999,8 +5999,8 @@ void tst_qqmlecmascript::include() // Remote - error { - TestHTTPServer server(8111); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(8111), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, testFileUrl("include_remote_missing.qml")); @@ -6024,8 +6024,8 @@ void tst_qqmlecmascript::includeRemoteSuccess() #endif // Remote - success - TestHTTPServer server(8111); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(8111), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, testFileUrl("include_remote.qml")); diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 3561635351..be417df325 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -2298,7 +2298,8 @@ void tst_qqmllanguage::basicRemote() QFETCH(QString, type); QFETCH(QString, error); - TestHTTPServer server(14447); + TestHTTPServer server; + QVERIFY2(server.listen(14447), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, url); @@ -2342,7 +2343,8 @@ void tst_qqmllanguage::importsRemote() QFETCH(QString, type); QFETCH(QString, error); - TestHTTPServer server(14447); + TestHTTPServer server; + QVERIFY2(server.listen(14447), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); testType(qml,type,error); @@ -2434,7 +2436,8 @@ void tst_qqmllanguage::importsInstalledRemote() QFETCH(QString, type); QFETCH(QString, error); - TestHTTPServer server(14447); + TestHTTPServer server; + QVERIFY2(server.listen(14447), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QString serverdir = "http://127.0.0.1:14447/lib/"; @@ -2500,7 +2503,8 @@ void tst_qqmllanguage::importsPath() QFETCH(QString, qml); QFETCH(QString, value); - TestHTTPServer server(14447); + TestHTTPServer server; + QVERIFY2(server.listen(14447), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); engine.setImportPathList(QStringList(defaultImportPathList) << importPath); @@ -3076,7 +3080,8 @@ void tst_qqmllanguage::registeredCompositeType() // QTBUG-18268 void tst_qqmllanguage::remoteLoadCrash() { - TestHTTPServer server(14448); + TestHTTPServer server; + QVERIFY2(server.listen(14448), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine); @@ -3566,7 +3571,8 @@ void tst_qqmllanguage::compositeSingletonQmlDirError() // Load a remote composite singleton type via qmldir that defines the type as a singleton void tst_qqmllanguage::compositeSingletonRemote() { - TestHTTPServer server(14447); + TestHTTPServer server; + QVERIFY2(server.listen(14447), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, testFile("singletonTest15.qml")); diff --git a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp index 15be1fdbc0..1861b37bea 100644 --- a/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp +++ b/tests/auto/qml/qqmlmoduleplugin/tst_qqmlmoduleplugin.cpp @@ -247,8 +247,8 @@ void tst_qqmlmoduleplugin::importPluginWithQmlFile() void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(m_dataImportsDirectory); QQmlEngine engine; @@ -268,8 +268,8 @@ void tst_qqmlmoduleplugin::remoteImportWithQuotedUrl() void tst_qqmlmoduleplugin::remoteImportWithUnquotedUri() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(m_dataImportsDirectory); QQmlEngine engine; diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 17becb3714..e1ccde2c42 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -240,13 +240,12 @@ void tst_qqmlxmlhttprequest::open() QFETCH(QString, url); QFETCH(bool, remote); - QScopedPointer<TestHTTPServer> server; // ensure deletion in case test fails + TestHTTPServer server; if (remote) { - server.reset(new TestHTTPServer(SERVER_PORT)); - QVERIFY(server->isValid()); - QVERIFY(server->wait(testFileUrl("open_network.expect"), - testFileUrl("open_network.reply"), - testFileUrl("testdocument.html"))); + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); + QVERIFY(server.wait(testFileUrl("open_network.expect"), + testFileUrl("open_network.reply"), + testFileUrl("testdocument.html"))); } QQmlComponent component(&engine, qmlFile); @@ -322,8 +321,8 @@ void tst_qqmlxmlhttprequest::open_arg_count() // Test valid setRequestHeader() calls void tst_qqmlxmlhttprequest::setRequestHeader() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("setRequestHeader.expect"), testFileUrl("setRequestHeader.reply"), testFileUrl("testdocument.html"))); @@ -340,8 +339,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader() // Test valid setRequestHeader() calls with different header cases void tst_qqmlxmlhttprequest::setRequestHeader_caseInsensitive() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("setRequestHeader.expect"), testFileUrl("setRequestHeader.reply"), testFileUrl("testdocument.html"))); @@ -397,8 +396,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_illegalName() { QFETCH(QString, name); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("open_network.expect"), testFileUrl("open_network.reply"), testFileUrl("testdocument.html"))); @@ -423,8 +422,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_illegalName() // Test that attempting to set a header after a request is sent throws an exception void tst_qqmlxmlhttprequest::setRequestHeader_sent() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("open_network.expect"), testFileUrl("open_network.reply"), testFileUrl("testdocument.html"))); @@ -475,8 +474,8 @@ void tst_qqmlxmlhttprequest::send_alreadySent() void tst_qqmlxmlhttprequest::send_ignoreData() { { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("send_ignoreData_GET.expect"), testFileUrl("send_ignoreData.reply"), testFileUrl("testdocument.html"))); @@ -492,8 +491,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() } { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("send_ignoreData_HEAD.expect"), testFileUrl("send_ignoreData.reply"), QUrl())); @@ -509,8 +508,8 @@ void tst_qqmlxmlhttprequest::send_ignoreData() } { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("send_ignoreData_DELETE.expect"), testFileUrl("send_ignoreData.reply"), QUrl())); @@ -532,8 +531,8 @@ void tst_qqmlxmlhttprequest::send_withdata() QFETCH(QString, file_expected); QFETCH(QString, file_qml); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl(file_expected), testFileUrl("send_data.reply"), testFileUrl("testdocument.html"))); @@ -602,8 +601,8 @@ void tst_qqmlxmlhttprequest::abort_opened() // Test abort() aborts in progress send void tst_qqmlxmlhttprequest::abort() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("abort.expect"), testFileUrl("abort.reply"), testFileUrl("testdocument.html"))); @@ -626,8 +625,8 @@ void tst_qqmlxmlhttprequest::getResponseHeader() { QQmlEngine engine; // Avoid cookie contamination - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("getResponseHeader.expect"), testFileUrl("getResponseHeader.reply"), testFileUrl("testdocument.html"))); @@ -693,8 +692,8 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders() { QQmlEngine engine; // Avoid cookie contamination - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("getResponseHeader.expect"), testFileUrl("getResponseHeader.reply"), testFileUrl("testdocument.html"))); @@ -754,8 +753,8 @@ void tst_qqmlxmlhttprequest::status() QFETCH(QUrl, replyUrl); QFETCH(int, status); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("status.expect"), replyUrl, testFileUrl("testdocument.html"))); @@ -793,8 +792,8 @@ void tst_qqmlxmlhttprequest::statusText() QFETCH(QUrl, replyUrl); QFETCH(QString, statusText); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("status.expect"), replyUrl, testFileUrl("testdocument.html"))); @@ -833,8 +832,8 @@ void tst_qqmlxmlhttprequest::responseText() QFETCH(QUrl, bodyUrl); QFETCH(QString, responseText); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); QVERIFY(server.wait(testFileUrl("status.expect"), replyUrl, bodyUrl)); @@ -934,8 +933,8 @@ void tst_qqmlxmlhttprequest::invalidMethodUsage() void tst_qqmlxmlhttprequest::redirects() { { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirecttarget.html"); server.serveDirectory(dataDirectory()); @@ -951,8 +950,8 @@ void tst_qqmlxmlhttprequest::redirects() } { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirectmissing.html"); server.serveDirectory(dataDirectory()); @@ -968,8 +967,8 @@ void tst_qqmlxmlhttprequest::redirects() } { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirect.html"); server.serveDirectory(dataDirectory()); @@ -1070,8 +1069,8 @@ void tst_qqmlxmlhttprequest::stateChangeCallingContext() // ensure that we don't crash by attempting to evaluate // without a valid calling context. - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQmlComponent component(&engine, testFileUrl("stateChangeCallingContext.qml")); diff --git a/tests/auto/qmltest/item/tst_layerInPositioner.qml b/tests/auto/qmltest/item/tst_layerInPositioner.qml new file mode 100644 index 0000000000..9144fe1d8f --- /dev/null +++ b/tests/auto/qmltest/item/tst_layerInPositioner.qml @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.0 + +Item { + id: root; + width: 400 + height: 400 + + TestCase { + id: testCase + name: "transparentForPositioner" + when: windowShown + function test_endresult() { + var image = grabImage(root); + + // Row of red, green, blue and white box inside blue + // At 10,10, spanning 10x10 pixels each + verify(image.pixel(10, 10) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(20, 10) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(30, 10) == Qt.rgba(0, 0, 1, 1)); + + // Column of red, green, blue and white box inside blue + // At 10,30, spanning 10x10 pixels each + verify(image.pixel(10, 30) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(10, 40) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(10, 50) == Qt.rgba(0, 0, 1, 1)); + + // Flow of red, green, blue and white box inside blue + // At 30,30, spanning 10x10 pixels each, wrapping after two boxes + verify(image.pixel(30, 30) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(40, 30) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(30, 40) == Qt.rgba(0, 0, 1, 1)); + + // Flow of red, green, blue and white box inside blue + // At 100,10, spanning 10x10 pixels each, wrapping after two boxes + verify(image.pixel(60, 10) == Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(70, 10) == Qt.rgba(0, 1, 0, 1)); + verify(image.pixel(60, 20) == Qt.rgba(0, 0, 1, 1)); + } + } + + Component { + id: greenPassThrough + ShaderEffect { + fragmentShader: + " + uniform lowp sampler2D source; + varying highp vec2 qt_TexCoord0; + void main() { + gl_FragColor = texture2D(source, qt_TexCoord0) * vec4(0, 1, 0, 1); + } + " + } + } + + Row { + id: theRow + x: 10 + y: 10 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInRow + width: 10 + height: 10 + color: "#0000ff" + } + } + + Column { + id: theColumn + x: 10 + y: 30 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInColumn + width: 10 + height: 10 + color: "#0000ff" + } + } + + Flow { + id: theFlow + x: 30 + y: 30 + width: 20 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInFlow + width: 10 + height: 10 + color: "#0000ff" + } + } + + Grid { + id: theGrid + x: 60 + y: 10 + columns: 2 + Rectangle { + width: 10 + height: 10 + color: "#ff0000" + layer.enabled: true + } + + Rectangle { + width: 10 + height: 10 + color: "#ffffff" + layer.enabled: true + layer.effect: greenPassThrough + } + + Rectangle { + id: blueInGrid + width: 10 + height: 10 + color: "#0000ff" + } + } + +} diff --git a/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml new file mode 100644 index 0000000000..4f827bbf33 --- /dev/null +++ b/tests/auto/qmltest/itemgrabber/tst_itemgrabber.qml @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtTest 1.0 + +Item { + id: root; + width: 400 + height: 400 + + TestCase { + id: testCase + name: "item-grabber" + when: imageOnDisk.ready && imageOnDiskSmall.ready && imageInCache.ready && imageInCacheSmall.ready + function test_endresult() { + var image = grabImage(root); + + // imageOnDisk at (0, 0) - (100x100) + compare(imageOnDisk.width, 100); + compare(imageOnDisk.height, 100); + verify(image.pixel(0, 0) === Qt.rgba(1, 0, 0, 1)); // Use verify because compare doesn't support colors (QTBUG-34878) + verify(image.pixel(99, 99) === Qt.rgba(0, 0, 1, 1)); + + // imageOnDiskSmall at (100, 0) - 50x50 + compare(imageOnDiskSmall.width, 50); + compare(imageOnDiskSmall.height, 50); + verify(image.pixel(100, 0) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(149, 49) === Qt.rgba(0, 0, 1, 1)); + + // imageInCache at (0, 100) - 100x100 + compare(imageInCache.width, 100); + compare(imageInCache.height, 100); + verify(image.pixel(0, 100) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(99, 199) === Qt.rgba(0, 0, 1, 1)); + + // imageInCacheSmall at (100, 100) - 50x50 + compare(imageInCacheSmall.width, 50); + compare(imageInCacheSmall.height, 50); + verify(image.pixel(100, 100) === Qt.rgba(1, 0, 0, 1)); + verify(image.pixel(149, 149) === Qt.rgba(0, 0, 1, 1)); + + // After all that has been going on, it should only have been called that one time.. + compare(imageOnDisk.callCount, 1); + } + + onWindowShownChanged: { + box.grabToImage(imageOnDisk.handleGrab); + box.grabToImage(imageOnDiskSmall.handleGrab, Qt.size(50, 50)); + box.grabToImage(imageInCache.handleGrab); + box.grabToImage(imageInCacheSmall.handleGrab, Qt.size(50, 50)); + } + + } + + Rectangle { + id: box + width: 100 + height: 100 + color: "red"; + + visible: false + + Rectangle { + anchors.bottom: parent.bottom; + anchors.right: parent.right; + width: 10 + height: 10 + color: "blue"; + } + } + + Image { + id: imageOnDisk + x: 0 + y: 0 + property int callCount: 0; + property bool ready: false; + function handleGrab(result) { + if (!result.saveToFile("image.png")) + print("Error: Failed to save image to disk..."); + source = "image.png"; + ready = true; + ++callCount; + } + } + + Image { + id: imageOnDiskSmall + x: 100 + y: 0 + property bool ready: false; + function handleGrab(result) { + if (!result.saveToFile("image_small.png")) + print("Error: Failed to save image to disk..."); + source = "image_small.png"; + ready = true; + } + } + + Image { + id: imageInCache + x: 0 + y: 100 + property bool ready: false; + function handleGrab(result) { + source = result.url; + ready = true; + } + } + + Image { + id: imageInCacheSmall + x: 100 + y: 100 + property bool ready: false; + function handleGrab(result) { + source = result.url; + ready = true; + } + } +} diff --git a/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml b/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml new file mode 100644 index 0000000000..ba0289fadc --- /dev/null +++ b/tests/auto/qmltest/shadersource/tst_DynamicallyCreated.qml @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Window 2.0 +import QtTest 1.0 + +Item { + width: 100 + height: 100 + + Window { + id: win + + width: 100 + height: 100 + + property bool rendered: false; + visible: true + + title: "QML window" + + onFrameSwapped: { + if (shaderSource.sourceItem) { + rendered = true; + } else { + var com = Qt.createQmlObject('import QtQuick 2.2; Rectangle { color: "red"; width: 100; height: 100 }', win); + shaderSource.sourceItem = com; + } + } + + ShaderEffectSource { + id: shaderSource + } + + } + + TestCase { + when: win.rendered; + name: "shadersource-dynamic-sourceobject" + function test_endresult() { + var image = grabImage(shaderSource); + compare(image.pixel(0, 0), Qt.rgba(1, 0, 0, 1)); + } + } +} diff --git a/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml b/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml new file mode 100644 index 0000000000..d9959d7fad --- /dev/null +++ b/tests/auto/qmltest/shadersource/tst_SourceInOtherWindow.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.0 + +import QtTest 1.0 + +Item { + Rectangle { + id: box + color: "red" + } + + Window { + id: childWindow + + width: 100 + height: 100 + + property bool rendered: false; + visible: true + onFrameSwapped: rendered = true; + + ShaderEffectSource { + id: theSource + sourceItem: box + } + + ShaderEffect { + property variant source: theSource; + anchors.fill: parent + } + } + + TestCase { + name: "shadersource-from-other-window" + when: childWindow.isRendered + function test_endresult() { + verify(true); // that we got here without problems... + } + } +} diff --git a/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml b/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml new file mode 100644 index 0000000000..436705befc --- /dev/null +++ b/tests/auto/qmltest/shadersource/tst_SourcedFromOtherWindow.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.0 + +import QtTest 1.0 + +Item { + Rectangle { + id: box + color: "red" + } + + ShaderEffectSource { + id: theSource + sourceItem: box + } + + Window { + id: childWindow + + width: 100 + height: 100 + + property bool rendered: false; + visible: true + onFrameSwapped: rendered = true; + + ShaderEffect { + property variant source: theSource; + anchors.fill: parent + } + } + + TestCase { + name: "shadersource-from-other-window" + when: childWindow.isRendered + function test_endresult() { + verify(true); // that we got here without problems... + } + } +} diff --git a/tests/auto/quick/nodes/tst_nodestest.cpp b/tests/auto/quick/nodes/tst_nodestest.cpp index 662e78ef6c..212337957e 100644 --- a/tests/auto/quick/nodes/tst_nodestest.cpp +++ b/tests/auto/quick/nodes/tst_nodestest.cpp @@ -52,6 +52,8 @@ #include <QtQuick/private/qsgcontext_p.h> #include <QtQuick/qsgsimplerectnode.h> +#include <QtQuick/qsgsimpletexturenode.h> +#include <QtQuick/private/qsgtexture_p.h> class NodesTest : public QObject { @@ -74,6 +76,8 @@ private Q_SLOTS: void isBlockedCheck(); + void textureNodeTextureOwnership(); + private: QOffscreenSurface *surface; QOpenGLContext *context; @@ -259,6 +263,32 @@ void NodesTest::isBlockedCheck() QVERIFY(!updater.isNodeBlocked(node, &root)); } +void NodesTest::textureNodeTextureOwnership() +{ + { // Check that it is not deleted by default + QPointer<QSGTexture> texture(new QSGPlainTexture()); + + QSGSimpleTextureNode *tn = new QSGSimpleTextureNode(); + QVERIFY(!tn->ownsTexture()); + + tn->setTexture(texture); + delete tn; + QVERIFY(!texture.isNull()); + } + + { // Check that it is deleted when we so desire + QPointer<QSGTexture> texture(new QSGPlainTexture()); + + QSGSimpleTextureNode *tn = new QSGSimpleTextureNode(); + tn->setOwnsTexture(true); + QVERIFY(tn->ownsTexture()); + + tn->setTexture(texture); + delete tn; + QVERIFY(texture.isNull()); + } +} + QTEST_MAIN(NodesTest); #include "tst_nodestest.moc" diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp index d10963b579..49bbb3a4c5 100644 --- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp +++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp @@ -259,8 +259,8 @@ void tst_qquickanimatedimage::remote() QFETCH(QString, fileName); QFETCH(bool, paused); - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; @@ -324,8 +324,8 @@ void tst_qquickanimatedimage::invalidSource() void tst_qquickanimatedimage::sourceSizeChanges() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; @@ -390,8 +390,8 @@ void tst_qquickanimatedimage::sourceSizeChanges() void tst_qquickanimatedimage::qtbug_16520() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; @@ -413,8 +413,8 @@ void tst_qquickanimatedimage::qtbug_16520() void tst_qquickanimatedimage::progressAndStatusChanges() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; diff --git a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp index dfcef43a7e..0993d03ee4 100644 --- a/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp +++ b/tests/auto/quick/qquickapplication/tst_qquickapplication.cpp @@ -60,6 +60,7 @@ private slots: void state(); void layoutDirection(); void inputMethod(); + void cleanup(); private: QQmlEngine engine; @@ -69,6 +70,14 @@ tst_qquickapplication::tst_qquickapplication() { } +void tst_qquickapplication::cleanup() +{ + if (QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ApplicationState)) { + QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationInactive); + QTest::waitForEvents(); + } +} + void tst_qquickapplication::active() { QQmlComponent component(&engine); @@ -98,12 +107,19 @@ void tst_qquickapplication::active() QVERIFY(item->property("active").toBool()); QVERIFY(item->property("active2").toBool()); - // not active again QWindowSystemInterface::handleWindowActivated(0); +#ifdef Q_OS_OSX + // OS X has the concept of "reactivation" + QTRY_VERIFY(QGuiApplication::focusWindow() != &window); + QVERIFY(item->property("active").toBool()); + QVERIFY(item->property("active2").toBool()); +#else + // not active again QTRY_VERIFY(QGuiApplication::focusWindow() != &window); QVERIFY(!item->property("active").toBool()); QVERIFY(!item->property("active2").toBool()); +#endif } void tst_qquickapplication::state() @@ -117,6 +133,7 @@ void tst_qquickapplication::state() " target: Qt.application; " " onStateChanged: state2 = Qt.application.state; " " } " + " Component.onCompleted: state2 = Qt.application.state; " "}", QUrl::fromLocalFile("")); QQuickItem *item = qobject_cast<QQuickItem *>(component.create()); QVERIFY(item); diff --git a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp index 4e7b6522dd..c02a5c7a87 100644 --- a/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp +++ b/tests/auto/quick/qquickborderimage/tst_qquickborderimage.cpp @@ -143,11 +143,10 @@ void tst_qquickborderimage::imageSource() QFETCH(bool, remote); QFETCH(QString, error); - TestHTTPServer *server = 0; + TestHTTPServer server; if (remote) { - server = new TestHTTPServer(SERVER_PORT); - QVERIFY(server->isValid()); - server->serveDirectory(dataDirectory()); + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); + server.serveDirectory(dataDirectory()); } if (!error.isEmpty()) @@ -177,7 +176,6 @@ void tst_qquickborderimage::imageSource() } delete obj; - delete server; } void tst_qquickborderimage::clearSource() @@ -292,11 +290,11 @@ void tst_qquickborderimage::sciSource() QFETCH(bool, valid); bool remote = source.startsWith("http"); - TestHTTPServer *server = 0; + + TestHTTPServer server; if (remote) { - server = new TestHTTPServer(SERVER_PORT); - QVERIFY(server->isValid()); - server->serveDirectory(dataDirectory()); + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); + server.serveDirectory(dataDirectory()); } QString componentStr = "import QtQuick 2.0\nBorderImage { source: \"" + source + "\"; width: 300; height: 300 }"; @@ -325,7 +323,6 @@ void tst_qquickborderimage::sciSource() } delete obj; - delete server; } void tst_qquickborderimage::sciSource_data() @@ -435,11 +432,10 @@ void tst_qquickborderimage::statusChanges() QFETCH(bool, remote); QFETCH(QQuickImageBase::Status, finalStatus); - TestHTTPServer *server = 0; + TestHTTPServer server; if (remote) { - server = new TestHTTPServer(SERVER_PORT); - QVERIFY(server->isValid()); - server->serveDirectory(dataDirectory(), TestHTTPServer::Delay); + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); + server.serveDirectory(dataDirectory()); } QString componentStr = "import QtQuick 2.0\nBorderImage { width: 300; height: 300 }"; @@ -452,18 +448,17 @@ void tst_qquickborderimage::statusChanges() QVERIFY(obj != 0); obj->setSource(source); if (remote) - server->sendDelayedItem(); + server.sendDelayedItem(); QTRY_VERIFY(obj->status() == finalStatus); QCOMPARE(spy.count(), emissions); delete obj; - delete server; } void tst_qquickborderimage::sourceSizeChanges() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; @@ -528,8 +523,8 @@ void tst_qquickborderimage::sourceSizeChanges() void tst_qquickborderimage::progressAndStatusChanges() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 8ab86bf2d3..7cc3350b05 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -100,7 +100,7 @@ private slots: void pressDelayWithLoader(); private: - void flickWithTouch(QWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to); + void flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to); QQmlEngine engine; }; @@ -1349,20 +1349,18 @@ void tst_qquickflickable::flickTwiceUsingTouches() QTRY_VERIFY(contentYAfterSecondFlick > (contentYAfterFirstFlick + 80.0f)); } -void tst_qquickflickable::flickWithTouch(QWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to) +void tst_qquickflickable::flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to) { - QTest::touchEvent(window, touchDevice) - .press(0, from, window); - QTest::qWait(1); + QTest::touchEvent(window, touchDevice).press(0, from, window); + QQuickTouchUtils::flush(window); + QPoint diff = to - from; for (int i = 1; i <= 8; ++i) { - QTest::touchEvent(window, touchDevice) - .move(0, from + i*diff/8, window); - QTest::qWait(1); + QTest::touchEvent(window, touchDevice).move(0, from + i*diff/8, window); + QQuickTouchUtils::flush(window); } - QTest::touchEvent(window, touchDevice) - .release(0, to, window); - QTest::qWait(1); + QTest::touchEvent(window, touchDevice).release(0, to, window); + QQuickTouchUtils::flush(window); } void tst_qquickflickable::nestedStopAtBounds_data() diff --git a/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp b/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp index bcb496eab7..5c2bbf1650 100644 --- a/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp +++ b/tests/auto/quick/qquickfontloader/tst_qquickfontloader.cpp @@ -75,8 +75,7 @@ private: TestHTTPServer server; }; -tst_qquickfontloader::tst_qquickfontloader() : - server(SERVER_PORT) +tst_qquickfontloader::tst_qquickfontloader() { } @@ -84,7 +83,7 @@ void tst_qquickfontloader::initTestCase() { QQmlDataTest::initTestCase(); server.serveDirectory(dataDirectory()); - QVERIFY(server.isValid()); + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); } void tst_qquickfontloader::noFont() diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index 0e012c5c6a..7951cb07cf 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -177,9 +177,9 @@ void tst_qquickimage::imageSource() QFETCH(bool, cache); QFETCH(QString, error); - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; if (remote) { - QVERIFY(server.isValid()); + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png"); } @@ -529,8 +529,8 @@ void tst_qquickimage::noLoading() { qRegisterMetaType<QQuickImageBase::Status>(); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); server.addRedirect("oldcolors.png", SERVER_ADDR "/colors.png"); @@ -690,8 +690,8 @@ void tst_qquickimage::nullPixmapPaint() void tst_qquickimage::imageCrash_QTBUG_22125() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); { @@ -761,8 +761,8 @@ void tst_qquickimage::sourceSize() void tst_qquickimage::sourceSizeChanges() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; @@ -827,8 +827,8 @@ void tst_qquickimage::sourceSizeChanges() void tst_qquickimage::progressAndStatusChanges() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(14449), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlEngine engine; @@ -935,8 +935,8 @@ void tst_qquickimage::correctStatus() void tst_qquickimage::highdpi() { - TestHTTPServer server(14449); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QString componentStr = "import QtQuick 2.0\nImage { source: srcImage ; }"; diff --git a/tests/auto/quick/qquickitem/qquickitem.pro b/tests/auto/quick/qquickitem/qquickitem.pro index d4bd0874d8..1d8ae0148b 100644 --- a/tests/auto/quick/qquickitem/qquickitem.pro +++ b/tests/auto/quick/qquickitem/qquickitem.pro @@ -3,6 +3,7 @@ TARGET = tst_qquickitem SOURCES += tst_qquickitem.cpp include (../../shared/util.pri) +include (../shared/util.pri) macx:CONFIG -= app_bundle diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 2db510a69e..40327b0666 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -51,6 +51,7 @@ #include <QTimer> #include <QQmlEngine> #include "../../shared/util.h" +#include "../shared/viewtestutil.h" class TestItem : public QQuickItem { @@ -1313,6 +1314,7 @@ void tst_qquickitem::touchEventAcceptIgnore() item->touchEventReached = false; bool accepted = window.event(&event); + QQuickTouchUtils::flush(&window); QVERIFY(item->touchEventReached); @@ -1336,6 +1338,7 @@ void tst_qquickitem::touchEventAcceptIgnore() item->touchEventReached = false; bool accepted = window.event(&event); + QQuickTouchUtils::flush(&window); QCOMPARE(item->touchEventReached, itemSupportsTouch); @@ -1359,6 +1362,7 @@ void tst_qquickitem::touchEventAcceptIgnore() item->touchEventReached = false; bool accepted = window.event(&event); + QQuickTouchUtils::flush(&window); QCOMPARE(item->touchEventReached, itemSupportsTouch); diff --git a/tests/auto/quick/qquickitem2/data/grabToImage.qml b/tests/auto/quick/qquickitem2/data/grabToImage.qml new file mode 100644 index 0000000000..9f25210ee2 --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/grabToImage.qml @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 320 + height: 480 + Rectangle { + objectName: "myItem"; + width: 100 + height: 100 + color: "red" + Rectangle { + anchors.right: parent.right + anchors.bottom: parent.bottom + width: 10 + height: 10 + color: "blue" + } + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index bcfafac93b..e7f0adb24d 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -43,6 +43,7 @@ #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcomponent.h> #include <QtQml/qqmlcontext.h> +#include <QtQuick/qquickitemgrabresult.h> #include <QtQuick/qquickview.h> #include <QtGui/private/qinputmethod_p.h> #include <QtQuick/private/qquickrectangle_p.h> @@ -91,6 +92,7 @@ private slots: void keyNavigation_RightToLeft(); void keyNavigation_skipNotVisible(); void keyNavigation_implicitSetting(); + void keyNavigation_focusReason(); void layoutMirroring(); void layoutMirroringIllegalParent(); void smooth(); @@ -120,6 +122,8 @@ private slots: void contains(); void childAt(); + void grab(); + private: QQmlEngine engine; bool qt_tab_all_widgets() { @@ -215,6 +219,21 @@ public: int mKey; }; +class FocusEventFilter : public QObject +{ +protected: + bool eventFilter(QObject *watched, QEvent *event) { + if ((event->type() == QEvent::FocusIn) || (event->type() == QEvent::FocusOut)) { + QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event); + lastFocusReason = focusEvent->reason(); + return false; + } else + return QObject::eventFilter(watched, event); + } +public: + Qt::FocusReason lastFocusReason; +}; + QML_DECLARE_TYPE(KeyTestItem); class HollowTestItem : public QQuickItem @@ -1961,6 +1980,62 @@ void tst_QQuickItem::keyNavigation_implicitSetting() delete window; } +void tst_QQuickItem::keyNavigation_focusReason() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(240,320)); + + FocusEventFilter focusEventFilter; + + window->setSource(testFileUrl("keynavigationtest.qml")); + window->show(); + window->requestActivate(); + + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // install event filter on first item + QQuickItem *item = findItem<QQuickItem>(window->rootObject(), "item1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + item->installEventFilter(&focusEventFilter); + + //install event filter on second item + item = findItem<QQuickItem>(window->rootObject(), "item2"); + QVERIFY(item); + item->installEventFilter(&focusEventFilter); + + //install event filter on third item + item = findItem<QQuickItem>(window->rootObject(), "item3"); + QVERIFY(item); + item->installEventFilter(&focusEventFilter); + + //install event filter on last item + item = findItem<QQuickItem>(window->rootObject(), "item4"); + QVERIFY(item); + item->installEventFilter(&focusEventFilter); + + // tab + QKeyEvent key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + QCOMPARE(focusEventFilter.lastFocusReason, Qt::TabFocusReason); + + // backtab + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + QCOMPARE(focusEventFilter.lastFocusReason, Qt::BacktabFocusReason); + + // some arbitrary cursor key + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + QCOMPARE(focusEventFilter.lastFocusReason, Qt::OtherFocusReason); + + delete window; +} + void tst_QQuickItem::smooth() { QQmlComponent component(&engine); @@ -2737,6 +2812,41 @@ void tst_QQuickItem::childAt() QCOMPARE(parent.childAt(300, 300), static_cast<QQuickItem *>(0)); } +void tst_QQuickItem::grab() +{ + QQuickView view; + view.setSource(testFileUrl("grabToImage.qml")); + view.show(); + QTest::qWaitForWindowExposed(&view); + + QQuickItem *root = qobject_cast<QQuickItem *>(view.rootObject()); + QVERIFY(root); + QQuickItem *item = root->findChild<QQuickItem *>("myItem"); + QVERIFY(item); + + { // Default size (item is 100x100) + QSharedPointer<QQuickItemGrabResult> result = item->grabToImage(); + QSignalSpy spy(result.data(), SIGNAL(ready())); + QTRY_VERIFY(spy.size() > 0); + QVERIFY(!result->url().isEmpty()); + QImage image = result->image(); + QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0)); + QCOMPARE(image.pixel(99, 99), qRgb(0, 0, 255)); + } + + { // Smaller size + QSharedPointer<QQuickItemGrabResult> result = item->grabToImage(QSize(50, 50)); + QVERIFY(!result.isNull()); + QSignalSpy spy(result.data(), SIGNAL(ready())); + QTRY_VERIFY(spy.size() > 0); + QVERIFY(!result->url().isEmpty()); + QImage image = result->image(); + QCOMPARE(image.pixel(0, 0), qRgb(255, 0, 0)); + QCOMPARE(image.pixel(49, 49), qRgb(0, 0, 255)); + } + +} + QTEST_MAIN(tst_QQuickItem) diff --git a/tests/auto/quick/qquicklistview/data/sizeTransitions.qml b/tests/auto/quick/qquicklistview/data/sizeTransitions.qml new file mode 100644 index 0000000000..6dfc5ee70a --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/sizeTransitions.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Window 2.1 + +Rectangle { + id: root + width: 500 + height: 600 + property int animationDuration: 10 + property int itemHeight: 40 + + Rectangle { + id: sightingsListPanel + border.width: 2 + border.color: "lightgray" + y: 200 + anchors.fill: parent + anchors.topMargin: 200 + anchors.leftMargin: 200 + ListView { + id: list + objectName: "list" + orientation: topToBottom ? ListView.Vertical : ListView.Horizontal + property bool transitionFinished: false + property bool scriptActionExecuted : false + anchors { fill: parent; margins: parent.border.width; } + model: testModel + delegate: listDelegate + // clip when we have no animation running + clip: false + add: Transition { + id: trans + onRunningChanged: { + if (!running) + list.transitionFinished = true; + } + SequentialAnimation { + ParallelAnimation { + NumberAnimation { properties: "x"; from: -100; duration: root.animationDuration } + NumberAnimation { properties: "y"; from: -100; duration: root.animationDuration } + NumberAnimation { properties: "width"; from: 1; to: list.width; duration: root.animationDuration;} + // Commenting out the height animation and it works + NumberAnimation { properties: "height"; from: 1; to: root.itemHeight; duration: root.animationDuration } + } + ScriptAction { script: list.scriptActionExecuted = true;} + } + + } + } + // Delegate for defining a template for an item in the list + Component { + id: listDelegate + Rectangle { + id: background + width: list.width + height: root.itemHeight + border.width: 2 + radius: 3 + } + } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 5cc3c7e642..c05434166d 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -194,6 +194,9 @@ private slots: void populateTransitions(); void populateTransitions_data(); + void sizeTransitions(); + void sizeTransitions_data(); + void addTransitions(); void addTransitions_data(); void moveTransitions(); @@ -5799,6 +5802,56 @@ void tst_QQuickListView::populateTransitions_data() QTest::newRow("empty to start with, no populate") << false << false << false; } + +/* + * Tests if the first visible item is not repositioned if the same item + * resized + changes position during a transition. The test does not test the + * actual position while it is transitioning (since its timing sensitive), but + * rather tests if the transition has reached its target state properly. + **/ +void tst_QQuickListView::sizeTransitions() +{ + QFETCH(bool, topToBottom); + QQuickView *window = getView(); + QQmlContext *ctxt = window->rootContext(); + QaimModel model; + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("topToBottom", topToBottom); + TestObject *testObject = new TestObject; + ctxt->setContextProperty("testObject", &model); + window->setSource(testFileUrl("sizeTransitions.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); + + // the following will start the transition + model.addItem(QLatin1String("Test"), ""); + + // This ensures early failure in case of failure (in which case + // transitionFinished == true and scriptActionExecuted == false) + QTRY_COMPARE(listview->property("scriptActionExecuted").toBool() || + listview->property("transitionFinished").toBool(), true); + QCOMPARE(listview->property("scriptActionExecuted").toBool(), true); + QCOMPARE(listview->property("transitionFinished").toBool(), true); + + releaseView(window); + delete testObject; +} + +void tst_QQuickListView::sizeTransitions_data() +{ + QTest::addColumn<bool>("topToBottom"); + + QTest::newRow("TopToBottom") + << true; + + QTest::newRow("LeftToRight") + << false; +} + void tst_QQuickListView::addTransitions() { QFETCH(int, initialItemCount); diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index 877bb59613..9ac2663f24 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -446,8 +446,8 @@ void tst_QQuickLoader::noResize() void tst_QQuickLoader::networkRequestUrl() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QQmlComponent component(&engine); @@ -470,8 +470,8 @@ void tst_QQuickLoader::networkRequestUrl() /* XXX Component waits until all dependencies are loaded. Is this actually possible? */ void tst_QQuickLoader::networkComponent() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQmlComponent component(&engine); @@ -503,8 +503,8 @@ void tst_QQuickLoader::networkComponent() void tst_QQuickLoader::failNetworkRequest() { - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); QTest::ignoreMessage(QtWarningMsg, SERVER_ADDR "/IDontExist.qml: File not found"); @@ -718,8 +718,8 @@ void tst_QQuickLoader::initialPropertyValues() QFETCH(QStringList, propertyNames); QFETCH(QVariantList, propertyValues); - TestHTTPServer server(SERVER_PORT); - QVERIFY(server.isValid()); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory()); foreach (const QString &warning, expectedWarnings) diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index d0a1c18885..ee29ed2075 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -86,6 +86,7 @@ private slots: void resetDrag(); void dragging_data() { acceptedButton_data(); } void dragging(); + void dragSmoothed(); void dragThreshold(); void invalidDrag_data() { rejectedButton_data(); } void invalidDrag(); @@ -334,6 +335,50 @@ void tst_QQuickMouseArea::dragging() QCOMPARE(blackRect->y(), 61.0); } +void tst_QQuickMouseArea::dragSmoothed() +{ + QQuickView window; + QByteArray errorMessage; + QVERIFY2(initView(window, testFileUrl("dragging.qml"), true, &errorMessage), errorMessage.constData()); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QVERIFY(window.rootObject() != 0); + + QQuickMouseArea *mouseRegion = window.rootObject()->findChild<QQuickMouseArea*>("mouseregion"); + QQuickDrag *drag = mouseRegion->drag(); + drag->setThreshold(5); + + mouseRegion->setAcceptedButtons(Qt::LeftButton); + QQuickItem *blackRect = window.rootObject()->findChild<QQuickItem*>("blackrect"); + QVERIFY(blackRect != 0); + QVERIFY(blackRect == drag->target()); + QVERIFY(!drag->active()); + QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); + QVERIFY(!drag->active()); + QTest::mouseMove(&window, QPoint(100, 102), 50); + QTest::mouseMove(&window, QPoint(100, 106), 50); + QTest::mouseMove(&window, QPoint(100, 122), 50); + QTRY_COMPARE(blackRect->x(), 50.0); + QTRY_COMPARE(blackRect->y(), 66.0); + QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(100,122)); + + // reset rect position + blackRect->setX(50.0); + blackRect->setY(50.0); + + // now try with smoothed disabled + drag->setSmoothed(false); + QVERIFY(!drag->active()); + QTest::mousePress(&window, Qt::LeftButton, 0, QPoint(100,100)); + QVERIFY(!drag->active()); + QTest::mouseMove(&window, QPoint(100, 102), 50); + QTest::mouseMove(&window, QPoint(100, 106), 50); + QTest::mouseMove(&window, QPoint(100, 122), 50); + QTRY_COMPARE(blackRect->x(), 50.0); + QTRY_COMPARE(blackRect->y(), 72.0); + QTest::mouseRelease(&window, Qt::LeftButton, 0, QPoint(100, 122)); +} void tst_QQuickMouseArea::dragThreshold() { diff --git a/tests/auto/quick/qquickmultipointtoucharea/qquickmultipointtoucharea.pro b/tests/auto/quick/qquickmultipointtoucharea/qquickmultipointtoucharea.pro index d3abc198d9..5724a7179e 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/qquickmultipointtoucharea.pro +++ b/tests/auto/quick/qquickmultipointtoucharea/qquickmultipointtoucharea.pro @@ -8,6 +8,7 @@ SOURCES += tst_qquickmultipointtoucharea.cpp TESTDATA = data/* include(../../shared/util.pri) +include(../shared/util.pri) QT += core-private gui-private qml-private quick-private testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 1d4932c432..842babddd9 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -48,12 +48,14 @@ #include <QtQuick/qquickview.h> #include <QtGui/QScreen> #include "../../shared/util.h" +#include "../shared/viewtestutil.h" class tst_QQuickMultiPointTouchArea : public QQmlDataTest { Q_OBJECT public: tst_QQuickMultiPointTouchArea() : device(0) { } + private slots: void initTestCase() { QQmlDataTest::initTestCase(); @@ -118,6 +120,7 @@ void tst_QQuickMultiPointTouchArea::signalTest() QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device); sequence.press(0, p1).press(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(area->property("touchPointPressCount").toInt(), 2); QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); @@ -126,6 +129,7 @@ void tst_QQuickMultiPointTouchArea::signalTest() QMetaObject::invokeMethod(area, "clearCounts"); sequence.stationary(0).stationary(1).press(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(area->property("touchPointPressCount").toInt(), 1); QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); @@ -136,6 +140,7 @@ void tst_QQuickMultiPointTouchArea::signalTest() p1 -= QPoint(10,10); p2 += QPoint(10,10); sequence.move(0, p1).move(1, p2).stationary(2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(area->property("touchPointPressCount").toInt(), 0); QCOMPARE(area->property("touchPointUpdateCount").toInt(), 2); @@ -146,6 +151,7 @@ void tst_QQuickMultiPointTouchArea::signalTest() p3 += QPoint(10,10); sequence.release(0, p1).release(1, p2) .move(2, p3).press(3, p4).press(4, p5).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(area->property("touchPointPressCount").toInt(), 2); QCOMPARE(area->property("touchPointUpdateCount").toInt(), 1); @@ -154,6 +160,7 @@ void tst_QQuickMultiPointTouchArea::signalTest() QMetaObject::invokeMethod(area, "clearCounts"); sequence.release(2, p3).release(3, p4).release(4, p5).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(area->property("touchPointPressCount").toInt(), 0); QCOMPARE(area->property("touchPointUpdateCount").toInt(), 0); @@ -177,12 +184,14 @@ void tst_QQuickMultiPointTouchArea::release() QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device); sequence.press(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point1->pressed(), true); p1 += QPoint(0,10); sequence.move(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point1->pressed(), true); QCOMPARE(point1->x(), qreal(20)); QCOMPARE(point1->y(), qreal(110)); @@ -190,6 +199,7 @@ void tst_QQuickMultiPointTouchArea::release() p1 += QPoint(4,10); sequence.release(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); //test that a release without a prior move to the release position successfully updates the point's position QCOMPARE(point1->pressed(), false); @@ -216,12 +226,14 @@ void tst_QQuickMultiPointTouchArea::reuse() QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device); sequence.press(0, p1).press(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point1->pressed(), true); QCOMPARE(point2->pressed(), true); QCOMPARE(point3->pressed(), false); sequence.release(0, p1).stationary(1).press(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); //we shouldn't reuse point 1 yet QCOMPARE(point1->pressed(), false); @@ -230,24 +242,28 @@ void tst_QQuickMultiPointTouchArea::reuse() //back to base state (no touches) sequence.release(1, p2).release(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point1->pressed(), false); QCOMPARE(point2->pressed(), false); QCOMPARE(point3->pressed(), false); sequence.press(0, p1).press(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point1->pressed(), true); QCOMPARE(point2->pressed(), true); QCOMPARE(point3->pressed(), false); sequence.release(0, p1).stationary(1).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point1->pressed(), false); QCOMPARE(point2->pressed(), true); QCOMPARE(point3->pressed(), false); sequence.press(4, p4).stationary(1).commit(); + QQuickTouchUtils::flush(window.data()); //the new touch point should reuse point 1 QCOMPARE(point1->pressed(), true); @@ -283,6 +299,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device); sequence.press(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), false); QCOMPARE(point12->pressed(), false); @@ -291,6 +308,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() QCOMPARE(point23->pressed(), false); sequence.stationary(0).press(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -304,6 +322,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() p1 += QPoint(0,10); p2 += QPoint(5,0); sequence.move(0, p1).move(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -315,6 +334,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); sequence.stationary(0).stationary(1).press(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -323,6 +343,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() QCOMPARE(point23->pressed(), false); sequence.stationary(0).stationary(1).stationary(2).press(3, p4).press(4, p5).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -342,6 +363,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() p4 += QPoint(1,-1); p5 += QPoint(-7,10); sequence.move(0, p1).move(1, p2).move(2, p3).move(3, p4).move(4, p5).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -356,6 +378,7 @@ void tst_QQuickMultiPointTouchArea::nonOverlapping() QCOMPARE(point23->x(), qreal(93)); QCOMPARE(point23->y(), qreal(30)); sequence.release(0, p1).release(1, p2).release(2, p3).release(3, p4).release(4, p5).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), false); QCOMPARE(point12->pressed(), false); @@ -388,6 +411,7 @@ void tst_QQuickMultiPointTouchArea::nested() QTest::QTouchEventSequence sequence = QTest::touchEvent(window.data(), device); sequence.press(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), false); QCOMPARE(point12->pressed(), false); @@ -396,6 +420,7 @@ void tst_QQuickMultiPointTouchArea::nested() QCOMPARE(point23->pressed(), false); sequence.stationary(0).press(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -409,6 +434,7 @@ void tst_QQuickMultiPointTouchArea::nested() p1 += QPoint(0,10); p2 += QPoint(5,0); sequence.move(0, p1).move(1, p2).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -420,6 +446,7 @@ void tst_QQuickMultiPointTouchArea::nested() QCOMPARE(point12->x(), qreal(45)); QCOMPARE(point12->y(), qreal(100)); sequence.stationary(0).stationary(1).press(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -435,6 +462,7 @@ void tst_QQuickMultiPointTouchArea::nested() QCOMPARE(point23->x(), qreal(60)); QCOMPARE(point23->y(), qreal(180)); sequence.stationary(0).stationary(1).stationary(2).press(3, QPoint(80,180)).press(4, QPoint(100,180)).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -455,6 +483,7 @@ void tst_QQuickMultiPointTouchArea::nested() p2 += QPoint(17,17); p3 += QPoint(3,0); sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -472,6 +501,7 @@ void tst_QQuickMultiPointTouchArea::nested() p2 += QPoint(17,17); p3 += QPoint(3,0); sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), false); QCOMPARE(point12->pressed(), false); @@ -489,6 +519,7 @@ void tst_QQuickMultiPointTouchArea::nested() sequence.release(0, p1).release(1, p2).release(2, p3).commit(); sequence.press(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), false); QCOMPARE(point12->pressed(), false); @@ -497,11 +528,13 @@ void tst_QQuickMultiPointTouchArea::nested() QCOMPARE(point23->pressed(), false); sequence.release(0, p1).commit(); + QQuickTouchUtils::flush(window.data()); //test with grabbing turned off window->rootObject()->setProperty("grabInnerArea", false); sequence.press(0, p1).press(1, p2).press(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -513,6 +546,7 @@ void tst_QQuickMultiPointTouchArea::nested() p2 -= QPoint(17,17); p3 -= QPoint(3,0); sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -530,6 +564,7 @@ void tst_QQuickMultiPointTouchArea::nested() p2 -= QPoint(17,17); p3 -= QPoint(3,0); sequence.move(0, p1).move(1, p2).move(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -545,6 +580,7 @@ void tst_QQuickMultiPointTouchArea::nested() QCOMPARE(point23->x(), qreal(60)); QCOMPARE(point23->y(), qreal(180)); sequence.release(0, p1).release(1, p2).release(2, p3).commit(); + QQuickTouchUtils::flush(window.data()); } void tst_QQuickMultiPointTouchArea::inFlickable() @@ -569,25 +605,30 @@ void tst_QQuickMultiPointTouchArea::inFlickable() //moving one point vertically QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); QVERIFY(flickable->contentY() < 0); QCOMPARE(point11->pressed(), false); QCOMPARE(point12->pressed(), false); QTest::touchEvent(window.data(), device).release(0, p1); - QTest::qWait(50); + QQuickTouchUtils::flush(window.data()); QTRY_VERIFY(!flickable->isMoving()); @@ -595,6 +636,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable() p1 = QPoint(20,100); QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2); QTest::mousePress(window.data(), Qt::LeftButton, 0, p1); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); QCOMPARE(point12->pressed(), true); @@ -604,18 +646,22 @@ void tst_QQuickMultiPointTouchArea::inFlickable() p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); QVERIFY(flickable->contentY() < 0); QCOMPARE(point11->pressed(), false); @@ -625,7 +671,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable() QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, p1); - QTest::qWait(50); + QQuickTouchUtils::flush(window.data()); QTRY_VERIFY(!flickable->isMoving()); @@ -633,6 +679,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable() p1 = QPoint(20,100); p2 = QPoint(40,100); QTest::touchEvent(window.data(), device).press(0, p1).press(1, p2); + QQuickTouchUtils::flush(window.data()); // ensure that mouse events do not fall through to the Flickable mpta->setMaximumTouchPoints(3); QTest::mousePress(window.data(), Qt::LeftButton, 0, p1); @@ -643,34 +690,42 @@ void tst_QQuickMultiPointTouchArea::inFlickable() p1 += QPoint(15,0); p2 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(15,0); p2 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(15,0); p2 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(15,0); p2 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); p2 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1).move(1, p2); QTest::mouseMove(window.data(), p1); + QQuickTouchUtils::flush(window.data()); QVERIFY(flickable->contentY() == 0); QCOMPARE(point11->pressed(), true); @@ -678,7 +733,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable() QTest::touchEvent(window.data(), device).release(0, p1).release(1, p2); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, p1); - QTest::qWait(50); + QQuickTouchUtils::flush(window.data()); } // test that dragging out of a Flickable containing a MPTA doesn't harm Flickable's state. @@ -699,28 +754,34 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() // move point horizontally, out of Flickable area QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::mousePress(window.data(), Qt::LeftButton, 0, p1); p1 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::mouseMove(window.data(), p1); p1 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::mouseMove(window.data(), p1); p1 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::mouseMove(window.data(), p1); p1 += QPoint(15,0); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::mouseMove(window.data(), p1); QVERIFY(!flickable->isMoving()); QVERIFY(point11->pressed()); QTest::touchEvent(window.data(), device).release(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, p1); QTest::qWait(50); @@ -729,26 +790,32 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() // Check that we can still move the Flickable p1 = QPoint(50,100); QTest::touchEvent(window.data(), device).press(0, p1); + QQuickTouchUtils::flush(window.data()); QCOMPARE(point11->pressed(), true); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); p1 += QPoint(0,15); QTest::touchEvent(window.data(), device).move(0, p1); + QQuickTouchUtils::flush(window.data()); QVERIFY(flickable->contentY() < 0); QVERIFY(flickable->isMoving()); QCOMPARE(point11->pressed(), true); QTest::touchEvent(window.data(), device).release(0, p1); + QQuickTouchUtils::flush(window.data()); QTest::qWait(50); QTRY_VERIFY(!flickable->isMoving()); @@ -859,7 +926,9 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Touch both, release one, manipulate other touchpoint with mouse QTest::touchEvent(window.data(), device).press(1, touch1); + QQuickTouchUtils::flush(window.data()); QTest::touchEvent(window.data(), device).press(2, touch2); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); @@ -867,12 +936,14 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() QTest::touchEvent(window.data(), device).release(1, touch1); touch1.setY(20); QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); QCOMPARE(touch2rect->property("y").toInt(), touch2.y()); QTest::touchEvent(window.data(), device).release(2, touch2); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, touch1); + QQuickTouchUtils::flush(window.data()); // Start with mouse, move it, touch second point, move it QTest::mousePress(window.data(), Qt::LeftButton, 0, touch1); @@ -882,12 +953,14 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); touch2.setX(60); QTest::touchEvent(window.data(), device).press(3, touch2); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); QCOMPARE(touch2rect->property("y").toInt(), touch2.y()); touch2.setY(150); QTest::touchEvent(window.data(), device).move(3, touch2); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); @@ -895,6 +968,7 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Touch third point - nothing happens QTest::touchEvent(window.data(), device).press(4, touch3); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); @@ -903,7 +977,9 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Release all QTest::mouseRelease(window.data(), Qt::LeftButton, 0, touch1); QTest::touchEvent(window.data(), device).release(3, touch2); + QQuickTouchUtils::flush(window.data()); QTest::touchEvent(window.data(), device).release(4, touch3); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); @@ -922,12 +998,14 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() QCOMPARE(touch1rect->property("x").toInt(), mouse1.x()); QCOMPARE(touch1rect->property("y").toInt(), mouse1.y()); QTest::touchEvent(window.data(), device).press(1, touch1); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), mouse1.x()); QCOMPARE(touch1rect->property("y").toInt(), mouse1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch1.x()); QCOMPARE(touch2rect->property("y").toInt(), touch1.y()); QTest::touchEvent(window.data(), device).press(2, touch2).press(3, touch3).press(4, touch4); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), mouse1.x()); QCOMPARE(touch1rect->property("y").toInt(), mouse1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch1.x()); @@ -942,6 +1020,7 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Release all QTest::mouseRelease(window.data(), Qt::LeftButton, 0, mouse1); QTest::touchEvent(window.data(), device).release(1, touch1).release(2, touch2).release(3, touch3).release(4, touch4); + QQuickTouchUtils::flush(window.data()); } dualmpta->setProperty("mouseEnabled", false); @@ -964,13 +1043,16 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() QCOMPARE(touch1rect->property("y").toInt(), 10); QTest::touchEvent(window.data(), device).press(1, touch1); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); touch1.setY(150); QTest::touchEvent(window.data(), device).move(1, touch1); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QTest::touchEvent(window.data(), device).press(2, touch2); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); @@ -979,7 +1061,9 @@ void tst_QQuickMultiPointTouchArea::mouseAsTouchpoint() // Release all QTest::mouseRelease(window.data(), Qt::LeftButton, 0, mouse1); QTest::touchEvent(window.data(), device).release(1, touch1); + QQuickTouchUtils::flush(window.data()); QTest::touchEvent(window.data(), device).release(2, touch2); + QQuickTouchUtils::flush(window.data()); QCOMPARE(touch1rect->property("x").toInt(), touch1.x()); QCOMPARE(touch1rect->property("y").toInt(), touch1.y()); QCOMPARE(touch2rect->property("x").toInt(), touch2.x()); @@ -1028,7 +1112,6 @@ void tst_QQuickMultiPointTouchArea::transformedTouchArea_data() QTest::newRow("3rd point inside") << QPoint(140, 260) << QPoint(260, 140) << QPoint(200, 140) << 0 << 0 << 1; - QTest::newRow("all points inside") << QPoint(200, 140) << QPoint(200, 260) << QPoint(140, 200) << 1 << 2 << 3; diff --git a/tests/auto/quick/qquickpincharea/qquickpincharea.pro b/tests/auto/quick/qquickpincharea/qquickpincharea.pro index 970ce48851..fa14afa261 100644 --- a/tests/auto/quick/qquickpincharea/qquickpincharea.pro +++ b/tests/auto/quick/qquickpincharea/qquickpincharea.pro @@ -6,6 +6,7 @@ macx:CONFIG -= app_bundle SOURCES += tst_qquickpincharea.cpp include (../../shared/util.pri) +include (../shared/util.pri) TESTDATA = data/* diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index b9d314b63e..1dbce1b730 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -48,6 +48,7 @@ #include <QtQuick/qquickview.h> #include <QtQml/qqmlcontext.h> #include "../../shared/util.h" +#include "../shared/viewtestutil.h" class tst_QQuickPinchArea: public QQmlDataTest { @@ -232,15 +233,18 @@ void tst_QQuickPinchArea::scale() { QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.press(0, p1, window).commit(); + QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. Otherwise if we let it // be destroyed and then start a new sequence, point 0 will default to being // stationary at 0, 0, and PinchArea will filter out that touchpoint because // it is outside its bounds. pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(root->property("scale").toReal(), 1.0); QVERIFY(root->property("pinchActive").toBool()); @@ -248,6 +252,7 @@ void tst_QQuickPinchArea::scale() p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(root->property("scale").toReal(), 1.5); QCOMPARE(root->property("center").toPointF(), QPointF(40, 40)); // blackrect is at 50,50 @@ -260,8 +265,10 @@ void tst_QQuickPinchArea::scale() { QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(blackRect->scale(), 2.0); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); } QVERIFY(!root->property("pinchActive").toBool()); } @@ -293,12 +300,15 @@ void tst_QQuickPinchArea::pan() { QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.press(0, p1, window).commit(); + QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 += QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(root->property("scale").toReal(), 1.0); QVERIFY(root->property("pinchActive").toBool()); @@ -306,6 +316,7 @@ void tst_QQuickPinchArea::pan() p1 += QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QQuickTouchUtils::flush(window); } QCOMPARE(root->property("center").toPointF(), QPointF(60, 60)); // blackrect is at 50,50 @@ -316,11 +327,13 @@ void tst_QQuickPinchArea::pan() p1 += QPoint(100,100); p2 += QPoint(100,100); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); QCOMPARE(blackRect->x(), 140.0); QCOMPARE(blackRect->y(), 160.0); QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window); + QQuickTouchUtils::flush(window); QVERIFY(!root->property("pinchActive").toBool()); } @@ -355,12 +368,15 @@ void tst_QQuickPinchArea::retouch() { QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.press(0, p1, window).commit(); + QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(root->property("scale").toReal(), 1.0); QVERIFY(root->property("pinchActive").toBool()); @@ -368,6 +384,7 @@ void tst_QQuickPinchArea::retouch() p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1,window).move(1, p2,window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(startedSpy.count(), 1); @@ -382,6 +399,7 @@ void tst_QQuickPinchArea::retouch() // Hold down the first finger but release the second one pinchSequence.stationary(0).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(startedSpy.count(), 1); QCOMPARE(finishedSpy.count(), 0); @@ -390,9 +408,11 @@ void tst_QQuickPinchArea::retouch() // Keep holding down the first finger and re-touch the second one, then move them both pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); // Lifting and retouching results in onPinchStarted being called again QCOMPARE(startedSpy.count(), 2); @@ -401,6 +421,7 @@ void tst_QQuickPinchArea::retouch() QCOMPARE(window->rootObject()->property("pointCount").toInt(), 2); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QVERIFY(!root->property("pinchActive").toBool()); QCOMPARE(startedSpy.count(), 2); @@ -456,14 +477,18 @@ void tst_QQuickPinchArea::transformedPinchArea() QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(view, device); // start pinch pinchSequence.press(0, p1, view).commit(); + QQuickTouchUtils::flush(view); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. pinchSequence.stationary(0).press(1, p2, view).commit(); + QQuickTouchUtils::flush(view); pinchSequence.stationary(0).move(1, p2 + QPoint(threshold * 2, 0), view).commit(); + QQuickTouchUtils::flush(view); QCOMPARE(pinchArea->property("pinching").toBool(), shouldPinch); // release pinch pinchSequence.release(0, p1, view).release(1, p2, view).commit(); + QQuickTouchUtils::flush(view); QCOMPARE(pinchArea->property("pinching").toBool(), false); } } diff --git a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp index 75bd468aef..f104154205 100644 --- a/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp +++ b/tests/auto/quick/qquickpixmapcache/tst_qquickpixmapcache.cpp @@ -59,7 +59,7 @@ class tst_qquickpixmapcache : public QQmlDataTest { Q_OBJECT public: - tst_qquickpixmapcache() : server(14452) {} + tst_qquickpixmapcache() {} private slots: void initTestCase(); @@ -116,6 +116,8 @@ void tst_qquickpixmapcache::initTestCase() { QQmlDataTest::initTestCase(); + QVERIFY2(server.listen(14452), qPrintable(server.errorString())); + // This avoids a race condition/deadlock bug in network config // manager when it is accessed by the HTTP server thread before // anything else. Bug report can be found at: @@ -379,7 +381,8 @@ void tst_qquickpixmapcache::shrinkcache() void createNetworkServer() { QEventLoop eventLoop; - TestHTTPServer server(14453); + TestHTTPServer server; + QVERIFY2(server.listen(14453), qPrintable(server.errorString())); server.serveDirectory(QQmlDataTest::instance()->testFile("http")); QTimer::singleShot(100, &eventLoop, SLOT(quit())); eventLoop.exec(); @@ -407,7 +410,8 @@ void tst_qquickpixmapcache::networkCrash() // QTBUG-22125 void tst_qquickpixmapcache::lockingCrash() { - TestHTTPServer server(14453); + TestHTTPServer server; + QVERIFY2(server.listen(14453), qPrintable(server.errorString())); server.serveDirectory(testFile("http"), TestHTTPServer::Delay); { diff --git a/tests/auto/quick/qquicktext/tst_qquicktext.cpp b/tests/auto/quick/qquicktext/tst_qquicktext.cpp index d36c55d687..9ca7cafe3d 100644 --- a/tests/auto/quick/qquicktext/tst_qquicktext.cpp +++ b/tests/auto/quick/qquicktext/tst_qquicktext.cpp @@ -2029,7 +2029,8 @@ void tst_qquicktext::embeddedImages() QFETCH(QUrl, qmlfile); QFETCH(QString, error); - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(testFile("http")); if (!error.isEmpty()) @@ -2767,7 +2768,8 @@ void tst_qquicktext::imgTagsBaseUrl() QFETCH(QUrl, contextUrl); QFETCH(qreal, imgHeight); - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(testFile("")); QByteArray baseUrlFragment; diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 1ffd67cbf1..f8fc4c10da 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -2604,7 +2604,8 @@ void tst_qquicktextedit::cursorDelegate() void tst_qquicktextedit::remoteCursorDelegate() { - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQuickView view; @@ -2741,7 +2742,8 @@ void tst_qquicktextedit::delegateLoading() QFETCH(QString, qmlfile); QFETCH(QString, error); - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(testFile("httpfail"), TestHTTPServer::Disconnect); server.serveDirectory(testFile("httpslow"), TestHTTPServer::Delay); server.serveDirectory(testFile("http")); @@ -5215,7 +5217,8 @@ void tst_qquicktextedit::embeddedImages() QFETCH(QUrl, qmlfile); QFETCH(QString, error); - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(testFile("http")); if (!error.isEmpty()) diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index e125c33a56..fc1be16bc8 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -232,6 +232,8 @@ private slots: void baselineOffset_data(); void baselineOffset(); + void ensureVisible(); + private: void simulateKey(QWindow *, int key); @@ -2490,7 +2492,7 @@ void tst_qquicktextinput::copyAndPaste() QCOMPARE(textInput->selectedText(), QString("Hello world!")); QCOMPARE(textInput->selectedText().length(), 12); textInput->setCursorPosition(0); - QVERIFY(textInput->canPaste()); + QTRY_VERIFY(textInput->canPaste()); textInput->paste(); QCOMPARE(textInput->text(), QString("Hello world!Hello world!")); QCOMPARE(textInput->text().length(), 24); @@ -2544,7 +2546,7 @@ void tst_qquicktextinput::copyAndPaste() QClipboard *clipboard = QGuiApplication::clipboard(); QVERIFY(clipboard); clipboard->clear(); - QVERIFY(!textInput->canPaste()); + QTRY_VERIFY(!textInput->canPaste()); // test that copy functionality is disabled // when echo mode is set to hide text/password mode @@ -2612,7 +2614,7 @@ void tst_qquicktextinput::copyAndPasteKeySequence() QClipboard *clipboard = QGuiApplication::clipboard(); QVERIFY(clipboard); clipboard->clear(); - QVERIFY(!textInput->canPaste()); + QTRY_VERIFY(!textInput->canPaste()); // test that copy functionality is disabled // when echo mode is set to hide text/password mode @@ -2853,7 +2855,8 @@ void tst_qquicktextinput::cursorDelegate() void tst_qquicktextinput::remoteCursorDelegate() { - TestHTTPServer server(SERVER_PORT); + TestHTTPServer server; + QVERIFY2(server.listen(SERVER_PORT), qPrintable(server.errorString())); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQuickView view; @@ -6463,6 +6466,50 @@ void tst_qquicktextinput::baselineOffset() } } +void tst_qquicktextinput::ensureVisible() +{ + QQmlComponent component(&engine); + component.setData("import QtQuick 2.0\n TextInput {}", QUrl()); + QScopedPointer<QObject> object(component.create()); + QQuickTextInput *input = qobject_cast<QQuickTextInput *>(object.data()); + QVERIFY(input); + + input->setWidth(QFontMetrics(input->font()).averageCharWidth() * 3); + input->setText("Hello World"); + + QTextLayout layout; + layout.setText(input->text()); + layout.setFont(input->font()); + + if (!qmlDisableDistanceField()) { + QTextOption option; + option.setUseDesignMetrics(true); + layout.setTextOption(option); + } + layout.beginLayout(); + QTextLine line = layout.createLine(); + layout.endLayout(); + + input->ensureVisible(0); + + QCOMPARE(input->boundingRect().x(), qreal(0)); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); + + QSignalSpy cursorSpy(input, SIGNAL(cursorRectangleChanged())); + QVERIFY(cursorSpy.isValid()); + + input->ensureVisible(input->length()); + + QCOMPARE(cursorSpy.count(), 1); + + QCOMPARE(input->boundingRect().x(), input->width() - line.naturalTextWidth()); + QCOMPARE(input->boundingRect().y(), qreal(0)); + QCOMPARE(input->boundingRect().width(), line.naturalTextWidth() + input->cursorRectangle().width()); + QCOMPARE(input->boundingRect().height(), line.height()); +} + QTEST_MAIN(tst_qquicktextinput) #include "tst_qquicktextinput.moc" diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro index 6bce209df9..e95b7dbb10 100644 --- a/tests/auto/quick/qquickwindow/qquickwindow.pro +++ b/tests/auto/quick/qquickwindow/qquickwindow.pro @@ -3,6 +3,7 @@ TARGET = tst_qquickwindow SOURCES += tst_qquickwindow.cpp include (../../shared/util.pri) +include(../shared/util.pri) macx:CONFIG -= app_bundle diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index b05146fa3a..a2e2980223 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -49,6 +49,7 @@ #include <QtQuick/private/qquickrectangle_p.h> #include "../../shared/util.h" #include "../shared/visualtestutil.h" +#include "../shared/viewtestutil.h" #include <QSignalSpy> #include <qpa/qwindowsysteminterface.h> #include <private/qquickwindow_p.h> @@ -327,6 +328,7 @@ private slots: void animationsWhileHidden(); void focusObject(); + void focusReason(); void ignoreUnhandledMouseEvents(); @@ -359,6 +361,9 @@ private slots: void contentItemSize(); + void defaultSurfaceFormat(); + void glslVersion(); + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -392,7 +397,7 @@ void tst_qquickwindow::aboutToStopSignal() window.hide(); - QVERIFY(spy.count() > 0); + QTRY_VERIFY(spy.count() > 0); } //If the item calls update inside updatePaintNode, it should schedule another sync pass @@ -519,7 +524,7 @@ void tst_qquickwindow::touchEvent_basic() // press multiple points QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window) .press(1, bottomItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); @@ -530,9 +535,9 @@ void tst_qquickwindow::touchEvent_basic() // touch point on top item moves to bottom item, but top item should still receive the event QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos))); @@ -540,9 +545,9 @@ void tst_qquickwindow::touchEvent_basic() // touch point on bottom item moves to top item, but bottom item should still receive the event QTest::touchEvent(window, touchDevice).press(0, bottomItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, touchDevice).move(0, topItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); COMPARE_TOUCH_DATA(bottomItem->lastEvent, makeTouchData(QEvent::TouchUpdate, window, Qt::TouchPointMoved, makeTouchPoint(bottomItem, bottomItem->mapFromItem(topItem, pos), pos))); @@ -550,10 +555,10 @@ void tst_qquickwindow::touchEvent_basic() // a single stationary press on an item shouldn't cause an event QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, touchDevice).stationary(0) .press(1, bottomItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); // received press only, not stationary QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); @@ -565,12 +570,13 @@ void tst_qquickwindow::touchEvent_basic() // Otherwise you will get an assertion failure: // ASSERT: "itemForTouchPointId.isEmpty()" in file items/qquickwindow.cpp QTest::touchEvent(window, touchDevice).release(0, pos.toPoint(), window).release(1, pos.toPoint(), window); + QQuickTouchUtils::flush(window); // move touch point from top item to bottom, and release QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(),window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); COMPARE_TOUCH_DATA(topItem->lastEvent, makeTouchData(QEvent::TouchEnd, window, Qt::TouchPointReleased, makeTouchPoint(topItem, topItem->mapFromItem(bottomItem, pos), pos))); @@ -579,12 +585,12 @@ void tst_qquickwindow::touchEvent_basic() // release while another point is pressed QTest::touchEvent(window, touchDevice).press(0, topItem->mapToScene(pos).toPoint(),window) .press(1, bottomItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, touchDevice).move(0, bottomItem->mapToScene(pos).toPoint(), window); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, touchDevice).release(0, bottomItem->mapToScene(pos).toPoint(), window) .stationary(1); - QTest::qWait(50); + QQuickTouchUtils::flush(window); QCOMPARE(topItem->lastEvent.touchPoints.count(), 1); QVERIFY(middleItem->lastEvent.touchPoints.isEmpty()); QCOMPARE(bottomItem->lastEvent.touchPoints.count(), 1); @@ -832,12 +838,15 @@ void tst_qquickwindow::touchEvent_velocity() tp.area = QRectF(pos, QSizeF(4, 4)); points << tp; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); points[0].state = Qt::TouchPointMoved; points[0].area.adjust(5, 5, 5, 5); QVector2D velocity(1.5, 2.5); points[0].velocity = velocity; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); - QCoreApplication::processEvents(); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); QCOMPARE(item->touchEventCount, 2); QCOMPARE(item->lastEvent.touchPoints.count(), 1); QCOMPARE(item->lastVelocity, velocity); @@ -849,7 +858,8 @@ void tst_qquickwindow::touchEvent_velocity() QVector2D transformedVelocity = transformMatrix.mapVector(velocity).toVector2D(); points[0].area.adjust(5, 5, 5, 5); QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); - QCoreApplication::processEvents(); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); QCOMPARE(item->lastVelocity, transformedVelocity); QPoint itemLocalPos = item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint(); QPoint itemLocalPosFromEvent = item->lastEvent.touchPoints[0].pos().toPoint(); @@ -857,7 +867,8 @@ void tst_qquickwindow::touchEvent_velocity() points[0].state = Qt::TouchPointReleased; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); - QCoreApplication::processEvents(); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); delete item; } @@ -889,14 +900,19 @@ void tst_qquickwindow::mouseFromTouch_basic() tp.area = QRectF(pos, QSizeF(4, 4)); points << tp; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); points[0].state = Qt::TouchPointMoved; points[0].area.adjust(5, 5, 5, 5); QVector2D velocity(1.5, 2.5); points[0].velocity = velocity; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); points[0].state = Qt::TouchPointReleased; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); - QCoreApplication::processEvents(); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); // The item should have received a mouse press, move, and release. QCOMPARE(item->mousePressNum, 1); @@ -915,16 +931,20 @@ void tst_qquickwindow::mouseFromTouch_basic() points[0].velocity = velocity; points[0].area = QRectF(pos, QSizeF(4, 4)); QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); points[0].state = Qt::TouchPointMoved; points[0].area.adjust(5, 5, 5, 5); QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); - QCoreApplication::processEvents(); + QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); QCOMPARE(item->lastMousePos.toPoint(), item->mapFromScene(window->mapFromGlobal(points[0].area.center().toPoint())).toPoint()); QCOMPARE(item->lastVelocityFromMouseMove, transformedVelocity); points[0].state = Qt::TouchPointReleased; QWindowSystemInterface::handleTouchEvent(window, touchDeviceWithVelocity, points); QCoreApplication::processEvents(); + QQuickTouchUtils::flush(window); delete item; } @@ -1272,6 +1292,33 @@ void tst_qquickwindow::focusObject() QCOMPARE(focusObjectSpy.count(), 3); } +void tst_qquickwindow::focusReason() +{ + QQuickWindow *window = new QQuickWindow; + QScopedPointer<QQuickWindow> cleanup(window); + window->resize(200, 200); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QQuickItem *firstItem = new QQuickItem; + firstItem->setSize(QSizeF(100, 100)); + firstItem->setParentItem(window->contentItem()); + + QQuickItem *secondItem = new QQuickItem; + secondItem->setSize(QSizeF(100, 100)); + secondItem->setParentItem(window->contentItem()); + + firstItem->forceActiveFocus(Qt::OtherFocusReason); + QCOMPARE(QQuickWindowPrivate::get(window)->lastFocusReason, Qt::OtherFocusReason); + + secondItem->forceActiveFocus(Qt::TabFocusReason); + QCOMPARE(QQuickWindowPrivate::get(window)->lastFocusReason, Qt::TabFocusReason); + + firstItem->forceActiveFocus(Qt::BacktabFocusReason); + QCOMPARE(QQuickWindowPrivate::get(window)->lastFocusReason, Qt::BacktabFocusReason); + +} + void tst_qquickwindow::ignoreUnhandledMouseEvents() { QQuickWindow *window = new QQuickWindow; @@ -1828,6 +1875,79 @@ void tst_qquickwindow::contentItemSize() QCOMPARE(QSizeF(rect->width(), rect->height()), size); } +void tst_qquickwindow::defaultSurfaceFormat() +{ + // It is quite difficult to verify anything for real since the resulting format after + // surface/context creation can be anything, depending on the platform and drivers, + // and many options and settings may fail in various configurations, but test at + // least using some harmless settings to check that the global, static format is + // taken into account in the requested format. + + QSurfaceFormat savedDefaultFormat = QQuickWindow::defaultFormat(); + + // Verify that depth and stencil are set, as they should be, unless they are disabled + // via environment variables. + QVERIFY(savedDefaultFormat.depthBufferSize() >= 16); + QVERIFY(savedDefaultFormat.stencilBufferSize() >= 8); + + QSurfaceFormat format = savedDefaultFormat; + format.setSwapInterval(0); + format.setRedBufferSize(8); + format.setGreenBufferSize(8); + format.setBlueBufferSize(8); + format.setProfile(QSurfaceFormat::CompatibilityProfile); + format.setOption(QSurfaceFormat::DebugContext); + QQuickWindow::setDefaultFormat(format); + + QQuickWindow window; + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + const QSurfaceFormat reqFmt = window.requestedFormat(); + QCOMPARE(format.swapInterval(), reqFmt.swapInterval()); + QCOMPARE(format.redBufferSize(), reqFmt.redBufferSize()); + QCOMPARE(format.greenBufferSize(), reqFmt.greenBufferSize()); + QCOMPARE(format.blueBufferSize(), reqFmt.blueBufferSize()); + QCOMPARE(format.profile(), reqFmt.profile()); + QCOMPARE(int(format.options()), int(reqFmt.options())); + + QQuickWindow::setDefaultFormat(savedDefaultFormat); +} + +void tst_qquickwindow::glslVersion() +{ + QQuickWindow window; + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + // Core profile is never requested by default. + QVERIFY(!window.glslIsCoreProfile()); + + // Get the format from the context, not the window. The actual OpenGL version and + // related settings are associated with the context and are only written back to the + // context's format. + QSurfaceFormat format = window.openglContext()->format(); + + if (format.renderableType() == QSurfaceFormat::OpenGL) { + if (format.majorVersion() == 2) + QCOMPARE(window.glslVersion(), QString()); + else if (format.majorVersion() == 3) + QVERIFY(window.glslVersion().startsWith('3') + || window.glslVersion() == QStringLiteral("130") + || window.glslVersion() == QStringLiteral("140") + || window.glslVersion() == QStringLiteral("150")); + else if (format.majorVersion() == 4) + QVERIFY(window.glslVersion().startsWith('4')); + QVERIFY(!window.glslVersion().contains(QStringLiteral("core"))); + QVERIFY(!window.glslVersion().contains(QStringLiteral("es"))); + } else if (format.renderableType() == QSurfaceFormat::OpenGLES) { + if (format.majorVersion() == 2) + QCOMPARE(window.glslVersion(), QString()); + else + QVERIFY(window.glslVersion().contains(QStringLiteral("es"))); + } +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" diff --git a/tests/auto/quick/rendernode/data/matrix.qml b/tests/auto/quick/rendernode/data/matrix.qml new file mode 100644 index 0000000000..8b721e5075 --- /dev/null +++ b/tests/auto/quick/rendernode/data/matrix.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import RenderNode 1.0 + +Item { + width: 320 + height: 480 + + Item { x: 10; y: 10; width: 10; height: 10; + StateRecorder { x: 10; y: 10; objectName: "no-clip; no-rotation"; } + } + + Item { x: 10; y: 10; width: 10; height: 10; clip: true + StateRecorder { x: 10; y: 10; objectName: "parent-clip; no-rotation"; } + } + + Item { x: 10; y: 10; width: 10; height: 10; + StateRecorder { x: 10; y: 10; objectName: "self-clip; no-rotation"; clip: true } + } + + + Item { x: 10; y: 10; width: 10; height: 10; rotation: 90 + StateRecorder { x: 10; y: 10; objectName: "no-clip; parent-rotation"; } + } + + Item { x: 10; y: 10; width: 10; height: 10; clip: true; rotation: 90 + StateRecorder { x: 10; y: 10; objectName: "parent-clip; parent-rotation"; } + } + + Item { x: 10; y: 10; width: 10; height: 10; rotation: 90 + StateRecorder { x: 10; y: 10; objectName: "self-clip; parent-rotation"; clip: true } + } + + + Item { x: 10; y: 10; width: 10; height: 10; + StateRecorder { x: 10; y: 10; objectName: "no-clip; self-rotation"; rotation: 90 } + } + + Item { x: 10; y: 10; width: 10; height: 10; clip: true; + StateRecorder { x: 10; y: 10; objectName: "parent-clip; self-rotation"; rotation: 90} + } + + Item { x: 10; y: 10; width: 10; height: 10; + StateRecorder { x: 10; y: 10; objectName: "self-clip; self-rotation"; clip: true; rotation: 90 } + } + +} diff --git a/tests/auto/quick/rendernode/rendernode.pro b/tests/auto/quick/rendernode/rendernode.pro index b55b7b0bec..bedcefde86 100644 --- a/tests/auto/quick/rendernode/rendernode.pro +++ b/tests/auto/quick/rendernode/rendernode.pro @@ -14,4 +14,6 @@ QT += core-private gui-private qml-private quick-private testlib OTHER_FILES += \ data/RenderOrder.qml \ data/MessUpState.qml \ + data/matrix.qml + DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quick/rendernode/tst_rendernode.cpp b/tests/auto/quick/rendernode/tst_rendernode.cpp index 06338e09e2..98e31329a0 100644 --- a/tests/auto/quick/rendernode/tst_rendernode.cpp +++ b/tests/auto/quick/rendernode/tst_rendernode.cpp @@ -72,6 +72,7 @@ public: private slots: void renderOrder(); void messUpState(); + void matrix(); }; class ClearNode : public QSGRenderNode @@ -261,6 +262,84 @@ void tst_rendernode::messUpState() QCOMPARE(fb.pixel(x2, y5), qRgb(0x00, 0x00, 0x00)); } +class StateRecordingRenderNode : public QSGRenderNode +{ +public: + StateFlags changedStates() { return StateFlags(-1); } + void render(const RenderState &) { + matrices[name] = *matrix(); + + } + + QString name; + static QHash<QString, QMatrix4x4> matrices; +}; + +QHash<QString, QMatrix4x4> StateRecordingRenderNode::matrices; + +class StateRecordingRenderNodeItem : public QQuickItem +{ + Q_OBJECT +public: + StateRecordingRenderNodeItem() { setFlag(ItemHasContents, true); } + QSGNode *updatePaintNode(QSGNode *r, UpdatePaintNodeData *) { + if (r) + return r; + StateRecordingRenderNode *rn = new StateRecordingRenderNode(); + rn->name = objectName(); + return rn; + } +}; + +void tst_rendernode::matrix() +{ + qmlRegisterType<StateRecordingRenderNodeItem>("RenderNode", 1, 0, "StateRecorder"); + StateRecordingRenderNode::matrices.clear(); + runTest("matrix.qml"); + + QMatrix4x4 noRotateOffset; + noRotateOffset.translate(20, 20); + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("no-clip; no-rotation")); + QCOMPARE(result, noRotateOffset); + } + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("parent-clip; no-rotation")); + QCOMPARE(result, noRotateOffset); + } + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("self-clip; no-rotation")); + QCOMPARE(result, noRotateOffset); + } + + QMatrix4x4 parentRotation; + parentRotation.translate(10, 10); // parent at x/y: 10 + parentRotation.translate(5, 5); // rotate 90 around center (width/height: 10) + parentRotation.rotate(90, 0, 0, 1); + parentRotation.translate(-5, -5); + parentRotation.translate(10, 10); // StateRecorder at: x/y: 10 + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("no-clip; parent-rotation")); + QCOMPARE(result, parentRotation); + } + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("parent-clip; parent-rotation")); + QCOMPARE(result, parentRotation); + } + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("self-clip; parent-rotation")); + QCOMPARE(result, parentRotation); + } + + QMatrix4x4 selfRotation; + selfRotation.translate(10, 10); // parent at x/y: 10 + selfRotation.translate(10, 10); // StateRecorder at: x/y: 10 + selfRotation.rotate(90, 0, 0, 1); // rotate 90, width/height: 0 + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("no-clip; self-rotation")); + QCOMPARE(result, selfRotation); + } + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("parent-clip; self-rotation")); + QCOMPARE(result, selfRotation); + } + { QMatrix4x4 result = StateRecordingRenderNode::matrices.value(QStringLiteral("self-clip; self-rotation")); + QCOMPARE(result, selfRotation); + } +} + QTEST_MAIN(tst_rendernode) diff --git a/tests/auto/quick/shared/viewtestutil.cpp b/tests/auto/quick/shared/viewtestutil.cpp index 0e3964d52d..aa6d7c4a93 100644 --- a/tests/auto/quick/shared/viewtestutil.cpp +++ b/tests/auto/quick/shared/viewtestutil.cpp @@ -47,6 +47,9 @@ #include <QtTest/QTest> +#include <private/qquickwindow_p.h> + + QQuickView *QQuickViewTestUtil::createView() { QQuickView *window = new QQuickView(0); @@ -341,3 +344,23 @@ QList<QPair<QString,QString> > QQuickViewTestUtil::ListRange::getModelDataValues return data; } +namespace QQuickTouchUtils { + + /* QQuickWindow does event compression and only delivers events just + * before it is about to render the next frame. Since some tests + * rely on events being delivered immediately AND that no other + * event processing has occurred in the meanwhile, we flush the + * event manually and immediately. + */ + void flush(QQuickWindow *window) { + if (!window) + return; + QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); + if (!wd || !wd->delayedTouch) + return; + wd->reallyDeliverTouchEvent(wd->delayedTouch); + delete wd->delayedTouch; + wd->delayedTouch = 0; + } + +} diff --git a/tests/auto/quick/shared/viewtestutil.h b/tests/auto/quick/shared/viewtestutil.h index 5b0b10b69c..e10966ddba 100644 --- a/tests/auto/quick/shared/viewtestutil.h +++ b/tests/auto/quick/shared/viewtestutil.h @@ -166,6 +166,10 @@ namespace QQuickViewTestUtil } } +namespace QQuickTouchUtils { + void flush(QQuickWindow *window); +} + Q_DECLARE_METATYPE(QQuickViewTestUtil::QaimModel*) Q_DECLARE_METATYPE(QQuickViewTestUtil::ListChange) Q_DECLARE_METATYPE(QList<QQuickViewTestUtil::ListChange>) diff --git a/tests/auto/quick/touchmouse/touchmouse.pro b/tests/auto/quick/touchmouse/touchmouse.pro index 445bee08ae..7d23dfc0ae 100644 --- a/tests/auto/quick/touchmouse/touchmouse.pro +++ b/tests/auto/quick/touchmouse/touchmouse.pro @@ -8,6 +8,7 @@ macx:CONFIG -= app_bundle SOURCES += tst_touchmouse.cpp include (../../shared/util.pri) +include (../shared/util.pri) TESTDATA = data/* diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index 5b4ad0ffa3..1d947b4d8c 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -58,6 +58,7 @@ #include <QtQml/qqmlproperty.h> #include "../../shared/util.h" +#include "../shared/viewtestutil.h" struct Event { @@ -221,12 +222,15 @@ void tst_TouchMouse::simpleTouchEvent() QPoint p1; p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 1); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 1); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 1); eventItem1->eventList.clear(); @@ -234,11 +238,14 @@ void tst_TouchMouse::simpleTouchEvent() eventItem1->acceptTouch = true; p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 1); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 3); eventItem1->eventList.clear(); @@ -251,6 +258,7 @@ void tst_TouchMouse::simpleTouchEvent() eventItem1->setAcceptedMouseButtons(Qt::LeftButton); p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); @@ -268,10 +276,12 @@ void tst_TouchMouse::simpleTouchEvent() p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 4); QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchUpdate); QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseMove); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 6); QCOMPARE(eventItem1->eventList.at(4).type, QEvent::TouchEnd); QCOMPARE(eventItem1->eventList.at(5).type, QEvent::MouseButtonRelease); @@ -286,13 +296,16 @@ void tst_TouchMouse::simpleTouchEvent() eventItem1->setAcceptedMouseButtons(Qt::LeftButton); p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); eventItem1->eventList.clear(); @@ -304,13 +317,16 @@ void tst_TouchMouse::simpleTouchEvent() eventItem1->setAcceptedMouseButtons(Qt::LeftButton); p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 1); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::TouchUpdate); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 3); QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd); eventItem1->eventList.clear(); @@ -376,6 +392,7 @@ void tst_TouchMouse::mouse() // item 2 doesn't accept anything, thus it sees a touch pass by QPoint p1 = QPoint(30, 30); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); @@ -412,14 +429,17 @@ void tst_TouchMouse::touchOverMouse() QCOMPARE(eventItem1->eventList.size(), 0); QPoint p1 = QPoint(20, 20); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 0); QCOMPARE(eventItem2->eventList.size(), 1); QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); p1 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem2->eventList.size(), 2); QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem2->eventList.size(), 3); QCOMPARE(eventItem2->eventList.at(2).type, QEvent::TouchEnd); eventItem2->eventList.clear(); @@ -456,6 +476,7 @@ void tst_TouchMouse::mouseOverTouch() QPoint p1 = QPoint(20, 20); QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 10); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 0); QCOMPARE(eventItem2->eventList.size(), 2); QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); @@ -510,10 +531,12 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(eventItem1->eventList.size(), 0); QPoint p1 = QPoint(20, 130); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QTRY_COMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 4); QCOMPARE(eventItem1->eventList.at(2).type, QEvent::TouchEnd); QCOMPARE(eventItem1->eventList.at(3).type, QEvent::MouseButtonRelease); @@ -522,9 +545,11 @@ void tst_TouchMouse::buttonOnFlickable() // touch button p1 = QPoint(10, 310); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem2->eventList.size(), 1); QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem2->eventList.size(), 2); QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchEnd); QCOMPARE(eventItem1->eventList.size(), 0); @@ -536,8 +561,10 @@ void tst_TouchMouse::buttonOnFlickable() // click above button, no events please p1 = QPoint(10, 90); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 0); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 0); eventItem1->eventList.clear(); @@ -548,6 +575,7 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(eventItem1->eventList.size(), 0); p1 = QPoint(10, 110); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); @@ -560,12 +588,13 @@ void tst_TouchMouse::buttonOnFlickable() p1 += QPoint(0, -10); QPoint p2 = p1 + QPoint(0, -10); QPoint p3 = p2 + QPoint(0, -10); - QTest::qWait(10); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).move(0, p1, window); - QTest::qWait(10); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).move(0, p2, window); - QTest::qWait(10); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).move(0, p3, window); + QQuickTouchUtils::flush(window); // we cannot really know when the events get grabbed away QVERIFY(eventItem1->eventList.size() >= 4); @@ -578,6 +607,7 @@ void tst_TouchMouse::buttonOnFlickable() QVERIFY(flickable->isMovingVertically()); QTest::touchEvent(window, device).release(0, p3, window); + QQuickTouchUtils::flush(window); delete window; } @@ -625,6 +655,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QCOMPARE(eventItem1->eventList.size(), 0); QPoint p1 = QPoint(10, 110); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); // Flickable initially steals events QCOMPARE(eventItem1->eventList.size(), 0); // but we'll get the delayed mouse press after a delay @@ -641,12 +672,13 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() p1 += QPoint(0, -10); QPoint p2 = p1 + QPoint(0, -10); QPoint p3 = p2 + QPoint(0, -10); - QTest::qWait(10); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).move(0, p1, window); - QTest::qWait(10); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).move(0, p2, window); - QTest::qWait(10); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).move(0, p3, window); + QQuickTouchUtils::flush(window); QVERIFY(flickable->isMovingVertically()); // flickable should have the mouse grab, and have moved the itemForTouchPointId @@ -656,6 +688,7 @@ void tst_TouchMouse::buttonOnDelayedPressFlickable() QCOMPARE(windowPriv->itemForTouchPointId[0], flickable); QTest::touchEvent(window, device).release(0, p3, window); + QQuickTouchUtils::flush(window); // We should not have received any synthesised mouse events from Qt gui. QCOMPARE(filteredEventList.count(), 0); @@ -709,7 +742,9 @@ void tst_TouchMouse::buttonOnTouch() // Normal touch click QPoint p1 = QPoint(10, 110); QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(eventItem1->eventList.size(), 4); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); QCOMPARE(eventItem1->eventList.at(1).type, QEvent::MouseButtonPress); @@ -730,7 +765,9 @@ void tst_TouchMouse::buttonOnTouch() // Start the events after each other QTest::touchEvent(window, device).press(0, p1, window); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).stationary(0).press(1, p2, window); + QQuickTouchUtils::flush(window); QCOMPARE(button1->scale(), 1.0); @@ -738,20 +775,24 @@ void tst_TouchMouse::buttonOnTouch() p1 -= QPoint(10, 0); p2 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); // QCOMPARE(button1->scale(), 1.5); qDebug() << "Button scale: " << button1->scale(); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); // QCOMPARE(button1->scale(), 2.0); qDebug() << "Button scale: " << button1->scale(); QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window); + QQuickTouchUtils::flush(window); // QVERIFY(eventItem1->eventList.isEmpty()); // QCOMPARE(button1->scale(), 2.0); qDebug() << "Button scale: " << button1->scale(); @@ -765,6 +806,7 @@ void tst_TouchMouse::buttonOnTouch() p1 = QPoint(40, 110); p2 = QPoint(60, 110); QTest::touchEvent(window, device).press(0, p1, window).press(1, p2, window); + QQuickTouchUtils::flush(window); QCOMPARE(button1->scale(), 1.0); QCOMPARE(eventItem1->eventList.count(), 2); QCOMPARE(eventItem1->eventList.at(0).type, QEvent::TouchBegin); @@ -774,20 +816,24 @@ void tst_TouchMouse::buttonOnTouch() p1 -= QPoint(10, 0); p2 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); //QCOMPARE(button1->scale(), 1.5); qDebug() << button1->scale(); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); QTest::touchEvent(window, device).move(0, p1, window).move(1, p2, window); + QQuickTouchUtils::flush(window); qDebug() << button1->scale(); //QCOMPARE(button1->scale(), 2.0); QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window); + QQuickTouchUtils::flush(window); // QCOMPARE(eventItem1->eventList.size(), 99); qDebug() << button1->scale(); //QCOMPARE(button1->scale(), 2.0); @@ -816,18 +862,22 @@ void tst_TouchMouse::pinchOnFlickable() QVERIFY(flickable->contentX() == 0.0); QPoint p = QPoint(100, 100); QTest::touchEvent(window, device).press(0, p, window); + QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); - QTest::qWait(10); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); - QTest::qWait(10); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p, window); + QQuickTouchUtils::flush(window); QGuiApplication::processEvents(); QTest::qWait(10); @@ -840,27 +890,35 @@ void tst_TouchMouse::pinchOnFlickable() QPoint p2 = QPoint(60, 20); QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); + QQuickTouchUtils::flush(window); pinchSequence.press(0, p1, window).commit(); + QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. Otherwise if we let it // be destroyed and then start a new sequence, point 0 will default to being // stationary at 0, 0, and PinchArea will filter out that touchpoint because // it is outside its bounds. pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(rect->scale(), 1.0); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QVERIFY(rect->scale() > 1.0); } @@ -885,17 +943,22 @@ void tst_TouchMouse::flickableOnPinch() QVERIFY(flickable->contentX() == 0.0); QPoint p = QPoint(100, 100); QTest::touchEvent(window, device).press(0, p, window); + QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); QTest::qWait(1000); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p, window); + QQuickTouchUtils::flush(window); QTest::qWait(1000); @@ -909,26 +972,33 @@ void tst_TouchMouse::flickableOnPinch() QPoint p2 = QPoint(60, 20); QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.press(0, p1, window).commit(); + QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. Otherwise if we let it // be destroyed and then start a new sequence, point 0 will default to being // stationary at 0, 0, and PinchArea will filter out that touchpoint because // it is outside its bounds. pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(rect->scale(), 1.0); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QVERIFY(rect->scale() > 1.0); } @@ -953,16 +1023,19 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QVERIFY(flickable->contentX() == 0.0); QPoint p = QPoint(100, 100); QTest::touchEvent(window, device).press(0, p, window); + QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); - QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); QTest::touchEvent(window, device).move(0, p, window); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p, window); - QGuiApplication::processEvents(); + QQuickTouchUtils::flush(window); //QVERIFY(flickable->isMovingHorizontally()); @@ -975,26 +1048,33 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QPoint p2 = QPoint(60, 20); QTest::QTouchEventSequence pinchSequence = QTest::touchEvent(window, device); pinchSequence.press(0, p1, window).commit(); + QQuickTouchUtils::flush(window); // In order for the stationary point to remember its previous position, // we have to reuse the same pinchSequence object. Otherwise if we let it // be destroyed and then start a new sequence, point 0 will default to being // stationary at 0, 0, and PinchArea will filter out that touchpoint because // it is outside its bounds. pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10,10); p2 += QPoint(10,10); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(rect->scale(), 1.0); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(10, 0); p2 += QPoint(10, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QVERIFY(rect->scale() > 1.0); // PinchArea should steal the event after flicking started @@ -1002,14 +1082,18 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() flickable->setContentX(0.0); p = QPoint(100, 100); pinchSequence.press(0, p, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(rect->position(), QPointF(200.0, 200.0)); p -= QPoint(10, 0); pinchSequence.move(0, p, window).commit(); + QQuickTouchUtils::flush(window); p -= QPoint(10, 0); pinchSequence.move(0, p, window).commit(); + QQuickTouchUtils::flush(window); QGuiApplication::processEvents(); p -= QPoint(10, 0); pinchSequence.move(0, p, window).commit(); + QQuickTouchUtils::flush(window); QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window); qDebug() << "Mouse Grabber: " << windowPriv->mouseGrabberItem << " itemForTouchPointId: " << windowPriv->itemForTouchPointId; @@ -1019,20 +1103,26 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() p1 = QPoint(40, 100); p2 = QPoint(60, 100); pinchSequence.stationary(0).press(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QCOMPARE(rect->scale(), 1.0); p1 -= QPoint(5, 0); p2 += QPoint(5, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(5, 0); p2 += QPoint(5, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); p1 -= QPoint(5, 0); p2 += QPoint(5, 0); pinchSequence.move(0, p1, window).move(1, p2, window).commit(); + QQuickTouchUtils::flush(window); pinchSequence.release(0, p1, window).release(1, p2, window).commit(); + QQuickTouchUtils::flush(window); QVERIFY(rect->scale() > 1.0); pinchSequence.release(0, p, window).commit(); + QQuickTouchUtils::flush(window); } /* @@ -1066,16 +1156,17 @@ void tst_TouchMouse::tapOnDismissiveTopMouseAreaClicksBottomOne() // tap the front mouse area (see qml file) QPoint p1(20, 20); QTest::touchEvent(window, device).press(0, p1, window); - QTest::qWait(1); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(bottomClickedSpy.count(), 1); QCOMPARE(bottomDoubleClickedSpy.count(), 0); - QTest::qWait(15); QTest::touchEvent(window, device).press(0, p1, window); - QTest::qWait(1); + QQuickTouchUtils::flush(window); QTest::touchEvent(window, device).release(0, p1, window); + QQuickTouchUtils::flush(window); QCOMPARE(bottomClickedSpy.count(), 1); QCOMPARE(bottomDoubleClickedSpy.count(), 1); diff --git a/tests/auto/shared/testhttpserver.cpp b/tests/auto/shared/testhttpserver.cpp index d3de584084..231f22b35b 100644 --- a/tests/auto/shared/testhttpserver.cpp +++ b/tests/auto/shared/testhttpserver.cpp @@ -71,7 +71,8 @@ slowFiles/slowMain.qml \endcode it can be added like this: \code -TestHTTPServer server(14445); +TestHTTPServer server; +QVERIFY2(server.listen(14445), qPrintable(server.errorString())); server.serveDirectory("disconnect", TestHTTPServer::Disconnect); server.serveDirectory("files"); server.serveDirectory("slowFiles", TestHTTPServer::Delay); @@ -87,17 +88,21 @@ The following request urls will then result in the appropriate action: \row \li http://localhost:14445/slowMain.qml \li slowMain.qml returned after 500ms \endtable */ -TestHTTPServer::TestHTTPServer(quint16 port) +TestHTTPServer::TestHTTPServer() : m_state(AwaitingHeader) { QObject::connect(&server, SIGNAL(newConnection()), this, SLOT(newConnection())); - server.listen(QHostAddress::LocalHost, port); } -bool TestHTTPServer::isValid() const +bool TestHTTPServer::listen(quint16 port) { - return server.isListening(); + return server.listen(QHostAddress::LocalHost, port); +} + +QString TestHTTPServer::errorString() const +{ + return server.errorString(); } bool TestHTTPServer::serveDirectory(const QString &dir, Mode mode) diff --git a/tests/auto/shared/testhttpserver.h b/tests/auto/shared/testhttpserver.h index ae7d137143..a71386ddec 100644 --- a/tests/auto/shared/testhttpserver.h +++ b/tests/auto/shared/testhttpserver.h @@ -51,9 +51,10 @@ class TestHTTPServer : public QObject { Q_OBJECT public: - TestHTTPServer(quint16 port); + TestHTTPServer(); - bool isValid() const; + bool listen(quint16 port); + QString errorString() const; enum Mode { Normal, Delay, Disconnect }; bool serveDirectory(const QString &, Mode = Normal); diff --git a/tests/manual/httpserver/main.cpp b/tests/manual/httpserver/main.cpp index ea729547ce..4ad44508b0 100644 --- a/tests/manual/httpserver/main.cpp +++ b/tests/manual/httpserver/main.cpp @@ -112,7 +112,11 @@ int main(int argc, char *argv[]) << "\":\n\n" << QDir(directory).entryList(QDir::Files).join(QLatin1Char('\n')) << "\n\non http://localhost:" << port << '\n'; - TestHTTPServer server(port); + TestHTTPServer server; + if (!server.listen(port)) { + std::wcout << "Couldn't listen on port " << port << server.errorString().toLocal8Bit(); + exit(-1); + } server.serveDirectory(directory); return a.exec(); diff --git a/tools/qmleasing/qmleasing.pro b/tools/qmleasing/qmleasing.pro index eadcb304c4..e334faa77a 100644 --- a/tools/qmleasing/qmleasing.pro +++ b/tools/qmleasing/qmleasing.pro @@ -1,5 +1,4 @@ QT += qml quick widgets -CONFIG -= app_bundle SOURCES += main.cpp \ splineeditor.cpp \ @@ -17,3 +16,5 @@ FORMS += \ properties.ui \ pane.ui \ import.ui + +load(qt_app) |