diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-12-16 17:05:21 +0100 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-12-16 17:05:21 +0100 |
commit | 13e88fe2b9b1680cb161a249289c3ba998f08c0c (patch) | |
tree | 496a9d88c69b441e8c88aa0416b327faca3a1532 /src/quick | |
parent | a2dad3ddee9c4bf274a7c6469342e4104605ceeb (diff) | |
parent | 470ba767663e4ad9d3183fb56ee89361354dfefb (diff) |
Merge remote-tracking branch 'origin/stable' into dev
Conflicts:
src/quick/items/qquickitem.cpp
src/quick/items/qquicktext.cpp
tests/auto/quick/qquicklistview/tst_qquicklistview.cpp
Change-Id: I0bc5786098193c2c40b6fd8905de75d90f6ed0cf
Diffstat (limited to 'src/quick')
38 files changed, 738 insertions, 437 deletions
diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h index 559f41e546..2d8fbb5f85 100644 --- a/src/quick/items/context2d/qquickcanvascontext_p.h +++ b/src/quick/items/context2d/qquickcanvascontext_p.h @@ -71,8 +71,6 @@ public: virtual void setV8Engine(QV8Engine *engine) = 0; virtual QV4::ReturnedValue v4value() const = 0; - virtual QSGDynamicTexture *texture() const = 0; - virtual QImage toImage(const QRectF& bounds) = 0; Q_SIGNALS: diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index ee1b5f316c..be9c592228 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -44,8 +44,10 @@ #include <private/qquickitem_p.h> #include <private/qquickcanvascontext_p.h> #include <private/qquickcontext2d_p.h> +#include <private/qquickcontext2dtexture_p.h> #include <qsgsimpletexturenode.h> #include <QtQuick/private/qquickpixmapcache_p.h> +#include <QtGui/QGuiApplication> #include <qqmlinfo.h> #include <private/qqmlengine_p.h> @@ -58,19 +60,15 @@ QT_BEGIN_NAMESPACE -QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image, QQuickWindow *window) +QQuickCanvasPixmap::QQuickCanvasPixmap(const QImage& image) : m_pixmap(0) , m_image(image) - , m_texture(0) - , m_window(window) { } -QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *window) +QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap) : m_pixmap(pixmap) - , m_texture(0) - , m_window(window) { } @@ -78,8 +76,6 @@ QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *windo QQuickCanvasPixmap::~QQuickCanvasPixmap() { delete m_pixmap; - if (m_texture) - m_texture->deleteLater(); } qreal QQuickCanvasPixmap::width() const @@ -105,18 +101,6 @@ bool QQuickCanvasPixmap::isValid() const return !m_image.isNull(); } -QSGTexture *QQuickCanvasPixmap::texture() -{ - if (!m_texture) { - if (m_pixmap) { - Q_ASSERT(m_pixmap->textureFactory()); - m_texture = m_pixmap->textureFactory()->createTexture(m_window); - } else { - m_texture = m_window->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas); - } - } - return m_texture; -} QImage QQuickCanvasPixmap::image() { if (m_image.isNull() && m_pixmap) @@ -194,7 +178,7 @@ QQuickCanvasItemPrivate::QQuickCanvasItemPrivate() , hasCanvasWindow(false) , available(false) , renderTarget(QQuickCanvasItem::Image) - , renderStrategy(QQuickCanvasItem::Cooperative) + , renderStrategy(QQuickCanvasItem::Immediate) { implicitAntialiasing = true; } @@ -246,10 +230,14 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() The Canvas.FramebufferObject render target utilizes OpenGL hardware acceleration rather than rendering into system memory, which in many cases - results in faster rendering. + results in faster rendering. Canvas.FramebufferObject relies on the + OpenGL extensions \c GL_EXT_framebuffer_multisample and + \c GL_EXT_framebuffer_blit for antialiasing. It will also use more + graphics memory when rendering strategy is anything other than + Canvas.Cooperative. The default render target is Canvas.Image and the default renderStrategy is - Canvas.Cooperative. + Canvas.Immediate. \section1 Tiled Canvas The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize @@ -489,7 +477,7 @@ void QQuickCanvasItem::setCanvasWindow(const QRectF& rect) context will choose appropriate options and Canvas will signal the change to the properties. - The default render target is \c Canvas.FramebufferObject. + The default render target is \c Canvas.Image. */ QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const { @@ -531,7 +519,7 @@ void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target) the GUI thread. Selecting \c Canvas.Cooperative, does not guarantee rendering will occur on a thread separate from the GUI thread. - The default value is \c Canvas.Cooperative. + The default value is \c Canvas.Immediate. \sa renderTarget */ @@ -689,6 +677,15 @@ void QQuickCanvasItem::updatePolish() } } +class QQuickCanvasNode : public QSGSimpleTextureNode +{ +public: + ~QQuickCanvasNode() + { + delete texture(); + } +}; + QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { Q_D(QQuickCanvasItem); @@ -698,9 +695,9 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData return 0; } - QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode*>(oldNode); + QQuickCanvasNode *node = static_cast<QQuickCanvasNode*>(oldNode); if (!node) - node = new QSGSimpleTextureNode; + node = new QQuickCanvasNode(); if (d->smooth) node->setFiltering(QSGTexture::Linear); @@ -712,8 +709,15 @@ QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData d->context->flush(); } - node->setTexture(d->context->texture()); - node->markDirty(QSGNode::DirtyMaterial); + QQuickContext2D *ctx = qobject_cast<QQuickContext2D *>(d->context); + QQuickContext2DTexture *factory = ctx->texture(); + QSGTexture *texture = factory->textureForNextFrame(node->texture()); + if (!texture) { + delete node; + return 0; + } + + node->setTexture(texture); node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size())); return node; } @@ -916,7 +920,7 @@ void QQuickCanvasItem::loadImage(const QUrl& url) if (!d->pixmaps.contains(fullPathUrl)) { QQuickPixmap* pix = new QQuickPixmap(); QQmlRefPointer<QQuickCanvasPixmap> canvasPix; - canvasPix.take(new QQuickCanvasPixmap(pix, d->window)); + canvasPix.take(new QQuickCanvasPixmap(pix)); d->pixmaps.insert(fullPathUrl, canvasPix); pix->load(qmlEngine(this) diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index 2ec36cfe73..3baf68d418 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -52,17 +52,15 @@ QT_BEGIN_NAMESPACE class QQuickCanvasContext; class QQuickCanvasItemPrivate; -class QSGTexture; class QQuickPixmap; class QQuickCanvasPixmap : public QQmlRefCount { public: - QQuickCanvasPixmap(const QImage& image, QQuickWindow *window); - QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *window); + QQuickCanvasPixmap(const QImage& image); + QQuickCanvasPixmap(QQuickPixmap *pixmap); ~QQuickCanvasPixmap(); - QSGTexture *texture(); QImage image(); qreal width() const; @@ -73,8 +71,6 @@ public: private: QQuickPixmap *m_pixmap; QImage m_image; - QSGTexture *m_texture; - QQuickWindow *m_window; }; class QQuickCanvasItem : public QQuickItem diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 2a9e0f2ac2..b6eb2db33d 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -484,7 +484,7 @@ public: QQuickJSContext2D(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QQuickContext2D* context; @@ -653,7 +653,7 @@ public: , patternRepeatX(false) , patternRepeatY(false) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); } QBrush brush; bool patternRepeatX:1; @@ -870,7 +870,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object QQuickJSContext2DPixelData(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); flags &= ~SimpleArray; } @@ -893,7 +893,7 @@ struct QQuickJSContext2DImageData : public QV4::Object QQuickJSContext2DImageData(QV4::ExecutionEngine *engine) : QV4::Object(engine) { - vtbl = &static_vtbl; + setVTable(&static_vtbl); pixelData = QV4::Primitive::undefinedValue(); QV4::Scope scope(engine); @@ -2985,14 +2985,14 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawImage(QV4::CallContext } else if (QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(qobjectWrapper->object())) { QImage img = canvas->toImage(); if (!img.isNull()) - pixmap.take(new QQuickCanvasPixmap(img, canvas->window())); + pixmap.take(new QQuickCanvasPixmap(img)); } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } } else if (QV4::Referenced<QQuickJSContext2DImageData> imageData = arg->asRef<QQuickJSContext2DImageData>()) { QV4::Scoped<QQuickJSContext2DPixelData> pix(scope, imageData->pixelData.as<QQuickJSContext2DPixelData>()); if (pix && !pix->image.isNull()) { - pixmap.take(new QQuickCanvasPixmap(pix->image, r->context->canvas()->window())); + pixmap.take(new QQuickCanvasPixmap(pix->image)); } else { V4THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch"); } @@ -3165,7 +3165,7 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(QV4::Managed *m, uint QV4::Scope scope(v4); QV4::Scoped<QQuickJSContext2DPixelData> r(scope, m->as<QQuickJSContext2DPixelData>()); if (!m) - return m->engine()->current->throwTypeError(); + return m->engine()->currentContext()->throwTypeError(); if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4)) { if (hasProperty) @@ -3200,7 +3200,7 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q QV4::Scoped<QQuickJSContext2DPixelData> r(scope, m->as<QQuickJSContext2DPixelData>()); if (!r) { - v4->current->throwTypeError(); + v4->currentContext()->throwTypeError(); return; } @@ -4084,6 +4084,15 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args QQuickWindow *window = canvasItem->window(); m_renderStrategy = canvasItem->renderStrategy(); +#ifdef Q_OS_WIN + if (m_renderTarget == QQuickCanvasItem::FramebufferObject + && (m_renderStrategy != QQuickCanvasItem::Cooperative)) { + // On windows a context needs to be unbound set up sharing, so + // for simplicity we disallow FBO + !coop here. + m_renderTarget = QQuickCanvasItem::Image; + } +#endif + switch (m_renderTarget) { case QQuickCanvasItem::Image: m_texture = new QQuickContext2DImageTexture; @@ -4099,6 +4108,7 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args m_texture->setCanvasSize(canvasItem->canvasSize().toSize()); m_texture->setSmooth(canvasItem->smooth()); m_texture->setAntialiasing(canvasItem->antialiasing()); + m_texture->setOnCustomThread(m_renderStrategy == QQuickCanvasItem::Threaded); m_thread = QThread::currentThread(); QThread *renderThread = m_thread; @@ -4129,28 +4139,31 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args void QQuickContext2D::prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing) { - QMetaObject::invokeMethod(m_texture - , "canvasChanged" - , Qt::AutoConnection - , Q_ARG(QSize, canvasSize) - , Q_ARG(QSize, tileSize) - , Q_ARG(QRect, canvasWindow) - , Q_ARG(QRect, dirtyRect) - , Q_ARG(bool, smooth) - , Q_ARG(bool, antialiasing)); + if (m_texture->thread() == QThread::currentThread()) { + m_texture->canvasChanged(canvasSize, tileSize, canvasWindow, dirtyRect, smooth, antialiasing); + } else { + QEvent *e = new QQuickContext2DTexture::CanvasChangeEvent(canvasSize, + tileSize, + canvasWindow, + dirtyRect, + smooth, + antialiasing); + QCoreApplication::postEvent(m_texture, e); + } } void QQuickContext2D::flush() { - if (m_buffer) - QMetaObject::invokeMethod(m_texture, - "paint", - Qt::AutoConnection, - Q_ARG(QQuickContext2DCommandBuffer*, m_buffer)); + if (m_buffer) { + if (m_texture->thread() == QThread::currentThread()) + m_texture->paint(m_buffer); + else + QCoreApplication::postEvent(m_texture, new QQuickContext2DTexture::PaintEvent(m_buffer)); + } m_buffer = new QQuickContext2DCommandBuffer(); } -QSGDynamicTexture *QQuickContext2D::texture() const +QQuickContext2DTexture *QQuickContext2D::texture() const { return m_texture; } @@ -4164,6 +4177,7 @@ QImage QQuickContext2D::toImage(const QRectF& bounds) qWarning() << "Pixel readback is not supported in Cooperative mode, please try Threaded or Immediate mode"; return QImage(); } else { + QCoreApplication::postEvent(m_texture, new QEvent(QEvent::Type(QEvent::User + 10))); QMetaObject::invokeMethod(m_texture, "grabImage", Qt::BlockingQueuedConnection, diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 4bef50d9cd..6399da3dee 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -74,6 +74,8 @@ class QOpenGLContext; class QQuickContext2D : public QQuickCanvasContext { + Q_OBJECT + public: Q_DISABLE_COPY(QQuickContext2D) @@ -171,8 +173,8 @@ public: void prepare(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing); void flush(); void sync(); - QThread *thread() const {return m_thread;} - QSGDynamicTexture *texture() const; + QThread *thread() const { return m_thread; } + QQuickContext2DTexture *texture() const; QImage toImage(const QRectF& bounds); QV4::ReturnedValue v4value() const; diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp index d2f4e3317d..06a0713365 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp @@ -420,38 +420,8 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s Q_ASSERT(!pix.isNull()); const bool hasShadow = HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor); - if (p->paintEngine()->type() != QPaintEngine::OpenGL2 || hasShadow){ - //TODO: generate shadow blur with shaders - qt_drawImage(p, state, pix->image(), sr, dr, hasShadow); - } else if (pix->texture()){ - QSGTexture *tex = pix->texture(); - QSGDynamicTexture *dynamicTexture = qobject_cast<QSGDynamicTexture *>(tex); - if (dynamicTexture) - dynamicTexture->updateTexture(); - - if (tex->textureId()) { - - if (sr.width() < 0) - sr.setWidth(tex->textureSize().width()); - if (sr.height() < 0) - sr.setHeight(tex->textureSize().height()); - - if (dr.width() < 0) - dr.setWidth(sr.width()); - if (dr.height() < 0) - dr.setHeight(sr.height()); - - qreal srBottom = sr.bottom(); - sr.setBottom(sr.top()); - sr.setTop(srBottom); - - tex->bind(); - if (p->paintEngine()->type() == QPaintEngine::OpenGL2) { - QOpenGL2PaintEngineEx *engine = static_cast<QOpenGL2PaintEngineEx *>(p->paintEngine()); - engine->drawTexture(dr, tex->textureId(), tex->textureSize(), sr); - } - } - } + //TODO: generate shadow blur with shaders + qt_drawImage(p, state, pix->image(), sr, dr, hasShadow); break; } case QQuickContext2D::GetImageData: diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h index 29cdc73708..9e79333a0c 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h @@ -58,8 +58,6 @@ public: void reset(); void clear(); - void lockQueue() { queueLock.lock(); } - void unlockQueue() { queueLock.unlock(); } inline int size() {return commands.size();} inline bool isEmpty() const {return commands.isEmpty(); } inline bool hasNext() const {return cmdIdx < commands.size(); } diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp index 8dc9978089..8dd48b4988 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture.cpp +++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp @@ -50,6 +50,7 @@ #include <QOpenGLFramebufferObject> #include <QOpenGLFramebufferObjectFormat> #include <QtCore/QThread> +#include <QtGui/QGuiApplication> QT_BEGIN_NAMESPACE @@ -90,7 +91,6 @@ struct GLAcquireContext { QQuickContext2DTexture::QQuickContext2DTexture() : m_context(0) , m_item(0) - , m_dirtyCanvas(false) , m_canvasWindowChanged(false) , m_dirtyTexture(false) , m_smooth(true) @@ -105,23 +105,20 @@ QQuickContext2DTexture::~QQuickContext2DTexture() clearTiles(); } -QSize QQuickContext2DTexture::textureSize() const -{ - return m_canvasWindow.size(); -} - void QQuickContext2DTexture::markDirtyTexture() { + if (m_onCustomThread) + m_mutex.lock(); m_dirtyTexture = true; - updateTexture(); emit textureChanged(); + if (m_onCustomThread) + m_mutex.unlock(); } bool QQuickContext2DTexture::setCanvasSize(const QSize &size) { if (m_canvasSize != size) { m_canvasSize = size; - m_dirtyCanvas = true; return true; } return false; @@ -131,7 +128,6 @@ bool QQuickContext2DTexture::setTileSize(const QSize &size) { if (m_tileSize != size) { m_tileSize = size; - m_dirtyCanvas = true; return true; } return false; @@ -195,7 +191,6 @@ void QQuickContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& if (canvasSize == canvasWindow.size()) { m_tiledCanvas = false; - m_dirtyCanvas = false; } else { m_tiledCanvas = true; } @@ -309,7 +304,6 @@ QRect QQuickContext2DTexture::createTiles(const QRect& window) m_tiles.clear(); if (window.isEmpty()) { - m_dirtyCanvas = false; return QRect(); } @@ -351,7 +345,6 @@ QRect QQuickContext2DTexture::createTiles(const QRect& window) qDeleteAll(oldTiles); - m_dirtyCanvas = false; return r; } @@ -366,6 +359,20 @@ QSize QQuickContext2DTexture::adjustedTileSize(const QSize &ts) return ts; } +bool QQuickContext2DTexture::event(QEvent *e) +{ + if ((int) e->type() == QEvent::User + 1) { + PaintEvent *pe = static_cast<PaintEvent *>(e); + paint(pe->buffer); + return true; + } else if ((int) e->type() == QEvent::User + 2) { + CanvasChangeEvent *ce = static_cast<CanvasChangeEvent *>(e); + canvasChanged(ce->canvasSize, ce->tileSize, ce->canvasWindow, ce->dirtyRect, ce->smooth, ce->antialiasing); + return true; + } + return QObject::event(e); +} + static inline QSize npotAdjustedSize(const QSize &size) { static bool checked = false; @@ -391,6 +398,9 @@ QQuickContext2DFBOTexture::QQuickContext2DFBOTexture() , m_multisampledFbo(0) , m_paint_device(0) { + m_displayTextures[0] = 0; + m_displayTextures[1] = 0; + m_displayTexture = -1; } QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture() @@ -403,17 +413,52 @@ QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture() delete m_fbo; delete m_multisampledFbo; delete m_paint_device; + + glDeleteTextures(2, m_displayTextures); } -QSize QQuickContext2DFBOTexture::adjustedTileSize(const QSize &ts) +QSGTexture *QQuickContext2DFBOTexture::textureForNextFrame(QSGTexture *lastTexture) { - return npotAdjustedSize(ts); + QSGPlainTexture *texture = static_cast<QSGPlainTexture *>(lastTexture); + + if (m_onCustomThread) + m_mutex.lock(); + + if (m_fbo) { + if (!texture) { + texture = new QSGPlainTexture(); + texture->setHasMipmaps(false); + texture->setHasAlphaChannel(true); + texture->setOwnsTexture(false); + m_dirtyTexture = true; + } + + if (m_dirtyTexture) { + if (!m_context->glContext()) { + // on a rendering thread, use the fbo directly... + texture->setTextureId(m_fbo->texture()); + } else { + // on GUI or custom thread, use display textures... + m_displayTexture = m_displayTexture == 0 ? 1 : 0; + texture->setTextureId(m_displayTextures[m_displayTexture]); + } + texture->setTextureSize(m_fbo->size()); + m_dirtyTexture = false; + } + + } + + if (m_onCustomThread) { + m_condition.wakeOne(); + m_mutex.unlock(); + } + + return texture; } -void QQuickContext2DFBOTexture::bind() +QSize QQuickContext2DFBOTexture::adjustedTileSize(const QSize &ts) { - glBindTexture(GL_TEXTURE_2D, textureId()); - updateBindOptions(); + return npotAdjustedSize(ts); } QRectF QQuickContext2DFBOTexture::normalizedTextureSubRect() const @@ -424,20 +469,6 @@ QRectF QQuickContext2DFBOTexture::normalizedTextureSubRect() const , qreal(m_canvasWindow.height()) / m_fboSize.height()); } - -int QQuickContext2DFBOTexture::textureId() const -{ - return m_fbo? m_fbo->texture() : 0; -} - - -bool QQuickContext2DFBOTexture::updateTexture() -{ - bool textureUpdated = m_dirtyTexture; - m_dirtyTexture = false; - return textureUpdated; -} - QQuickContext2DTile* QQuickContext2DFBOTexture::createTile() const { return new QQuickContext2DFBOTile(); @@ -461,7 +492,6 @@ bool QQuickContext2DFBOTexture::doMultisampling() const void QQuickContext2DFBOTexture::grabImage(const QRectF& rf) { Q_ASSERT(rf.isValid()); - if (!m_fbo) { m_context->setGrabbedImage(QImage()); return; @@ -531,7 +561,6 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() } else { QOpenGLFramebufferObjectFormat format; format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - m_fbo = new QOpenGLFramebufferObject(m_fboSize, format); } } @@ -541,7 +570,6 @@ QPaintDevice* QQuickContext2DFBOTexture::beginPainting() else m_fbo->bind(); - if (!m_paint_device) { QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size()); gl_device->setPaintFlipped(true); @@ -557,25 +585,47 @@ void QQuickContext2DFBOTexture::endPainting() QQuickContext2DTexture::endPainting(); if (m_multisampledFbo) QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo); + + if (m_context->glContext()) { + /* 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 + * the texture we are displaying in the SG rendering thread to avoid + * stalls and read/write issues in the GL pipeline as the FBO's texture + * could then potentially be used in different threads. + * + * We could have gotten away with only one display texture, but this + * would have implied that beginPainting would have to wait for SG + * to release that texture. + */ + + if (m_onCustomThread) + m_mutex.lock(); + + if (m_displayTextures[0] == 0) { + m_displayTexture = 1; + glGenTextures(2, m_displayTextures); + } + + m_fbo->bind(); + GLuint target = m_displayTexture == 0 ? 1 : 0; + glBindTexture(GL_TEXTURE_2D, m_displayTextures[target]); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, m_fbo->width(), m_fbo->height(), 0); + + if (m_onCustomThread) + m_mutex.unlock(); + } + + m_fbo->bindDefault(); } QQuickContext2DImageTexture::QQuickContext2DImageTexture() : QQuickContext2DTexture() - , m_texture(0) { } QQuickContext2DImageTexture::~QQuickContext2DImageTexture() { - if (m_texture && m_texture->thread() != QThread::currentThread()) - m_texture->deleteLater(); - else - delete m_texture; -} - -int QQuickContext2DImageTexture::textureId() const -{ - return imageTexture()->textureId(); } QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const @@ -583,22 +633,6 @@ QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const return QQuickCanvasItem::Image; } -void QQuickContext2DImageTexture::bind() -{ - imageTexture()->setFiltering(filtering()); - imageTexture()->bind(); -} - -bool QQuickContext2DImageTexture::updateTexture() -{ - bool textureUpdated = m_dirtyTexture; - if (m_dirtyTexture) { - imageTexture()->setImage(m_image); - m_dirtyTexture = false; - } - return textureUpdated; -} - QQuickContext2DTile* QQuickContext2DImageTexture::createTile() const { return new QQuickContext2DImageTile(); @@ -608,19 +642,32 @@ void QQuickContext2DImageTexture::grabImage(const QRectF& rf) { Q_ASSERT(rf.isValid()); Q_ASSERT(m_context); - QImage grabbed = m_image.copy(rf.toRect()); + QImage grabbed = m_displayImage.copy(rf.toRect()); m_context->setGrabbedImage(grabbed); } -QSGPlainTexture *QQuickContext2DImageTexture::imageTexture() const +QSGTexture *QQuickContext2DImageTexture::textureForNextFrame(QSGTexture *last) { - if (!m_texture) { - QQuickContext2DImageTexture *that = const_cast<QQuickContext2DImageTexture *>(this); - that->m_texture = new QSGPlainTexture; - that->m_texture->setOwnsTexture(true); - that->m_texture->setHasMipmaps(false); + QSGPlainTexture *texture = static_cast<QSGPlainTexture *>(last); + + if (m_onCustomThread) + m_mutex.lock(); + + if (!texture) { + texture = new QSGPlainTexture(); + texture->setHasMipmaps(false); + texture->setHasAlphaChannel(true); + m_dirtyTexture = true; + } + if (m_dirtyTexture) { + texture->setImage(m_displayImage); + m_dirtyTexture = false; } - return m_texture; + + if (m_onCustomThread) + m_mutex.unlock(); + + return texture; } QPaintDevice* QQuickContext2DImageTexture::beginPainting() @@ -639,6 +686,16 @@ QPaintDevice* QQuickContext2DImageTexture::beginPainting() return &m_image; } +void QQuickContext2DImageTexture::endPainting() +{ + QQuickContext2DTexture::endPainting(); + if (m_onCustomThread) + m_mutex.lock(); + m_displayImage = m_image; + if (m_onCustomThread) + m_mutex.unlock(); +} + void QQuickContext2DImageTexture::compositeTile(QQuickContext2DTile* tile) { Q_ASSERT(!tile->dirty()); diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h index 2a5b7a318a..cf0e8e3fa9 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture_p.h +++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h @@ -58,16 +58,44 @@ QT_BEGIN_NAMESPACE class QQuickContext2DTile; class QQuickContext2DCommandBuffer; -class QQuickContext2DTexture : public QSGDynamicTexture +class QQuickContext2DTexture : public QObject { Q_OBJECT public: + class PaintEvent : public QEvent { + public: + PaintEvent(QQuickContext2DCommandBuffer *b) : QEvent(QEvent::Type(QEvent::User + 1)), buffer(b) {} + QQuickContext2DCommandBuffer *buffer; + }; + + class CanvasChangeEvent : public QEvent { + public: + CanvasChangeEvent(const QSize &cSize, + const QSize &tSize, + const QRect &cWindow, + const QRect &dRect, + bool sm, + bool aa) + : QEvent(QEvent::Type(QEvent::User + 2)) + , canvasSize(cSize) + , tileSize(tSize) + , canvasWindow(cWindow) + , dirtyRect(dRect) + , smooth(sm) + , antialiasing(aa) + { + } + QSize canvasSize; + QSize tileSize; + QRect canvasWindow; + QRect dirtyRect; + bool smooth; + bool antialiasing; + }; + QQuickContext2DTexture(); ~QQuickContext2DTexture(); - virtual bool hasAlphaChannel() const {return true;} - virtual bool hasMipmaps() const {return false;} - virtual QSize textureSize() const; virtual QQuickCanvasItem::RenderTarget renderTarget() const = 0; static QRect tiledRect(const QRectF& window, const QSize& tileSize); @@ -78,15 +106,20 @@ public: void setAntialiasing(bool antialiasing); bool setDirtyRect(const QRect &dirtyRect); bool canvasDestroyed(); + void setOnCustomThread(bool is) { m_onCustomThread = is; } + + // Called during sync() on the scene graph thread while GUI is blocked. + virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame) = 0; + bool event(QEvent *e); Q_SIGNALS: void textureChanged(); public Q_SLOTS: - void markDirtyTexture(); - void setItem(QQuickCanvasItem* item); void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth, bool antialiasing); void paint(QQuickContext2DCommandBuffer *ccb); + void markDirtyTexture(); + void setItem(QQuickCanvasItem* item); virtual void grabImage(const QRectF& region = QRectF()) = 0; protected: @@ -110,13 +143,16 @@ protected: QSize m_tileSize; QRect m_canvasWindow; - uint m_dirtyCanvas : 1; + QMutex m_mutex; + QWaitCondition m_condition; + uint m_canvasWindowChanged : 1; uint m_dirtyTexture : 1; uint m_smooth : 1; uint m_antialiasing : 1; uint m_tiledCanvas : 1; uint m_painting : 1; + uint m_onCustomThread : 1; // Not GUI and not SGRender }; class QQuickContext2DFBOTexture : public QQuickContext2DTexture @@ -126,17 +162,16 @@ class QQuickContext2DFBOTexture : public QQuickContext2DTexture public: QQuickContext2DFBOTexture(); ~QQuickContext2DFBOTexture(); - virtual int textureId() const; - virtual bool updateTexture(); virtual QQuickContext2DTile* createTile() const; virtual QPaintDevice* beginPainting(); virtual void endPainting(); QRectF normalizedTextureSubRect() const; virtual QQuickCanvasItem::RenderTarget renderTarget() const; virtual void compositeTile(QQuickContext2DTile* tile); - virtual void bind(); QSize adjustedTileSize(const QSize &ts); + QSGTexture *textureForNextFrame(QSGTexture *); + public Q_SLOTS: virtual void grabImage(const QRectF& region = QRectF()); @@ -144,10 +179,12 @@ private: bool doMultisampling() const; QOpenGLFramebufferObject *m_fbo; QOpenGLFramebufferObject *m_multisampledFbo; - QMutex m_mutex; - QWaitCondition m_condition; QSize m_fboSize; QPaintDevice *m_paint_device; + + + GLuint m_displayTextures[2]; + int m_displayTexture; }; class QSGPlainTexture; @@ -158,24 +195,23 @@ class QQuickContext2DImageTexture : public QQuickContext2DTexture public: QQuickContext2DImageTexture(); ~QQuickContext2DImageTexture(); - virtual int textureId() const; - virtual void bind(); virtual QQuickCanvasItem::RenderTarget renderTarget() const; - virtual bool updateTexture(); virtual QQuickContext2DTile* createTile() const; virtual QPaintDevice* beginPainting(); + virtual void endPainting(); virtual void compositeTile(QQuickContext2DTile* tile); + virtual QSGTexture *textureForNextFrame(QSGTexture *lastFrame); + public Q_SLOTS: virtual void grabImage(const QRectF& region = QRectF()); private: - QSGPlainTexture *imageTexture() const; QImage m_image; + QImage m_displayImage; QPainter m_painter; - QSGPlainTexture* m_texture; }; QT_END_NAMESPACE diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 3996512f9d..41cdb3526b 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -39,6 +39,7 @@ HEADERS += \ $$PWD/qquickpincharea_p_p.h \ $$PWD/qquickflickable_p.h \ $$PWD/qquickflickable_p_p.h \ + $$PWD/qquickflickablebehavior_p.h \ $$PWD/qquicklistview_p.h \ $$PWD/qquickrepeater_p.h \ $$PWD/qquickrepeater_p_p.h \ diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index ae174d86e0..fa18d4aa30 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -41,6 +41,7 @@ #include "qquickflickable_p.h" #include "qquickflickable_p_p.h" +#include "qquickflickablebehavior_p.h" #include "qquickwindow.h" #include "qquickwindow_p.h" #include "qquickevents_p_p.h" @@ -58,52 +59,6 @@ QT_BEGIN_NAMESPACE -// The maximum number of pixels a flick can overshoot -#ifndef QML_FLICK_OVERSHOOT -#define QML_FLICK_OVERSHOOT 150 -#endif - -// The number of samples to use in calculating the velocity of a flick -#ifndef QML_FLICK_SAMPLEBUFFER -#define QML_FLICK_SAMPLEBUFFER 3 -#endif - -// The number of samples to discard when calculating the flick velocity. -// Touch panels often produce inaccurate results as the finger is lifted. -#ifndef QML_FLICK_DISCARDSAMPLES -#define QML_FLICK_DISCARDSAMPLES 0 -#endif - -// The default maximum velocity of a flick. -#ifndef QML_FLICK_DEFAULTMAXVELOCITY -#define QML_FLICK_DEFAULTMAXVELOCITY 2500 -#endif - -// The default deceleration of a flick. -#ifndef QML_FLICK_DEFAULTDECELERATION -#define QML_FLICK_DEFAULTDECELERATION 1500 -#endif - -// How much faster to decelerate when overshooting -#ifndef QML_FLICK_OVERSHOOTFRICTION -#define QML_FLICK_OVERSHOOTFRICTION 8 -#endif - -// Multiflick acceleration minimum flick velocity threshold -#ifndef QML_FLICK_MULTIFLICK_THRESHOLD -#define QML_FLICK_MULTIFLICK_THRESHOLD 1250 -#endif - -// Multiflick acceleration minimum contentSize/viewSize ratio -#ifndef QML_FLICK_MULTIFLICK_RATIO -#define QML_FLICK_MULTIFLICK_RATIO 10 -#endif - -// Multiflick acceleration maximum velocity multiplier -#ifndef QML_FLICK_MULTIFLICK_MAXBOOST -#define QML_FLICK_MULTIFLICK_MAXBOOST 3.0 -#endif - // FlickThreshold determines how far the "mouse" must have moved // before we perform a flick. static const int FlickThreshold = 15; @@ -508,16 +463,27 @@ void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExt data.vTime = timeline.time(); } +static bool fuzzyLessThanOrEqualTo(qreal a, qreal b) +{ + if (a == 0.0 || b == 0.0) { + // qFuzzyCompare is broken + a += 1.0; + b += 1.0; + } + return a <= b || qFuzzyCompare(a, b); +} + void QQuickFlickablePrivate::updateBeginningEnd() { Q_Q(QQuickFlickable); bool atBoundaryChange = false; // Vertical - const int maxyextent = int(-q->maxYExtent()); + const qreal maxyextent = -q->maxYExtent(); + const qreal minyextent = -q->minYExtent(); const qreal ypos = -vData.move.value(); - bool atBeginning = (ypos <= -q->minYExtent()); - bool atEnd = (maxyextent <= ypos); + bool atBeginning = fuzzyLessThanOrEqualTo(ypos, minyextent); + bool atEnd = fuzzyLessThanOrEqualTo(maxyextent, ypos); if (atBeginning != vData.atBeginning) { vData.atBeginning = atBeginning; @@ -529,10 +495,11 @@ void QQuickFlickablePrivate::updateBeginningEnd() } // Horizontal - const int maxxextent = int(-q->maxXExtent()); + const qreal maxxextent = -q->maxXExtent(); + const qreal minxextent = -q->minXExtent(); const qreal xpos = -hData.move.value(); - atBeginning = (xpos <= -q->minXExtent()); - atEnd = (maxxextent <= xpos); + atBeginning = fuzzyLessThanOrEqualTo(xpos, minxextent); + atEnd = fuzzyLessThanOrEqualTo(maxxextent, xpos); if (atBeginning != hData.atBeginning) { hData.atBeginning = atBeginning; @@ -1046,17 +1013,20 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) // the estimate to be altered const qreal minY = vData.dragMinBound + vData.startMargin; const qreal maxY = vData.dragMaxBound - vData.endMargin; - if (newY > minY) - newY = minY + (newY - minY) / 2; - if (newY < maxY && maxY - minY <= 0) - newY = maxY + (newY - maxY) / 2; - if (boundsBehavior == QQuickFlickable::StopAtBounds && newY <= maxY) { - newY = maxY; - rejectY = vData.pressPos == maxY && dy < 0; - } - if (boundsBehavior == QQuickFlickable::StopAtBounds && newY >= minY) { - newY = minY; - rejectY = vData.pressPos == minY && dy > 0; + if (boundsBehavior == QQuickFlickable::StopAtBounds) { + if (newY <= maxY) { + newY = maxY; + rejectY = vData.pressPos == maxY && vData.move.value() == maxY && dy < 0; + } + if (newY >= minY) { + newY = minY; + rejectY = vData.pressPos == minY && vData.move.value() == minY && dy > 0; + } + } else { + if (newY > minY) + newY = minY + (newY - minY) / 2; + if (newY < maxY && maxY - minY <= 0) + newY = maxY + (newY - maxY) / 2; } if (!rejectY && stealMouse && dy != 0.0) { clearTimeline(); @@ -1077,17 +1047,20 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) qreal newX = dx + hData.pressPos - hData.dragStartOffset; const qreal minX = hData.dragMinBound + hData.startMargin; const qreal maxX = hData.dragMaxBound - hData.endMargin; - if (newX > minX) - newX = minX + (newX - minX) / 2; - if (newX < maxX && maxX - minX <= 0) - newX = maxX + (newX - maxX) / 2; - if (boundsBehavior == QQuickFlickable::StopAtBounds && newX <= maxX) { - newX = maxX; - rejectX = hData.pressPos == maxX && dx < 0; - } - if (boundsBehavior == QQuickFlickable::StopAtBounds && newX >= minX) { - newX = minX; - rejectX = hData.pressPos == minX && dx > 0; + if (boundsBehavior == QQuickFlickable::StopAtBounds) { + if (newX <= maxX) { + newX = maxX; + rejectX = hData.pressPos == maxX && hData.move.value() == maxX && dx < 0; + } + if (newX >= minX) { + newX = minX; + rejectX = hData.pressPos == minX && hData.move.value() == minX && dx > 0; + } + } else { + if (newX > minX) + newX = minX + (newX - minX) / 2; + if (newX < maxX && maxX - minX <= 0) + newX = maxX + (newX - maxX) / 2; } if (!rejectX && stealMouse && dx != 0.0) { @@ -2075,12 +2048,13 @@ bool QQuickFlickable::sendMouseEvent(QQuickItem *item, QMouseEvent *event) if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) { d->clearDelayedPress(); grabMouse(); + } else if (d->delayedPressEvent) { + grabMouse(); } - // Do not accept this event when filtering, as this would force the mouse grab to the child const bool filtered = stealThisEvent || d->delayedPressEvent || grabberDisabled; if (filtered) { - event->setAccepted(false); + event->setAccepted(true); } return filtered; } else if (d->lastPosTime != -1) { diff --git a/src/quick/items/qquickflickablebehavior_p.h b/src/quick/items/qquickflickablebehavior_p.h new file mode 100644 index 0000000000..317d6512e6 --- /dev/null +++ b/src/quick/items/qquickflickablebehavior_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFLICKABLEBEHAVIOR_H +#define QQUICKFLICKABLEBEHAVIOR_H + +/* ### Platform specific flickable mechanics are defined either here, or in + mkspec files. Long-term (QtQuick 3) Flickable needs to allow such + mechanic details to be controlled via QML so that platforms can easily + load custom behavior at QML compile time. +*/ + +// The maximum number of pixels a flick can overshoot +#ifndef QML_FLICK_OVERSHOOT +#define QML_FLICK_OVERSHOOT 150 +#endif + +// The number of samples to use in calculating the velocity of a flick +#ifndef QML_FLICK_SAMPLEBUFFER +#define QML_FLICK_SAMPLEBUFFER 3 +#endif + +// The number of samples to discard when calculating the flick velocity. +// Touch panels often produce inaccurate results as the finger is lifted. +#ifndef QML_FLICK_DISCARDSAMPLES +#define QML_FLICK_DISCARDSAMPLES 0 +#endif + +// The default maximum velocity of a flick. +#ifndef QML_FLICK_DEFAULTMAXVELOCITY +#ifdef Q_OS_BLACKBERRY +#define QML_FLICK_DEFAULTMAXVELOCITY 10000 +#else +#define QML_FLICK_DEFAULTMAXVELOCITY 2500 +#endif +#endif + +// The default deceleration of a flick. +#ifndef QML_FLICK_DEFAULTDECELERATION +#ifdef Q_OS_BLACKBERRY +#define QML_FLICK_DEFAULTDECELERATION 5000 +#else +#define QML_FLICK_DEFAULTDECELERATION 1500 +#endif +#endif + +// How much faster to decelerate when overshooting +#ifndef QML_FLICK_OVERSHOOTFRICTION +#define QML_FLICK_OVERSHOOTFRICTION 8 +#endif + +// Multiflick acceleration minimum flick velocity threshold +#ifndef QML_FLICK_MULTIFLICK_THRESHOLD +#define QML_FLICK_MULTIFLICK_THRESHOLD 1250 +#endif + +// Multiflick acceleration minimum contentSize/viewSize ratio +#ifndef QML_FLICK_MULTIFLICK_RATIO +#define QML_FLICK_MULTIFLICK_RATIO 10 +#endif + +// Multiflick acceleration maximum velocity multiplier +#ifndef QML_FLICK_MULTIFLICK_MAXBOOST +#define QML_FLICK_MULTIFLICK_MAXBOOST 3.0 +#endif + +#endif //QQUICKFLICKABLEBEHAVIOR_H diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 96797a7588..d6dd7cb61a 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -6572,15 +6572,7 @@ void QQuickItem::grabMouse() if (!d->window) return; QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(d->window); - if (windowPriv->mouseGrabberItem == this) - return; - - QQuickItem *oldGrabber = windowPriv->mouseGrabberItem; - windowPriv->mouseGrabberItem = this; - if (oldGrabber) { - QEvent ev(QEvent::UngrabMouse); - d->window->sendEvent(oldGrabber, &ev); - } + windowPriv->setMouseGrabber(this); } /*! @@ -6960,7 +6952,10 @@ bool QQuickItem::event(QEvent *ev) touchEvent(static_cast<QTouchEvent*>(ev)); break; case QEvent::StyleAnimationUpdate: - update(); + if (isVisible()) { + ev->accept(); + update(); + } break; case QEvent::HoverEnter: hoverEnterEvent(static_cast<QHoverEvent*>(ev)); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 3987ff0cfb..ef5c63e40f 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -451,7 +451,7 @@ public: // dirtyToString() TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | - Size | Window, + Window, ComplexTransformUpdateMask = Transform | Window, ContentUpdateMask = Size | Content | Smooth | Window | Antialiasing, ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Window diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h index 5931d6c09e..ad026a3152 100644 --- a/src/quick/items/qquickitemview_p.h +++ b/src/quick/items/qquickitemview_p.h @@ -334,6 +334,21 @@ public: } } + void setSections(const QString &prev, const QString §, const QString &next) { + bool prevChanged = prev != m_prevSection; + bool sectChanged = sect != m_section; + bool nextChanged = next != m_nextSection; + m_prevSection = prev; + m_section = sect; + m_nextSection = next; + if (prevChanged) + Q_EMIT prevSectionChanged(); + if (sectChanged) + Q_EMIT sectionChanged(); + if (nextChanged) + Q_EMIT nextSectionChanged(); + } + void emitAdd() { Q_EMIT add(); } void emitRemove() { Q_EMIT remove(); } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 7f6c7fdbb9..6324d7960a 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -143,7 +143,8 @@ public: QQuickListView::SnapMode snapMode; QSmoothedAnimation *highlightPosAnimator; - QSmoothedAnimation *highlightSizeAnimator; + QSmoothedAnimation *highlightWidthAnimator; + QSmoothedAnimation *highlightHeightAnimator; qreal highlightMoveVelocity; qreal highlightResizeVelocity; int highlightResizeDuration; @@ -168,7 +169,7 @@ public: , visiblePos(0) , averageSize(100.0), spacing(0.0) , snapMode(QQuickListView::NoSnap) - , highlightPosAnimator(0), highlightSizeAnimator(0) + , highlightPosAnimator(0), highlightWidthAnimator(0), highlightHeightAnimator(0) , highlightMoveVelocity(400), highlightResizeVelocity(400), highlightResizeDuration(-1) , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0) , overshootDist(0.0), correctFlick(false), inFlickCorrection(false) @@ -177,7 +178,8 @@ public: } ~QQuickListViewPrivate() { delete highlightPosAnimator; - delete highlightSizeAnimator; + delete highlightWidthAnimator; + delete highlightHeightAnimator; } friend class QQuickViewSection; @@ -487,7 +489,7 @@ QString QQuickListViewPrivate::sectionAt(int modelIndex) return item->attached->section(); QString section; - if (sectionCriteria) { + if (sectionCriteria && modelIndex >= 0 && modelIndex < itemCount) { QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); section = sectionCriteria->sectionString(propValue); } @@ -565,19 +567,19 @@ FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item) // initialise attached properties if (sectionCriteria) { QString propValue = model->stringValue(modelIndex, sectionCriteria->property()); - listItem->attached->setSection(sectionCriteria->sectionString(propValue)); + QString section = sectionCriteria->sectionString(propValue); + QString prevSection; + QString nextSection; if (modelIndex > 0) { if (FxViewItem *item = itemBefore(modelIndex)) - listItem->attached->setPrevSection(item->attached->section()); + prevSection = item->attached->section(); else - listItem->attached->setPrevSection(sectionAt(modelIndex-1)); + prevSection = sectionAt(modelIndex-1); } if (modelIndex < model->count()-1) { - if (FxViewItem *item = visibleItem(modelIndex+1)) - listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section()); - else - listItem->attached->setNextSection(sectionAt(modelIndex+1)); + nextSection = sectionAt(modelIndex+1); } + listItem->attached->setSections(prevSection, section, nextSection); } return listItem; @@ -855,9 +857,11 @@ void QQuickListViewPrivate::createHighlight() highlight = 0; delete highlightPosAnimator; - delete highlightSizeAnimator; + delete highlightWidthAnimator; + delete highlightHeightAnimator; highlightPosAnimator = 0; - highlightSizeAnimator = 0; + highlightWidthAnimator = 0; + highlightHeightAnimator = 0; changed = true; } @@ -878,11 +882,15 @@ void QQuickListViewPrivate::createHighlight() highlightPosAnimator->velocity = highlightMoveVelocity; highlightPosAnimator->userDuration = highlightMoveDuration; - const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width"); - highlightSizeAnimator = new QSmoothedAnimation; - highlightSizeAnimator->velocity = highlightResizeVelocity; - highlightSizeAnimator->userDuration = highlightResizeDuration; - highlightSizeAnimator->target = QQmlProperty(item, sizeProp); + highlightWidthAnimator = new QSmoothedAnimation; + highlightWidthAnimator->velocity = highlightResizeVelocity; + highlightWidthAnimator->userDuration = highlightResizeDuration; + highlightWidthAnimator->target = QQmlProperty(item, "width"); + + highlightHeightAnimator = new QSmoothedAnimation; + highlightHeightAnimator->velocity = highlightResizeVelocity; + highlightHeightAnimator->userDuration = highlightResizeDuration; + highlightHeightAnimator->target = QQmlProperty(item, "height"); highlight = newHighlight; changed = true; @@ -905,7 +913,8 @@ void QQuickListViewPrivate::updateHighlight() highlightPosAnimator->to = isContentFlowReversed() ? -listItem->itemPosition()-listItem->itemSize() : listItem->itemPosition(); - highlightSizeAnimator->to = listItem->itemSize(); + highlightWidthAnimator->to = listItem->item->width(); + highlightHeightAnimator->to = listItem->item->height(); if (orient == QQuickListView::Vertical) { if (highlight->item->width() == 0) highlight->item->setWidth(currentItem->item->width()); @@ -915,7 +924,8 @@ void QQuickListViewPrivate::updateHighlight() } highlightPosAnimator->restart(); - highlightSizeAnimator->restart(); + highlightWidthAnimator->restart(); + highlightHeightAnimator->restart(); } updateTrackedItem(); } @@ -1968,8 +1978,10 @@ void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight) if (!autoHighlight) { if (d->highlightPosAnimator) d->highlightPosAnimator->stop(); - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->stop(); + if (d->highlightWidthAnimator) + d->highlightWidthAnimator->stop(); + if (d->highlightHeightAnimator) + d->highlightHeightAnimator->stop(); } QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight); } @@ -2296,8 +2308,10 @@ void QQuickListView::setHighlightResizeVelocity(qreal speed) Q_D(QQuickListView); if (d->highlightResizeVelocity != speed) { d->highlightResizeVelocity = speed; - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->velocity = d->highlightResizeVelocity; + if (d->highlightWidthAnimator) + d->highlightWidthAnimator->velocity = d->highlightResizeVelocity; + if (d->highlightHeightAnimator) + d->highlightHeightAnimator->velocity = d->highlightResizeVelocity; emit highlightResizeVelocityChanged(); } } @@ -2313,8 +2327,10 @@ void QQuickListView::setHighlightResizeDuration(int duration) Q_D(QQuickListView); if (d->highlightResizeDuration != duration) { d->highlightResizeDuration = duration; - if (d->highlightSizeAnimator) - d->highlightSizeAnimator->userDuration = d->highlightResizeDuration; + if (d->highlightWidthAnimator) + d->highlightWidthAnimator->userDuration = d->highlightResizeDuration; + if (d->highlightHeightAnimator) + d->highlightHeightAnimator->userDuration = d->highlightResizeDuration; emit highlightResizeDurationChanged(); } } diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 7d04be2393..b83c21428c 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -106,6 +106,7 @@ void QQuickLoaderPrivate::clear() component->deleteLater(); component = 0; } + componentStrongReference.clear(); source = QUrl(); if (item) { @@ -472,6 +473,10 @@ void QQuickLoader::setSourceComponent(QQmlComponent *comp) d->clear(); d->component = comp; + if (comp) { + if (QQmlData *ddata = QQmlData::get(comp)) + d->componentStrongReference = ddata->jsWrapper.value(); + } d->loadingFromSource = false; if (d->active) diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h index 9c94b4ce38..32c271222d 100644 --- a/src/quick/items/qquickloader_p_p.h +++ b/src/quick/items/qquickloader_p_p.h @@ -106,6 +106,7 @@ public: QQuickItem *item; QObject *object; QQmlComponent *component; + QV4::PersistentValue componentStrongReference; // To ensure GC doesn't delete components created by Qt.createComponent QQmlContext *itemContext; QQuickLoaderIncubator *incubator; QV4::PersistentValue initialPropertyValues; diff --git a/src/quick/items/qquickmousearea.cpp b/src/quick/items/qquickmousearea.cpp index 808c2638e2..438ec29fd0 100644 --- a/src/quick/items/qquickmousearea.cpp +++ b/src/quick/items/qquickmousearea.cpp @@ -822,6 +822,12 @@ void QQuickMouseArea::ungrabMouse() d->pressed = 0; d->stealMouse = false; setKeepMouseGrab(false); + +#ifndef QT_NO_DRAGANDDROP + if (d->drag) + d->drag->setActive(false); +#endif + emit canceled(); emit pressedChanged(); emit pressedButtonsChanged(); diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index 1f41fe04e5..0080f54d20 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -42,6 +42,7 @@ #include "qquickpathview_p.h" #include "qquickpathview_p_p.h" #include "qquickwindow.h" +#include "qquickflickablebehavior_p.h" //Contains flicking behavior defines #include <QtQuick/private/qquickstate_p.h> #include <private/qqmlglobal_p.h> @@ -56,22 +57,6 @@ #include <QtCore/qmath.h> #include <math.h> -// The number of samples to use in calculating the velocity of a flick -#ifndef QML_FLICK_SAMPLEBUFFER -#define QML_FLICK_SAMPLEBUFFER 1 -#endif - -// The number of samples to discard when calculating the flick velocity. -// Touch panels often produce inaccurate results as the finger is lifted. -#ifndef QML_FLICK_DISCARDSAMPLES -#define QML_FLICK_DISCARDSAMPLES 0 -#endif - -// The default maximum velocity of a flick. -#ifndef QML_FLICK_DEFAULTMAXVELOCITY -#define QML_FLICK_DEFAULTMAXVELOCITY 2500 -#endif - QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index 3ab13dbbc7..a615cb6f91 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -146,7 +146,6 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri continue; } } - qWarning("ShaderEffect: source or provider missing when binding textures"); glBindTexture(GL_TEXTURE_2D, 0); } else if (d.specialType == UniformData::Opacity) { program()->setUniformValue(loc, state.opacity()); diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index e076a342df..98203c51e5 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -158,16 +158,24 @@ QQuickShaderEffectTexture::QQuickShaderEffectTexture(QQuickItem *shaderSource) QQuickShaderEffectTexture::~QQuickShaderEffectTexture() { - if (m_renderer) - disconnect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture())); + invalidated(); +} + +void QQuickShaderEffectTexture::invalidated() +{ delete m_renderer; + m_renderer = 0; delete m_fbo; delete m_secondaryFbo; + m_fbo = m_secondaryFbo = 0; #ifdef QSG_DEBUG_FBO_OVERLAY delete m_debugOverlay; + m_debugOverlay = 0; #endif - if (m_transparentTexture) + if (m_transparentTexture) { glDeleteTextures(1, &m_transparentTexture); + m_transparentTexture = 0; + } } int QQuickShaderEffectTexture::textureId() const @@ -609,6 +617,7 @@ void QQuickShaderEffectSource::ensureTexture() "Cannot be used outside the rendering thread"); m_texture = new QQuickShaderEffectTexture(this); + connect(QQuickItemPrivate::get(this)->window, SIGNAL(sceneGraphInvalidated()), m_texture, SLOT(invalidated()), Qt::DirectConnection); connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update())); connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted())); } diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 6218775700..efa963fe64 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -124,6 +124,7 @@ Q_SIGNALS: public Q_SLOTS: void markDirtyTexture(); + void invalidated(); private: void grab(); diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 9c153febb6..beb2039924 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -296,44 +296,6 @@ qreal QQuickTextPrivate::getImplicitHeight() const The default is true. */ -/*! - \qmlproperty enumeration QtQuick::Text::renderType - - Override the default rendering type for this component. - - Supported render types are: - \list - \li Text.QtRendering - the default - \li Text.NativeRendering - \endlist - - Select Text.NativeRendering if you prefer text to look native on the target platform and do - not require advanced features such as transformation of the text. Using such features in - combination with the NativeRendering render type will lend poor and sometimes pixelated - results. - - On HighDpi "retina" displays and mobile and embedded platforms, this property is ignored - and QtRendering is always used. -*/ -QQuickText::RenderType QQuickText::renderType() const -{ - Q_D(const QQuickText); - return d->renderType; -} - -void QQuickText::setRenderType(QQuickText::RenderType renderType) -{ - Q_D(QQuickText); - if (d->renderType == renderType) - return; - - d->renderType = renderType; - emit renderTypeChanged(); - - if (isComponentComplete()) - d->updateLayout(); -} - void QQuickText::q_imagesLoaded() { Q_D(QQuickText); @@ -640,17 +602,6 @@ void QQuickTextLine::setY(qreal y) m_line->setPosition(QPointF(m_line->x(), y)); } -/*! - \qmlmethod QtQuick::Text::doLayout() - - Triggers a re-layout of the displayed text. -*/ -void QQuickText::doLayout() -{ - Q_D(QQuickText); - d->updateSize(); -} - bool QQuickTextPrivate::isLineLaidOutConnected() { Q_Q(QQuickText); @@ -2683,4 +2634,53 @@ void QQuickText::hoverLeaveEvent(QHoverEvent *event) d->processHoverEvent(event); } +/*! + \qmlproperty enumeration QtQuick::Text::renderType + + Override the default rendering type for this component. + + Supported render types are: + \list + \li Text.QtRendering - the default + \li Text.NativeRendering + \endlist + + Select Text.NativeRendering if you prefer text to look native on the target platform and do + not require advanced features such as transformation of the text. Using such features in + combination with the NativeRendering render type will lend poor and sometimes pixelated + results. + + On HighDpi "retina" displays and mobile and embedded platforms, this property is ignored + and QtRendering is always used. +*/ +QQuickText::RenderType QQuickText::renderType() const +{ + Q_D(const QQuickText); + return d->renderType; +} + +void QQuickText::setRenderType(QQuickText::RenderType renderType) +{ + Q_D(QQuickText); + if (d->renderType == renderType) + return; + + d->renderType = renderType; + emit renderTypeChanged(); + + if (isComponentComplete()) + d->updateLayout(); +} + +/*! + \qmlmethod QtQuick::Text::doLayout() + + Triggers a re-layout of the displayed text. +*/ +void QQuickText::doLayout() +{ + Q_D(QQuickText); + d->updateSize(); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index d4427eb47e..1dd1dfa57e 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1679,6 +1679,7 @@ bool QQuickTextInput::event(QEvent* ev) || ke == QKeySequence::SelectAll || ke == QKeySequence::SelectEndOfDocument) { ke->accept(); + return true; } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier || ke->modifiers() == Qt::KeypadModifier) { if (ke->key() < Qt::Key_Escape) { @@ -1692,6 +1693,7 @@ bool QQuickTextInput::event(QEvent* ev) case Qt::Key_Backspace: case Qt::Key_Left: case Qt::Key_Right: + ke->accept(); return true; default: break; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 10906dd64c..63a3b5d820 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -600,6 +600,28 @@ bool QQuickWindowPrivate::translateTouchToMouse(QQuickItem *item, QTouchEvent *e return false; } +void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) +{ + Q_Q(QQuickWindow); + if (mouseGrabberItem == grabber) + return; + + QQuickItem *oldGrabber = mouseGrabberItem; + mouseGrabberItem = grabber; + + if (touchMouseId != -1) { + // update the touch item for mouse touch id to the new grabber + itemForTouchPointId.remove(touchMouseId); + if (grabber) + itemForTouchPointId[touchMouseId] = grabber; + } + + if (oldGrabber) { + QEvent ev(QEvent::UngrabMouse); + q->sendEvent(oldGrabber, &ev); + } +} + void QQuickWindowPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform) { QMatrix4x4 transformMatrix(transform); @@ -661,6 +683,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + QQuickItem *currentActiveFocusItem = activeFocusItem; QQuickItem *newActiveFocusItem = 0; QVarLengthArray<QQuickItem *, 20> changed; @@ -737,7 +760,8 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q q->sendEvent(newActiveFocusItem, &event); } - emit q->focusObjectChanged(activeFocusItem); + if (activeFocusItem != currentActiveFocusItem) + emit q->focusObjectChanged(activeFocusItem); if (!changed.isEmpty()) notifyFocusChangesRecur(changed.data(), changed.count() - 1); @@ -764,6 +788,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, return;//No focus, nothing to do. } + QQuickItem *currentActiveFocusItem = activeFocusItem; QQuickItem *oldActiveFocusItem = 0; QQuickItem *newActiveFocusItem = 0; @@ -820,7 +845,8 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, q->sendEvent(newActiveFocusItem, &event); } - emit q->focusObjectChanged(activeFocusItem); + if (activeFocusItem != currentActiveFocusItem) + emit q->focusObjectChanged(activeFocusItem); if (!changed.isEmpty()) notifyFocusChangesRecur(changed.data(), changed.count() - 1); @@ -1218,8 +1244,11 @@ bool QQuickWindow::event(QEvent *e) case QEvent::TouchEnd: { QTouchEvent *touch = static_cast<QTouchEvent*>(e); d->translateTouchEvent(touch); - // return in order to avoid the QWindow::event below - return d->deliverTouchEvent(touch); + d->deliverTouchEvent(touch); + // we consume all touch events ourselves to avoid duplicate + // mouse delivery by QtGui mouse synthesis + e->accept(); + return true; } break; case QEvent::TouchCancel: @@ -1798,7 +1827,10 @@ bool QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QTouchEv // First check whether the parent wants to be a filter, // and if the parent accepts the event we are done. if (sendFilteredTouchEvent(item->parentItem(), item, event)) { - event->accept(); + // If the touch was accepted (regardless by whom or in what form), + // update acceptedNewPoints + foreach (int id, matchingNewPoints) + acceptedNewPoints->insert(id); return true; } @@ -2303,6 +2335,8 @@ void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item) p->groupNode = 0; p->paintNode = 0; + + p->dirty(QQuickItemPrivate::Window); } for (int ii = 0; ii < p->childItems.count(); ++ii) diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index c23745b5f1..418633b6ac 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -127,6 +127,7 @@ public: QPointF lastMousePosition; bool translateTouchToMouse(QQuickItem *item, QTouchEvent *event); void translateTouchEvent(QTouchEvent *touchEvent); + void setMouseGrabber(QQuickItem *grabber); static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform); static QMouseEvent *cloneMouseEvent(QMouseEvent *event, QPointF *transformedLocalPos = 0); bool deliverInitialMousePressEvent(QQuickItem *, QMouseEvent *); diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index b91edc2fd5..44a4bf3db6 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -45,12 +45,50 @@ #include <QtCore/QCoreApplication> #include <QtQml/QQmlEngine> +#include <private/qguiapplication_p.h> +#include <private/qqmlengine_p.h> +#include <qpa/qplatformintegration.h> + QT_BEGIN_NAMESPACE class QQuickWindowQmlImpl : public QQuickWindow, public QQmlParserStatus { Q_INTERFACES(QQmlParserStatus) Q_OBJECT + + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) + Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) + +public: + QQuickWindowQmlImpl(QWindow *parent = 0) + : QQuickWindow(parent) + , m_complete(false) + , m_visible(isVisible()) + , m_visibility(AutomaticVisibility) + { + connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged); + connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged); + } + + void setVisible(bool visible) { + if (!m_complete) + m_visible = visible; + else + QQuickWindow::setVisible(visible); + } + + void setVisibility(Visibility visibility) + { + if (!m_complete) + m_visibility = visibility; + else + QQuickWindow::setVisibility(visibility); + } + +Q_SIGNALS: + void visibleChanged(bool arg); + void visibilityChanged(QWindow::Visibility visibility); + protected: void classBegin() { //Give QQuickView behavior when created from QML with QQmlApplicationEngine @@ -61,7 +99,47 @@ protected: } } - void componentComplete() {} + void componentComplete() { + m_complete = true; + + // We have deferred window creation until we have the full picture of what + // the user wanted in terms of window state, geometry, visibility, etc. + + if ((m_visibility == Hidden && m_visible) || (m_visibility > AutomaticVisibility && !m_visible)) { + QQmlData *data = QQmlData::get(this); + Q_ASSERT(data && data->context); + + QQmlError error; + error.setObject(this); + + const QQmlContextData* urlContext = data->context; + while (urlContext && urlContext->url.isEmpty()) + urlContext = urlContext->parent; + error.setUrl(urlContext ? urlContext->url : QUrl()); + + QString objectId = data->context->findObjectId(this); + if (!objectId.isEmpty()) + error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl", + "Conflicting properties 'visible' and 'visibility' for Window '%1'").arg(objectId)); + else + error.setDescription(QCoreApplication::translate("QQuickWindowQmlImpl", + "Conflicting properties 'visible' and 'visibility'")); + + QQmlEnginePrivate::get(data->context->engine)->warning(error); + } + + if (m_visibility == AutomaticVisibility) { + setWindowState(QGuiApplicationPrivate::platformIntegration()->defaultWindowState(flags())); + setVisible(m_visible); + } else { + setVisibility(m_visibility); + } + } + +private: + bool m_complete; + bool m_visible; + Visibility m_visibility; }; void QQuickWindowModule::defineModule() diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 79b5de72c0..8ff68e20bc 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -590,15 +590,18 @@ void Element::computeBounds() } bounds.map(*node->matrix()); - if (!qIsFinite(bounds.tl.x)) + if (!qIsFinite(bounds.tl.x) || bounds.tl.x == FLT_MAX) bounds.tl.x = -FLT_MAX; - if (!qIsFinite(bounds.tl.y)) + if (!qIsFinite(bounds.tl.y) || bounds.tl.y == FLT_MAX) bounds.tl.y = -FLT_MAX; - if (!qIsFinite(bounds.br.x)) + if (!qIsFinite(bounds.br.x) || bounds.br.x == -FLT_MAX) bounds.br.x = FLT_MAX; - if (!qIsFinite(bounds.br.y)) + if (!qIsFinite(bounds.br.y) || bounds.br.y == -FLT_MAX) bounds.br.y = FLT_MAX; + Q_ASSERT(bounds.tl.x <= bounds.br.x); + Q_ASSERT(bounds.tl.y <= bounds.br.y); + boundsOutsideFloatRange = bounds.isOutsideFloatRange(); } @@ -2112,6 +2115,9 @@ void Renderer::renderUnmergedBatch(const Batch *batch) offset += a.tupleSize * size_of_type(a.type); } + if (g->drawingMode() == GL_LINE_STRIP || g->drawingMode() == GL_LINE_LOOP || g->drawingMode() == GL_LINES) + glLineWidth(g->lineWidth()); + if (g->indexCount()) glDrawElements(g->drawingMode(), g->indexCount(), g->indexType(), iOffset); else diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 95e111552d..5404b669a0 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -104,8 +104,6 @@ struct Rect { tl.y = pt.y; if (pt.y > br.y) br.y = pt.y; - Q_ASSERT(tl.x <= br.x); - Q_ASSERT(tl.y <= br.y); } void operator |= (const Rect &r) { @@ -117,8 +115,6 @@ struct Rect { br.x = r.br.x; if (r.br.y > br.y) br.y = r.br.y; - Q_ASSERT(tl.x <= br.x); - Q_ASSERT(tl.y <= br.y); } void map(const QMatrix4x4 &m); diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 3c9c353bd8..df70b5c5eb 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE //#define RENDERER_DEBUG //#define QT_GL_NO_SCISSOR_TEST - +static bool qsg_sanity_check = qgetenv("QSG_SANITY_CHECK").toInt(); #ifndef QSG_NO_RENDER_TIMING static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty(); @@ -243,9 +243,8 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) bindTime = frameTimer.nsecsElapsed(); #endif -#ifndef QT_NO_DEBUG // Sanity check that attribute registers are disabled - { + if (qsg_sanity_check) { GLint count = 0; glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &count); GLint enabled; @@ -256,7 +255,6 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) } } } -#endif render(); #ifndef QSG_NO_RENDER_TIMING diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 52df55fa92..ac1bdb7841 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -238,6 +238,8 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) m_windows.remove(window); QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + if (gl) + gl->makeCurrent(window); cd->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 850a463c3e..d779285a44 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -48,6 +48,7 @@ #include <QtGui/QGuiApplication> #include <QtGui/QScreen> +#include <QtGui/QOffscreenSurface> #include <QtQuick/QQuickWindow> #include <private/qquickwindow_p.h> @@ -205,12 +206,14 @@ public: class WMTryReleaseEvent : public WMWindowEvent { public: - WMTryReleaseEvent(QQuickWindow *win, bool destroy) + WMTryReleaseEvent(QQuickWindow *win, bool destroy, QOffscreenSurface *fallback) : WMWindowEvent(win, WM_TryRelease) , inDestructor(destroy) + , fallbackSurface(fallback) {} bool inDestructor; + QOffscreenSurface *fallbackSurface; }; class WMExposeEvent : public WMWindowEvent @@ -295,7 +298,7 @@ public: delete sgrc; } - void invalidateOpenGL(QQuickWindow *window, bool inDestructor); + void invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface); void initializeOpenGL(); bool event(QEvent *); @@ -362,17 +365,9 @@ bool QSGRenderThread::event(QEvent *e) case WM_Expose: { QSG_RT_DEBUG("WM_Expose"); WMExposeEvent *se = static_cast<WMExposeEvent *>(e); - - pendingUpdate |= RepaintRequest; - Q_ASSERT(!window || window == se->window); - + pendingUpdate |= RepaintRequest; windowSize = se->size; - if (window) { - QSG_RT_DEBUG(" - window already added..."); - return true; - } - window = se->window; return true; } @@ -383,7 +378,7 @@ bool QSGRenderThread::event(QEvent *e) mutex.lock(); if (window) { - QSG_RT_DEBUG(" - removed one..."); + QSG_RT_DEBUG(" - removed window..."); window = 0; } waitCondition.wakeOne(); @@ -402,10 +397,11 @@ bool QSGRenderThread::event(QEvent *e) case WM_TryRelease: { QSG_RT_DEBUG("WM_TryRelease"); mutex.lock(); + wm->m_locked = true; WMTryReleaseEvent *wme = static_cast<WMTryReleaseEvent *>(e); if (!window || wme->inDestructor) { QSG_RT_DEBUG(" - setting exit flag and invalidating GL"); - invalidateOpenGL(wme->window, wme->inDestructor); + invalidateOpenGL(wme->window, wme->inDestructor, wme->fallbackSurface); active = gl; if (sleeping) stopEventProcessing = true; @@ -413,6 +409,7 @@ bool QSGRenderThread::event(QEvent *e) QSG_RT_DEBUG(" - not releasing anything because we have active windows..."); } waitCondition.wakeOne(); + wm->m_locked = false; mutex.unlock(); return true; } @@ -453,7 +450,7 @@ bool QSGRenderThread::event(QEvent *e) return QThread::event(e); } -void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor) +void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *fallback) { QSG_RT_DEBUG("invalidateOpenGL()"); @@ -469,7 +466,13 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor) bool wipeSG = inDestructor || !window->isPersistentSceneGraph(); bool wipeGL = inDestructor || (wipeSG && !window->isPersistentOpenGLContext()); - gl->makeCurrent(window); + bool current = gl->makeCurrent(fallback ? static_cast<QSurface *>(fallback) : static_cast<QSurface *>(window)); + if (!current) { +#ifndef QT_NO_DEBUG + qWarning() << "Scene Graph failed to acquire GL context during cleanup"; +#endif + return; + } // The canvas nodes must be cleaned up regardless if we are in the destructor.. if (wipeSG) { @@ -477,6 +480,7 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor) dd->cleanupNodesOnShutdown(); } else { QSG_RT_DEBUG(" - persistent SG, avoiding cleanup"); + gl->doneCurrent(); return; } @@ -510,7 +514,6 @@ void QSGRenderThread::sync() if (windowSize.width() > 0 && windowSize.height() > 0) current = gl->makeCurrent(window); if (current) { - gl->makeCurrent(window); QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); bool hadRenderer = d->renderer != 0; d->syncSceneGraph(); @@ -528,7 +531,6 @@ void QSGRenderThread::sync() QSG_RT_DEBUG(" - window has bad size, waiting..."); } - QSG_RT_DEBUG(" - window has bad size, waiting..."); waitCondition.wakeOne(); mutex.unlock(); } @@ -690,7 +692,7 @@ QSGThreadedRenderLoop::QSGThreadedRenderLoop() : sg(QSGContext::createDefaultContext()) , m_animation_timer(0) { -#if defined(QSG_RENDER_LOOP_DEBUG_BASIC) || defined (QSG_RENDER_LOOP_DEBUG_FULL) +#if defined(QSG_RENDER_LOOP_DEBUG) qsgrl_timer.start(); #endif @@ -817,7 +819,6 @@ void QSGThreadedRenderLoop::show(QQuickWindow *window) win.thread = new QSGRenderThread(this, QQuickWindowPrivate::get(window)->context); win.timerId = 0; win.updateDuringSync = false; - win.gotBrokenExposeFromPlatformPlugin = false; m_windows << win; } @@ -883,19 +884,6 @@ void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window) } } -void QSGThreadedRenderLoop::resize(QQuickWindow *window) -{ - Window *w = windowFor(m_windows, window); - if (w - && w->gotBrokenExposeFromPlatformPlugin - && window->width() > 0 && window->height() > 0 - && w->window->geometry().intersects(w->window->screen()->availableGeometry())) { - w->gotBrokenExposeFromPlatformPlugin = false; - handleExposure(w); - } -} - - /*! Will post an event to the render thread that this window should start to render. @@ -909,8 +897,6 @@ void QSGThreadedRenderLoop::handleExposure(Window *w) #ifndef QT_NO_DEBUG qWarning("QSGThreadedRenderLoop: expose event received for window with invalid geometry."); #endif - w->gotBrokenExposeFromPlatformPlugin = true; - return; } // Because we are going to bind a GL context to it, make sure it @@ -994,6 +980,9 @@ void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window) */ void QSGThreadedRenderLoop::maybeUpdate(Window *w) { + if (!QCoreApplication::instance()) + return; + Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_locked, "QQuickItem::update()", "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); @@ -1052,9 +1041,26 @@ void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestru w->thread->mutex.lock(); if (w->thread->isRunning() && w->thread->active) { + + // The platform window might have been destroyed before + // hide/release/windowDestroyed is called, so we need to have a + // fallback surface to perform the cleanup of the scene graph + // and the OpenGL resources. + // QOffscreenSurface must be created on the GUI thread, so we + // create it here and pass it on to QSGRenderThread::invalidateGL() + QOffscreenSurface *fallback = 0; + if (!window->handle()) { + QSG_GUI_DEBUG(w->window, " - using fallback surface"); + fallback = new QOffscreenSurface(); + fallback->setFormat(window->requestedFormat()); + fallback->create(); + } + QSG_GUI_DEBUG(w->window, " - posting release request to render thread"); - w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor)); + w->thread->postEvent(new WMTryReleaseEvent(window, inDestructor, fallback)); w->thread->waitCondition.wait(&w->thread->mutex); + + delete fallback; } w->thread->mutex.unlock(); } diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index 844d180788..5943d0bd08 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -60,7 +60,6 @@ public: void show(QQuickWindow *window); void hide(QQuickWindow *window); - void resize(QQuickWindow *window); void windowDestroyed(QQuickWindow *window); void exposureChanged(QQuickWindow *window); @@ -90,7 +89,6 @@ private: QSGRenderThread *thread; int timerId; uint updateDuringSync : 1; - uint gotBrokenExposeFromPlatformPlugin : 1; }; friend class QSGRenderThread; diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 0c128d5cae..0b6d42aca6 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -240,7 +240,11 @@ void QSGWindowsRenderLoop::hide(QQuickWindow *window) if (window->isExposed()) handleObscurity(); + if (!m_gl) + return; + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + m_gl->makeCurrent(window); cd->cleanupNodesOnShutdown(); // If this is the last tracked window, check for persistent SG and GL and diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp index 8678d106ff..75bf0b6e3c 100644 --- a/src/quick/scenegraph/util/qsgatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgatlastexture.cpp @@ -448,6 +448,7 @@ QSGTexture *Texture::removedFromAtlas() const if (!m_nonatlas_texture) { m_nonatlas_texture = new QSGPlainTexture(); m_nonatlas_texture->setImage(m_image); + m_nonatlas_texture->setFiltering(filtering()); } return m_nonatlas_texture; } diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp index 244e13888c..fb7c900252 100644 --- a/src/quick/util/qquickapplication.cpp +++ b/src/quick/util/qquickapplication.cpp @@ -123,7 +123,7 @@ bool QQuickApplication::eventFilter(QObject *, QEvent *event) if (d->isActive != wasActive) { emit activeChanged(); } - } else if (event->type() == QEvent::LayoutDirectionChange) { + } else if (event->type() == QEvent::ApplicationLayoutDirectionChange) { Qt::LayoutDirection newDirection = QGuiApplication::layoutDirection(); if (d->direction != newDirection) { d->direction = newDirection; diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 26258fdc5f..055d6b7e29 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -519,6 +519,9 @@ void QQuickPixmapReader::processJobs() runningJob->loading = true; QUrl url = runningJob->url; + QQmlPixmapProfiler pixmapProfiler; + pixmapProfiler.startLoading(url); + QSize requestSize = runningJob->requestSize; locker.unlock(); processJob(runningJob, url, requestSize); @@ -897,8 +900,7 @@ bool QQuickPixmapReply::event(QEvent *event) pixmapProfiler.finishLoading(data->url); data->textureFactory = de->textureFactory; data->implicitSize = de->implicitSize; - if (data->implicitSize.width() > 0) - pixmapProfiler.setSize(url, data->implicitSize.width(), data->implicitSize.height()); + pixmapProfiler.setSize(url, data->requestSize.width() > 0 ? data->requestSize : data->implicitSize); } else { pixmapProfiler.errorLoading(data->url); data->errorString = de->errorString; @@ -968,8 +970,6 @@ void QQuickPixmapData::addToCache() inCache = true; QQmlPixmapProfiler pixmapProfiler; pixmapProfiler.cacheCountChanged(url, pixmapStore()->m_cache.count()); - if (implicitSize.width() > 0) - pixmapProfiler.setSize(url, implicitSize.width(), implicitSize.height()); } } @@ -1033,17 +1033,6 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q if (localFile.isEmpty()) return 0; - // check for "retina" high-dpi and use @2x file if it exixts - if (qApp->devicePixelRatio() > 1) { - const int dotIndex = localFile.lastIndexOf(QLatin1Char('.')); - if (dotIndex != -1) { - QString retinaFile = localFile; - retinaFile.insert(dotIndex, QStringLiteral("@2x")); - if (QFile(retinaFile).exists()) - localFile = retinaFile; - } - } - QFile f(localFile); QSize readSize; QString errorString; @@ -1256,8 +1245,7 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques d = createPixmapDataSync(this, engine, url, requestSize, &ok); if (ok) { pixmapProfiler.finishLoading(url); - if (d->implicitSize.width() > 0) - QQmlPixmapProfiler().setSize(url, d->implicitSize.width(), d->implicitSize.height()); + pixmapProfiler.setSize(url, d->requestSize.width() > 0 ? d->requestSize : d->implicitSize); if (options & QQuickPixmap::Cache) d->addToCache(); return; |