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

Change-Id: I0ec164ce6e8099e6e4d6b40a3c7340737473ef4b
This commit is contained in:
Liang Qi 2017-03-14 10:49:51 +01:00
commit 12e82111ab
37 changed files with 832 additions and 146 deletions

View File

@ -57,9 +57,18 @@ void BirthdayParty::setHost(Person *c)
QQmlListProperty<Person> BirthdayParty::guests()
{
return QQmlListProperty<Person>(this, m_guests);
return QQmlListProperty<Person>(this, this,
&BirthdayParty::appendGuest,
&BirthdayParty::guestCount,
&BirthdayParty::guest,
&BirthdayParty::clearGuests);
}
void BirthdayParty::appendGuest(Person* p) {
m_guests.append(p);
}
int BirthdayParty::guestCount() const
{
return m_guests.count();
@ -69,5 +78,25 @@ Person *BirthdayParty::guest(int index) const
{
return m_guests.at(index);
}
void BirthdayParty::clearGuests() {
return m_guests.clear();
}
// ![0]
void BirthdayParty::appendGuest(QQmlListProperty<Person>* list, Person* p) {
reinterpret_cast< BirthdayParty* >(list->data)->appendGuest(p);
}
void BirthdayParty::clearGuests(QQmlListProperty<Person>* list) {
reinterpret_cast< BirthdayParty* >(list->data)->clearGuests();
}
Person* BirthdayParty::guest(QQmlListProperty<Person>* list, int i) {
return reinterpret_cast< BirthdayParty* >(list->data)->guest(i);
}
int BirthdayParty::guestCount(QQmlListProperty<Person>* list) {
return reinterpret_cast< BirthdayParty* >(list->data)->guestCount();
}

View File

@ -41,6 +41,7 @@
#define BIRTHDAYPARTY_H
#include <QObject>
#include <QVector>
#include <QQmlListProperty>
#include "person.h"
@ -63,12 +64,19 @@ public:
void setHost(Person *);
QQmlListProperty<Person> guests();
void appendGuest(Person*);
int guestCount() const;
Person *guest(int) const;
void clearGuests();
private:
static void appendGuest(QQmlListProperty<Person>*, Person*);
static int guestCount(QQmlListProperty<Person>*);
static Person* guest(QQmlListProperty<Person>*, int);
static void clearGuests(QQmlListProperty<Person>*);
Person *m_host;
QList<Person *> m_guests;
QVector<Person *> m_guests;
};
// ![3]

View File

@ -253,6 +253,7 @@ public:
{
}
#if defined(V4_BOOTSTRAP)
template <typename LabelType>
class Jump {
template<class TemplateAssemblerType>
@ -291,6 +292,7 @@ public:
private:
AssemblerLabel m_label;
};
#endif
// Stack operations:

View File

@ -20,7 +20,7 @@ qtHaveModule(quick) {
sharedimage \
testlib
qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \
qtConfig(quick-shadereffect):qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \
SUBDIRS += particles
}

View File

@ -230,7 +230,12 @@ void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal)
QMutexLocker locker(&m_lock);
if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) {
m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext));
if (QV4::ExecutionContext *parentContext
= m_engine->parentContext(m_engine->currentContext)) {
m_currentContext.set(m_engine, *parentContext);
} else {
m_currentContext.clear();
}
m_stepping = StepOver;
m_returnedValue.set(m_engine, retVal);
}

View File

@ -94,7 +94,7 @@ void QSGOpenVGFontGlyphCache::populate(const QVector<quint32> &glyphs)
referencedGlyphs.insert(glyphIndex);
if (!m_cachedGlyphs.contains(glyphIndex)) {
if (!m_glyphReferences.contains(glyphIndex)) {
newGlyphs.insert(glyphIndex);
}
}
@ -119,17 +119,9 @@ void QSGOpenVGFontGlyphCache::requestGlyphs(const QSet<quint32> &glyphs)
{
VGfloat origin[2];
VGfloat escapement[2];
QRectF metrics;
QRawFont rawFont = m_referenceFont;
// Before adding any new glyphs, remove any unused glyphs
for (auto glyph : qAsConst(m_unusedGlyphs)) {
vgClearGlyph(m_font, glyph);
}
for (auto glyph : glyphs) {
m_cachedGlyphs.insert(glyph);
// Calculate the path for the glyph and cache it.
QPainterPath path = rawFont.pathForGlyph(glyph);
VGPath vgPath;
@ -151,12 +143,23 @@ void QSGOpenVGFontGlyphCache::requestGlyphs(const QSet<quint32> &glyphs)
void QSGOpenVGFontGlyphCache::referenceGlyphs(const QSet<quint32> &glyphs)
{
m_unusedGlyphs -= glyphs;
for (auto glyph : glyphs) {
if (m_glyphReferences.contains(glyph))
m_glyphReferences[glyph] += 1;
else
m_glyphReferences.insert(glyph, 1);
}
}
void QSGOpenVGFontGlyphCache::releaseGlyphs(const QSet<quint32> &glyphs)
{
m_unusedGlyphs += glyphs;
for (auto glyph : glyphs) {
int references = m_glyphReferences[glyph] -= 1;
if (references == 0) {
vgClearGlyph(m_font, glyph);
m_glyphReferences.remove(glyph);
}
}
}
QT_END_NAMESPACE

View File

@ -87,8 +87,7 @@ private:
int m_glyphCount;
VGFont m_font;
QSet<quint32> m_cachedGlyphs;
QSet<quint32> m_unusedGlyphs;
QHash<quint32, int> m_glyphReferences;
};

View File

@ -2116,7 +2116,8 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias;
object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias;
object->flags = serializedObject->flags;
object->id = serializedObject->id;
object->location = serializedObject->location;
object->locationOfIdProperty = serializedObject->locationOfIdProperty;
@ -2175,6 +2176,15 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
object->properties->append(p);
}
{
const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable();
for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) {
QmlIR::Alias *a = pool->New<QmlIR::Alias>();
*static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias;
object->aliases->append(a);
}
}
QQmlJS::Engine *jsParserEngine = &output->jsParserEngine;
const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable();
@ -2205,6 +2215,11 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
const QString name = unit->stringAt(compiledFunction->nameIndex);
f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0);
f->formals.allocate(pool, int(compiledFunction->nFormals));
formalNameIdx = compiledFunction->formalsTable();
for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
f->formals[i] = *formalNameIdx;
object->functions->append(f);
}

View File

