diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index 616f54d174bb2378a16f11d8d6fe0b38a0ef29fa..e087785901ee911081b2704f6e25e5634e88b388 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1542,7 +1542,6 @@ void QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) callData->args[0] = QV4::Primitive::fromUInt32(s); f->call(callData); if (scope.hasException()) { - ctx->catchException(); QQmlError error = QV4::ExecutionEngine::catchExceptionAsQmlError(ctx); QQmlEnginePrivate::warning(QQmlEnginePrivate::get(d()->v8->engine()), error); } diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index e2f1cffb161ebdb3637cb725d2a09e014b717458..91ddcf57e514184e1509418f2c9c3b768d4dcb49 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1526,6 +1526,18 @@ void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelInd _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); } +bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const +{ + for (int i = 0, c = parents.count(); i < c; ++i) { + for (QPersistentModelIndex parent = desc; parent.isValid(); parent = parent.parent()) { + if (parent == parents[i]) + return true; + } + } + + return false; +} + void QQmlDelegateModel::_q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) { Q_D(QQmlDelegateModel); @@ -1534,8 +1546,9 @@ void QQmlDelegateModel::_q_layoutAboutToBeChanged(const QList<QPersistentModelIn if (hint == QAbstractItemModel::VerticalSortHint) { d->m_storedPersistentIndexes.clear(); - if (!parents.contains(d->m_adaptorModel.rootIndex)) + if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) { return; + } for (int i = 0; i < d->m_count; ++i) { const QModelIndex index = d->m_adaptorModel.aim()->index(i, 0, d->m_adaptorModel.rootIndex); @@ -1555,20 +1568,16 @@ void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &par return; if (hint == QAbstractItemModel::VerticalSortHint) { - if (!parents.contains(d->m_adaptorModel.rootIndex)) + if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) { return; + } for (int i = 0, c = d->m_storedPersistentIndexes.count(); i < c; ++i) { const QPersistentModelIndex &index = d->m_storedPersistentIndexes.at(i); if (i == index.row()) continue; - QVector<Compositor::Insert> inserts; - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, i, index.row(), 1, &removes, &inserts); - if (!removes.isEmpty() || !inserts.isEmpty()) { - d->itemsMoved(removes, inserts); - } + _q_itemsMoved(i, index.row(), 1); } d->m_storedPersistentIndexes.clear(); diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h index 0b67179163d41bd150a5364a25927245ae592b3b..53cc94bbdf8e1b8338899f909b8fbc2f5744417b 100644 --- a/src/qml/types/qqmldelegatemodel_p.h +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -143,6 +143,8 @@ private Q_SLOTS: void _q_layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint); private: + bool isDescendantOf(const QPersistentModelIndex &desc, const QList<QPersistentModelIndex> &parents) const; + Q_DISABLE_COPY(QQmlDelegateModel) }; diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 4b70934320682cb134665923f55455868e8d2ea3..57c7bd4a00b3cb1a4c09083341569f0bc5fb1f29 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -4031,6 +4031,8 @@ public: void run() Q_DECL_OVERRIDE { delete texture; } }; +QMutex QQuickContext2D::mutex; + QQuickContext2D::QQuickContext2D(QObject *parent) : QQuickCanvasContext(parent) , m_buffer(new QQuickContext2DCommandBuffer) @@ -4044,6 +4046,8 @@ QQuickContext2D::QQuickContext2D(QObject *parent) QQuickContext2D::~QQuickContext2D() { + mutex.lock(); + m_texture->setItem(0); delete m_buffer; if (m_renderTarget == QQuickCanvasItem::FramebufferObject) { @@ -4064,7 +4068,7 @@ QQuickContext2D::~QQuickContext2D() c->texture = m_texture; m_canvas->window()->scheduleRenderJob(c, QQuickWindow::AfterSynchronizingStage); } else { - delete m_texture; + m_texture->deleteLater(); } } } else { @@ -4073,6 +4077,7 @@ QQuickContext2D::~QQuickContext2D() // currently be doing. m_texture->deleteLater(); } + mutex.unlock(); } QV4::ReturnedValue QQuickContext2D::v4value() const @@ -4143,6 +4148,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args m_glContext->setShareContext(cc); if (renderThread != QThread::currentThread()) m_glContext->moveToThread(renderThread); + m_texture->initializeOpenGL(m_glContext, m_surface.data()); } connect(m_texture, SIGNAL(textureChanged()), SIGNAL(textureChanged())); @@ -4331,10 +4337,4 @@ void QQuickContext2D::setV8Engine(QV8Engine *engine) } } -QQuickContext2DCommandBuffer* QQuickContext2D::nextBuffer() -{ - QMutexLocker lock(&m_mutex); - return m_bufferQueue.isEmpty() ? 0 : m_bufferQueue.dequeue(); -} - QT_END_NAMESPACE diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index bd1a83ce08e733f0230ba2062bd09c5440fa0092..c679bc33cb6456bcb3cf415dd4bfc46b33460d0a 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -185,7 +185,6 @@ public: QQuickCanvasItem* canvas() const { return m_canvas; } QQuickContext2DCommandBuffer* buffer() const { return m_buffer; } - QQuickContext2DCommandBuffer* nextBuffer(); bool bufferValid() const { return m_buffer != 0; } void popState(); @@ -255,7 +254,7 @@ public: QImage m_grabbedImage; bool m_grabbed:1; - QMutex m_mutex; + static QMutex mutex; }; diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 7e88e18c650940fea71efaee9595ade929e3e227..17d4feae6b3c9ec2f279967c6c1083fc69c80d37 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -90,6 +90,8 @@ struct GLAcquireContext { QQuickContext2DTexture::QQuickContext2DTexture() : m_context(0) + , m_gl(0) + , m_surface(0) , m_item(0) , m_canvasWindowChanged(false) , m_dirtyTexture(false) @@ -146,8 +148,12 @@ void QQuickContext2DTexture::setAntialiasing(bool antialiasing) void QQuickContext2DTexture::setItem(QQuickCanvasItem* item) { m_item = item; - m_context = (QQuickContext2D*)item->rawContext(); // FIXME - m_state = m_context->state; + if (m_item) { + m_context = (QQuickContext2D*) item->rawContext(); // FIXME + m_state = m_context->state; + } else { + m_context = 0; + } } bool QQuickContext2DTexture::setCanvasWindow(const QRect& r) @@ -239,12 +245,15 @@ bool QQuickContext2DTexture::canvasDestroyed() void QQuickContext2DTexture::paint(QQuickContext2DCommandBuffer *ccb) { + QQuickContext2D::mutex.lock(); if (canvasDestroyed()) { delete ccb; + QQuickContext2D::mutex.unlock(); return; } + QQuickContext2D::mutex.unlock(); - GLAcquireContext currentContext(m_context->glContext(), m_context->surface()); + GLAcquireContext currentContext(m_gl, m_surface); if (!m_tiledCanvas) { paintWithoutTiles(ccb); @@ -442,7 +451,7 @@ QSGTexture *QQuickContext2DFBOTexture::textureForNextFrame(QSGTexture *lastTextu } if (m_dirtyTexture) { - if (!m_context->glContext()) { + if (!m_gl) { // on a rendering thread, use the fbo directly... texture->setTextureId(m_fbo->texture()); } else { @@ -500,18 +509,18 @@ bool QQuickContext2DFBOTexture::doMultisampling() const void QQuickContext2DFBOTexture::grabImage(const QRectF& rf) { Q_ASSERT(rf.isValid()); - if (!m_fbo) { - m_context->setGrabbedImage(QImage()); - return; - } - - QImage grabbed; - { - GLAcquireContext ctx(m_context->glContext(), m_context->surface()); - grabbed = m_fbo->toImage().scaled(m_fboSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).mirrored().copy(rf.toRect()); + QQuickContext2D::mutex.lock(); + if (m_context) { + if (!m_fbo) { + m_context->setGrabbedImage(QImage()); + } else { + QImage grabbed; + GLAcquireContext ctx(m_gl, m_surface); + grabbed = m_fbo->toImage().scaled(m_fboSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).mirrored().copy(rf.toRect()); + m_context->setGrabbedImage(grabbed); + } } - - m_context->setGrabbedImage(grabbed); + QQuickContext2D::mutex.unlock(); } void QQuickContext2DFBOTexture::compositeTile(QQuickContext2DTile* tile) @@ -601,7 +610,7 @@ void QQuickContext2DFBOTexture::endPainting() if (m_multisampledFbo) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); - if (m_context->glContext()) { + if (m_gl) { /* When rendering happens on the render thread, the fbo's texture is * used directly for display. If we are on the GUI thread or a * dedicated Canvas render thread, we need to decouple the FBO from @@ -657,9 +666,12 @@ QQuickContext2DTile* QQuickContext2DImageTexture::createTile() const void QQuickContext2DImageTexture::grabImage(const QRectF& rf) { Q_ASSERT(rf.isValid()); - Q_ASSERT(m_context); - QImage grabbed = m_displayImage.copy(rf.toRect()); - m_context->setGrabbedImage(grabbed); + QQuickContext2D::mutex.lock(); + if (m_context) { + QImage grabbed = m_displayImage.copy(rf.toRect()); + m_context->setGrabbedImage(grabbed); + } + QQuickContext2D::mutex.unlock(); } QSGTexture *QQuickContext2DImageTexture::textureForNextFrame(QSGTexture *last) diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h index 7c4845385739fff4e6c459377ddbb477b20ef61e..169eef8b95d0a9af3424138919cc13b08a159cc8 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture_p.h +++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h @@ -113,6 +113,11 @@ public: virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame) = 0; bool event(QEvent *e); + void initializeOpenGL(QOpenGLContext *gl, QOffscreenSurface *s) { + m_gl = gl; + m_surface = s; + } + Q_SIGNALS: void textureChanged(); @@ -137,7 +142,10 @@ protected: QRect createTiles(const QRect& window); QList<QQuickContext2DTile*> m_tiles; - QQuickContext2D* m_context; + QQuickContext2D *m_context; + + QOpenGLContext *m_gl; + QSurface *m_surface; QQuickContext2D::State m_state; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9ae2f5b20d5e8b9e0a4dd23b55a0122e2d2fdaa1..eb97dde6cb1d58128825845c94085aa1aa8490a9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -3233,6 +3233,8 @@ QQmlIncubationController *QQuickWindow::incubationController() const \warning Make very sure that a signal handler for beforeSynchronizing leaves the GL context in the same state as it was when the signal handler was entered. Failing to do so can result in the scene not rendering properly. + + \sa resetOpenGLState() */ /*! @@ -3254,6 +3256,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const do so can result in the scene not rendering properly. \since 5.3 + \sa resetOpenGLState() */ /*! @@ -3274,6 +3277,8 @@ QQmlIncubationController *QQuickWindow::incubationController() const \warning Make very sure that a signal handler for beforeRendering leaves the GL context in the same state as it was when the signal handler was entered. Failing to do so can result in the scene not rendering properly. + + \sa resetOpenGLState() */ /*! @@ -3293,6 +3298,8 @@ QQmlIncubationController *QQuickWindow::incubationController() const \warning Make very sure that a signal handler for afterRendering() leaves the GL context in the same state as it was when the signal handler was entered. Failing to do so can result in the scene not rendering properly. + + \sa resetOpenGLState() */ /*! @@ -3345,7 +3352,7 @@ QQmlIncubationController *QQuickWindow::incubationController() const context in the same state as it was when the signal handler was entered. Failing to do so can result in the scene not rendering properly. - \sa sceneGraphInvalidated() + \sa sceneGraphInvalidated(), resetOpenGLState() \since 5.3 */ diff --git a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml index 70682bed0bfa6b47134fb7c9a514138fbd7e74e6..c8c2f4960f7ba961edbc2d85df922e84a055dd19 100644 --- a/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml +++ b/tests/auto/quick/qquickcanvasitem/data/CanvasTestCase.qml @@ -19,8 +19,8 @@ TestCase { // { tag:"image cooperative", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Cooperative}}, { tag:"image immediate", properties:{width:100, height:100, renderTarget:Canvas.Image, renderStrategy:Canvas.Immediate}}, // { tag:"fbo cooperative", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Cooperative}}, -// { tag:"fbo immediate", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Immediate}}, -// { tag:"fbo threaded", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Threaded}} + { tag:"fbo immediate", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Immediate}}, + { tag:"fbo threaded", properties:{width:100, height:100, renderTarget:Canvas.FramebufferObject, renderStrategy:Canvas.Threaded}} ]; return []; } diff --git a/tests/auto/quick/qquicklistview/data/layoutChangeSort.qml b/tests/auto/quick/qquicklistview/data/layoutChangeSort.qml new file mode 100644 index 0000000000000000000000000000000000000000..e54f164e45c1c0a1cd0b5a7a682902adac6c9169 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/layoutChangeSort.qml @@ -0,0 +1,58 @@ +import QtQuick 2.0 +import QtQml.Models 2.1 + +Rectangle { + id: root + width: 240 + height: 320 + color: "#ffffff" + + + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + height: 20 + width: 240 + Text { + objectName: "delegateText" + text: display + } + color: ListView.isCurrentItem ? "lightsteelblue" : "white" + } + } + + DelegateModel { + id: delegateModel + objectName: "delegateModel" + model: testModel + delegate: myDelegate + } + + ListView { + id: list + objectName: "listView" + model: delegateModel; + focus: true + anchors.fill: parent + + + section { + property: "SortRole"; + delegate: Rectangle { + width: parent.width; + height: 20; + color: "steelblue"; + + Text { + anchors { + fill: parent; + margins: 5; + } + text: section; + } + } + } + } +} diff --git a/tests/auto/quick/qquicklistview/data/qtbug39492.qml b/tests/auto/quick/qquicklistview/data/qtbug39492.qml new file mode 100644 index 0000000000000000000000000000000000000000..4df3a080d7084fbbeb7724d9743e3615f8a79088 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/qtbug39492.qml @@ -0,0 +1,40 @@ +import QtQuick 2.0 +import QtQml.Models 2.1 + +Rectangle { + id: root + width: 240 + height: 320 + color: "#ffffff" + + + Component { + id: myDelegate + Rectangle { + id: wrapper + objectName: "wrapper" + height: 20 + width: 240 + Text { + objectName: "delegateText" + text: display + } + color: ListView.isCurrentItem ? "lightsteelblue" : "white" + } + } + + DelegateModel { + id: delegateModel + objectName: "delegateModel" + model: testModel + delegate: myDelegate + } + + ListView { + id: list + objectName: "listView" + model: delegateModel; + focus: true + anchors.fill: parent + } +} diff --git a/tests/auto/quick/qquicklistview/qquicklistview.pro b/tests/auto/quick/qquicklistview/qquicklistview.pro index 2ae04d32fedddfd6e8933780a878629c227102ed..c9b634b9e825bbad9c31be371a55141a1fccf510 100644 --- a/tests/auto/quick/qquicklistview/qquicklistview.pro +++ b/tests/auto/quick/qquicklistview/qquicklistview.pro @@ -4,10 +4,13 @@ TARGET = tst_qquicklistview macx:CONFIG -= app_bundle HEADERS += incrementalmodel.h \ - proxytestinnermodel.h + proxytestinnermodel.h \ + randomsortmodel.h SOURCES += tst_qquicklistview.cpp \ incrementalmodel.cpp \ - proxytestinnermodel.cpp + proxytestinnermodel.cpp \ + randomsortmodel.cpp + include (../../shared/util.pri) include (../shared/util.pri) diff --git a/tests/auto/quick/qquicklistview/randomsortmodel.cpp b/tests/auto/quick/qquicklistview/randomsortmodel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e9082e39c9935547658cc0339f589db1408b81c3 --- /dev/null +++ b/tests/auto/quick/qquicklistview/randomsortmodel.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "randomsortmodel.h" + +RandomSortModel::RandomSortModel(QObject* parent): + QAbstractListModel(parent) +{ + for (int i = 0; i < 10; ++i) { + mData.append(qMakePair(QString::fromLatin1("Item %1").arg(i), i * 10)); + } +} + +QHash<int, QByteArray> RandomSortModel::roleNames() const +{ + QHash<int,QByteArray> roles = QAbstractItemModel::roleNames(); + roles[Qt::UserRole] = "SortRole"; + return roles; +} + + +int RandomSortModel::rowCount(const QModelIndex& parent) const +{ + if (!parent.isValid()) + return mData.count(); + + return 0; +} + +QVariant RandomSortModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) { + return QVariant(); + } + + if (index.row() >= mData.count()) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return QString::fromLatin1("%1 (weight %2)").arg(mData[index.row()].first).arg(mData[index.row()].second); + } else if (role == Qt::UserRole) { + return mData[index.row()].second; + } + + return QVariant(); +} + +void RandomSortModel::randomize() +{ + const int row = qrand() % mData.count(); + int random; + bool exists = false; + // Make sure we won't end up with two items with the same weight, as that + // would make unit-testing much harder + do { + exists = false; + random = qrand() % (mData.count() * 10); + QList<QPair<QString, int> >::ConstIterator iter, end; + for (iter = mData.constBegin(), end = mData.constEnd(); iter != end; ++iter) { + if ((*iter).second == random) { + exists = true; + break; + } + } + } while (exists); + mData[row].second = random; + Q_EMIT dataChanged(index(row, 0), index(row, 0)); +} diff --git a/tests/auto/quick/qquicklistview/randomsortmodel.h b/tests/auto/quick/qquicklistview/randomsortmodel.h new file mode 100644 index 0000000000000000000000000000000000000000..8d28698d9b8e74ad2cbf209aca25ffa90bcd06b3 --- /dev/null +++ b/tests/auto/quick/qquicklistview/randomsortmodel.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RANDOMSORTMODEL_H +#define RANDOMSORTMODEL_H + +#include <QAbstractListModel> + +class RandomSortModel : public QAbstractListModel +{ + Q_OBJECT + +public: + explicit RandomSortModel(QObject* parent = 0); + QHash<int, QByteArray> roleNames() const; + + QVariant data(const QModelIndex& index, int role) const; + int rowCount(const QModelIndex& parent = QModelIndex()) const; + + void randomize(); + + private: + QList<QPair<QString, int> > mData; +}; + +#endif // RANDOMSORTMODEL_H diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 39610e57f75b86001c3aea3eca43be019aaee6c1..50bf1e03f1dde6abd80c3a52fbb08ff9c56bf8e9 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -41,6 +41,8 @@ #include <QtTest/QtTest> #include <QtCore/QStringListModel> +#include <QtCore/QSortFilterProxyModel> +#include <QtGui/QStandardItemModel> #include <QtQuick/qquickview.h> #include <QtQml/qqmlengine.h> #include <QtQml/qqmlcontext.h> @@ -50,11 +52,13 @@ #include <QtQuick/private/qquicktext_p.h> #include <QtQml/private/qqmlobjectmodel_p.h> #include <QtQml/private/qqmllistmodel_p.h> +#include <QtQml/private/qqmldelegatemodel_p.h> #include "../../shared/util.h" #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" #include "incrementalmodel.h" #include "proxytestinnermodel.h" +#include "randomsortmodel.h" #include <math.h> Q_DECLARE_METATYPE(Qt::LayoutDirection) @@ -62,6 +66,7 @@ Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection) Q_DECLARE_METATYPE(QQuickItemView::PositionMode) Q_DECLARE_METATYPE(QQuickListView::Orientation) Q_DECLARE_METATYPE(Qt::Key) +Q_DECLARE_METATYPE(QPersistentModelIndex) using namespace QQuickViewTestUtil; using namespace QQuickVisualTestUtil; @@ -237,6 +242,11 @@ private slots: void QTBUG_38209(); void programmaticFlickAtBounds(); + void layoutChange(); + + void QTBUG_39492_data(); + void QTBUG_39492(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -7827,8 +7837,120 @@ void tst_QQuickListView::programmaticFlickAtBounds() QVERIFY(!spy.wait(100)); } -QTEST_MAIN(tst_QQuickListView) +void tst_QQuickListView::layoutChange() +{ + RandomSortModel *model = new RandomSortModel; + QSortFilterProxyModel *sortModel = new QSortFilterProxyModel; + sortModel->setSourceModel(model); + sortModel->setSortRole(Qt::UserRole); + sortModel->setDynamicSortFilter(true); + sortModel->sort(0); -#include "tst_qquicklistview.moc" + QScopedPointer<QQuickView> window(createView()); + window->rootContext()->setContextProperty("testModel", QVariant::fromValue(sortModel)); + window->setSource(testFileUrl("layoutChangeSort.qml")); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); + + QQuickListView *listview = window->rootObject()->findChild<QQuickListView *>("listView"); + QVERIFY(listview); + + for (int iter = 0; iter < 100; iter++) { + for (int i = 0; i < sortModel->rowCount(); ++i) { + QQuickItem *delegateItem = listview->itemAt(10, 10 + 2 * i * 20 + 20); // item + group + QVERIFY(delegateItem); + QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>("delegateText"); + QVERIFY(delegateText); + + QCOMPARE(delegateText->property("text").toString(), + sortModel->index(i, 0, QModelIndex()).data().toString()); + } + + model->randomize(); + listview->forceLayout(); + QTest::qWait(5); // give view a chance to update + } +} + +void tst_QQuickListView::QTBUG_39492_data() +{ + QStandardItemModel *sourceModel = new QStandardItemModel(this); + for (int i = 0; i < 5; ++i) { + QStandardItem *item = new QStandardItem(QString::number(i)); + for (int j = 0; j < 5; ++j) { + QStandardItem *subItem = new QStandardItem(QString("%1-%2").arg(i).arg(j)); + item->appendRow(subItem); + } + sourceModel->appendRow(item); + } + + QSortFilterProxyModel *sortModel = new QSortFilterProxyModel(this); + sortModel->setSourceModel(sourceModel); + + QTest::addColumn<QSortFilterProxyModel*>("model"); + QTest::addColumn<QPersistentModelIndex>("rootIndex"); + + QTest::newRow("invalid rootIndex") + << sortModel + << QPersistentModelIndex(); + QTest::newRow("rootIndex 1") + << sortModel + << QPersistentModelIndex(sortModel->index(1, 0)); + QTest::newRow("rootIndex 3") + << sortModel + << QPersistentModelIndex(sortModel->index(3, 0)); + + const QModelIndex rootIndex2 = sortModel->index(2, 0); + QTest::newRow("rootIndex 2-1") + << sortModel + << QPersistentModelIndex(sortModel->index(1, 0, rootIndex2)); +} + +void tst_QQuickListView::QTBUG_39492() +{ + QFETCH(QSortFilterProxyModel*, model); + QFETCH(QPersistentModelIndex, rootIndex); + + QQuickView *window = getView(); + window->rootContext()->setContextProperty("testModel", QVariant::fromValue(model)); + window->setSource(testFileUrl("qtbug39492.qml")); + + QQuickListView *listview = window->rootObject()->findChild<QQuickListView *>("listView"); + QVERIFY(listview); + + QQmlDelegateModel *delegateModel = window->rootObject()->findChild<QQmlDelegateModel *>("delegateModel"); + QVERIFY(delegateModel); + + delegateModel->setRootIndex(QVariant::fromValue(QModelIndex(rootIndex))); + model->sort(0, Qt::AscendingOrder); + listview->forceLayout(); + + for (int i = 0; i < model->rowCount(rootIndex); ++i) { + QQuickItem *delegateItem = listview->itemAt(10, 10 + i * 20); + QVERIFY(delegateItem); + QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>("delegateText"); + QVERIFY(delegateText); + QCOMPARE(delegateText->property("text").toString(), + model->index(i, 0, rootIndex).data().toString()); + } + + model->sort(0, Qt::DescendingOrder); + listview->forceLayout(); + + for (int i = 0; i < model->rowCount(rootIndex); ++i) { + QQuickItem *delegateItem = listview->itemAt(10, 10 + i * 20); + QVERIFY(delegateItem); + QQuickItem *delegateText = delegateItem->findChild<QQuickItem *>("delegateText"); + QVERIFY(delegateText); + QCOMPARE(delegateText->property("text").toString(), + model->index(i, 0, rootIndex).data().toString()); + } + + releaseView(window); +} + +QTEST_MAIN(tst_QQuickListView) + +#include "tst_qquicklistview.moc" diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index bc55c40434d62f89598a140b57144bc30b45c809..4934d20f75201709c85fb599627f7e42f43e5833 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -69,6 +69,10 @@ #ifdef Q_OS_UNIX #include <signal.h> #endif +#ifdef Q_OS_WIN +#include <crtdbg.h> +#include <qt_windows.h> +#endif QString pluginImportPath; bool verbose = false; @@ -306,6 +310,7 @@ QSet<const QMetaObject *> collectReachableMetaObjects(QQmlEngine *engine, std::cerr << "Got " << qPrintable( tyName ) << " (" << qPrintable( QString::fromUtf8(ty->typeName()) ) << ")" << std::endl; collectReachableMetaObjects(object, &metas); + object->deleteLater(); } else { std::cerr << "Could not create" << qPrintable(tyName) << std::endl; } @@ -731,6 +736,13 @@ void printDebugMessage(QtMsgType, const QMessageLogContext &, const QString &msg int main(int argc, char *argv[]) { +#ifdef Q_OS_WIN + // we do not want windows popping up if the module loaded triggers an assert + SetErrorMode(SEM_NOGPFAULTERRORBOX); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG); + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); +#endif // The default message handler might not print to console on some systems. Enforce this. qInstallMessageHandler(printDebugMessage); #ifdef Q_OS_UNIX