Merge remote-tracking branch 'origin/5.11' into dev
Change-Id: I4a9c7802c180757e70fa4dd16df3287104a088bc
This commit is contained in:
commit
f0f01cc379
|
@ -600,6 +600,7 @@ void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGen
|
|||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
return;
|
||||
}
|
||||
n->activateMaterial(m_item->window(), gradMat);
|
||||
if (d->effectiveDirty & DirtyFillGradient) {
|
||||
|
|
|
@ -784,9 +784,10 @@ bool Codegen::visit(BinaryExpression *ast)
|
|||
left = left.asLValue();
|
||||
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
|
||||
return false;
|
||||
expression(ast->right).loadInAccumulator();
|
||||
Reference r = expression(ast->right);
|
||||
if (hasError)
|
||||
return false;
|
||||
r.loadInAccumulator();
|
||||
if (_expr.accept(nx))
|
||||
_expr.setResult(left.storeConsumeAccumulator());
|
||||
else
|
||||
|
|
|
@ -188,7 +188,7 @@ public:
|
|||
Const
|
||||
} type = Invalid;
|
||||
|
||||
bool isLValue() const { return !isReadonly; }
|
||||
bool isLValue() const { return !isReadonly && type > Accumulator; }
|
||||
|
||||
Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {}
|
||||
Reference() {}
|
||||
|
|
|
@ -257,29 +257,32 @@ struct Context {
|
|||
usedVariables.insert(name);
|
||||
}
|
||||
|
||||
void addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
|
||||
bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
|
||||
{
|
||||
if (! name.isEmpty()) {
|
||||
if (type != FunctionDefinition) {
|
||||
for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
|
||||
if (it->name == name)
|
||||
return;
|
||||
}
|
||||
MemberMap::iterator it = members.find(name);
|
||||
if (it == members.end()) {
|
||||
Member m;
|
||||
m.type = type;
|
||||
m.function = function;
|
||||
m.scope = scope;
|
||||
members.insert(name, m);
|
||||
} else {
|
||||
Q_ASSERT(scope == (*it).scope);
|
||||
if ((*it).type <= type) {
|
||||
(*it).type = type;
|
||||
(*it).function = function;
|
||||
}
|
||||
}
|
||||
if (name.isEmpty())
|
||||
return true;
|
||||
|
||||
if (type != FunctionDefinition) {
|
||||
for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
|
||||
if (it->name == name)
|
||||
return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope);
|
||||
}
|
||||
MemberMap::iterator it = members.find(name);
|
||||
if (it != members.end()) {
|
||||
if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope)
|
||||
return false;
|
||||
if ((*it).type <= type) {
|
||||
(*it).type = type;
|
||||
(*it).function = function;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Member m;
|
||||
m.type = type;
|
||||
m.function = function;
|
||||
m.scope = scope;
|
||||
members.insert(name, m);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -215,14 +215,10 @@ bool ScanFunctions::visit(VariableDeclaration *ast)
|
|||
return false;
|
||||
}
|
||||
QString name = ast->name.toString();
|
||||
const Context::Member *m = nullptr;
|
||||
if (_context->memberInfo(name, &m)) {
|
||||
if (m->isLexicallyScoped() || ast->isLexicallyScoped()) {
|
||||
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
|
||||
return false;
|
||||
}
|
||||
if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
|
||||
_cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
|
||||
return false;
|
||||
}
|
||||
_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -397,16 +393,22 @@ bool ScanFunctions::visit(Block *ast) {
|
|||
|
||||
void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr)
|
||||
{
|
||||
if (_context) {
|
||||
_context->hasNestedFunctions = true;
|
||||
Context *outerContext = _context;
|
||||
enterEnvironment(ast, FunctionCode);
|
||||
|
||||
if (outerContext) {
|
||||
outerContext->hasNestedFunctions = true;
|
||||
// The identifier of a function expression cannot be referenced from the enclosing environment.
|
||||
if (expr)
|
||||
_context->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr);
|
||||
if (expr) {
|
||||
if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) {
|
||||
_cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (name == QLatin1String("arguments"))
|
||||
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
|
||||
outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
|
||||
}
|
||||
|
||||
enterEnvironment(ast, FunctionCode);
|
||||
if (formalsContainName(formals, QStringLiteral("arguments")))
|
||||
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
|
||||
|
||||
|
|
|
@ -297,6 +297,8 @@ qmlRegisterRevision<BaseType,1>("MyTypes", 1, 1);
|
|||
This is useful when deriving from base classes provided by other authors,
|
||||
e.g. when extending classes from the Qt Quick module.
|
||||
|
||||
\note The QML engine does not support revisions for properties or signals of
|
||||
grouped and attached property objects.
|
||||
|
||||
\section2 Registering Extension Objects
|
||||
|
||||
|
|
|
@ -75,11 +75,26 @@ component, which is accessible via QQuickView::rootObject():
|
|||
\endtable
|
||||
|
||||
This \c object is the instance of the \c MyItem.qml component that has been
|
||||
created. You can now modify the item's properties using QObject::setProperty()
|
||||
or QQmlProperty:
|
||||
created. You can now modify the item's properties using
|
||||
\l QObject::setProperty() or \l QQmlProperty::write():
|
||||
|
||||
\snippet qml/qtbinding/loading/main.cpp properties
|
||||
|
||||
The difference between \c QObject::setProperty() and \c QQmlProperty::write()
|
||||
is that the latter will also remove the binding in addition to setting the
|
||||
property value. For example, suppose the \c width assignment above had been a
|
||||
binding to \c height:
|
||||
|
||||
\code
|
||||
width: height
|
||||
\endcode
|
||||
|
||||
If the \c height of the \c Item changed after the
|
||||
\c {object->setProperty("width", 500)} call, the \c width would be updated
|
||||
again, as the binding remains active. However, if the \c height changes after the
|
||||
\c {QQmlProperty(object, "width").write(500)} call, the \c width will not be
|
||||
changed, as the binding does not exist anymore.
|
||||
|
||||
Alternatively, you can cast the object to its actual type and call methods with
|
||||
compile-time safety. In this case the base object of \c MyItem.qml is an
|
||||
\l Item, which is defined by the QQuickItem class:
|
||||
|
|
|
@ -96,7 +96,6 @@ void Heap::ArrayBuffer::init(size_t length)
|
|||
Object::init();
|
||||
data = QTypedArrayData<char>::allocate(length + 1);
|
||||
if (!data) {
|
||||
data = nullptr;
|
||||
internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
|
||||
return;
|
||||
}
|
||||
|
@ -113,7 +112,7 @@ void Heap::ArrayBuffer::init(const QByteArray& array)
|
|||
|
||||
void Heap::ArrayBuffer::destroy()
|
||||
{
|
||||
if (!data->ref.deref())
|
||||
if (data && !data->ref.deref())
|
||||
QTypedArrayData<char>::deallocate(data);
|
||||
Object::destroy();
|
||||
}
|
||||
|
|
|
@ -662,14 +662,13 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor)
|
|||
class ArrayElementLessThan
|
||||
{
|
||||
public:
|
||||
inline ArrayElementLessThan(ExecutionEngine *engine, Object *thisObject, const Value &comparefn)
|
||||
: m_engine(engine), thisObject(thisObject), m_comparefn(comparefn) {}
|
||||
inline ArrayElementLessThan(ExecutionEngine *engine, const Value &comparefn)
|
||||
: m_engine(engine), m_comparefn(comparefn) {}
|
||||
|
||||
bool operator()(Value v1, Value v2) const;
|
||||
|
||||
private:
|
||||
ExecutionEngine *m_engine;
|
||||
Object *thisObject;
|
||||
const Value &m_comparefn;
|
||||
};
|
||||
|
||||
|
@ -842,7 +841,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
|
|||
}
|
||||
|
||||
|
||||
ArrayElementLessThan lessThan(engine, thisObject, static_cast<const FunctionObject &>(comparefn));
|
||||
ArrayElementLessThan lessThan(engine, static_cast<const FunctionObject &>(comparefn));
|
||||
|
||||
Value *begin = thisObject->arrayData()->values.values;
|
||||
sortHelper(begin, begin + len, *begin, lessThan);
|
||||
|
|
|
@ -75,7 +75,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
|
|||
jsConstruct = QV4::FunctionObject::callAsConstructor;
|
||||
|
||||
Object::init();
|
||||
function = nullptr;
|
||||
this->scope.set(scope->engine(), scope->d());
|
||||
Scope s(scope->engine());
|
||||
ScopedFunctionObject f(s, this);
|
||||
|
@ -88,7 +87,6 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
|
|||
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
|
||||
|
||||
Object::init();
|
||||
function = nullptr;
|
||||
this->scope.set(scope->engine(), scope->d());
|
||||
Scope s(scope->engine());
|
||||
ScopedFunctionObject f(s, this);
|
||||
|
@ -101,8 +99,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function
|
|||
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
|
||||
|
||||
Object::init();
|
||||
this->function = function;
|
||||
function->compilationUnit->addref();
|
||||
setFunction(function);
|
||||
this->scope.set(scope->engine(), scope->d());
|
||||
Scope s(scope->engine());
|
||||
ScopedString name(s, function->name());
|
||||
|
@ -123,13 +120,18 @@ void Heap::FunctionObject::init()
|
|||
jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
|
||||
|
||||
Object::init();
|
||||
function = nullptr;
|
||||
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
|
||||
Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
|
||||
setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue());
|
||||
}
|
||||
|
||||
|
||||
void Heap::FunctionObject::setFunction(Function *f)
|
||||
{
|
||||
if (f) {
|
||||
function = f;
|
||||
function->compilationUnit->addref();
|
||||
}
|
||||
}
|
||||
void Heap::FunctionObject::destroy()
|
||||
{
|
||||
if (function)
|
||||
|
@ -347,20 +349,36 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
|
|||
{
|
||||
QV4::Scope scope(b);
|
||||
ScopedFunctionObject target(scope, thisObject);
|
||||
if (!target)
|
||||
if (!target || target->isBinding())
|
||||
return scope.engine->throwTypeError();
|
||||
|
||||
ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue());
|
||||
Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)nullptr);
|
||||
if (argc > 1) {
|
||||
boundArgs = MemberData::allocate(scope.engine, argc - 1);
|
||||
boundArgs->d()->values.size = argc - 1;
|
||||
for (uint i = 0, ei = static_cast<uint>(argc - 1); i < ei; ++i)
|
||||
|
||||
int nArgs = (argc - 1 >= 0) ? argc - 1 : 0;
|
||||
if (target->isBoundFunction()) {
|
||||
BoundFunction *bound = static_cast<BoundFunction *>(target.getPointer());
|
||||
Scoped<MemberData> oldArgs(scope, bound->boundArgs());
|
||||
boundThis = bound->boundThis();
|
||||
int oldSize = oldArgs->size();
|
||||
boundArgs = MemberData::allocate(scope.engine, oldSize + nArgs);
|
||||
boundArgs->d()->values.size = oldSize + nArgs;
|
||||
for (uint i = 0; i < static_cast<uint>(oldSize); ++i)
|
||||
boundArgs->set(scope.engine, i, oldArgs->data()[i]);
|
||||
for (uint i = 0; i < static_cast<uint>(nArgs); ++i)
|
||||
boundArgs->set(scope.engine, oldSize + i, argv[i + 1]);
|
||||
target = bound->target();
|
||||
} else if (nArgs) {
|
||||
boundArgs = MemberData::allocate(scope.engine, nArgs);
|
||||
boundArgs->d()->values.size = nArgs;
|
||||
for (uint i = 0, ei = static_cast<uint>(nArgs); i < ei; ++i)
|
||||
boundArgs->set(scope.engine, i, argv[i + 1]);
|
||||
}
|
||||
|
||||
ExecutionContext *global = scope.engine->rootContext();
|
||||
return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue();
|
||||
ScopedContext ctx(scope, target->scope());
|
||||
Heap::BoundFunction *bound = BoundFunction::create(ctx, target, boundThis, boundArgs);
|
||||
bound->setFunction(target->function());
|
||||
return bound->asReturnedValue();
|
||||
}
|
||||
|
||||
DEFINE_OBJECT_VTABLE(ScriptFunction);
|
||||
|
@ -392,8 +410,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
|
|||
FunctionObject::init();
|
||||
this->scope.set(scope->engine(), scope->d());
|
||||
|
||||
this->function = function;
|
||||
function->compilationUnit->addref();
|
||||
setFunction(function);
|
||||
Q_ASSERT(function);
|
||||
Q_ASSERT(function->code);
|
||||
|
||||
|
|
|
@ -90,6 +90,8 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) {
|
|||
void init();
|
||||
void destroy();
|
||||
|
||||
void setFunction(Function *f);
|
||||
|
||||
unsigned int formalParameterCount() { return function ? function->nFormals : 0; }
|
||||
unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; }
|
||||
|
||||
|
|
|
@ -637,7 +637,7 @@ struct Stringify
|
|||
Stringify(ExecutionEngine *e) : v4(e), replacerFunction(nullptr), propertyList(nullptr), propertyListSize(0) {}
|
||||
|
||||
QString Str(const QString &key, const Value &v);
|
||||
QString JA(ArrayObject *a);
|
||||
QString JA(Object *a);
|
||||
QString JO(Object *o);
|
||||
|
||||
QString makeMember(const QString &key, const Value &v);
|
||||
|
@ -743,8 +743,8 @@ QString Stringify::Str(const QString &key, const Value &v)
|
|||
o = value->asReturnedValue();
|
||||
if (o) {
|
||||
if (!o->as<FunctionObject>()) {
|
||||
if (o->as<ArrayObject>()) {
|
||||
return JA(static_cast<ArrayObject *>(o.getPointer()));
|
||||
if (o->as<ArrayObject>() || o->isListType()) {
|
||||
return JA(o.getPointer());
|
||||
} else {
|
||||
return JO(o);
|
||||
}
|
||||
|
@ -827,7 +827,7 @@ QString Stringify::JO(Object *o)
|
|||
return result;
|
||||
}
|
||||
|
||||
QString Stringify::JA(ArrayObject *a)
|
||||
QString Stringify::JA(Object *a)
|
||||
{
|
||||
if (stackContains(a)) {
|
||||
v4->throwTypeError();
|
||||
|
|
|
@ -1092,9 +1092,7 @@ bool Object::setArrayLength(uint newLen)
|
|||
uint oldLen = getLength();
|
||||
bool ok = true;
|
||||
if (newLen < oldLen) {
|
||||
if (!arrayData()) {
|
||||
Q_ASSERT(!newLen);
|
||||
} else {
|
||||
if (arrayData()) {
|
||||
uint l = arrayData()->vtable()->truncate(this, newLen);
|
||||
if (l != newLen)
|
||||
ok = false;
|
||||
|
|
|
@ -466,9 +466,12 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
|
|||
|
||||
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
|
||||
|
||||
QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
|
||||
QV4::ScopedContext ctx(scope, bindingFunction->scope());
|
||||
newBinding = QQmlBinding::create(property, bindingFunction->function(), object, callingQmlContext, ctx);
|
||||
newBinding = QQmlBinding::create(property, f->function(), object, callingQmlContext, ctx);
|
||||
newBinding->setSourceLocation(bindingFunction->currentLocation());
|
||||
if (f->isBoundFunction())
|
||||
newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
|
||||
newBinding->setTarget(object, *property, nullptr);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1271,7 +1271,9 @@ QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine
|
|||
|
||||
ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
|
||||
{
|
||||
return engine->qmlContext()->asReturnedValue();
|
||||
Heap::QmlContext *ctx = engine->qmlContext();
|
||||
Q_ASSERT(ctx);
|
||||
return ctx->asReturnedValue();
|
||||
}
|
||||
|
||||
ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id)
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include <private/qqmlvaluetypewrapper_p.h>
|
||||
#include <private/qv4qobjectwrapper_p.h>
|
||||
#include <private/qv4variantobject_p.h>
|
||||
#include <private/qv4jscall_p.h>
|
||||
|
||||
#include <QVariant>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
@ -97,6 +98,21 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QQmlScr
|
|||
return b;
|
||||
}
|
||||
|
||||
QQmlSourceLocation QQmlBinding::sourceLocation() const
|
||||
{
|
||||
if (m_sourceLocation)
|
||||
return *m_sourceLocation;
|
||||
return QQmlJavaScriptExpression::sourceLocation();
|
||||
}
|
||||
|
||||
void QQmlBinding::setSourceLocation(const QQmlSourceLocation &location)
|
||||
{
|
||||
if (m_sourceLocation)
|
||||
delete m_sourceLocation;
|
||||
m_sourceLocation = new QQmlSourceLocation(location);
|
||||
}
|
||||
|
||||
|
||||
QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, const QString &str, QObject *obj,
|
||||
QQmlContextData *ctxt, const QString &url, quint16 lineNumber)
|
||||
{
|
||||
|
@ -128,6 +144,7 @@ QQmlBinding *QQmlBinding::create(const QQmlPropertyData *property, QV4::Function
|
|||
|
||||
QQmlBinding::~QQmlBinding()
|
||||
{
|
||||
delete m_sourceLocation;
|
||||
}
|
||||
|
||||
void QQmlBinding::setNotifyOnValueChanged(bool v)
|
||||
|
@ -171,6 +188,28 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags)
|
|||
setUpdatingFlag(false);
|
||||
}
|
||||
|
||||
QV4::ReturnedValue QQmlBinding::evaluate(bool *isUndefined)
|
||||
{
|
||||
QV4::ExecutionEngine *v4 = context()->engine->handle();
|
||||
int argc = 0;
|
||||
const QV4::Value *argv = nullptr;
|
||||
const QV4::Value *thisObject = nullptr;
|
||||
QV4::BoundFunction *b = nullptr;
|
||||
if ((b = static_cast<QV4::BoundFunction *>(m_boundFunction.valueRef()))) {
|
||||
QV4::Heap::MemberData *args = b->boundArgs();
|
||||
if (args) {
|
||||
argc = args->values.size;
|
||||
argv = args->values.data();
|
||||
}
|
||||
thisObject = &b->d()->boundThis;
|
||||
}
|
||||
QV4::Scope scope(v4);
|
||||
QV4::JSCallData jsCall(scope, argc, argv, thisObject);
|
||||
|
||||
return QQmlJavaScriptExpression::evaluate(jsCall.callData(), isUndefined);
|
||||
}
|
||||
|
||||
|
||||
// QQmlBindingBinding is for target properties which are of type "binding" (instead of, say, int or
|
||||
// double). The reason for being is that GenericBinding::fastWrite needs a compile-time constant
|
||||
// expression for the switch for the compiler to generate the optimal code, but
|
||||
|
@ -203,7 +242,7 @@ protected:
|
|||
|
||||
bool isUndefined = false;
|
||||
|
||||
QV4::ScopedValue result(scope, QQmlJavaScriptExpression::evaluate(&isUndefined));
|
||||
QV4::ScopedValue result(scope, evaluate(&isUndefined));
|
||||
|
||||
bool error = false;
|
||||
if (!watcher.wasDeleted() && isAddedToObject() && !hasError())
|
||||
|
@ -302,9 +341,14 @@ public:
|
|||
{
|
||||
setCompilationUnit(compilationUnit);
|
||||
m_binding = binding;
|
||||
setSourceLocation(QQmlSourceLocation(compilationUnit->fileName(), binding->valueLocation.line, binding->valueLocation.column));
|
||||
}
|
||||
|
||||
QQmlSourceLocation sourceLocation() const override final
|
||||
{
|
||||
return QQmlSourceLocation(m_compilationUnit->fileName(), m_binding->valueLocation.line, m_binding->valueLocation.column);
|
||||
}
|
||||
|
||||
|
||||
void doUpdate(const DeleteWatcher &watcher,
|
||||
QQmlPropertyData::WriteFlags flags, QV4::Scope &scope) override final
|
||||
{
|
||||
|
|
|
@ -104,6 +104,12 @@ public:
|
|||
QString expressionIdentifier() const override;
|
||||
void expressionChanged() override;
|
||||
|
||||
QQmlSourceLocation sourceLocation() const override;
|
||||
void setSourceLocation(const QQmlSourceLocation &location);
|
||||
void setBoundFunction(QV4::BoundFunction *boundFunction) {
|
||||
m_boundFunction.set(boundFunction->engine(), *boundFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a snapshot of the currently tracked dependencies of
|
||||
* this binding. The dependencies can change upon reevaluation. This method is
|
||||
|
@ -123,6 +129,8 @@ protected:
|
|||
bool slowWrite(const QQmlPropertyData &core, const QQmlPropertyData &valueTypeData,
|
||||
const QV4::Value &result, bool isUndefined, QQmlPropertyData::WriteFlags flags);
|
||||
|
||||
QV4::ReturnedValue evaluate(bool *isUndefined);
|
||||
|
||||
private:
|
||||
inline bool updatingFlag() const;
|
||||
inline void setUpdatingFlag(bool);
|
||||
|
@ -130,6 +138,9 @@ private:
|
|||
inline void setEnabledFlag(bool);
|
||||
|
||||
static QQmlBinding *newBinding(QQmlEnginePrivate *engine, const QQmlPropertyData *property);
|
||||
|
||||
QQmlSourceLocation *m_sourceLocation = nullptr; // used for Qt.binding() created functions
|
||||
QV4::PersistentValue m_boundFunction; // used for Qt.binding() that are created from a bound function object
|
||||
};
|
||||
|
||||
bool QQmlBinding::updatingFlag() const
|
||||
|
|
|
@ -97,8 +97,7 @@ QQmlJavaScriptExpression::QQmlJavaScriptExpression()
|
|||
m_context(nullptr),
|
||||
m_prevExpression(nullptr),
|
||||
m_nextExpression(nullptr),
|
||||
m_v4Function(nullptr),
|
||||
m_sourceLocation(nullptr)
|
||||
m_v4Function(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -115,8 +114,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression()
|
|||
clearError();
|
||||
if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion.
|
||||
m_scopeObject.asT2()->_s = nullptr;
|
||||
|
||||
delete m_sourceLocation;
|
||||
}
|
||||
|
||||
void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v)
|
||||
|
@ -137,20 +134,11 @@ void QQmlJavaScriptExpression::resetNotifyOnValueChanged()
|
|||
|
||||
QQmlSourceLocation QQmlJavaScriptExpression::sourceLocation() const
|
||||
{
|
||||
if (m_sourceLocation)
|
||||
return *m_sourceLocation;
|
||||
if (m_v4Function)
|
||||
return m_v4Function->sourceLocation();
|
||||
return QQmlSourceLocation();
|
||||
}
|
||||
|
||||
void QQmlJavaScriptExpression::setSourceLocation(const QQmlSourceLocation &location)
|
||||
{
|
||||
if (m_sourceLocation)
|
||||
delete m_sourceLocation;
|
||||
m_sourceLocation = new QQmlSourceLocation(location);
|
||||
}
|
||||
|
||||
void QQmlJavaScriptExpression::setContext(QQmlContextData *context)
|
||||
{
|
||||
if (m_prevExpression) {
|
||||
|
|
|
@ -116,8 +116,7 @@ public:
|
|||
inline QObject *scopeObject() const;
|
||||
inline void setScopeObject(QObject *v);
|
||||
|
||||
QQmlSourceLocation sourceLocation() const;
|
||||
void setSourceLocation(const QQmlSourceLocation &location);
|
||||
virtual QQmlSourceLocation sourceLocation() const;
|
||||
|
||||
bool isValid() const { return context() != nullptr; }
|
||||
|
||||
|
@ -188,7 +187,6 @@ private:
|
|||
QV4::PersistentValue m_qmlScope;
|
||||
QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
|
||||
QV4::Function *m_v4Function;
|
||||
QQmlSourceLocation *m_sourceLocation; // used for Qt.binding() created functions
|
||||
};
|
||||
|
||||
class QQmlPropertyCapture
|
||||
|
|
|
@ -388,6 +388,7 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value
|
|||
QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
|
||||
CompiledData::CompilationUnit *cu = td->compilationUnit();
|
||||
myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
|
||||
td->release();
|
||||
} else {
|
||||
myQmlType = qenginepriv->metaObjectForType(myTypeId);
|
||||
}
|
||||
|
|
|
@ -463,8 +463,12 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
|
|||
|
||||
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
|
||||
|
||||
QV4::ScopedContext ctx(scope, bindingFunction->scope());
|
||||
QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx);
|
||||
QV4::ScopedFunctionObject f(scope, bindingFunction->bindingFunction());
|
||||
QV4::ScopedContext ctx(scope, f->scope());
|
||||
QQmlBinding *newBinding = QQmlBinding::create(&cacheData, f->function(), referenceObject, context, ctx);
|
||||
newBinding->setSourceLocation(bindingFunction->currentLocation());
|
||||
if (f->isBoundFunction())
|
||||
newBinding->setBoundFunction(static_cast<QV4::BoundFunction *>(f.getPointer()));
|
||||
newBinding->setSourceLocation(bindingFunction->currentLocation());
|
||||
newBinding->setTarget(referenceObject, cacheData, pd);
|
||||
QQmlPropertyPrivate::setBinding(newBinding);
|
||||
|
|
|
@ -1350,11 +1350,12 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co
|
|||
}
|
||||
#endif
|
||||
|
||||
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *originalFunction)
|
||||
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction)
|
||||
{
|
||||
Scope scope(originalFunction->engine());
|
||||
ScopedContext context(scope, originalFunction->scope());
|
||||
FunctionObject::init(context, originalFunction->function());
|
||||
Scope scope(bindingFunction->engine());
|
||||
ScopedContext context(scope, bindingFunction->scope());
|
||||
FunctionObject::init(context, bindingFunction->function());
|
||||
this->bindingFunction.set(internalClass->engine, bindingFunction->d());
|
||||
}
|
||||
|
||||
QQmlSourceLocation QQmlBindingFunction::currentLocation() const
|
||||
|
|
|
@ -80,8 +80,11 @@ struct ConsoleObject : Object {
|
|||
void init();
|
||||
};
|
||||
|
||||
struct QQmlBindingFunction : FunctionObject {
|
||||
void init(const QV4::FunctionObject *originalFunction);
|
||||
#define QQmlBindingFunctionMembers(class, Member) \
|
||||
Member(class, Pointer, FunctionObject *, bindingFunction)
|
||||
DECLARE_HEAP_OBJECT(QQmlBindingFunction, FunctionObject) {
|
||||
DECLARE_MARKOBJECTS(QQmlBindingFunction)
|
||||
void init(const QV4::FunctionObject *bindingFunction);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -181,6 +184,7 @@ struct QQmlBindingFunction : public QV4::FunctionObject
|
|||
{
|
||||
V4_OBJECT2(QQmlBindingFunction, FunctionObject)
|
||||
|
||||
Heap::FunctionObject *bindingFunction() const { return d()->bindingFunction; }
|
||||
QQmlSourceLocation currentLocation() const; // from caller stack trace
|
||||
};
|
||||
|
||||
|
|
|
@ -182,4 +182,13 @@
|
|||
|
||||
#include "tst_mytest.moc"
|
||||
\endcode
|
||||
|
||||
\section1 Licenses
|
||||
|
||||
Qt Quick Tests is available under commercial licenses from \l{The Qt Company}.
|
||||
In addition, it is available under free software licenses. Since Qt 5.4,
|
||||
these free software licenses are
|
||||
\l{GNU Lesser General Public License, version 3}, or
|
||||
the \l{GNU General Public License, version 2}.
|
||||
See \l{Qt Licensing} for further details.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2018 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the documentation of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:BSD$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** BSD License Usage
|
||||
** Alternatively, 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 The Qt Company Ltd 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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//! [ext]
|
||||
// Assuming the "pics" directory contains the following files:
|
||||
// dog.jpg
|
||||
// cat.png
|
||||
// cat.pkm
|
||||
|
||||
Image {
|
||||
source: "pics/cat.png" // loads cat.png
|
||||
}
|
||||
|
||||
Image {
|
||||
source: "pics/dog" // loads dog.jpg
|
||||
}
|
||||
|
||||
Image {
|
||||
source: "pics/cat" // normally loads cat.pkm, but if no OpenGL, loads cat.png instead.
|
||||
}
|
||||
//! [ext]
|
|
@ -39,6 +39,15 @@
|
|||
|
||||
Visit the \l{Qt Quick Layouts Overview} page to get started.
|
||||
|
||||
\section1 Licenses
|
||||
|
||||
Qt Quick Layouts is available under commercial licenses from \l{The Qt Company}.
|
||||
In addition, it is available under free software licenses. Since Qt 5.4,
|
||||
these free software licenses are
|
||||
\l{GNU Lesser General Public License, version 3}, or
|
||||
the \l{GNU General Public License, version 2}.
|
||||
See \l{Qt Licensing} for further details.
|
||||
|
||||
\section1 Layouts
|
||||
|
||||
\annotatedlist layouts
|
||||
|
|
|
@ -135,6 +135,48 @@ QQuickImagePrivate::QQuickImagePrivate()
|
|||
|
||||
\clearfloat
|
||||
|
||||
\section1 OpenGL Texture Files
|
||||
|
||||
When the default OpenGL \l{Qt Quick Scene Graph}{scene graph} backend is in
|
||||
use, images can also be supplied in compressed texture files. The content
|
||||
must be a simple RGB(A) format 2D texture. Supported compression schemes
|
||||
are only limited by the underlying OpenGL driver and GPU. The following
|
||||
container file formats are supported:
|
||||
|
||||
\list
|
||||
\li \c PKM (since Qt 5.10)
|
||||
\li \c KTX (since Qt 5.11)
|
||||
\endlist
|
||||
|
||||
\note Semi-transparent original images require alpha pre-multiplication
|
||||
prior to texture compression in order to be correctly displayed in Qt
|
||||
Quick. This can be done with the following ImageMagick command
|
||||
line:
|
||||
\badcode
|
||||
convert MYORIGIMAGE \( +clone -alpha Extract \) -channel RGB -compose Multiply -composite MYPMIMAGE
|
||||
\endcode
|
||||
|
||||
\section1 Automatic Detection of File Extension
|
||||
|
||||
If the \l source URL indicates a non-existing local file or resource, the
|
||||
Image element attempts to auto-detect the file extension. If an existing
|
||||
file can be found by appending any of the supported image file extensions
|
||||
to the \l source URL, then that file will be loaded.
|
||||
|
||||
If the OpenGL \l{Qt Quick Scene Graph}{scene graph} backend is in use, the
|
||||
file search the attempts the OpenGL texture file extensions first. If the
|
||||
search is unsuccessful, it attempts to search with the file extensions for
|
||||
the \l{QImageReader::supportedImageFormats()}{conventional image file
|
||||
types}. For example:
|
||||
|
||||
\snippet qml/image-ext.qml ext
|
||||
|
||||
This functionality facilitates deploying different image asset file types
|
||||
on different target platforms. This can be useful in order to tune
|
||||
application performance and adapt to different graphics hardware.
|
||||
|
||||
This functionality was introduced in Qt 5.11.
|
||||
|
||||
\section1 Performance
|
||||
|
||||
By default, locally available images are loaded immediately, and the user interface
|
||||
|
@ -154,7 +196,7 @@ QQuickImagePrivate::QQuickImagePrivate()
|
|||
size bounded via the \l sourceSize property. This is especially important for content
|
||||
that is loaded from external sources or provided by the user.
|
||||
|
||||
\sa {Qt Quick Examples - Image Elements}, QQuickImageProvider
|
||||
\sa {Qt Quick Examples - Image Elements}, QQuickImageProvider, QImageReader::setAutoDetectImageFormat()
|
||||
*/
|
||||
|
||||
QQuickImage::QQuickImage(QQuickItem *parent)
|
||||
|
@ -461,7 +503,7 @@ qreal QQuickImage::paintedHeight() const
|
|||
|
||||
The URL may be absolute, or relative to the URL of the component.
|
||||
|
||||
\sa QQuickImageProvider
|
||||
\sa QQuickImageProvider {OpenGL Texture Files} {Automatic Detection of File Extension}
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
|
|
@ -2318,8 +2318,10 @@ void QQuickText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeo
|
|||
|
||||
if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
|
||||
if (newGeometry.height() > oldGeometry.height()) {
|
||||
if (!d->heightExceeded) // Height is adequate and growing.
|
||||
if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
|
||||
// Height is adequate and growing, and it wasn't 0 previously.
|
||||
goto geomChangeDone;
|
||||
}
|
||||
if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
|
||||
goto geomChangeDone;
|
||||
} else if (newGeometry.height() < oldGeometry.height()) {
|
||||
|
|
|
@ -129,6 +129,7 @@ private slots:
|
|||
void arraySort();
|
||||
void lookupOnDisappearingProperty();
|
||||
void arrayConcat();
|
||||
void recursiveBoundFunctions();
|
||||
|
||||
void qRegExpInport_data();
|
||||
void qRegExpInport();
|
||||
|
@ -3025,6 +3026,18 @@ void tst_QJSEngine::arrayConcat()
|
|||
QCOMPARE(v.toString(), QString::fromLatin1("6,10,11,12"));
|
||||
}
|
||||
|
||||
void tst_QJSEngine::recursiveBoundFunctions()
|
||||
{
|
||||
|
||||
QJSEngine eng;
|
||||
QJSValue v = eng.evaluate("function foo(x, y, z)"
|
||||
"{ return this + x + y + z; }"
|
||||
"var bar = foo.bind(-1, 10);"
|
||||
"var baz = bar.bind(-2, 20);"
|
||||
"baz(30)");
|
||||
QCOMPARE(v.toInt(), 59);
|
||||
}
|
||||
|
||||
static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
|
||||
|
||||
void tst_QJSEngine::qRegExpInport_data()
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import QtQuick 2.6
|
||||
|
||||
QtObject {
|
||||
property bool success: false
|
||||
property var num: 100
|
||||
property var simple: 0
|
||||
property var complex: 0
|
||||
|
||||
|
||||
Component.onCompleted: {
|
||||
function s(x) {
|
||||
return x
|
||||
}
|
||||
function c(x) {
|
||||
return x + num
|
||||
}
|
||||
|
||||
var bound = s.bind(undefined, 100)
|
||||
simple = Qt.binding(bound)
|
||||
if (simple != 100)
|
||||
return;
|
||||
var bound = c.bind(undefined, 100)
|
||||
complex = Qt.binding(bound);
|
||||
|
||||
if (complex != 200)
|
||||
return;
|
||||
num = 0;
|
||||
if (complex != 100)
|
||||
return;
|
||||
|
||||
print("success!!!");
|
||||
success = true;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,11 @@ Item {
|
|||
objectName: "msco"
|
||||
}
|
||||
|
||||
Component {
|
||||
id: mscoComponent
|
||||
MySequenceConversionObject { }
|
||||
}
|
||||
|
||||
property bool success: false
|
||||
|
||||
property variant intList
|
||||
|
@ -252,4 +257,13 @@ Item {
|
|||
if (testSequence.valueOf() == prevValueOf) referenceDeletion = false;
|
||||
if (testSequence.length == prevLength) referenceDeletion = false;
|
||||
}
|
||||
|
||||
function jsonConversion() {
|
||||
success = true
|
||||
var msco = mscoComponent.createObject()
|
||||
if (JSON.stringify(msco.intListProperty) != "[1,2,3,4]") success = false;
|
||||
if (JSON.stringify(msco.qrealListProperty) != "[1.1,2.2,3.3,4.4]") success = false;
|
||||
if (JSON.stringify(msco.boolListProperty) != "[true,false,true,false]") success = false;
|
||||
if (JSON.stringify(msco.stringListProperty) != "[\"first\",\"second\",\"third\",\"fourth\"]") success = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -289,6 +289,7 @@ private slots:
|
|||
void withStatement();
|
||||
void tryStatement();
|
||||
void replaceBinding();
|
||||
void bindingBoundFunctions();
|
||||
void deleteRootObjectInCreation();
|
||||
void onDestruction();
|
||||
void onDestructionViaGC();
|
||||
|
@ -5562,17 +5563,18 @@ void tst_qqmlecmascript::sequenceConversionArray()
|
|||
// ensure that in JS the returned sequences act just like normal JS Arrays.
|
||||
QUrl qmlFile = testFileUrl("sequenceConversion.array.qml");
|
||||
QQmlComponent component(&engine, qmlFile);
|
||||
QObject *object = component.create();
|
||||
QScopedPointer<QObject> object(component.create());
|
||||
QVERIFY(object != nullptr);
|
||||
QMetaObject::invokeMethod(object, "indexedAccess");
|
||||
QMetaObject::invokeMethod(object.data(), "indexedAccess");
|
||||
QVERIFY(object->property("success").toBool());
|
||||
QMetaObject::invokeMethod(object, "arrayOperations");
|
||||
QMetaObject::invokeMethod(object.data(), "arrayOperations");
|
||||
QVERIFY(object->property("success").toBool());
|
||||
QMetaObject::invokeMethod(object, "testEqualitySemantics");
|
||||
QMetaObject::invokeMethod(object.data(), "testEqualitySemantics");
|
||||
QVERIFY(object->property("success").toBool());
|
||||
QMetaObject::invokeMethod(object, "testReferenceDeletion");
|
||||
QMetaObject::invokeMethod(object.data(), "testReferenceDeletion");
|
||||
QCOMPARE(object->property("referenceDeletion").toBool(), true);
|
||||
delete object;
|
||||
QMetaObject::invokeMethod(object.data(), "jsonConversion");
|
||||
QVERIFY(object->property("success").toBool());
|
||||
}
|
||||
|
||||
|
||||
|
@ -7246,6 +7248,17 @@ void tst_qqmlecmascript::replaceBinding()
|
|||
delete obj;
|
||||
}
|
||||
|
||||
void tst_qqmlecmascript::bindingBoundFunctions()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent c(&engine, testFileUrl("bindingBoundFunctions.qml"));
|
||||
QObject *obj = c.create();
|
||||
QVERIFY(obj != nullptr);
|
||||
|
||||
QVERIFY(obj->property("success").toBool());
|
||||
delete obj;
|
||||
}
|
||||
|
||||
void tst_qqmlecmascript::deleteRootObjectInCreation()
|
||||
{
|
||||
{
|
||||
|
|
|
@ -86,6 +86,7 @@ tst_examples::tst_examples()
|
|||
excludedDirs << "snippets/qml/visualdatamodel_rootindex";
|
||||
excludedDirs << "snippets/qml/qtbinding";
|
||||
excludedDirs << "snippets/qml/imports";
|
||||
excludedFiles << "snippets/qml/image-ext.qml";
|
||||
excludedFiles << "examples/quick/shapes/content/main.qml"; // relies on resources
|
||||
excludedFiles << "examples/quick/shapes/content/interactive.qml"; // relies on resources
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import QtQuick 2.0
|
||||
|
||||
Item {
|
||||
width: 100
|
||||
height: 30
|
||||
|
||||
Text {
|
||||
width: parent ? parent.width : 0
|
||||
height: parent ? parent.height : 0
|
||||
elide: Text.ElideRight
|
||||
text: "wot"
|
||||
}
|
||||
}
|
|
@ -38,6 +38,7 @@
|
|||
#include <QFontMetrics>
|
||||
#include <qmath.h>
|
||||
#include <QtQuick/QQuickView>
|
||||
#include <QtQuick/qquickitemgrabresult.h>
|
||||
#include <private/qguiapplication_p.h>
|
||||
#include <limits.h>
|
||||
#include <QtGui/QMouseEvent>
|
||||
|
@ -64,6 +65,7 @@ private slots:
|
|||
void width();
|
||||
void wrap();
|
||||
void elide();
|
||||
void elideParentChanged();
|
||||
void multilineElide_data();
|
||||
void multilineElide();
|
||||
void implicitElide_data();
|
||||
|
@ -554,6 +556,45 @@ void tst_qquicktext::elide()
|
|||
}
|
||||
}
|
||||
|
||||
// QTBUG-60328
|
||||
// Tests that text with elide set is rendered after
|
||||
// having its parent cleared and then set again.
|
||||
void tst_qquicktext::elideParentChanged()
|
||||
{
|
||||
QQuickView window;
|
||||
window.setSource(testFileUrl("elideParentChanged.qml"));
|
||||
QTRY_COMPARE(window.status(), QQuickView::Ready);
|
||||
|
||||
window.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&window));
|
||||
|
||||
QQuickItem *root = window.rootObject();
|
||||
QVERIFY(root);
|
||||
QCOMPARE(root->childItems().size(), 1);
|
||||
|
||||
// Store a snapshot of the scene so that we can compare it later.
|
||||
QSharedPointer<QQuickItemGrabResult> grabResult = root->grabToImage();
|
||||
QTRY_VERIFY(!grabResult->image().isNull());
|
||||
const QImage expectedItemImageGrab(grabResult->image());
|
||||
|
||||
// Clear the text's parent. It shouldn't render anything.
|
||||
QQuickItem *text = root->childItems().first();
|
||||
text->setParentItem(nullptr);
|
||||
QCOMPARE(text->width(), 0.0);
|
||||
QCOMPARE(text->height(), 0.0);
|
||||
|
||||
// Set the parent back to what it was. The text should
|
||||
// be rendered identically to how it was before.
|
||||
text->setParentItem(root);
|
||||
QCOMPARE(text->width(), 100.0);
|
||||
QCOMPARE(text->height(), 30.0);
|
||||
|
||||
grabResult = root->grabToImage();
|
||||
QTRY_VERIFY(!grabResult->image().isNull());
|
||||
const QImage actualItemImageGrab(grabResult->image());
|
||||
QCOMPARE(actualItemImageGrab, expectedItemImageGrab);
|
||||
}
|
||||
|
||||
void tst_qquicktext::multilineElide_data()
|
||||
{
|
||||
QTest::addColumn<QQuickText::TextFormat>("format");
|
||||
|
|
Loading…
Reference in New Issue