@ -2269,7 +2269,7 @@ bool Codegen::visit(DoWhileStatement *ast)
_block = loopbody;
statement(ast->statement);
_block->JUMP(loopcond);
setLocation(_block->JUMP(loopcond), ast->statement->lastSourceLocation());
_block = loopcond;
condition(ast->expression, loopbody, loopend);
@ -2334,7 +2334,7 @@ bool Codegen::visit(ForEachStatement *ast)
return false;
move(*init, _block->TEMP(temp));
statement(ast->statement);
_block->JUMP(foreachin);
setLocation(_block->JUMP(foreachin), ast->lastSourceLocation());
_block = foreachin;
@ -2373,7 +2373,7 @@ bool Codegen::visit(ForStatement *ast)
_block = forbody;
statement(ast->statement);
_block->JUMP(forstep);
setLocation(_block->JUMP(forstep), ast->lastSourceLocation());
_block = forstep;
statement(ast->expression);
@ -2473,7 +2473,7 @@ bool Codegen::visit(LocalForEachStatement *ast)
int temp = _block->newTemp();
move(identifier(ast->declaration->name.toString()), _block->TEMP(temp));
statement(ast->statement);
_block->JUMP(foreachin);
setLocation(_block->JUMP(foreachin), ast->lastSourceLocation());
_block = foreachin;
@ -2512,7 +2512,7 @@ bool Codegen::visit(LocalForStatement *ast)
_block = forbody;
statement(ast->statement);
_block->JUMP(forstep);
setLocation(_block->JUMP(forstep), ast->lastSourceLocation());
_block = forstep;
statement(ast->expression);
@ -2813,7 +2813,7 @@ bool Codegen::visit(WhileStatement *ast)
_block = whilebody;
statement(ast->statement);
_block->JUMP(whilecond);
setLocation(_block->JUMP(whilecond), ast->lastSourceLocation());
_block = whileend;
leaveLoop();

View File

@ -76,7 +76,7 @@ bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const
return false;
}
{
if (header->sourceTimeStamp) {
QFileInfo sourceCode(sourcePath);
QDateTime sourceTimeStamp;
if (sourceCode.exists())

View File

@ -77,13 +77,7 @@ namespace QV4 {
namespace CompiledData {
#ifdef V4_BOOTSTRAP
static QString cacheFilePath(const QString &localSourcePath)
{
const QString localCachePath = localSourcePath + QLatin1Char('c');
return localCachePath;
}
#else
#if !defined(V4_BOOTSTRAP)
static QString cacheFilePath(const QUrl &url)
{
const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
@ -101,8 +95,8 @@ static QString cacheFilePath(const QUrl &url)
#ifndef V4_BOOTSTRAP
CompilationUnit::CompilationUnit()
: data(0)
, engine(0)
, runtimeStrings(0)
, engine(0)
, runtimeLookups(0)
, runtimeRegularExpressions(0)
, runtimeClasses(0)
@ -374,7 +368,7 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory
const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit);
if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
*errorString = QStringLiteral("QML source file has moved to a different location.");
return false;
}
@ -415,28 +409,29 @@ bool CompilationUnit::memoryMapCode(QString *errorString)
#endif // V4_BOOTSTRAP
#if defined(V4_BOOTSTRAP)
bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString)
bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString)
#else
bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
#endif
{
errorString->clear();
#if !defined(V4_BOOTSTRAP)
if (data->sourceTimeStamp == 0) {
*errorString = QStringLiteral("Missing time stamp for source file");
return false;
}
#if !defined(V4_BOOTSTRAP)
if (!QQmlFile::isLocalFile(unitUrl)) {
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
const QString outputFileName = cacheFilePath(unitUrl);
#endif
#if QT_CONFIG(temporaryfile)
// Foo.qml -> Foo.qmlc
QSaveFile cacheFile(cacheFilePath(unitUrl));
QSaveFile cacheFile(outputFileName);
if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
*errorString = cacheFile.errorString();
return false;
@ -492,10 +487,22 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable);
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit;
QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data);
QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(compilationUnit->data);
auto ensureWritableUnit = [&jsUnit, &compilationUnit]() {
if (jsUnit == compilationUnit->data) {
char *unitCopy = (char*)malloc(jsUnit->unitSize);
memcpy(unitCopy, jsUnit, jsUnit->unitSize);
jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy);
}
};
QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable;
if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) {
ensureWritableUnit();
jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName);
}
// Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example)
// and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize
// the string table.
@ -558,6 +565,7 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
}
if (!signalParameterNameTable.isEmpty()) {
ensureWritableUnit();
Q_ASSERT(jsUnit != compilationUnit->data);
const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32);
uint newSize = jsUnit->unitSize + signalParameterTableSize;

View File

@ -817,13 +817,11 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount
// Called only when building QML, when we build the header for JS first and append QML data
virtual QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
#ifndef V4_BOOTSTRAP
ExecutionEngine *engine;
#endif
QV4::Heap::String **runtimeStrings; // Array
#ifndef V4_BOOTSTRAP
ExecutionEngine *engine;
QString fileName() const { return data->stringAt(data->sourceFileIndex); }
QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
@ -909,7 +907,7 @@ protected:
public:
#if defined(V4_BOOTSTRAP)
bool saveToDisk(const QString &unitUrl, QString *errorString);
bool saveToDisk(const QString &outputFileName, QString *errorString);
#else
bool saveToDisk(const QUrl &unitUrl, QString *errorString);
#endif

View File

@ -348,11 +348,7 @@ Module::~Module()
void Module::setFileName(const QString &name)
{
if (fileName.isEmpty())
fileName = name;
else {
Q_ASSERT(fileName == name);
}
fileName = name;
}
Function::Function(Module *module, Function *outer, const QString &name)

View File

@ -42,6 +42,7 @@
#include "qqmldebugserviceinterfaces_p.h"
#include <private/qqmlengine_p.h>
#include <private/qv4compileddata_p.h>
QT_BEGIN_NAMESPACE
@ -181,12 +182,12 @@ bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName,
return connector ? connector->open(configuration) : false;
}
enum { HookCount = 3 };
enum { HookCount = 4 };
// Only add to the end, and bump version if you do.
quintptr Q_QML_EXPORT qtDeclarativeHookData[] = {
// Version of this Array. Bump if you add to end.
1,
2,
// Number of entries in this array.
HookCount,
@ -194,7 +195,10 @@ quintptr Q_QML_EXPORT qtDeclarativeHookData[] = {
// TypeInformationVersion, an integral value, bumped whenever private
// object sizes or member offsets that are used in Qt Creator's
// data structure "pretty printing" change.
2
3,
// Version of the cache data.
QV4_DATA_STRUCTURE_VERSION
};
Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0]));

View File

@ -210,7 +210,6 @@ QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringR
} // namespace
#if QT_CONFIG(library)
struct RegisteredPlugin {
QString uri;
QPluginLoader* loader;
@ -221,21 +220,23 @@ struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> {
};
Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders
void qmlClearEnginePlugins()
{
StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes();
QMutexLocker lock(&plugins->mutex);
#if QT_CONFIG(library)
for (auto &plugin : qAsConst(*plugins)) {
QPluginLoader* loader = plugin.loader;
if (loader && !loader->unload())
qWarning("Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString()));
delete loader;
}
#endif
plugins->clear();
}
typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair;
#endif
/*!
\internal
@ -332,10 +333,9 @@ public:
const QString &uri, const QString &url,
int vmaj, int vmin, QV4::CompiledData::Import::ImportType type,
QList<QQmlError> *errors, bool lowPrecedence = false);
#if QT_CONFIG(library)
bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QStringList &versionUris,
bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QStringList &versionUris,
const QString &qmldirPath, QList<QQmlError> *errors);
#endif
};
/*!
@ -959,7 +959,6 @@ static QStringList versionUriList(const QString &uri, int vmaj, int vmin)
return result;
}
#if QT_CONFIG(library)
static QVector<QStaticPlugin> makePlugins()
{
QVector<QStaticPlugin> plugins;
@ -1009,7 +1008,6 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res
}
return true;
}
#endif
#if defined(QT_SHARED) || !QT_CONFIG(library)
static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why)
@ -1030,7 +1028,6 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
const QQmlTypeLoaderQmldirContent *qmldir,
QList<QQmlError> *errors)
{
#if QT_CONFIG(library)
Q_ASSERT(qmldir);
if (qmlImportTrace())
@ -1143,22 +1140,6 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath,
database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath);
}
#else
Q_UNUSED(vmaj);
Q_UNUSED(vmin);
Q_UNUSED(database);
Q_UNUSED(qmldir);
if (errors) {
QQmlError error;
error.setDescription(msgCannotLoadPlugin(uri, QQmlImportDatabase::tr("library loading is disabled")));
error.setUrl(QUrl::fromLocalFile(qmldirFilePath));
errors->prepend(error);
}
return false;
#endif // library
return true;
}
@ -2014,7 +1995,6 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b
bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &basePath,
const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors)
{
#if QT_CONFIG(library)
// Dynamic plugins are differentiated by their filepath. For static plugins we
// don't have that information so we use their address as key instead.
const QString uniquePluginID = QString::asprintf("%p", instance);
@ -2050,15 +2030,6 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba
}
return true;
#else
Q_UNUSED(instance);
Q_UNUSED(basePath);
Q_UNUSED(uri);
Q_UNUSED(typeNamespace);
Q_UNUSED(vmaj);
Q_UNUSED(errors);
return false;
#endif
}
/*!

View File

@ -2407,6 +2407,7 @@ void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit>
m_document.reset(new QmlIR::Document(isDebugging()));
QmlIR::IRLoader loader(unit->data, m_document.data());
loader.load();
m_document->jsModule.setFileName(finalUrlString());
m_document->javaScriptCompilationUnit = unit;
continueLoadFromIR();
}
@ -2507,6 +2508,8 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
{
Q_ASSERT(m_compiledData.isNull());
const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && m_document->javaScriptCompilationUnit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation;
QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache);
m_compiledData = compiler.compile();
@ -2515,7 +2518,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
return;
}
const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode;
const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation;
if (trySaveToDisk) {
QString errorString;
if (m_compiledData->saveToDisk(url(), &errorString)) {

View File

@ -837,10 +837,9 @@ void QQDMIncubationTask::statusChanged(Status status)
} else if (isDoneIncubating(status)) {
Q_ASSERT(incubating);
// The model was deleted from under our feet, cleanup ourselves
if (incubating->object) {
delete incubating->object;
incubating->object = 0;
delete incubating->object;
incubating->object = 0;
if (incubating->contextData) {
incubating->contextData->destroy();
incubating->contextData = 0;
}

View File

@ -44,6 +44,7 @@
#include <private/qquickcontext2d_p.h>
#include <private/qquickcontext2dtexture_p.h>
#include <private/qsgadaptationlayer_p.h>
#include <qsgtextureprovider.h>
#include <QtQuick/private/qquickpixmapcache_p.h>
#include <QtGui/QGuiApplication>

View File

@ -42,7 +42,9 @@
#include "qquickcanvasitem_p.h"
#include <private/qquickcontext2dtexture_p.h>
#include <private/qquickitem_p.h>
#if QT_CONFIG(quick_shadereffect)
#include <QtQuick/private/qquickshadereffectsource_p.h>
#endif
#include <qsgrendererinterface.h>
#include <QtQuick/private/qsgcontext_p.h>

View File

@ -52,6 +52,7 @@
//
#include "qquickitem_p.h"
#include "qquickpainteditem.h"
#include <QtGui/qcolor.h>
QT_BEGIN_NAMESPACE

View File

@ -2397,7 +2397,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e
} else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
moveEvent->setAccepted(true);
for (++grabItem; grabItem != grabber->end();) {
QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
if ((**grabItem)->contains(p)) {
@ -2472,7 +2471,10 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte
event->keyboardModifiers(),
event->type());
QQuickDropEventEx::copyActions(&translatedEvent, *event);
translatedEvent.setAccepted(event->isAccepted());
QCoreApplication::sendEvent(item, &translatedEvent);
event->setAccepted(translatedEvent.isAccepted());
event->setDropAction(translatedEvent.dropAction());
if (event->type() == QEvent::DragEnter) {
if (translatedEvent.isAccepted()) {
grabber->grab(item);
@ -2593,9 +2595,14 @@ bool QQuickWindowPrivate::sendFilteredTouchEvent(QQuickItem *target, QQuickItem
qCDebug(DBG_TOUCH) << " - second chance intercepted on childMouseEventFilter by " << target;
if (t != QEvent::MouseButtonRelease) {
qCDebug(DBG_TOUCH_TARGET) << "TP" << tp.id() << "->" << target;
touchMouseId = tp.id();
touchMouseDevice = event->device();
touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabber(target);
if (touchMouseId == -1) {
// the point was grabbed as a pure touch point before, now it will be treated as mouse
// but the old receiver still needs to be informed
if (auto oldGrabber = touchMouseDevice->pointerEvent()->pointById(tp.id())->grabber())
oldGrabber->touchUngrabEvent();
}
touchMouseId = tp.id();
target->grabMouse();
}
filtered = true;

View File

@ -101,7 +101,7 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window)
}
}
void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window)
void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose)
{
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window);
if (!m_windows.contains(window))
@ -174,7 +174,10 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window)
if (alsoSwap && window->isVisible()) {
//Flush backingstore to window
m_backingStores[window]->flush(softwareRenderer->flushRegion());
if (!isNewExpose)
m_backingStores[window]->flush(softwareRenderer->flushRegion());
else
m_backingStores[window]->flush(QRegion(QRect(QPoint(0,0), window->size())));
cd->fireFrameSwapped();
}
@ -206,7 +209,7 @@ void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window)
{
if (window->isExposed()) {
m_windows[window].updatePending = true;
renderWindow(window);
renderWindow(window, true);
}
}

View File

@ -69,7 +69,7 @@ public:
void windowDestroyed(QQuickWindow *window) override;
void renderWindow(QQuickWindow *window);
void renderWindow(QQuickWindow *window, bool isNewExpose = false);
void exposureChanged(QQuickWindow *window) override;
QImage grab(QQuickWindow *window) override;

View File

@ -42,11 +42,33 @@
QT_BEGIN_NAMESPACE
QSGDefaultGlyphNode::QSGDefaultGlyphNode()
: m_glyphNodeType(RootGlyphNode)
, m_dirtyGeometry(false)
{
setFlag(UsePreprocess);
}
QSGDefaultGlyphNode::~QSGDefaultGlyphNode()
{
if (m_glyphNodeType == SubGlyphNode)
return;
qDeleteAll(m_nodesToDelete);
m_nodesToDelete.clear();
}
void QSGDefaultGlyphNode::setMaterialColor(const QColor &color)
{
static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color);
}
void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
{
QSGBasicGlyphNode::setGlyphs(position, glyphs);
m_dirtyGeometry = true;
}
void QSGDefaultGlyphNode::update()
{
QRawFont font = m_glyphs.rawFont();
@ -84,4 +106,110 @@ void QSGDefaultGlyphNode::update()
markDirty(DirtyGeometry);
}
void QSGDefaultGlyphNode::preprocess()
{
qDeleteAll(m_nodesToDelete);
m_nodesToDelete.clear();
if (m_dirtyGeometry)
updateGeometry();
}
void QSGDefaultGlyphNode::updateGeometry()
{
// Remove previously created sub glyph nodes
// We assume all the children are sub glyph nodes
QSGNode *subnode = firstChild();
while (subnode) {
// We can't delete the node now as it might be in the preprocess list
// It will be deleted in the next preprocess
m_nodesToDelete.append(subnode);
subnode = subnode->nextSibling();
}
removeAllChildNodes();
GlyphInfo glyphInfo;
const QVector<quint32> indexes = m_glyphs.glyphIndexes();
const QVector<QPointF> positions = m_glyphs.positions();
const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384
const int maxVertices = maxGlyphs * 4; // 65536
const int maxIndexes = maxGlyphs * 6; // 98304
for (int i = 0; i < indexes.size(); ++i) {
const int glyphIndex = indexes.at(i);
const QPointF position = positions.at(i);
// As we use UNSIGNED_SHORT indexing in the geometry, we overload the
// "glyphsInOtherNodes" concept as overflow for if there are more than
// 65536 (16384 * 4) vertices to render which would otherwise exceed
// the maximum index size. This will cause sub-nodes to be recursively
// created to handle any number of glyphs.
if (i >= maxGlyphs) {
glyphInfo.indexes.append(glyphIndex);
glyphInfo.positions.append(position);
continue;
}
}
if (!glyphInfo.indexes.isEmpty()) {
QGlyphRun subNodeGlyphRun(m_glyphs);
subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes);
subNodeGlyphRun.setPositions(glyphInfo.positions);
QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode();
subNode->setGlyphNodeType(SubGlyphNode);
subNode->setColor(m_color);
subNode->setStyle(m_style);
subNode->setStyleColor(m_styleColor);
subNode->setGlyphs(m_position, subNodeGlyphRun);
subNode->update();
subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
appendChildNode(subNode);
QSGGeometry *g = geometry();
QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D();
quint16 *indexData = g->indexDataAsUShort();
QVector<QSGGeometry::TexturedPoint2D> tempVertexData(maxVertices);
QVector<quint16> tempIndexData(maxIndexes);
for (int i = 0; i < maxGlyphs; i++) {
tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0];
tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1];
tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2];
tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3];
tempIndexData[i * 6 + 0] = indexData[i * 6 + 0];
tempIndexData[i * 6 + 1] = indexData[i * 6 + 1];
tempIndexData[i * 6 + 2] = indexData[i * 6 + 2];
tempIndexData[i * 6 + 3] = indexData[i * 6 + 3];
tempIndexData[i * 6 + 4] = indexData[i * 6 + 4];
tempIndexData[i * 6 + 5] = indexData[i * 6 + 5];
}
g->allocate(maxVertices, maxIndexes);
vertexData = g->vertexDataAsTexturedPoint2D();
indexData = g->indexDataAsUShort();
for (int i = 0; i < maxGlyphs; i++) {
vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0];
vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1];
vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2];
vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3];
indexData[i * 6 + 0] = tempIndexData[i * 6 + 0];
indexData[i * 6 + 1] = tempIndexData[i * 6 + 1];
indexData[i * 6 + 2] = tempIndexData[i * 6 + 2];
indexData[i * 6 + 3] = tempIndexData[i * 6 + 3];
indexData[i * 6 + 4] = tempIndexData[i * 6 + 4];
indexData[i * 6 + 5] = tempIndexData[i * 6 + 5];
}
}
m_dirtyGeometry = false;
}
QT_END_NAMESPACE

View File

@ -59,8 +59,31 @@ QT_BEGIN_NAMESPACE
class QSGDefaultGlyphNode : public QSGBasicGlyphNode
{
public:
QSGDefaultGlyphNode();
~QSGDefaultGlyphNode();
void setMaterialColor(const QColor &color) override;
void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override;
void update() override;
void preprocess() override;
void updateGeometry();
private:
enum DefaultGlyphNodeType {
RootGlyphNode,
SubGlyphNode
};
void setGlyphNodeType(DefaultGlyphNodeType type) { m_glyphNodeType = type; }
DefaultGlyphNodeType m_glyphNodeType;
QLinkedList<QSGNode *> m_nodesToDelete;
struct GlyphInfo {
QVector<quint32> indexes;
QVector<QPointF> positions;
};
uint m_dirtyGeometry: 1;
};
QT_END_NAMESPACE

View File

@ -345,12 +345,13 @@ void QQuickTransformAnimatorJob::postSync()
}
QQuickItemPrivate *d = QQuickItemPrivate::get(m_target);
#if QT_CONFIG(quick_shadereffect)
if (d->extra.isAllocated()
&& d->extra->layer
&& d->extra->layer->enabled()) {
d = QQuickItemPrivate::get(d->extra->layer->m_effectSource);
}
#endif
m_helper->node = d->itemNode();
}

View File

@ -1140,7 +1140,7 @@ QSize QQuickWidget::initialSize() const
/*!
Returns the view's root \l {QQuickItem} {item}. Can be null
when setContents/setSource has not been called, if they were called with
when setSource() has not been called, if it was called with
broken QtQuick code or while the QtQuick contents are being created.
*/
QQuickItem *QQuickWidget::rootObject() const

View File

@ -10,7 +10,7 @@ qtHaveModule(gui):qtConfig(animation) {
quick \
qmltest
qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \
qtConfig(quick-shadereffect):qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \
SUBDIRS += particles
qtHaveModule(widgets): SUBDIRS += quickwidgets
}

View File

@ -176,6 +176,7 @@ public:
, m_captureContextInfo(false)
, m_thrownValue(-1)
, collector(engine)
, m_resumeSpeed(QV4Debugger::FullThrottle)
, m_debugger(0)
{
}
@ -214,7 +215,7 @@ public slots:
if (m_captureContextInfo)
captureContextInfo(debugger);
debugger->resume(QV4Debugger::FullThrottle);
debugger->resume(m_resumeSpeed);
}
public:
@ -280,6 +281,7 @@ public:
int context;
};
QVector<ExpressionRequest> m_expressionRequests;
QV4Debugger::Speed m_resumeSpeed;
QList<QJsonObject> m_expressionResults;
QList<QJsonArray> m_expressionRefs;
QV4Debugger *m_debugger;
@ -324,7 +326,10 @@ private slots:
void breakInWith();
void evaluateExpression();
void stepToEndOfScript();
void lastLineOfLoop_data();
void lastLineOfLoop();
private:
QV4Debugger *debugger() const
{
@ -758,6 +763,70 @@ void tst_qv4debugger::evaluateExpression()
}
}
void tst_qv4debugger::stepToEndOfScript()
{
QString script =
"var ret = 0;\n"
"ret += 4;\n"
"ret += 1;\n"
"ret += 5;\n";
debugger()->addBreakPoint("toEnd", 1);
m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver;
evaluateJavaScript(script, "toEnd");
QVERIFY(m_debuggerAgent->m_wasPaused);
QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Step);
QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 5);
for (int i = 0; i < 4; ++i) {
QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(i);
QCOMPARE(state.fileName, QString("toEnd"));
QCOMPARE(state.lineNumber, i + 1);
}
QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(4);
QCOMPARE(state.fileName, QString("toEnd"));
QCOMPARE(state.lineNumber, -4); // A return instruction without proper line number.
}
void tst_qv4debugger::lastLineOfLoop_data()
{
QTest::addColumn<QString>("loopHead");
QTest::addColumn<QString>("loopTail");
QTest::newRow("for") << "for (var i = 0; i < 10; ++i) {\n" << "}\n";
QTest::newRow("for..in") << "for (var i in [0, 1, 2, 3, 4]) {\n" << "}\n";
QTest::newRow("while") << "while (ret < 10) {\n" << "}\n";
QTest::newRow("do..while") << "do {\n" << "} while (ret < 10);\n";
}
void tst_qv4debugger::lastLineOfLoop()
{
QFETCH(QString, loopHead);
QFETCH(QString, loopTail);
QString script =
"var ret = 0;\n"
+ loopHead +
" if (ret == 2)\n"
" ret += 4;\n" // breakpoint, then step over
" else \n"
" ret += 1;\n"
+ loopTail;
debugger()->addBreakPoint("trueBranch", 4);
m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver;
evaluateJavaScript(script, "trueBranch");
QVERIFY(m_debuggerAgent->m_wasPaused);
QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Step);
QVERIFY(m_debuggerAgent->m_statesWhenPaused.count() > 1);
QV4Debugger::ExecutionState firstState = m_debuggerAgent->m_statesWhenPaused.first();
QCOMPARE(firstState.fileName, QString("trueBranch"));
QCOMPARE(firstState.lineNumber, 4);
QV4Debugger::ExecutionState secondState = m_debuggerAgent->m_statesWhenPaused.at(1);
QCOMPARE(secondState.fileName, QString("trueBranch"));
QCOMPARE(secondState.lineNumber, 7);
}
QTEST_MAIN(tst_qv4debugger)
#include "tst_qv4debugger.moc"

View File

@ -1,2 +0,0 @@
[inFlickable]
*

View File

@ -800,7 +800,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable2()
QVERIFY(flickable->contentY() < 0);
QVERIFY(flickable->isMoving());
QCOMPARE(point11->pressed(), true);
QCOMPARE(point11->pressed(), false);
QTest::touchEvent(window.data(), device).release(0, p1);
QQuickTouchUtils::flush(window.data());

View File

@ -28,6 +28,7 @@
#include <qtest.h>
#include <QDebug>
#include <QMimeData>
#include <QTouchEvent>
#include <QtQuick/QQuickItem>
#include <QtQuick/QQuickView>
@ -372,6 +373,8 @@ private slots:
void grabContentItemToImage();
void testDragEventPropertyPropagation();
private:
QTouchDevice *touchDevice;
QTouchDevice *touchDeviceWithVelocity;
@ -2567,6 +2570,261 @@ void tst_qquickwindow::grabContentItemToImage()
QTRY_COMPARE(created->property("success").toInt(), 1);
}
class TestDropTarget : public QQuickItem
{
Q_OBJECT
public:
TestDropTarget(QQuickItem *parent = 0)
: QQuickItem(parent)
, enterDropAction(Qt::CopyAction)
, moveDropAction(Qt::CopyAction)
, dropDropAction(Qt::CopyAction)
, enterAccept(true)
, moveAccept(true)
, dropAccept(true)
{
setFlags(ItemAcceptsDrops);
}
void reset()
{
enterDropAction = Qt::CopyAction;
moveDropAction = Qt::CopyAction;
dropDropAction = Qt::CopyAction;
enterAccept = true;
moveAccept = true;
dropAccept = true;
}
void dragEnterEvent(QDragEnterEvent *event)
{
event->setAccepted(enterAccept);
event->setDropAction(enterDropAction);
}
void dragMoveEvent(QDragMoveEvent *event)
{
event->setAccepted(moveAccept);
event->setDropAction(moveDropAction);
}
void dropEvent(QDropEvent *event)
{
event->setAccepted(dropAccept);
event->setDropAction(dropDropAction);
}
Qt::DropAction enterDropAction;
Qt::DropAction moveDropAction;
Qt::DropAction dropDropAction;
bool enterAccept;
bool moveAccept;
bool dropAccept;
};
class DragEventTester {
public:
DragEventTester()
: pos(60, 60)
, actions(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction)
, buttons(Qt::LeftButton)
, modifiers(Qt::NoModifier)
{
}
~DragEventTester() {
qDeleteAll(events);
events.clear();
enterEvent = 0;
moveEvent = 0;
dropEvent = 0;
leaveEvent = 0;
}
void addEnterEvent()
{
enterEvent = new QDragEnterEvent(pos, actions, &data, buttons, modifiers);
events.append(enterEvent);
}
void addMoveEvent()
{
moveEvent = new QDragMoveEvent(pos, actions, &data, buttons, modifiers, QEvent::DragMove);
events.append(moveEvent);
}
void addDropEvent()
{
dropEvent = new QDropEvent(pos, actions, &data, buttons, modifiers, QEvent::Drop);
events.append(dropEvent);
}
void addLeaveEvent()
{
leaveEvent = new QDragLeaveEvent();
events.append(leaveEvent);
}
void sendDragEventSequence(QQuickWindow *window) const {
for (int i = 0; i < events.size(); ++i) {
QCoreApplication::sendEvent(window, events[i]);
}
}
// Used for building events.
QMimeData data;
QPoint pos;
Qt::DropActions actions;
Qt::MouseButtons buttons;
Qt::KeyboardModifiers modifiers;
// Owns events.
QList<QEvent *> events;
// Non-owner pointers for easy acccess.
QDragEnterEvent *enterEvent;
QDragMoveEvent *moveEvent;
QDropEvent *dropEvent;
QDragLeaveEvent *leaveEvent;
};
void tst_qquickwindow::testDragEventPropertyPropagation()
{
QQuickWindow window;
TestDropTarget dropTarget(window.contentItem());
// Setting the size is important because the QQuickWindow checks if the drag happened inside
// the drop target.
dropTarget.setSize(QSizeF(100, 100));
// Test enter events property propagation.
// For enter events, only isAccepted gets propagated.
{
DragEventTester builder;
dropTarget.enterAccept = false;
dropTarget.enterDropAction = Qt::IgnoreAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragEnterEvent* enterEvent = builder.enterEvent;
QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept);
}
{
DragEventTester builder;
dropTarget.enterAccept = false;
dropTarget.enterDropAction = Qt::CopyAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragEnterEvent* enterEvent = builder.enterEvent;
QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept);
}
{
DragEventTester builder;
dropTarget.enterAccept = true;
dropTarget.enterDropAction = Qt::IgnoreAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragEnterEvent* enterEvent = builder.enterEvent;
QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept);
}
{
DragEventTester builder;
dropTarget.enterAccept = true;
dropTarget.enterDropAction = Qt::CopyAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragEnterEvent* enterEvent = builder.enterEvent;
QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept);
}
// Test move events property propagation.
// For move events, both isAccepted and dropAction get propagated.
dropTarget.reset();
{
DragEventTester builder;
dropTarget.moveAccept = false;
dropTarget.moveDropAction = Qt::IgnoreAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragMoveEvent* moveEvent = builder.moveEvent;
QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept);
QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction);
}
{
DragEventTester builder;
dropTarget.moveAccept = false;
dropTarget.moveDropAction = Qt::CopyAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragMoveEvent* moveEvent = builder.moveEvent;
QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept);
QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction);
}
{
DragEventTester builder;
dropTarget.moveAccept = true;
dropTarget.moveDropAction = Qt::IgnoreAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragMoveEvent* moveEvent = builder.moveEvent;
QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept);
QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction);
}
{
DragEventTester builder;
dropTarget.moveAccept = true;
dropTarget.moveDropAction = Qt::CopyAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent();
builder.sendDragEventSequence(&window);
QDragMoveEvent* moveEvent = builder.moveEvent;
QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept);
QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction);
}
// Test drop events property propagation.
// For drop events, both isAccepted and dropAction get propagated.
dropTarget.reset();
{
DragEventTester builder;
dropTarget.dropAccept = false;
dropTarget.dropDropAction = Qt::IgnoreAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent();
builder.sendDragEventSequence(&window);
QDropEvent* dropEvent = builder.dropEvent;
QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept);
QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction);
}
{
DragEventTester builder;
dropTarget.dropAccept = false;
dropTarget.dropDropAction = Qt::CopyAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent();
builder.sendDragEventSequence(&window);
QDropEvent* dropEvent = builder.dropEvent;
QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept);
QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction);
}
{
DragEventTester builder;
dropTarget.dropAccept = true;
dropTarget.dropDropAction = Qt::IgnoreAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent();
builder.sendDragEventSequence(&window);
QDropEvent* dropEvent = builder.dropEvent;
QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept);
QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction);
}
{
DragEventTester builder;
dropTarget.dropAccept = true;
dropTarget.dropDropAction = Qt::CopyAction;
builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent();
builder.sendDragEventSequence(&window);
QDropEvent* dropEvent = builder.dropEvent;
QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept);
QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction);
}
}
QTEST_MAIN(tst_qquickwindow)
#include "tst_qquickwindow.moc"

View File

@ -71,7 +71,7 @@ Q_SIGNALS:
public:
EventItem(QQuickItem *parent = 0)
: QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1)
: QQuickItem(parent), touchUngrabCount(0), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1)
{
setAcceptedMouseButtons(Qt::LeftButton);
}
@ -111,11 +111,17 @@ public:
eventList.append(Event(QEvent::UngrabMouse, QPoint(0,0), QPoint(0,0)));
}
void touchUngrabEvent()
{
++touchUngrabCount;
}
bool event(QEvent *event) {
return QQuickItem::event(event);
}
QList<Event> eventList;
int touchUngrabCount;
bool acceptMouse;
bool acceptTouch;
bool filterTouch; // when used as event filter
@ -158,6 +164,7 @@ private slots:
void mouseOverTouch();
void buttonOnFlickable();
void touchButtonOnFlickable();
void buttonOnDelayedPressFlickable_data();
void buttonOnDelayedPressFlickable();
void buttonOnTouch();
@ -568,9 +575,10 @@ void tst_TouchMouse::buttonOnFlickable()
QCOMPARE(pointerEvent->point(0)->grabber(), eventItem1);
QCOMPARE(window->mouseGrabberItem(), eventItem1);
p1 += QPoint(0, -10);
QPoint p2 = p1 + QPoint(0, -10);
QPoint p3 = p2 + QPoint(0, -10);
int dragDelta = -qApp->styleHints()->startDragDistance();
p1 += QPoint(0, dragDelta);
QPoint p2 = p1 + QPoint(0, dragDelta);
QPoint p3 = p2 + QPoint(0, dragDelta);
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
@ -593,6 +601,66 @@ void tst_TouchMouse::buttonOnFlickable()
QQuickTouchUtils::flush(window.data());
}
void tst_TouchMouse::touchButtonOnFlickable()
{
// flickable - height 500 / 1000
// - eventItem1 y: 100, height 100
// - eventItem2 y: 300, height 100
QScopedPointer<QQuickView> window(createView());
window->setSource(testFileUrl("buttononflickable.qml"));
window->show();
QQuickViewTestUtil::centerOnScreen(window.data());
QVERIFY(QTest::qWaitForWindowActive(window.data()));
QVERIFY(window->rootObject() != 0);
QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable");
QVERIFY(flickable);
EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2");
QVERIFY(eventItem2);
QCOMPARE(eventItem2->eventList.size(), 0);
eventItem2->acceptTouch = true;
// press via touch, then drag: check that flickable moves and that the button gets ungrabbed
QCOMPARE(eventItem2->eventList.size(), 0);
QPoint p1 = QPoint(10, 310);
QTest::touchEvent(window.data(), device).press(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QCOMPARE(eventItem2->eventList.size(), 1);
QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin);
QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data());
QVERIFY(windowPriv->touchMouseId == -1);
auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent();
QCOMPARE(pointerEvent->point(0)->grabber(), eventItem2);
QCOMPARE(window->mouseGrabberItem(), nullptr);
int dragDelta = qApp->styleHints()->startDragDistance() * -0.7;
p1 += QPoint(0, dragDelta);
QPoint p2 = p1 + QPoint(0, dragDelta);
QPoint p3 = p2 + QPoint(0, dragDelta);
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p1, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p2, window.data());
QQuickTouchUtils::flush(window.data());
QTest::touchEvent(window.data(), device).move(0, p3, window.data());
QQuickTouchUtils::flush(window.data());
QVERIFY(eventItem2->eventList.size() > 2);
QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate);
QCOMPARE(eventItem2->touchUngrabCount, 1);
QCOMPARE(window->mouseGrabberItem(), flickable);
QVERIFY(windowPriv->touchMouseId != -1);
QCOMPARE(pointerEvent->point(0)->grabber(), flickable);
QVERIFY(flickable->isMovingVertically());
QTest::touchEvent(window.data(), device).release(0, p3, window.data());
QQuickTouchUtils::flush(window.data());
}
void tst_TouchMouse::buttonOnDelayedPressFlickable_data()
{
QTest::addColumn<bool>("scrollBeforeDelayIsOver");

View File

@ -109,7 +109,7 @@ void tst_toolsupport::offsets_data()
= QTest::newRow("CompiledData::CompilationUnit::runtimeStrings")
<< pmm_to_offsetof(&QV4::CompiledData::CompilationUnit::runtimeStrings);
data << 16 << 32;
data << 12 << 24;
}
{

View File

@ -1,12 +1,52 @@
qtPrepareTool(QML_CACHEGEN, qmlcachegen)
static {
message("QML cache generation ahead of time is not supported in static builds")
return()
}
qtPrepareTool(QML_CACHEGEN, qmlcachegen, _ARCH_CHECK)
isEmpty(TARGETPATH): error("Must set TARGETPATH (QML import name) for ahead-of-time QML cache generation")
!isEmpty(QT_TARGET_ARCH):QML_CACHEGEN_ARCH=$$QT_TARGET_ARCH
else:QML_CACHEGEN_ARCH=$$QT_ARCH
qmlcachegen.input = QML_FILES
qmlcachegen.output = ${QMAKE_FILE_IN}c
qmlcachegen.commands = $$QML_CACHEGEN --target-architecture=$$QML_CACHEGEN_ARCH ${QMAKE_FILE_IN}
qmlcachegen.name = Generate QML Cache ${QMAKE_FILE_IN}
qmlcachegen.variable_out = AUX_QML_FILES
QML_CACHEGEN_ARGS=--target-architecture=$$QML_CACHEGEN_ARCH
QMAKE_EXTRA_COMPILERS += qmlcachegen
!system($$QML_CACHEGEN_ARCH_CHECK $$QML_CACHEGEN_ARGS --check-if-supported) {
message("QML cache generation requested but target architecture $$QML_CACHEGEN_ARCH is not supported.")
return()
}
load(qt_build_paths)
prefix_build: QMLCACHE_DESTDIR = $$MODULE_BASE_OUTDIR/qml/$$TARGETPATH
else: QMLCACHE_DESTDIR = $$[QT_INSTALL_QML]/$$TARGETPATH
CACHEGEN_FILES=
qmlcacheinst.files =
for(qmlf, QML_FILES) {
contains(qmlf,.*\\.js$)|contains(qmlf,.*\\.qml$) {
CACHEGEN_FILES += $$absolute_path($$qmlf, $$_PRO_FILE_PWD_)
qmlcacheinst.files += $$QMLCACHE_DESTDIR/$$relative_path($$qmlf, $$_PRO_FILE_PWD_)c
}
}
defineReplace(qmlCacheOutputFileName) {
return($$relative_path($$QMLCACHE_DESTDIR/$$relative_path($$1, $$_PRO_FILE_PWD_)c, $$OUT_PWD))
}
qmlcacheinst.base = $$QMLCACHE_DESTDIR
qmlcacheinst.path = $$[QT_INSTALL_QML]/$$TARGETPATH
qmlcacheinst.CONFIG = no_check_exist
qmlcachegen.input = CACHEGEN_FILES
qmlcachegen.output = ${QMAKE_FUNC_FILE_IN_qmlCacheOutputFileName}
qmlcachegen.CONFIG = no_link target_predeps
qmlcachegen.commands = $$QML_CACHEGEN $$QML_CACHEGEN_ARGS -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN}
qmlcachegen.name = Generate QML Cache ${QMAKE_FILE_IN}
qmlcachegen.variable_out = GENERATED_FILES
!debug_and_release|!build_all|CONFIG(release, debug|release) {
QMAKE_EXTRA_COMPILERS += qmlcachegen
INSTALLS += qmlcacheinst
}

View File

@ -80,7 +80,40 @@ QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::Diagnostic
return message;
}
static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *iselFactory, Error *error)
// Ensure that ListElement objects keep all property assignments in their string form
static void annotateListElements(QmlIR::Document *document)
{
QStringList listElementNames;
foreach (const QV4::CompiledData::Import *import, document->imports) {
const QString uri = document->stringAt(import->uriIndex);
if (uri != QStringLiteral("QtQml.Models") && uri != QStringLiteral("QtQuick"))
continue;
QString listElementName = QStringLiteral("ListElement");
const QString qualifier = document->stringAt(import->qualifierIndex);
if (!qualifier.isEmpty()) {
listElementName.prepend(QLatin1Char('.'));
listElementName.prepend(qualifier);
}
listElementNames.append(listElementName);
}
if (listElementNames.isEmpty())
return;
foreach (QmlIR::Object *object, document->objects) {
if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex)))
continue;
for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) {
if (binding->type != QV4::CompiledData::Binding::Type_Script)
continue;
binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex));
}
}
}
static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, Error *error)
{
QmlIR::Document irDocument(/*debugMode*/false);
@ -96,7 +129,6 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i
error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString();
return false;
}
irDocument.jsModule.sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch();
}
{
@ -112,8 +144,10 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i
}
}
annotateListElements(&irDocument);
{
QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable);
QmlIR::JSCodeGen v4CodeGen(/*empty input file name*/QString(), irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable);
for (QmlIR::Object *object: qAsConst(irDocument.objects)) {
if (object->functionsAndExpressions->count == 0)
continue;
@ -153,7 +187,7 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i
unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation;
irDocument.javaScriptCompilationUnit->data = unit;
if (!irDocument.javaScriptCompilationUnit->saveToDisk(inputFileName, &error->message))
if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message))
return false;
free(unit);
@ -161,7 +195,7 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i
return true;
}
static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *iselFactory, Error *error)
static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, Error *error)
{
QmlIR::Document irDocument(/*debugMode*/false);
@ -177,7 +211,6 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is
error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString();
return false;
}
irDocument.jsModule.sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch();
}
QQmlJS::Engine *engine = &irDocument.jsParserEngine;
@ -217,7 +250,7 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is
{
QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable);
v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode);
v4CodeGen.generateFromProgram(/*empty input file name*/QString(), sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode);
QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors();
if (!jsErrors.isEmpty()) {
for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) {
@ -233,7 +266,8 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is
// ### translation binding simplification
QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, /*executable allocator*/nullptr, &irDocument.jsModule, &irDocument.jsGenerator));
QV4::ExecutableAllocator allocator;
QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator));
// Disable lookups in non-standalone (aka QML) mode
isel->setUseFastLookups(false);
irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false);
@ -243,7 +277,7 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is
unit->flags |= QV4::CompiledData::Unit::StaticData;
irDocument.javaScriptCompilationUnit->data = unit;
if (!irDocument.javaScriptCompilationUnit->saveToDisk(inputFileName, &error->message)) {
if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message)) {
engine->setDirectives(oldDirs);
return false;
}
@ -270,11 +304,35 @@ int main(int argc, char **argv)
QCommandLineOption targetArchitectureOption(QStringLiteral("target-architecture"), QCoreApplication::translate("main", "Target architecture"), QCoreApplication::translate("main", "architecture"));
parser.addOption(targetArchitectureOption);
QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name"));
parser.addOption(outputFileOption);
QCommandLineOption checkIfSupportedOption(QStringLiteral("check-if-supported"), QCoreApplication::translate("main", "Check if cache generate is supported on the specified target architecture"));
parser.addOption(checkIfSupportedOption);
parser.addPositionalArgument(QStringLiteral("[qml file]"),
QStringLiteral("QML source file to generate cache for."));
parser.process(app);
if (!parser.isSet(targetArchitectureOption)) {
fprintf(stderr, "Target architecture not specified. Please specify with --target-architecture=<arch>\n");
parser.showHelp();
return EXIT_FAILURE;
}
QScopedPointer<QV4::EvalISelFactory> isel;
const QString targetArchitecture = parser.value(targetArchitectureOption);
isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture));
if (parser.isSet(checkIfSupportedOption)) {
if (isel.isNull())
return EXIT_FAILURE;
else
return EXIT_SUCCESS;
}
const QStringList sources = parser.positionalArguments();
if (sources.isEmpty()){
parser.showHelp();
@ -284,23 +342,22 @@ int main(int argc, char **argv)
}
const QString inputFile = sources.first();
QScopedPointer<QV4::EvalISelFactory> isel;
const QString targetArchitecture = parser.value(targetArchitectureOption);
isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture));
if (!isel)
isel.reset(new QV4::Moth::ISelFactory);
Error error;
QString outputFileName = inputFile + QLatin1Char('c');
if (parser.isSet(outputFileOption))
outputFileName = parser.value(outputFileOption);
if (inputFile.endsWith(QLatin1String(".qml"))) {
if (!compileQmlFile(inputFile, isel.data(), &error)) {
if (!compileQmlFile(inputFile, outputFileName, isel.data(), &error)) {
error.augment(QLatin1String("Error compiling qml file: ")).print();
return EXIT_FAILURE;
}
} else if (inputFile.endsWith(QLatin1String(".js"))) {
if (!compileJSFile(inputFile, isel.data(), &error)) {
if (!compileJSFile(inputFile, outputFileName, isel.data(), &error)) {
error.augment(QLatin1String("Error compiling qml file: ")).print();
return EXIT_FAILURE;
}

View File

@ -6,19 +6,9 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
SOURCES = qmlcachegen.cpp
TARGET = qmlcachegen
BUILD_INTEGRATION = qmlcache.prf
!force_independent {
qmake_integration.input = BUILD_INTEGRATION
qmake_integration.output = $$[QT_HOST_DATA]/mkspecs/features/${QMAKE_FILE_BASE}.prf
qmake_integration.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
qmake_integration.name = COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
qmake_integration.CONFIG = no_clean no_link
!contains(TEMPLATE, vc.*): qmake_integration.variable_out = GENERATED_FILES
QMAKE_EXTRA_COMPILERS += qmake_integration
}
qmake_integration_installs.files = $$BUILD_INTEGRATION
qmake_integration_installs.path = $$[QT_HOST_DATA]/mkspecs/features
INSTALLS += qmake_integration_installs
build_integration.files = qmlcache.prf
build_integration.path = $$[QT_HOST_DATA]/mkspecs/features
prefix_build: INSTALLS += build_integration
else: COPIES += build_integration
load(qt_tool)