aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/items
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/items')
-rwxr-xr-xsrc/declarative/items/checksync.pl108
-rw-r--r--src/declarative/items/items.pri114
-rw-r--r--src/declarative/items/qsganchors.cpp1111
-rw-r--r--src/declarative/items/qsganchors_p.h201
-rw-r--r--src/declarative/items/qsganchors_p_p.h173
-rw-r--r--src/declarative/items/qsganimatedimage.cpp304
-rw-r--r--src/declarative/items/qsganimatedimage_p.h117
-rw-r--r--src/declarative/items/qsganimatedimage_p_p.h88
-rw-r--r--src/declarative/items/qsganimation.cpp442
-rw-r--r--src/declarative/items/qsganimation_p.h132
-rw-r--r--src/declarative/items/qsganimation_p_p.h97
-rw-r--r--src/declarative/items/qsgborderimage.cpp362
-rw-r--r--src/declarative/items/qsgborderimage_p.h110
-rw-r--r--src/declarative/items/qsgborderimage_p_p.h109
-rw-r--r--src/declarative/items/qsgcanvas.cpp1934
-rw-r--r--src/declarative/items/qsgcanvas.h123
-rw-r--r--src/declarative/items/qsgcanvas_p.h189
-rw-r--r--src/declarative/items/qsgcanvasitem.cpp441
-rw-r--r--src/declarative/items/qsgcanvasitem_p.h89
-rw-r--r--src/declarative/items/qsgclipnode.cpp121
-rw-r--r--src/declarative/items/qsgclipnode_p.h71
-rw-r--r--src/declarative/items/qsgcontext2d.cpp2716
-rw-r--r--src/declarative/items/qsgcontext2d_p.h409
-rw-r--r--src/declarative/items/qsgcontext2d_p_p.h227
-rw-r--r--src/declarative/items/qsgevents.cpp47
-rw-r--r--src/declarative/items/qsgevents_p_p.h142
-rw-r--r--src/declarative/items/qsgflickable.cpp1495
-rw-r--r--src/declarative/items/qsgflickable_p.h230
-rw-r--r--src/declarative/items/qsgflickable_p_p.h243
-rw-r--r--src/declarative/items/qsgflipable.cpp255
-rw-r--r--src/declarative/items/qsgflipable_p.h104
-rw-r--r--src/declarative/items/qsgfocusscope.cpp57
-rw-r--r--src/declarative/items/qsgfocusscope_p.h68
-rw-r--r--src/declarative/items/qsggridview.cpp2661
-rw-r--r--src/declarative/items/qsggridview_p.h290
-rw-r--r--src/declarative/items/qsgimage.cpp306
-rw-r--r--src/declarative/items/qsgimage_p.h104
-rw-r--r--src/declarative/items/qsgimage_p_p.h81
-rw-r--r--src/declarative/items/qsgimagebase.cpp285
-rw-r--r--src/declarative/items/qsgimagebase_p.h117
-rw-r--r--src/declarative/items/qsgimagebase_p_p.h93
-rw-r--r--src/declarative/items/qsgimplicitsizeitem.cpp93
-rw-r--r--src/declarative/items/qsgimplicitsizeitem_p.h101
-rw-r--r--src/declarative/items/qsgimplicitsizeitem_p_p.h92
-rw-r--r--src/declarative/items/qsgitem.cpp3143
-rw-r--r--src/declarative/items/qsgitem.h399
-rw-r--r--src/declarative/items/qsgitem_p.h712
-rw-r--r--src/declarative/items/qsgitemchangelistener_p.h82
-rw-r--r--src/declarative/items/qsgitemsmodule.cpp212
-rw-r--r--src/declarative/items/qsgitemsmodule_p.h65
-rw-r--r--src/declarative/items/qsglistview.cpp3064
-rw-r--r--src/declarative/items/qsglistview_p.h374
-rw-r--r--src/declarative/items/qsgloader.cpp340
-rw-r--r--src/declarative/items/qsgloader_p.h107
-rw-r--r--src/declarative/items/qsgloader_p_p.h91
-rw-r--r--src/declarative/items/qsgmousearea.cpp800
-rw-r--r--src/declarative/items/qsgmousearea_p.h219
-rw-r--r--src/declarative/items/qsgmousearea_p_p.h115
-rw-r--r--src/declarative/items/qsgninepatchnode.cpp292
-rw-r--r--src/declarative/items/qsgninepatchnode_p.h98
-rw-r--r--src/declarative/items/qsgpainteditem.cpp475
-rw-r--r--src/declarative/items/qsgpainteditem.h118
-rw-r--r--src/declarative/items/qsgpainteditem_p.h71
-rw-r--r--src/declarative/items/qsgpathview.cpp1416
-rw-r--r--src/declarative/items/qsgpathview_p.h254
-rw-r--r--src/declarative/items/qsgpathview_p_p.h193
-rw-r--r--src/declarative/items/qsgpincharea.cpp421
-rw-r--r--src/declarative/items/qsgpincharea_p.h315
-rw-r--r--src/declarative/items/qsgpincharea_p_p.h115
-rw-r--r--src/declarative/items/qsgpositioners.cpp788
-rw-r--r--src/declarative/items/qsgpositioners_p.h242
-rw-r--r--src/declarative/items/qsgpositioners_p_p.h174
-rw-r--r--src/declarative/items/qsgrectangle.cpp308
-rw-r--r--src/declarative/items/qsgrectangle_p.h189
-rw-r--r--src/declarative/items/qsgrectangle_p_p.h109
-rw-r--r--src/declarative/items/qsgrepeater.cpp294
-rw-r--r--src/declarative/items/qsgrepeater_p.h111
-rw-r--r--src/declarative/items/qsgrepeater_p_p.h83
-rw-r--r--src/declarative/items/qsgscalegrid.cpp213
-rw-r--r--src/declarative/items/qsgscalegrid_p_p.h134
-rw-r--r--src/declarative/items/qsgshadereffectitem.cpp590
-rw-r--r--src/declarative/items/qsgshadereffectitem_p.h158
-rw-r--r--src/declarative/items/qsgshadereffectmesh.cpp218
-rw-r--r--src/declarative/items/qsgshadereffectmesh_p.h102
-rw-r--r--src/declarative/items/qsgshadereffectnode.cpp322
-rw-r--r--src/declarative/items/qsgshadereffectnode_p.h148
-rw-r--r--src/declarative/items/qsgshadereffectsource.cpp818
-rw-r--r--src/declarative/items/qsgshadereffectsource_p.h246
-rw-r--r--src/declarative/items/qsgstateoperations.cpp1347
-rw-r--r--src/declarative/items/qsgstateoperations_p.h275
-rw-r--r--src/declarative/items/qsgtext.cpp1255
-rw-r--r--src/declarative/items/qsgtext_p.h214
-rw-r--r--src/declarative/items/qsgtext_p_p.h156
-rw-r--r--src/declarative/items/qsgtextedit.cpp1232
-rw-r--r--src/declarative/items/qsgtextedit_p.h303
-rw-r--r--src/declarative/items/qsgtextedit_p_p.h143
-rw-r--r--src/declarative/items/qsgtextinput.cpp1295
-rw-r--r--src/declarative/items/qsgtextinput_p.h302
-rw-r--r--src/declarative/items/qsgtextinput_p_p.h152
-rw-r--r--src/declarative/items/qsgtextnode.cpp457
-rw-r--r--src/declarative/items/qsgtextnode_p.h84
-rw-r--r--src/declarative/items/qsgtranslate.cpp297
-rw-r--r--src/declarative/items/qsgtranslate_p.h162
-rw-r--r--src/declarative/items/qsgview.cpp466
-rw-r--r--src/declarative/items/qsgview.h122
-rw-r--r--src/declarative/items/qsgvisualitemmodel.cpp1254
-rw-r--r--src/declarative/items/qsgvisualitemmodel_p.h257
-rw-r--r--src/declarative/items/syncexcludes11
108 files changed, 45174 insertions, 0 deletions
diff --git a/src/declarative/items/checksync.pl b/src/declarative/items/checksync.pl
new file mode 100755
index 0000000000..26288bf1f4
--- /dev/null
+++ b/src/declarative/items/checksync.pl
@@ -0,0 +1,108 @@
+#!/usr/bin/perl
+#############################################################################
+##
+## Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+## All rights reserved.
+## Contact: Nokia Corporation (qt-info@nokia.com)
+##
+## This file is part of the Declarative module of the Qt Toolkit.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## No Commercial Usage
+## This file contains pre-release code and may not be distributed.
+## You may use this file in accordance with the terms and conditions
+## contained in the Technology Preview License Agreement accompanying
+## this package.
+##
+## 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, Nokia gives you certain additional
+## rights. These rights are described in the Nokia Qt LGPL Exception
+## version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+##
+## If you have questions regarding the use of this file, please contact
+## Nokia at qt-info@nokia.com.
+##
+##
+##
+##
+##
+##
+##
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+use strict;
+use warnings;
+
+die "Usage: $0 <QML directory>" if (@ARGV != 1);
+
+my @excludes;
+open (SYNCEXCLUDES, "<", "syncexcludes");
+while (<SYNCEXCLUDES>) {
+ if (/^([a-zA-Z0-9\._]+)/) {
+ my $exclude = $1;
+ push (@excludes, $exclude);
+ }
+}
+
+my $portdir = ".";
+my $qmldir = $ARGV[0];
+
+opendir (PORTDIR, $portdir) or die "Cannot open port directory";
+opendir (QMLDIR, $qmldir) or die "Cannot open QML directory";
+
+my @portfiles = readdir(PORTDIR);
+my @qmlfiles = readdir(QMLDIR);
+
+closedir(PORTDIR);
+closedir(QMLDIR);
+
+foreach my $qmlfile (@qmlfiles) {
+ if ($qmlfile =~ /^qdeclarative.*\.cpp$/ or $qmlfile =~ /qdeclarative.*\.h$/) {
+
+ if (grep { $_ eq $qmlfile} @excludes) {
+ next;
+ }
+
+ my $portfile = $qmlfile;
+ $portfile =~ s/^qdeclarative/qsg/;
+
+ if (grep { $_ eq $portfile} @portfiles) {
+
+ open (PORTFILE, "<", "$portdir/$portfile") or die("Cannot open $portdir/$portfile for reading");
+
+ my $firstline = <PORTFILE>;
+
+ close (PORTFILE);
+
+ if ($firstline and $firstline =~ /^\/\/ Commit: ([a-z0-9]+)/) {
+ my $sha1 = $1;
+ my $commitSha1 = "";
+
+ my $output = `cd $qmldir; git log $qmlfile | head -n 1`;
+ if ($output =~ /commit ([a-z0-9]+)/) {
+ $commitSha1 = $1;
+ }
+
+ if ($commitSha1 eq $sha1) {
+ print ("$portfile: OK\n");
+ } else {
+ print ("$portfile: OUT OF DATE\n");
+ }
+ } else {
+ print ("$portfile: OUT OF DATE\n");
+ }
+ } else {
+ print ("$portfile: MISSING\n");
+ }
+ }
+}
diff --git a/src/declarative/items/items.pri b/src/declarative/items/items.pri
new file mode 100644
index 0000000000..d6942973cd
--- /dev/null
+++ b/src/declarative/items/items.pri
@@ -0,0 +1,114 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qsgevents_p_p.h \
+ $$PWD/qsgitemchangelistener_p.h \
+ $$PWD/qsganchors_p.h \
+ $$PWD/qsganchors_p_p.h \
+ $$PWD/qsgitem.h \
+ $$PWD/qsgitem_p.h \
+ $$PWD/qsgrectangle_p.h \
+ $$PWD/qsgrectangle_p_p.h \
+ $$PWD/qsgcanvas.h \
+ $$PWD/qsgcanvas_p.h \
+ $$PWD/qsgfocusscope_p.h \
+ $$PWD/qsgitemsmodule_p.h \
+ $$PWD/qsgpainteditem.h \
+ $$PWD/qsgpainteditem_p.h \
+ $$PWD/qsgtext_p.h \
+ $$PWD/qsgtext_p_p.h \
+ $$PWD/qsgtextnode_p.h \
+ $$PWD/qsgtextinput_p.h \
+ $$PWD/qsgtextinput_p_p.h \
+ $$PWD/qsgtextedit_p.h \
+ $$PWD/qsgtextedit_p_p.h \
+ $$PWD/qsgimagebase_p.h \
+ $$PWD/qsgimagebase_p_p.h \
+ $$PWD/qsgimage_p.h \
+ $$PWD/qsgimage_p_p.h \
+ $$PWD/qsgborderimage_p.h \
+ $$PWD/qsgborderimage_p_p.h \
+ $$PWD/qsgninepatchnode_p.h \
+ $$PWD/qsgscalegrid_p_p.h \
+ $$PWD/qsgmousearea_p.h \
+ $$PWD/qsgmousearea_p_p.h \
+ $$PWD/qsgpincharea_p.h \
+ $$PWD/qsgpincharea_p_p.h \
+ $$PWD/qsgflickable_p.h \
+ $$PWD/qsgflickable_p_p.h \
+ $$PWD/qsglistview_p.h \
+ $$PWD/qsgvisualitemmodel_p.h \
+ $$PWD/qsgrepeater_p.h \
+ $$PWD/qsgrepeater_p_p.h \
+ $$PWD/qsggridview_p.h \
+ $$PWD/qsgpathview_p.h \
+ $$PWD/qsgpathview_p_p.h \
+ $$PWD/qsgpositioners_p.h \
+ $$PWD/qsgpositioners_p_p.h \
+ $$PWD/qsgloader_p.h \
+ $$PWD/qsgloader_p_p.h \
+ $$PWD/qsganimatedimage_p.h \
+ $$PWD/qsganimatedimage_p_p.h \
+ $$PWD/qsgflipable_p.h \
+ $$PWD/qsgtranslate_p.h \
+ $$PWD/qsgclipnode_p.h \
+ $$PWD/qsgview.h \
+ $$PWD/qsganimation_p.h \
+ $$PWD/qsganimation_p_p.h \
+ $$PWD/qsgstateoperations_p.h \
+ $$PWD/qsgimplicitsizeitem_p.h \
+ $$PWD/qsgimplicitsizeitem_p_p.h \
+ $$PWD/qsgcanvasitem_p.h \
+ $$PWD/qsgcontext2d_p.h \
+ $$PWD/qsgcontext2d_p_p.h \
+
+SOURCES += \
+ $$PWD/qsgevents.cpp \
+ $$PWD/qsganchors.cpp \
+ $$PWD/qsgitem.cpp \
+ $$PWD/qsgrectangle.cpp \
+ $$PWD/qsgcanvas.cpp \
+ $$PWD/qsgfocusscope.cpp \
+ $$PWD/qsgitemsmodule.cpp \
+ $$PWD/qsgpainteditem.cpp \
+ $$PWD/qsgtext.cpp \
+ $$PWD/qsgtextnode.cpp \
+ $$PWD/qsgtextinput.cpp \
+ $$PWD/qsgtextedit.cpp \
+ $$PWD/qsgimagebase.cpp \
+ $$PWD/qsgimage.cpp \
+ $$PWD/qsgborderimage.cpp \
+ $$PWD/qsgninepatchnode.cpp \
+ $$PWD/qsgscalegrid.cpp \
+ $$PWD/qsgmousearea.cpp \
+ $$PWD/qsgpincharea.cpp \
+ $$PWD/qsgflickable.cpp \
+ $$PWD/qsglistview.cpp \
+ $$PWD/qsgvisualitemmodel.cpp \
+ $$PWD/qsgrepeater.cpp \
+ $$PWD/qsggridview.cpp \
+ $$PWD/qsgpathview.cpp \
+ $$PWD/qsgpositioners.cpp \
+ $$PWD/qsgloader.cpp \
+ $$PWD/qsganimatedimage.cpp \
+ $$PWD/qsgflipable.cpp \
+ $$PWD/qsgtranslate.cpp \
+ $$PWD/qsgclipnode.cpp \
+ $$PWD/qsgview.cpp \
+ $$PWD/qsganimation.cpp \
+ $$PWD/qsgstateoperations.cpp \
+ $$PWD/qsgimplicitsizeitem.cpp \
+ $$PWD/qsgcanvasitem.cpp \
+ $$PWD/qsgcontext2d.cpp \
+
+SOURCES += \
+ $$PWD/qsgshadereffectitem.cpp \
+ $$PWD/qsgshadereffectmesh.cpp \
+ $$PWD/qsgshadereffectnode.cpp \
+ $$PWD/qsgshadereffectsource.cpp \
+
+HEADERS += \
+ $$PWD/qsgshadereffectitem_p.h \
+ $$PWD/qsgshadereffectmesh_p.h \
+ $$PWD/qsgshadereffectnode_p.h \
+ $$PWD/qsgshadereffectsource_p.h \
diff --git a/src/declarative/items/qsganchors.cpp b/src/declarative/items/qsganchors.cpp
new file mode 100644
index 0000000000..ff9351edbc
--- /dev/null
+++ b/src/declarative/items/qsganchors.cpp
@@ -0,0 +1,1111 @@
+// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsganchors_p_p.h"
+
+#include "qsgitem.h"
+#include "qsgitem_p.h"
+
+#include <qdeclarativeinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+//TODO: should we cache relationships, so we don't have to check each time (parent-child or sibling)?
+//TODO: support non-parent, non-sibling (need to find lowest common ancestor)
+
+static qreal hcenter(QSGItem *item)
+{
+ qreal width = item->width();
+ int iw = width;
+ if (iw % 2)
+ return (width + 1) / 2;
+ else
+ return width / 2;
+}
+
+static qreal vcenter(QSGItem *item)
+{
+ qreal height = item->height();
+ int ih = height;
+ if (ih % 2)
+ return (height + 1) / 2;
+ else
+ return height / 2;
+}
+
+//### const item?
+//local position
+static qreal position(QSGItem *item, QSGAnchorLine::AnchorLine anchorLine)
+{
+ qreal ret = 0.0;
+ switch(anchorLine) {
+ case QSGAnchorLine::Left:
+ ret = item->x();
+ break;
+ case QSGAnchorLine::Right:
+ ret = item->x() + item->width();
+ break;
+ case QSGAnchorLine::Top:
+ ret = item->y();
+ break;
+ case QSGAnchorLine::Bottom:
+ ret = item->y() + item->height();
+ break;
+ case QSGAnchorLine::HCenter:
+ ret = item->x() + hcenter(item);
+ break;
+ case QSGAnchorLine::VCenter:
+ ret = item->y() + vcenter(item);
+ break;
+ case QSGAnchorLine::Baseline:
+ ret = item->y() + item->baselineOffset();
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+//position when origin is 0,0
+static qreal adjustedPosition(QSGItem *item, QSGAnchorLine::AnchorLine anchorLine)
+{
+ qreal ret = 0.0;
+ switch(anchorLine) {
+ case QSGAnchorLine::Left:
+ ret = 0.0;
+ break;
+ case QSGAnchorLine::Right:
+ ret = item->width();
+ break;
+ case QSGAnchorLine::Top:
+ ret = 0.0;
+ break;
+ case QSGAnchorLine::Bottom:
+ ret = item->height();
+ break;
+ case QSGAnchorLine::HCenter:
+ ret = hcenter(item);
+ break;
+ case QSGAnchorLine::VCenter:
+ ret = vcenter(item);
+ break;
+ case QSGAnchorLine::Baseline:
+ ret = item->baselineOffset();
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+QSGAnchors::QSGAnchors(QSGItem *item, QObject *parent)
+: QObject(*new QSGAnchorsPrivate(item), parent)
+{
+}
+
+QSGAnchors::~QSGAnchors()
+{
+ Q_D(QSGAnchors);
+ d->remDepend(d->fill);
+ d->remDepend(d->centerIn);
+ d->remDepend(d->left.item);
+ d->remDepend(d->right.item);
+ d->remDepend(d->top.item);
+ d->remDepend(d->bottom.item);
+ d->remDepend(d->vCenter.item);
+ d->remDepend(d->hCenter.item);
+ d->remDepend(d->baseline.item);
+}
+
+void QSGAnchorsPrivate::fillChanged()
+{
+ Q_Q(QSGAnchors);
+ if (!fill || !isItemComplete())
+ return;
+
+ if (updatingFill < 2) {
+ ++updatingFill;
+
+ qreal horizontalMargin = q->mirrored() ? rightMargin : leftMargin;
+
+ if (fill == item->parentItem()) { //child-parent
+ setItemPos(QPointF(horizontalMargin, topMargin));
+ } else if (fill->parentItem() == item->parentItem()) { //siblings
+ setItemPos(QPointF(fill->x()+horizontalMargin, fill->y()+topMargin));
+ }
+ setItemSize(QSizeF(fill->width()-leftMargin-rightMargin, fill->height()-topMargin-bottomMargin));
+
+ --updatingFill;
+ } else {
+ // ### Make this certain :)
+ qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on fill.");
+ }
+
+}
+
+void QSGAnchorsPrivate::centerInChanged()
+{
+ Q_Q(QSGAnchors);
+ if (!centerIn || fill || !isItemComplete())
+ return;
+
+ if (updatingCenterIn < 2) {
+ ++updatingCenterIn;
+
+ qreal effectiveHCenterOffset = q->mirrored() ? -hCenterOffset : hCenterOffset;
+ if (centerIn == item->parentItem()) {
+ QPointF p(hcenter(item->parentItem()) - hcenter(item) + effectiveHCenterOffset,
+ vcenter(item->parentItem()) - vcenter(item) + vCenterOffset);
+ setItemPos(p);
+
+ } else if (centerIn->parentItem() == item->parentItem()) {
+ QPointF p(centerIn->x() + hcenter(centerIn) - hcenter(item) + effectiveHCenterOffset,
+ centerIn->y() + vcenter(centerIn) - vcenter(item) + vCenterOffset);
+ setItemPos(p);
+ }
+
+ --updatingCenterIn;
+ } else {
+ // ### Make this certain :)
+ qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on centerIn.");
+ }
+}
+
+void QSGAnchorsPrivate::clearItem(QSGItem *item)
+{
+ if (!item)
+ return;
+ if (fill == item)
+ fill = 0;
+ if (centerIn == item)
+ centerIn = 0;
+ if (left.item == item) {
+ left.item = 0;
+ usedAnchors &= ~QSGAnchors::LeftAnchor;
+ }
+ if (right.item == item) {
+ right.item = 0;
+ usedAnchors &= ~QSGAnchors::RightAnchor;
+ }
+ if (top.item == item) {
+ top.item = 0;
+ usedAnchors &= ~QSGAnchors::TopAnchor;
+ }
+ if (bottom.item == item) {
+ bottom.item = 0;
+ usedAnchors &= ~QSGAnchors::BottomAnchor;
+ }
+ if (vCenter.item == item) {
+ vCenter.item = 0;
+ usedAnchors &= ~QSGAnchors::VCenterAnchor;
+ }
+ if (hCenter.item == item) {
+ hCenter.item = 0;
+ usedAnchors &= ~QSGAnchors::HCenterAnchor;
+ }
+ if (baseline.item == item) {
+ baseline.item = 0;
+ usedAnchors &= ~QSGAnchors::BaselineAnchor;
+ }
+}
+
+void QSGAnchorsPrivate::addDepend(QSGItem *item)
+{
+ if (!item)
+ return;
+
+ QSGItemPrivate *p = QSGItemPrivate::get(item);
+ p->addItemChangeListener(this, QSGItemPrivate::Geometry);
+}
+
+void QSGAnchorsPrivate::remDepend(QSGItem *item)
+{
+ if (!item)
+ return;
+
+ QSGItemPrivate *p = QSGItemPrivate::get(item);
+ p->removeItemChangeListener(this, QSGItemPrivate::Geometry);
+}
+
+bool QSGAnchors::mirrored()
+{
+ Q_D(QSGAnchors);
+ return QSGItemPrivate::get(d->item)->effectiveLayoutMirror;
+}
+
+bool QSGAnchorsPrivate::isItemComplete() const
+{
+ return componentComplete;
+}
+
+void QSGAnchors::classBegin()
+{
+ Q_D(QSGAnchors);
+ d->componentComplete = false;
+}
+
+void QSGAnchors::componentComplete()
+{
+ Q_D(QSGAnchors);
+ d->componentComplete = true;
+}
+
+void QSGAnchorsPrivate::setItemHeight(qreal v)
+{
+ updatingMe = true;
+ item->setHeight(v);
+ updatingMe = false;
+}
+
+void QSGAnchorsPrivate::setItemWidth(qreal v)
+{
+ updatingMe = true;
+ item->setWidth(v);
+ updatingMe = false;
+}
+
+void QSGAnchorsPrivate::setItemX(qreal v)
+{
+ updatingMe = true;
+ item->setX(v);
+ updatingMe = false;
+}
+
+void QSGAnchorsPrivate::setItemY(qreal v)
+{
+ updatingMe = true;
+ item->setY(v);
+ updatingMe = false;
+}
+
+void QSGAnchorsPrivate::setItemPos(const QPointF &v)
+{
+ updatingMe = true;
+ item->setPos(v);
+ updatingMe = false;
+}
+
+void QSGAnchorsPrivate::setItemSize(const QSizeF &v)
+{
+ updatingMe = true;
+ item->setSize(v);
+ updatingMe = false;
+}
+
+void QSGAnchorsPrivate::updateMe()
+{
+ if (updatingMe) {
+ updatingMe = false;
+ return;
+ }
+
+ fillChanged();
+ centerInChanged();
+ updateHorizontalAnchors();
+ updateVerticalAnchors();
+}
+
+void QSGAnchorsPrivate::updateOnComplete()
+{
+ fillChanged();
+ centerInChanged();
+ updateHorizontalAnchors();
+ updateVerticalAnchors();
+}
+
+void QSGAnchorsPrivate::itemGeometryChanged(QSGItem *, const QRectF &newG, const QRectF &oldG)
+{
+ fillChanged();
+ centerInChanged();
+ if (newG.x() != oldG.x() || newG.width() != oldG.width())
+ updateHorizontalAnchors();
+ if (newG.y() != oldG.y() || newG.height() != oldG.height())
+ updateVerticalAnchors();
+}
+
+QSGItem *QSGAnchors::fill() const
+{
+ Q_D(const QSGAnchors);
+ return d->fill;
+}
+
+void QSGAnchors::setFill(QSGItem *f)
+{
+ Q_D(QSGAnchors);
+ if (d->fill == f)
+ return;
+
+ if (!f) {
+ d->remDepend(d->fill);
+ d->fill = f;
+ emit fillChanged();
+ return;
+ }
+ if (f != d->item->parentItem() && f->parentItem() != d->item->parentItem()){
+ qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling.");
+ return;
+ }
+ d->remDepend(d->fill);
+ d->fill = f;
+ d->addDepend(d->fill);
+ emit fillChanged();
+ d->fillChanged();
+}
+
+void QSGAnchors::resetFill()
+{
+ setFill(0);
+}
+
+QSGItem *QSGAnchors::centerIn() const
+{
+ Q_D(const QSGAnchors);
+ return d->centerIn;
+}
+
+void QSGAnchors::setCenterIn(QSGItem* c)
+{
+ Q_D(QSGAnchors);
+ if (d->centerIn == c)
+ return;
+
+ if (!c) {
+ d->remDepend(d->centerIn);
+ d->centerIn = c;
+ emit centerInChanged();
+ return;
+ }
+ if (c != d->item->parentItem() && c->parentItem() != d->item->parentItem()){
+ qmlInfo(d->item) << tr("Cannot anchor to an item that isn't a parent or sibling.");
+ return;
+ }
+
+ d->remDepend(d->centerIn);
+ d->centerIn = c;
+ d->addDepend(d->centerIn);
+ emit centerInChanged();
+ d->centerInChanged();
+}
+
+void QSGAnchors::resetCenterIn()
+{
+ setCenterIn(0);
+}
+
+bool QSGAnchorsPrivate::calcStretch(const QSGAnchorLine &edge1,
+ const QSGAnchorLine &edge2,
+ qreal offset1,
+ qreal offset2,
+ QSGAnchorLine::AnchorLine line,
+ qreal &stretch)
+{
+ bool edge1IsParent = (edge1.item == item->parentItem());
+ bool edge2IsParent = (edge2.item == item->parentItem());
+ bool edge1IsSibling = (edge1.item->parentItem() == item->parentItem());
+ bool edge2IsSibling = (edge2.item->parentItem() == item->parentItem());
+
+ bool invalid = false;
+ if ((edge2IsParent && edge1IsParent) || (edge2IsSibling && edge1IsSibling)) {
+ stretch = (position(edge2.item, edge2.anchorLine) + offset2)
+ - (position(edge1.item, edge1.anchorLine) + offset1);
+ } else if (edge2IsParent && edge1IsSibling) {
+ stretch = (position(edge2.item, edge2.anchorLine) + offset2)
+ - (position(item->parentItem(), line)
+ + position(edge1.item, edge1.anchorLine) + offset1);
+ } else if (edge2IsSibling && edge1IsParent) {
+ stretch = (position(item->parentItem(), line) + position(edge2.item, edge2.anchorLine) + offset2)
+ - (position(edge1.item, edge1.anchorLine) + offset1);
+ } else
+ invalid = true;
+
+ return invalid;
+}
+
+void QSGAnchorsPrivate::updateVerticalAnchors()
+{
+ if (fill || centerIn || !isItemComplete())
+ return;
+
+ if (updatingVerticalAnchor < 2) {
+ ++updatingVerticalAnchor;
+ if (usedAnchors & QSGAnchors::TopAnchor) {
+ //Handle stretching
+ bool invalid = true;
+ qreal height = 0.0;
+ if (usedAnchors & QSGAnchors::BottomAnchor) {
+ invalid = calcStretch(top, bottom, topMargin, -bottomMargin, QSGAnchorLine::Top, height);
+ } else if (usedAnchors & QSGAnchors::VCenterAnchor) {
+ invalid = calcStretch(top, vCenter, topMargin, vCenterOffset, QSGAnchorLine::Top, height);
+ height *= 2;
+ }
+ if (!invalid)
+ setItemHeight(height);
+
+ //Handle top
+ if (top.item == item->parentItem()) {
+ setItemY(adjustedPosition(top.item, top.anchorLine) + topMargin);
+ } else if (top.item->parentItem() == item->parentItem()) {
+ setItemY(position(top.item, top.anchorLine) + topMargin);
+ }
+ } else if (usedAnchors & QSGAnchors::BottomAnchor) {
+ //Handle stretching (top + bottom case is handled above)
+ if (usedAnchors & QSGAnchors::VCenterAnchor) {
+ qreal height = 0.0;
+ bool invalid = calcStretch(vCenter, bottom, vCenterOffset, -bottomMargin,
+ QSGAnchorLine::Top, height);
+ if (!invalid)
+ setItemHeight(height*2);
+ }
+
+ //Handle bottom
+ if (bottom.item == item->parentItem()) {
+ setItemY(adjustedPosition(bottom.item, bottom.anchorLine) - item->height() - bottomMargin);
+ } else if (bottom.item->parentItem() == item->parentItem()) {
+ setItemY(position(bottom.item, bottom.anchorLine) - item->height() - bottomMargin);
+ }
+ } else if (usedAnchors & QSGAnchors::VCenterAnchor) {
+ //(stetching handled above)
+
+ //Handle vCenter
+ if (vCenter.item == item->parentItem()) {
+ setItemY(adjustedPosition(vCenter.item, vCenter.anchorLine)
+ - vcenter(item) + vCenterOffset);
+ } else if (vCenter.item->parentItem() == item->parentItem()) {
+ setItemY(position(vCenter.item, vCenter.anchorLine) - vcenter(item) + vCenterOffset);
+ }
+ } else if (usedAnchors & QSGAnchors::BaselineAnchor) {
+ //Handle baseline
+ if (baseline.item == item->parentItem()) {
+ setItemY(adjustedPosition(baseline.item, baseline.anchorLine) - item->baselineOffset() + baselineOffset);
+ } else if (baseline.item->parentItem() == item->parentItem()) {
+ setItemY(position(baseline.item, baseline.anchorLine) - item->baselineOffset() + baselineOffset);
+ }
+ }
+ --updatingVerticalAnchor;
+ } else {
+ // ### Make this certain :)
+ qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on vertical anchor.");
+ }
+}
+
+inline QSGAnchorLine::AnchorLine reverseAnchorLine(QSGAnchorLine::AnchorLine anchorLine)
+{
+ if (anchorLine == QSGAnchorLine::Left) {
+ return QSGAnchorLine::Right;
+ } else if (anchorLine == QSGAnchorLine::Right) {
+ return QSGAnchorLine::Left;
+ } else {
+ return anchorLine;
+ }
+}
+
+void QSGAnchorsPrivate::updateHorizontalAnchors()
+{
+ Q_Q(QSGAnchors);
+ if (fill || centerIn || !isItemComplete())
+ return;
+
+ if (updatingHorizontalAnchor < 3) {
+ ++updatingHorizontalAnchor;
+ qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset;
+ QSGAnchorLine effectiveLeft, effectiveRight, effectiveHorizontalCenter;
+ QSGAnchors::Anchor effectiveLeftAnchor, effectiveRightAnchor;
+ if (q->mirrored()) {
+ effectiveLeftAnchor = QSGAnchors::RightAnchor;
+ effectiveRightAnchor = QSGAnchors::LeftAnchor;
+ effectiveLeft.item = right.item;
+ effectiveLeft.anchorLine = reverseAnchorLine(right.anchorLine);
+ effectiveRight.item = left.item;
+ effectiveRight.anchorLine = reverseAnchorLine(left.anchorLine);
+ effectiveHorizontalCenter.item = hCenter.item;
+ effectiveHorizontalCenter.anchorLine = reverseAnchorLine(hCenter.anchorLine);
+ effectiveLeftMargin = rightMargin;
+ effectiveRightMargin = leftMargin;
+ effectiveHorizontalCenterOffset = -hCenterOffset;
+ } else {
+ effectiveLeftAnchor = QSGAnchors::LeftAnchor;
+ effectiveRightAnchor = QSGAnchors::RightAnchor;
+ effectiveLeft = left;
+ effectiveRight = right;
+ effectiveHorizontalCenter = hCenter;
+ effectiveLeftMargin = leftMargin;
+ effectiveRightMargin = rightMargin;
+ effectiveHorizontalCenterOffset = hCenterOffset;
+ }
+
+ if (usedAnchors & effectiveLeftAnchor) {
+ //Handle stretching
+ bool invalid = true;
+ qreal width = 0.0;
+ if (usedAnchors & effectiveRightAnchor) {
+ invalid = calcStretch(effectiveLeft, effectiveRight, effectiveLeftMargin, -effectiveRightMargin, QSGAnchorLine::Left, width);
+ } else if (usedAnchors & QSGAnchors::HCenterAnchor) {
+ invalid = calcStretch(effectiveLeft, effectiveHorizontalCenter, effectiveLeftMargin, effectiveHorizontalCenterOffset, QSGAnchorLine::Left, width);
+ width *= 2;
+ }
+ if (!invalid)
+ setItemWidth(width);
+
+ //Handle left
+ if (effectiveLeft.item == item->parentItem()) {
+ setItemX(adjustedPosition(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin);
+ } else if (effectiveLeft.item->parentItem() == item->parentItem()) {
+ setItemX(position(effectiveLeft.item, effectiveLeft.anchorLine) + effectiveLeftMargin);
+ }
+ } else if (usedAnchors & effectiveRightAnchor) {
+ //Handle stretching (left + right case is handled in updateLeftAnchor)
+ if (usedAnchors & QSGAnchors::HCenterAnchor) {
+ qreal width = 0.0;
+ bool invalid = calcStretch(effectiveHorizontalCenter, effectiveRight, effectiveHorizontalCenterOffset, -effectiveRightMargin,
+ QSGAnchorLine::Left, width);
+ if (!invalid)
+ setItemWidth(width*2);
+ }
+
+ //Handle right
+ if (effectiveRight.item == item->parentItem()) {
+ setItemX(adjustedPosition(effectiveRight.item, effectiveRight.anchorLine) - item->width() - effectiveRightMargin);
+ } else if (effectiveRight.item->parentItem() == item->parentItem()) {
+ setItemX(position(effectiveRight.item, effectiveRight.anchorLine) - item->width() - effectiveRightMargin);
+ }
+ } else if (usedAnchors & QSGAnchors::HCenterAnchor) {
+ //Handle hCenter
+ if (effectiveHorizontalCenter.item == item->parentItem()) {
+ setItemX(adjustedPosition(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset);
+ } else if (effectiveHorizontalCenter.item->parentItem() == item->parentItem()) {
+ setItemX(position(effectiveHorizontalCenter.item, effectiveHorizontalCenter.anchorLine) - hcenter(item) + effectiveHorizontalCenterOffset);
+ }
+ }
+ --updatingHorizontalAnchor;
+ } else {
+ // ### Make this certain :)
+ qmlInfo(item) << QSGAnchors::tr("Possible anchor loop detected on horizontal anchor.");
+ }
+}
+
+QSGAnchorLine QSGAnchors::top() const
+{
+ Q_D(const QSGAnchors);
+ return d->top;
+}
+
+void QSGAnchors::setTop(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkVAnchorValid(edge) || d->top == edge)
+ return;
+
+ d->usedAnchors |= TopAnchor;
+
+ if (!d->checkVValid()) {
+ d->usedAnchors &= ~TopAnchor;
+ return;
+ }
+
+ d->remDepend(d->top.item);
+ d->top = edge;
+ d->addDepend(d->top.item);
+ emit topChanged();
+ d->updateVerticalAnchors();
+}
+
+void QSGAnchors::resetTop()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~TopAnchor;
+ d->remDepend(d->top.item);
+ d->top = QSGAnchorLine();
+ emit topChanged();
+ d->updateVerticalAnchors();
+}
+
+QSGAnchorLine QSGAnchors::bottom() const
+{
+ Q_D(const QSGAnchors);
+ return d->bottom;
+}
+
+void QSGAnchors::setBottom(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkVAnchorValid(edge) || d->bottom == edge)
+ return;
+
+ d->usedAnchors |= BottomAnchor;
+
+ if (!d->checkVValid()) {
+ d->usedAnchors &= ~BottomAnchor;
+ return;
+ }
+
+ d->remDepend(d->bottom.item);
+ d->bottom = edge;
+ d->addDepend(d->bottom.item);
+ emit bottomChanged();
+ d->updateVerticalAnchors();
+}
+
+void QSGAnchors::resetBottom()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~BottomAnchor;
+ d->remDepend(d->bottom.item);
+ d->bottom = QSGAnchorLine();
+ emit bottomChanged();
+ d->updateVerticalAnchors();
+}
+
+QSGAnchorLine QSGAnchors::verticalCenter() const
+{
+ Q_D(const QSGAnchors);
+ return d->vCenter;
+}
+
+void QSGAnchors::setVerticalCenter(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkVAnchorValid(edge) || d->vCenter == edge)
+ return;
+
+ d->usedAnchors |= VCenterAnchor;
+
+ if (!d->checkVValid()) {
+ d->usedAnchors &= ~VCenterAnchor;
+ return;
+ }
+
+ d->remDepend(d->vCenter.item);
+ d->vCenter = edge;
+ d->addDepend(d->vCenter.item);
+ emit verticalCenterChanged();
+ d->updateVerticalAnchors();
+}
+
+void QSGAnchors::resetVerticalCenter()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~VCenterAnchor;
+ d->remDepend(d->vCenter.item);
+ d->vCenter = QSGAnchorLine();
+ emit verticalCenterChanged();
+ d->updateVerticalAnchors();
+}
+
+QSGAnchorLine QSGAnchors::baseline() const
+{
+ Q_D(const QSGAnchors);
+ return d->baseline;
+}
+
+void QSGAnchors::setBaseline(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkVAnchorValid(edge) || d->baseline == edge)
+ return;
+
+ d->usedAnchors |= BaselineAnchor;
+
+ if (!d->checkVValid()) {
+ d->usedAnchors &= ~BaselineAnchor;
+ return;
+ }
+
+ d->remDepend(d->baseline.item);
+ d->baseline = edge;
+ d->addDepend(d->baseline.item);
+ emit baselineChanged();
+ d->updateVerticalAnchors();
+}
+
+void QSGAnchors::resetBaseline()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~BaselineAnchor;
+ d->remDepend(d->baseline.item);
+ d->baseline = QSGAnchorLine();
+ emit baselineChanged();
+ d->updateVerticalAnchors();
+}
+
+QSGAnchorLine QSGAnchors::left() const
+{
+ Q_D(const QSGAnchors);
+ return d->left;
+}
+
+void QSGAnchors::setLeft(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkHAnchorValid(edge) || d->left == edge)
+ return;
+
+ d->usedAnchors |= LeftAnchor;
+
+ if (!d->checkHValid()) {
+ d->usedAnchors &= ~LeftAnchor;
+ return;
+ }
+
+ d->remDepend(d->left.item);
+ d->left = edge;
+ d->addDepend(d->left.item);
+ emit leftChanged();
+ d->updateHorizontalAnchors();
+}
+
+void QSGAnchors::resetLeft()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~LeftAnchor;
+ d->remDepend(d->left.item);
+ d->left = QSGAnchorLine();
+ emit leftChanged();
+ d->updateHorizontalAnchors();
+}
+
+QSGAnchorLine QSGAnchors::right() const
+{
+ Q_D(const QSGAnchors);
+ return d->right;
+}
+
+void QSGAnchors::setRight(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkHAnchorValid(edge) || d->right == edge)
+ return;
+
+ d->usedAnchors |= RightAnchor;
+
+ if (!d->checkHValid()) {
+ d->usedAnchors &= ~RightAnchor;
+ return;
+ }
+
+ d->remDepend(d->right.item);
+ d->right = edge;
+ d->addDepend(d->right.item);
+ emit rightChanged();
+ d->updateHorizontalAnchors();
+}
+
+void QSGAnchors::resetRight()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~RightAnchor;
+ d->remDepend(d->right.item);
+ d->right = QSGAnchorLine();
+ emit rightChanged();
+ d->updateHorizontalAnchors();
+}
+
+QSGAnchorLine QSGAnchors::horizontalCenter() const
+{
+ Q_D(const QSGAnchors);
+ return d->hCenter;
+}
+
+void QSGAnchors::setHorizontalCenter(const QSGAnchorLine &edge)
+{
+ Q_D(QSGAnchors);
+ if (!d->checkHAnchorValid(edge) || d->hCenter == edge)
+ return;
+
+ d->usedAnchors |= HCenterAnchor;
+
+ if (!d->checkHValid()) {
+ d->usedAnchors &= ~HCenterAnchor;
+ return;
+ }
+
+ d->remDepend(d->hCenter.item);
+ d->hCenter = edge;
+ d->addDepend(d->hCenter.item);
+ emit horizontalCenterChanged();
+ d->updateHorizontalAnchors();
+}
+
+void QSGAnchors::resetHorizontalCenter()
+{
+ Q_D(QSGAnchors);
+ d->usedAnchors &= ~HCenterAnchor;
+ d->remDepend(d->hCenter.item);
+ d->hCenter = QSGAnchorLine();
+ emit horizontalCenterChanged();
+ d->updateHorizontalAnchors();
+}
+
+qreal QSGAnchors::leftMargin() const
+{
+ Q_D(const QSGAnchors);
+ return d->leftMargin;
+}
+
+void QSGAnchors::setLeftMargin(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->leftMargin == offset)
+ return;
+ d->leftMargin = offset;
+ if(d->fill)
+ d->fillChanged();
+ else
+ d->updateHorizontalAnchors();
+ emit leftMarginChanged();
+}
+
+qreal QSGAnchors::rightMargin() const
+{
+ Q_D(const QSGAnchors);
+ return d->rightMargin;
+}
+
+void QSGAnchors::setRightMargin(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->rightMargin == offset)
+ return;
+ d->rightMargin = offset;
+ if(d->fill)
+ d->fillChanged();
+ else
+ d->updateHorizontalAnchors();
+ emit rightMarginChanged();
+}
+
+qreal QSGAnchors::margins() const
+{
+ Q_D(const QSGAnchors);
+ return d->margins;
+}
+
+void QSGAnchors::setMargins(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->margins == offset)
+ return;
+ //###Is it significantly faster to set them directly so we can call fillChanged only once?
+ if(!d->rightMargin || d->rightMargin == d->margins)
+ setRightMargin(offset);
+ if(!d->leftMargin || d->leftMargin == d->margins)
+ setLeftMargin(offset);
+ if(!d->topMargin || d->topMargin == d->margins)
+ setTopMargin(offset);
+ if(!d->bottomMargin || d->bottomMargin == d->margins)
+ setBottomMargin(offset);
+ d->margins = offset;
+ emit marginsChanged();
+
+}
+
+qreal QSGAnchors::horizontalCenterOffset() const
+{
+ Q_D(const QSGAnchors);
+ return d->hCenterOffset;
+}
+
+void QSGAnchors::setHorizontalCenterOffset(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->hCenterOffset == offset)
+ return;
+ d->hCenterOffset = offset;
+ if(d->centerIn)
+ d->centerInChanged();
+ else
+ d->updateHorizontalAnchors();
+ emit horizontalCenterOffsetChanged();
+}
+
+qreal QSGAnchors::topMargin() const
+{
+ Q_D(const QSGAnchors);
+ return d->topMargin;
+}
+
+void QSGAnchors::setTopMargin(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->topMargin == offset)
+ return;
+ d->topMargin = offset;
+ if(d->fill)
+ d->fillChanged();
+ else
+ d->updateVerticalAnchors();
+ emit topMarginChanged();
+}
+
+qreal QSGAnchors::bottomMargin() const
+{
+ Q_D(const QSGAnchors);
+ return d->bottomMargin;
+}
+
+void QSGAnchors::setBottomMargin(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->bottomMargin == offset)
+ return;
+ d->bottomMargin = offset;
+ if(d->fill)
+ d->fillChanged();
+ else
+ d->updateVerticalAnchors();
+ emit bottomMarginChanged();
+}
+
+qreal QSGAnchors::verticalCenterOffset() const
+{
+ Q_D(const QSGAnchors);
+ return d->vCenterOffset;
+}
+
+void QSGAnchors::setVerticalCenterOffset(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->vCenterOffset == offset)
+ return;
+ d->vCenterOffset = offset;
+ if(d->centerIn)
+ d->centerInChanged();
+ else
+ d->updateVerticalAnchors();
+ emit verticalCenterOffsetChanged();
+}
+
+qreal QSGAnchors::baselineOffset() const
+{
+ Q_D(const QSGAnchors);
+ return d->baselineOffset;
+}
+
+void QSGAnchors::setBaselineOffset(qreal offset)
+{
+ Q_D(QSGAnchors);
+ if (d->baselineOffset == offset)
+ return;
+ d->baselineOffset = offset;
+ d->updateVerticalAnchors();
+ emit baselineOffsetChanged();
+}
+
+QSGAnchors::Anchors QSGAnchors::usedAnchors() const
+{
+ Q_D(const QSGAnchors);
+ return d->usedAnchors;
+}
+
+bool QSGAnchorsPrivate::checkHValid() const
+{
+ if (usedAnchors & QSGAnchors::LeftAnchor &&
+ usedAnchors & QSGAnchors::RightAnchor &&
+ usedAnchors & QSGAnchors::HCenterAnchor) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot specify left, right, and hcenter anchors.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QSGAnchorsPrivate::checkHAnchorValid(QSGAnchorLine anchor) const
+{
+ if (!anchor.item) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor to a null item.");
+ return false;
+ } else if (anchor.anchorLine & QSGAnchorLine::Vertical_Mask) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor a horizontal edge to a vertical edge.");
+ return false;
+ } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor to an item that isn't a parent or sibling.");
+ return false;
+ } else if (anchor.item == item) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor item to self.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QSGAnchorsPrivate::checkVValid() const
+{
+ if (usedAnchors & QSGAnchors::TopAnchor &&
+ usedAnchors & QSGAnchors::BottomAnchor &&
+ usedAnchors & QSGAnchors::VCenterAnchor) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot specify top, bottom, and vcenter anchors.");
+ return false;
+ } else if (usedAnchors & QSGAnchors::BaselineAnchor &&
+ (usedAnchors & QSGAnchors::TopAnchor ||
+ usedAnchors & QSGAnchors::BottomAnchor ||
+ usedAnchors & QSGAnchors::VCenterAnchor)) {
+ qmlInfo(item) << QSGAnchors::tr("Baseline anchor cannot be used in conjunction with top, bottom, or vcenter anchors.");
+ return false;
+ }
+
+ return true;
+}
+
+bool QSGAnchorsPrivate::checkVAnchorValid(QSGAnchorLine anchor) const
+{
+ if (!anchor.item) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor to a null item.");
+ return false;
+ } else if (anchor.anchorLine & QSGAnchorLine::Horizontal_Mask) {
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor a vertical edge to a horizontal edge.");
+ return false;
+ } else if (anchor.item != item->parentItem() && anchor.item->parentItem() != item->parentItem()){
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor to an item that isn't a parent or sibling.");
+ return false;
+ } else if (anchor.item == item){
+ qmlInfo(item) << QSGAnchors::tr("Cannot anchor item to self.");
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include <moc_qsganchors_p.cpp>
+
diff --git a/src/declarative/items/qsganchors_p.h b/src/declarative/items/qsganchors_p.h
new file mode 100644
index 0000000000..d26fb57961
--- /dev/null
+++ b/src/declarative/items/qsganchors_p.h
@@ -0,0 +1,201 @@
+// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGANCHORS_P_H
+#define QSGANCHORS_P_H
+
+#include <qdeclarative.h>
+
+#include <QtCore/QObject>
+
+#include <private/qdeclarativeglobal_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGItem;
+class QSGAnchorsPrivate;
+class QSGAnchorLine;
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGAnchors : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QSGAnchorLine left READ left WRITE setLeft RESET resetLeft NOTIFY leftChanged)
+ Q_PROPERTY(QSGAnchorLine right READ right WRITE setRight RESET resetRight NOTIFY rightChanged)
+ Q_PROPERTY(QSGAnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter NOTIFY horizontalCenterChanged)
+ Q_PROPERTY(QSGAnchorLine top READ top WRITE setTop RESET resetTop NOTIFY topChanged)
+ Q_PROPERTY(QSGAnchorLine bottom READ bottom WRITE setBottom RESET resetBottom NOTIFY bottomChanged)
+ Q_PROPERTY(QSGAnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter NOTIFY verticalCenterChanged)
+ Q_PROPERTY(QSGAnchorLine baseline READ baseline WRITE setBaseline RESET resetBaseline NOTIFY baselineChanged)
+ Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged)
+ Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
+ Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
+ Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged)
+ Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged)
+ Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
+ Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged)
+ Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged)
+ Q_PROPERTY(QSGItem *fill READ fill WRITE setFill RESET resetFill NOTIFY fillChanged)
+ Q_PROPERTY(QSGItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged)
+ Q_PROPERTY(bool mirrored READ mirrored NOTIFY mirroredChanged)
+
+public:
+ QSGAnchors(QSGItem *item, QObject *parent=0);
+ virtual ~QSGAnchors();
+
+ enum Anchor {
+ LeftAnchor = 0x01,
+ RightAnchor = 0x02,
+ TopAnchor = 0x04,
+ BottomAnchor = 0x08,
+ HCenterAnchor = 0x10,
+ VCenterAnchor = 0x20,
+ BaselineAnchor = 0x40,
+ Horizontal_Mask = LeftAnchor | RightAnchor | HCenterAnchor,
+ Vertical_Mask = TopAnchor | BottomAnchor | VCenterAnchor | BaselineAnchor
+ };
+ Q_DECLARE_FLAGS(Anchors, Anchor)
+
+ QSGAnchorLine left() const;
+ void setLeft(const QSGAnchorLine &edge);
+ void resetLeft();
+
+ QSGAnchorLine right() const;
+ void setRight(const QSGAnchorLine &edge);
+ void resetRight();
+
+ QSGAnchorLine horizontalCenter() const;
+ void setHorizontalCenter(const QSGAnchorLine &edge);
+ void resetHorizontalCenter();
+
+ QSGAnchorLine top() const;
+ void setTop(const QSGAnchorLine &edge);
+ void resetTop();
+
+ QSGAnchorLine bottom() const;
+ void setBottom(const QSGAnchorLine &edge);
+ void resetBottom();
+
+ QSGAnchorLine verticalCenter() const;
+ void setVerticalCenter(const QSGAnchorLine &edge);
+ void resetVerticalCenter();
+
+ QSGAnchorLine baseline() const;
+ void setBaseline(const QSGAnchorLine &edge);
+ void resetBaseline();
+
+ qreal leftMargin() const;
+ void setLeftMargin(qreal);
+
+ qreal rightMargin() const;
+ void setRightMargin(qreal);
+
+ qreal horizontalCenterOffset() const;
+ void setHorizontalCenterOffset(qreal);
+
+ qreal topMargin() const;
+ void setTopMargin(qreal);
+
+ qreal bottomMargin() const;
+ void setBottomMargin(qreal);
+
+ qreal margins() const;
+ void setMargins(qreal);
+
+ qreal verticalCenterOffset() const;
+ void setVerticalCenterOffset(qreal);
+
+ qreal baselineOffset() const;
+ void setBaselineOffset(qreal);
+
+ QSGItem *fill() const;
+ void setFill(QSGItem *);
+ void resetFill();
+
+ QSGItem *centerIn() const;
+ void setCenterIn(QSGItem *);
+ void resetCenterIn();
+
+ Anchors usedAnchors() const;
+
+ bool mirrored();
+
+ void classBegin();
+ void componentComplete();
+
+Q_SIGNALS:
+ void leftChanged();
+ void rightChanged();
+ void topChanged();
+ void bottomChanged();
+ void verticalCenterChanged();
+ void horizontalCenterChanged();
+ void baselineChanged();
+ void fillChanged();
+ void centerInChanged();
+ void leftMarginChanged();
+ void rightMarginChanged();
+ void topMarginChanged();
+ void bottomMarginChanged();
+ void marginsChanged();
+ void verticalCenterOffsetChanged();
+ void horizontalCenterOffsetChanged();
+ void baselineOffsetChanged();
+ void mirroredChanged();
+
+private:
+ friend class QSGItemPrivate;
+ Q_DISABLE_COPY(QSGAnchors)
+ Q_DECLARE_PRIVATE(QSGAnchors)
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGAnchors::Anchors)
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGAnchors)
+
+QT_END_HEADER
+
+#endif // QSGANCHORS_P_H
diff --git a/src/declarative/items/qsganchors_p_p.h b/src/declarative/items/qsganchors_p_p.h
new file mode 100644
index 0000000000..cb9b950c8f
--- /dev/null
+++ b/src/declarative/items/qsganchors_p_p.h
@@ -0,0 +1,173 @@
+// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGANCHORS_P_P_H
+#define QSGANCHORS_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsganchors_p.h"
+#include "qsgitemchangelistener_p.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGAnchorLine
+{
+public:
+ QSGAnchorLine() : item(0), anchorLine(Invalid) {}
+
+ enum AnchorLine {
+ Invalid = 0x0,
+ Left = 0x01,
+ Right = 0x02,
+ Top = 0x04,
+ Bottom = 0x08,
+ HCenter = 0x10,
+ VCenter = 0x20,
+ Baseline = 0x40,
+ Horizontal_Mask = Left | Right | HCenter,
+ Vertical_Mask = Top | Bottom | VCenter | Baseline
+ };
+
+ QSGItem *item;
+ AnchorLine anchorLine;
+};
+
+inline bool operator==(const QSGAnchorLine& a, const QSGAnchorLine& b)
+{
+ return a.item == b.item && a.anchorLine == b.anchorLine;
+}
+
+class QSGAnchorsPrivate : public QObjectPrivate, public QSGItemChangeListener
+{
+ Q_DECLARE_PUBLIC(QSGAnchors)
+public:
+ QSGAnchorsPrivate(QSGItem *i)
+ : componentComplete(true), updatingMe(false), updatingHorizontalAnchor(0),
+ updatingVerticalAnchor(0), updatingFill(0), updatingCenterIn(0), item(i), usedAnchors(0), fill(0),
+ centerIn(0), leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0),
+ margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)
+ {
+ }
+
+ void clearItem(QSGItem *);
+
+ void addDepend(QSGItem *);
+ void remDepend(QSGItem *);
+ bool isItemComplete() const;
+
+ bool componentComplete:1;
+ bool updatingMe:1;
+ uint updatingHorizontalAnchor:2;
+ uint updatingVerticalAnchor:2;
+ uint updatingFill:2;
+ uint updatingCenterIn:2;
+
+ void setItemHeight(qreal);
+ void setItemWidth(qreal);
+ void setItemX(qreal);
+ void setItemY(qreal);
+ void setItemPos(const QPointF &);
+ void setItemSize(const QSizeF &);
+
+ void updateOnComplete();
+ void updateMe();
+
+ // QSGItemGeometryListener interface
+ void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &);
+ QSGAnchorsPrivate *anchorPrivate() { return this; }
+
+ bool checkHValid() const;
+ bool checkVValid() const;
+ bool checkHAnchorValid(QSGAnchorLine anchor) const;
+ bool checkVAnchorValid(QSGAnchorLine anchor) const;
+ bool calcStretch(const QSGAnchorLine &edge1, const QSGAnchorLine &edge2, qreal offset1, qreal offset2, QSGAnchorLine::AnchorLine line, qreal &stretch);
+
+ bool isMirrored() const;
+ void updateHorizontalAnchors();
+ void updateVerticalAnchors();
+ void fillChanged();
+ void centerInChanged();
+
+ QSGItem *item;
+ QSGAnchors::Anchors usedAnchors;
+
+ QSGItem *fill;
+ QSGItem *centerIn;
+
+ QSGAnchorLine left;
+ QSGAnchorLine right;
+ QSGAnchorLine top;
+ QSGAnchorLine bottom;
+ QSGAnchorLine vCenter;
+ QSGAnchorLine hCenter;
+ QSGAnchorLine baseline;
+
+ qreal leftMargin;
+ qreal rightMargin;
+ qreal topMargin;
+ qreal bottomMargin;
+ qreal margins;
+ qreal vCenterOffset;
+ qreal hCenterOffset;
+ qreal baselineOffset;
+
+ static inline QSGAnchorsPrivate *get(QSGAnchors *o) {
+ return static_cast<QSGAnchorsPrivate *>(QObjectPrivate::get(o));
+ }
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSGAnchorLine)
+
+#endif
diff --git a/src/declarative/items/qsganimatedimage.cpp b/src/declarative/items/qsganimatedimage.cpp
new file mode 100644
index 0000000000..f036042ce2
--- /dev/null
+++ b/src/declarative/items/qsganimatedimage.cpp
@@ -0,0 +1,304 @@
+// Commit: af33f9f2e7ec433b81f5c18e3e7395db4a56c5fe
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsganimatedimage_p.h"
+#include "qsganimatedimage_p_p.h"
+
+#ifndef QT_NO_MOVIE
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qmovie.h>
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qnetworkreply.h>
+
+#include <private/qdeclarativeengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGAnimatedImage::QSGAnimatedImage(QSGItem *parent)
+ : QSGImage(*(new QSGAnimatedImagePrivate), parent)
+{
+}
+
+QSGAnimatedImage::~QSGAnimatedImage()
+{
+ Q_D(QSGAnimatedImage);
+ delete d->_movie;
+}
+
+bool QSGAnimatedImage::isPaused() const
+{
+ Q_D(const QSGAnimatedImage);
+ if(!d->_movie)
+ return false;
+ return d->_movie->state()==QMovie::Paused;
+}
+
+void QSGAnimatedImage::setPaused(bool pause)
+{
+ Q_D(QSGAnimatedImage);
+ if(pause == d->paused)
+ return;
+ d->paused = pause;
+ if(!d->_movie)
+ return;
+ d->_movie->setPaused(pause);
+}
+
+bool QSGAnimatedImage::isPlaying() const
+{
+ Q_D(const QSGAnimatedImage);
+ if (!d->_movie)
+ return false;
+ return d->_movie->state()!=QMovie::NotRunning;
+}
+
+void QSGAnimatedImage::setPlaying(bool play)
+{
+ Q_D(QSGAnimatedImage);
+ if(play == d->playing)
+ return;
+ d->playing = play;
+ if (!d->_movie)
+ return;
+ if (play)
+ d->_movie->start();
+ else
+ d->_movie->stop();
+}
+
+int QSGAnimatedImage::currentFrame() const
+{
+ Q_D(const QSGAnimatedImage);
+ if (!d->_movie)
+ return d->preset_currentframe;
+ return d->_movie->currentFrameNumber();
+}
+
+void QSGAnimatedImage::setCurrentFrame(int frame)
+{
+ Q_D(QSGAnimatedImage);
+ if (!d->_movie) {
+ d->preset_currentframe = frame;
+ return;
+ }
+ d->_movie->jumpToFrame(frame);
+}
+
+int QSGAnimatedImage::frameCount() const
+{
+ Q_D(const QSGAnimatedImage);
+ if (!d->_movie)
+ return 0;
+ return d->_movie->frameCount();
+}
+
+void QSGAnimatedImage::setSource(const QUrl &url)
+{
+ Q_D(QSGAnimatedImage);
+ if (url == d->url)
+ return;
+
+ delete d->_movie;
+ d->_movie = 0;
+
+ if (d->reply) {
+ d->reply->deleteLater();
+ d->reply = 0;
+ }
+
+ d->url = url;
+ emit sourceChanged(d->url);
+
+ if (isComponentComplete())
+ load();
+}
+
+void QSGAnimatedImage::load()
+{
+ Q_D(QSGAnimatedImage);
+
+ QSGImageBase::Status oldStatus = d->status;
+ qreal oldProgress = d->progress;
+
+ if (d->url.isEmpty()) {
+ delete d->_movie;
+ d->setPixmap(QPixmap());
+ d->progress = 0;
+ d->status = Null;
+ if (d->status != oldStatus)
+ emit statusChanged(d->status);
+ if (d->progress != oldProgress)
+ emit progressChanged(d->progress);
+ } else {
+#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
+ QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
+ if (!lf.isEmpty()) {
+ //### should be unified with movieRequestFinished
+ d->_movie = new QMovie(lf);
+ if (!d->_movie->isValid()){
+ qmlInfo(this) << "Error Reading Animated Image File " << d->url.toString();
+ delete d->_movie;
+ d->_movie = 0;
+ d->status = Error;
+ if (d->status != oldStatus)
+ emit statusChanged(d->status);
+ return;
+ }
+ connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
+ this, SLOT(playingStatusChanged()));
+ connect(d->_movie, SIGNAL(frameChanged(int)),
+ this, SLOT(movieUpdate()));
+ d->_movie->setCacheMode(QMovie::CacheAll);
+ if(d->playing)
+ d->_movie->start();
+ else
+ d->_movie->jumpToFrame(0);
+ if(d->paused)
+ d->_movie->setPaused(true);
+ d->setPixmap(d->_movie->currentPixmap());
+ d->status = Ready;
+ d->progress = 1.0;
+ if (d->status != oldStatus)
+ emit statusChanged(d->status);
+ if (d->progress != oldProgress)
+ emit progressChanged(d->progress);
+ return;
+ }
+#endif
+ d->status = Loading;
+ d->progress = 0;
+ emit statusChanged(d->status);
+ emit progressChanged(d->progress);
+ QNetworkRequest req(d->url);
+ req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+ d->reply = qmlEngine(this)->networkAccessManager()->get(req);
+ QObject::connect(d->reply, SIGNAL(finished()),
+ this, SLOT(movieRequestFinished()));
+ QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
+ this, SLOT(requestProgress(qint64,qint64)));
+ }
+}
+
+#define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16
+
+void QSGAnimatedImage::movieRequestFinished()
+{
+ Q_D(QSGAnimatedImage);
+
+ d->redirectCount++;
+ if (d->redirectCount < ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION) {
+ QVariant redirect = d->reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirect.isValid()) {
+ QUrl url = d->reply->url().resolved(redirect.toUrl());
+ d->reply->deleteLater();
+ d->reply = 0;
+ setSource(url);
+ return;
+ }
+ }
+ d->redirectCount=0;
+
+ d->_movie = new QMovie(d->reply);
+ if (!d->_movie->isValid()){
+#ifndef QT_NO_DEBUG_STREAM
+ qmlInfo(this) << "Error Reading Animated Image File " << d->url;
+#endif
+ delete d->_movie;
+ d->_movie = 0;
+ d->status = Error;
+ emit statusChanged(d->status);
+ return;
+ }
+ connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)),
+ this, SLOT(playingStatusChanged()));
+ connect(d->_movie, SIGNAL(frameChanged(int)),
+ this, SLOT(movieUpdate()));
+ d->_movie->setCacheMode(QMovie::CacheAll);
+ if(d->playing)
+ d->_movie->start();
+ if (d->paused || !d->playing) {
+ d->_movie->jumpToFrame(d->preset_currentframe);
+ d->preset_currentframe = 0;
+ }
+ if(d->paused)
+ d->_movie->setPaused(true);
+ d->setPixmap(d->_movie->currentPixmap());
+ d->status = Ready;
+ emit statusChanged(d->status);
+}
+
+void QSGAnimatedImage::movieUpdate()
+{
+ Q_D(QSGAnimatedImage);
+ d->setPixmap(d->_movie->currentPixmap());
+ emit frameChanged();
+}
+
+void QSGAnimatedImage::playingStatusChanged()
+{
+ Q_D(QSGAnimatedImage);
+ if((d->_movie->state() != QMovie::NotRunning) != d->playing){
+ d->playing = (d->_movie->state() != QMovie::NotRunning);
+ emit playingChanged();
+ }
+ if((d->_movie->state() == QMovie::Paused) != d->paused){
+ d->playing = (d->_movie->state() == QMovie::Paused);
+ emit pausedChanged();
+ }
+}
+
+void QSGAnimatedImage::componentComplete()
+{
+ Q_D(QSGAnimatedImage);
+ QSGItem::componentComplete(); // NOT QSGImage
+ if (d->url.isValid())
+ load();
+ if (!d->reply) {
+ setCurrentFrame(d->preset_currentframe);
+ d->preset_currentframe = 0;
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_MOVIE
diff --git a/src/declarative/items/qsganimatedimage_p.h b/src/declarative/items/qsganimatedimage_p.h
new file mode 100644
index 0000000000..64319a0f0d
--- /dev/null
+++ b/src/declarative/items/qsganimatedimage_p.h
@@ -0,0 +1,117 @@
+// Commit: 80d0fe9cbd92288a08d5ced8767f1edb651dae37
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGANIMATEDIMAGE_P_H
+#define QSGANIMATEDIMAGE_P_H
+
+#include "qsgimage_p.h"
+
+#ifndef QT_NO_MOVIE
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QMovie;
+class QSGAnimatedImagePrivate;
+
+class Q_AUTOTEST_EXPORT QSGAnimatedImage : public QSGImage
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool playing READ isPlaying WRITE setPlaying NOTIFY playingChanged)
+ Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged)
+ Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY frameChanged)
+ Q_PROPERTY(int frameCount READ frameCount)
+
+ // read-only for AnimatedImage
+ Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged)
+
+public:
+ QSGAnimatedImage(QSGItem *parent=0);
+ ~QSGAnimatedImage();
+
+ bool isPlaying() const;
+ void setPlaying(bool play);
+
+ bool isPaused() const;
+ void setPaused(bool pause);
+
+ int currentFrame() const;
+ void setCurrentFrame(int frame);
+
+ int frameCount() const;
+
+ // Extends QSGImage's src property*/
+ virtual void setSource(const QUrl&);
+
+Q_SIGNALS:
+ void playingChanged();
+ void pausedChanged();
+ void frameChanged();
+ void sourceSizeChanged();
+
+private Q_SLOTS:
+ void movieUpdate();
+ void movieRequestFinished();
+ void playingStatusChanged();
+
+protected:
+ virtual void load();
+ void componentComplete();
+
+private:
+ Q_DISABLE_COPY(QSGAnimatedImage)
+ Q_DECLARE_PRIVATE(QSGAnimatedImage)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGAnimatedImage)
+
+QT_END_HEADER
+
+#endif // QT_NO_MOVIE
+
+#endif // QSGANIMATEDIMAGE_P_H
diff --git a/src/declarative/items/qsganimatedimage_p_p.h b/src/declarative/items/qsganimatedimage_p_p.h
new file mode 100644
index 0000000000..560c8c1d43
--- /dev/null
+++ b/src/declarative/items/qsganimatedimage_p_p.h
@@ -0,0 +1,88 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGANIMATEDIMAGE_P_P_H
+#define QSGANIMATEDIMAGE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgimage_p_p.h"
+
+#ifndef QT_NO_MOVIE
+
+QT_BEGIN_NAMESPACE
+
+class QMovie;
+class QNetworkReply;
+
+class QSGAnimatedImagePrivate : public QSGImagePrivate
+{
+ Q_DECLARE_PUBLIC(QSGAnimatedImage)
+
+public:
+ QSGAnimatedImagePrivate()
+ : playing(true), paused(false), preset_currentframe(0), _movie(0), reply(0), redirectCount(0)
+ {
+ }
+
+ bool playing;
+ bool paused;
+ int preset_currentframe;
+ QMovie *_movie;
+ QNetworkReply *reply;
+ int redirectCount;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_MOVIE
+
+#endif // QSGANIMATEDIMAGE_P_P_H
diff --git a/src/declarative/items/qsganimation.cpp b/src/declarative/items/qsganimation.cpp
new file mode 100644
index 0000000000..ad6ed030fd
--- /dev/null
+++ b/src/declarative/items/qsganimation.cpp
@@ -0,0 +1,442 @@
+// Commit: 91501cc9b542de644cd70098a6bc5ff738cdeb49
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsganimation_p.h"
+#include "qsganimation_p_p.h"
+#include "qsgstateoperations_p.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qsequentialanimationgroup.h>
+#include <QtCore/qparallelanimationgroup.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGParentAnimation::QSGParentAnimation(QObject *parent)
+ : QDeclarativeAnimationGroup(*(new QSGParentAnimationPrivate), parent)
+{
+ Q_D(QSGParentAnimation);
+ d->topLevelGroup = new QSequentialAnimationGroup;
+ QDeclarative_setParent_noEvent(d->topLevelGroup, this);
+
+ d->startAction = new QActionAnimation;
+ QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
+ d->topLevelGroup->addAnimation(d->startAction);
+
+ d->ag = new QParallelAnimationGroup;
+ QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
+ d->topLevelGroup->addAnimation(d->ag);
+
+ d->endAction = new QActionAnimation;
+ QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
+ d->topLevelGroup->addAnimation(d->endAction);
+}
+
+QSGParentAnimation::~QSGParentAnimation()
+{
+}
+
+QSGItem *QSGParentAnimation::target() const
+{
+ Q_D(const QSGParentAnimation);
+ return d->target;
+}
+
+void QSGParentAnimation::setTarget(QSGItem *target)
+{
+ Q_D(QSGParentAnimation);
+ if (target == d->target)
+ return;
+
+ d->target = target;
+ emit targetChanged();
+}
+
+QSGItem *QSGParentAnimation::newParent() const
+{
+ Q_D(const QSGParentAnimation);
+ return d->newParent;
+}
+
+void QSGParentAnimation::setNewParent(QSGItem *newParent)
+{
+ Q_D(QSGParentAnimation);
+ if (newParent == d->newParent)
+ return;
+
+ d->newParent = newParent;
+ emit newParentChanged();
+}
+
+QSGItem *QSGParentAnimation::via() const
+{
+ Q_D(const QSGParentAnimation);
+ return d->via;
+}
+
+void QSGParentAnimation::setVia(QSGItem *via)
+{
+ Q_D(QSGParentAnimation);
+ if (via == d->via)
+ return;
+
+ d->via = via;
+ emit viaChanged();
+}
+
+//### mirrors same-named function in QSGItem
+QPointF QSGParentAnimationPrivate::computeTransformOrigin(QSGItem::TransformOrigin origin, qreal width, qreal height) const
+{
+ switch(origin) {
+ default:
+ case QSGItem::TopLeft:
+ return QPointF(0, 0);
+ case QSGItem::Top:
+ return QPointF(width / 2., 0);
+ case QSGItem::TopRight:
+ return QPointF(width, 0);
+ case QSGItem::Left:
+ return QPointF(0, height / 2.);
+ case QSGItem::Center:
+ return QPointF(width / 2., height / 2.);
+ case QSGItem::Right:
+ return QPointF(width, height / 2.);
+ case QSGItem::BottomLeft:
+ return QPointF(0, height);
+ case QSGItem::Bottom:
+ return QPointF(width / 2., height);
+ case QSGItem::BottomRight:
+ return QPointF(width, height);
+ }
+}
+
+void QSGParentAnimation::transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction)
+{
+ Q_D(QSGParentAnimation);
+
+ struct QSGParentAnimationData : public QAbstractAnimationAction
+ {
+ QSGParentAnimationData() {}
+ ~QSGParentAnimationData() { qDeleteAll(pc); }
+
+ QDeclarativeStateActions actions;
+ //### reverse should probably apply on a per-action basis
+ bool reverse;
+ QList<QSGParentChange *> pc;
+ virtual void doAction()
+ {
+ for (int ii = 0; ii < actions.count(); ++ii) {
+ const QDeclarativeAction &action = actions.at(ii);
+ if (reverse)
+ action.event->reverse();
+ else
+ action.event->execute();
+ }
+ }
+ };
+
+ QSGParentAnimationData *data = new QSGParentAnimationData;
+ QSGParentAnimationData *viaData = new QSGParentAnimationData;
+
+ bool hasExplicit = false;
+ if (d->target && d->newParent) {
+ data->reverse = false;
+ QDeclarativeAction myAction;
+ QSGParentChange *pc = new QSGParentChange;
+ pc->setObject(d->target);
+ pc->setParent(d->newParent);
+ myAction.event = pc;
+ data->pc << pc;
+ data->actions << myAction;
+ hasExplicit = true;
+ if (d->via) {
+ viaData->reverse = false;
+ QDeclarativeAction myVAction;
+ QSGParentChange *vpc = new QSGParentChange;
+ vpc->setObject(d->target);
+ vpc->setParent(d->via);
+ myVAction.event = vpc;
+ viaData->pc << vpc;
+ viaData->actions << myVAction;
+ }
+ //### once actions have concept of modified,
+ // loop to match appropriate ParentChanges and mark as modified
+ }
+
+ if (!hasExplicit)
+ for (int i = 0; i < actions.size(); ++i) {
+ QDeclarativeAction &action = actions[i];
+ if (action.event && action.event->typeName() == QLatin1String("ParentChange")
+ && (!d->target || static_cast<QSGParentChange*>(action.event)->object() == d->target)) {
+
+ QSGParentChange *pc = static_cast<QSGParentChange*>(action.event);
+ QDeclarativeAction myAction = action;
+ data->reverse = action.reverseEvent;
+
+ //### this logic differs from PropertyAnimation
+ // (probably a result of modified vs. done)
+ if (d->newParent) {
+ QSGParentChange *epc = new QSGParentChange;
+ epc->setObject(static_cast<QSGParentChange*>(action.event)->object());
+ epc->setParent(d->newParent);
+ myAction.event = epc;
+ data->pc << epc;
+ data->actions << myAction;
+ pc = epc;
+ } else {
+ action.actionDone = true;
+ data->actions << myAction;
+ }
+
+ if (d->via) {
+ viaData->reverse = false;
+ QDeclarativeAction myAction;
+ QSGParentChange *vpc = new QSGParentChange;
+ vpc->setObject(pc->object());
+ vpc->setParent(d->via);
+ myAction.event = vpc;
+ viaData->pc << vpc;
+ viaData->actions << myAction;
+ QDeclarativeAction dummyAction;
+ QDeclarativeAction &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
+ QDeclarativeAction &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
+ QDeclarativeAction &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
+ QDeclarativeAction &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction;
+ QSGItem *target = pc->object();
+ QSGItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent();
+
+ //### this mirrors the logic in QSGParentChange.
+ bool ok;
+ const QTransform &transform = targetParent->itemTransform(d->via, &ok);
+ if (transform.type() >= QTransform::TxShear || !ok) {
+ qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under complex transform");
+ ok = false;
+ }
+
+ qreal scale = 1;
+ qreal rotation = 0;
+ bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
+ if (ok && !isRotate) {
+ if (transform.m11() == transform.m22())
+ scale = transform.m11();
+ else {
+ qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
+ ok = false;
+ }
+ } else if (ok && isRotate) {
+ if (transform.m11() == transform.m22())
+ scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
+ else {
+ qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under non-uniform scale");
+ ok = false;
+ }
+
+ if (scale != 0)
+ rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
+ else {
+ qmlInfo(this) << QSGParentAnimation::tr("Unable to preserve appearance under scale of 0");
+ ok = false;
+ }
+ }
+
+ const QPointF &point = transform.map(QPointF(xAction.toValue.toReal(),yAction.toValue.toReal()));
+ qreal x = point.x();
+ qreal y = point.y();
+ if (ok && target->transformOrigin() != QSGItem::TopLeft) {
+ qreal w = target->width();
+ qreal h = target->height();
+ if (pc->widthIsSet() && i < actions.size() - 1)
+ w = actions[++i].toValue.toReal();
+ if (pc->heightIsSet() && i < actions.size() - 1)
+ h = actions[++i].toValue.toReal();
+ const QPointF &transformOrigin
+ = d->computeTransformOrigin(target->transformOrigin(), w,h);
+ qreal tempxt = transformOrigin.x();
+ qreal tempyt = transformOrigin.y();
+ QTransform t;
+ t.translate(-tempxt, -tempyt);
+ t.rotate(rotation);
+ t.scale(scale, scale);
+ t.translate(tempxt, tempyt);
+ const QPointF &offset = t.map(QPointF(0,0));
+ x += offset.x();
+ y += offset.y();
+ }
+
+ if (ok) {
+ //qDebug() << x << y << rotation << scale;
+ xAction.toValue = x;
+ yAction.toValue = y;
+ sAction.toValue = sAction.toValue.toReal() * scale;
+ rAction.toValue = rAction.toValue.toReal() + rotation;
+ }
+ }
+ }
+ }
+
+ if (data->actions.count()) {
+ if (direction == QDeclarativeAbstractAnimation::Forward) {
+ d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
+ d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
+ } else {
+ d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
+ d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
+ }
+ } else {
+ delete data;
+ delete viaData;
+ }
+
+ //take care of any child animations
+ bool valid = d->defaultProperty.isValid();
+ for (int ii = 0; ii < d->animations.count(); ++ii) {
+ if (valid)
+ d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
+ d->animations.at(ii)->transition(actions, modified, direction);
+ }
+
+}
+
+QAbstractAnimation *QSGParentAnimation::qtAnimation()
+{
+ Q_D(QSGParentAnimation);
+ return d->topLevelGroup;
+}
+
+QSGAnchorAnimation::QSGAnchorAnimation(QObject *parent)
+: QDeclarativeAbstractAnimation(*(new QSGAnchorAnimationPrivate), parent)
+{
+ Q_D(QSGAnchorAnimation);
+ d->va = new QDeclarativeBulkValueAnimator;
+ QDeclarative_setParent_noEvent(d->va, this);
+}
+
+QSGAnchorAnimation::~QSGAnchorAnimation()
+{
+}
+
+QAbstractAnimation *QSGAnchorAnimation::qtAnimation()
+{
+ Q_D(QSGAnchorAnimation);
+ return d->va;
+}
+
+QDeclarativeListProperty<QSGItem> QSGAnchorAnimation::targets()
+{
+ Q_D(QSGAnchorAnimation);
+ return QDeclarativeListProperty<QSGItem>(this, d->targets);
+}
+
+int QSGAnchorAnimation::duration() const
+{
+ Q_D(const QSGAnchorAnimation);
+ return d->va->duration();
+}
+
+void QSGAnchorAnimation::setDuration(int duration)
+{
+ if (duration < 0) {
+ qmlInfo(this) << tr("Cannot set a duration of < 0");
+ return;
+ }
+
+ Q_D(QSGAnchorAnimation);
+ if (d->va->duration() == duration)
+ return;
+ d->va->setDuration(duration);
+ emit durationChanged(duration);
+}
+
+QEasingCurve QSGAnchorAnimation::easing() const
+{
+ Q_D(const QSGAnchorAnimation);
+ return d->va->easingCurve();
+}
+
+void QSGAnchorAnimation::setEasing(const QEasingCurve &e)
+{
+ Q_D(QSGAnchorAnimation);
+ if (d->va->easingCurve() == e)
+ return;
+
+ d->va->setEasingCurve(e);
+ emit easingChanged(e);
+}
+
+void QSGAnchorAnimation::transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction)
+{
+ Q_UNUSED(modified);
+ Q_D(QSGAnchorAnimation);
+ QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
+ data->interpolatorType = QMetaType::QReal;
+ data->interpolator = d->interpolator;
+
+ data->reverse = direction == Backward ? true : false;
+ data->fromSourced = false;
+ data->fromDefined = false;
+
+ for (int ii = 0; ii < actions.count(); ++ii) {
+ QDeclarativeAction &action = actions[ii];
+ if (action.event && action.event->typeName() == QLatin1String("AnchorChanges")
+ && (d->targets.isEmpty() || d->targets.contains(static_cast<QSGAnchorChanges*>(action.event)->object()))) {
+ data->actions << static_cast<QSGAnchorChanges*>(action.event)->additionalActions();
+ }
+ }
+
+ if (data->actions.count()) {
+ if (!d->rangeIsSet) {
+ d->va->setStartValue(qreal(0));
+ d->va->setEndValue(qreal(1));
+ d->rangeIsSet = true;
+ }
+ d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
+ d->va->setFromSourcedValue(&data->fromSourced);
+ } else {
+ delete data;
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsganimation_p.h b/src/declarative/items/qsganimation_p.h
new file mode 100644
index 0000000000..7c63331cfe
--- /dev/null
+++ b/src/declarative/items/qsganimation_p.h
@@ -0,0 +1,132 @@
+// Commit: e39a2e39451bf106a9845f8a60fc571faaa4dde5
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGANIMATION_H
+#define QSGANIMATION_H
+
+#include "qsgitem.h"
+
+#include <private/qdeclarativeanimation_p.h>
+
+#include <QtCore/qabstractanimation.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGParentAnimationPrivate;
+class QSGParentAnimation : public QDeclarativeAnimationGroup
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGParentAnimation)
+
+ Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged)
+ Q_PROPERTY(QSGItem *newParent READ newParent WRITE setNewParent NOTIFY newParentChanged)
+ Q_PROPERTY(QSGItem *via READ via WRITE setVia NOTIFY viaChanged)
+
+public:
+ QSGParentAnimation(QObject *parent=0);
+ virtual ~QSGParentAnimation();
+
+ QSGItem *target() const;
+ void setTarget(QSGItem *);
+
+ QSGItem *newParent() const;
+ void setNewParent(QSGItem *);
+
+ QSGItem *via() const;
+ void setVia(QSGItem *);
+
+Q_SIGNALS:
+ void targetChanged();
+ void newParentChanged();
+ void viaChanged();
+
+protected:
+ virtual void transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction);
+ virtual QAbstractAnimation *qtAnimation();
+};
+
+class QSGAnchorAnimationPrivate;
+class QSGAnchorAnimation : public QDeclarativeAbstractAnimation
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGAnchorAnimation)
+ Q_PROPERTY(QDeclarativeListProperty<QSGItem> targets READ targets)
+ Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged)
+ Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged)
+
+public:
+ QSGAnchorAnimation(QObject *parent=0);
+ virtual ~QSGAnchorAnimation();
+
+ QDeclarativeListProperty<QSGItem> targets();
+
+ int duration() const;
+ void setDuration(int);
+
+ QEasingCurve easing() const;
+ void setEasing(const QEasingCurve &);
+
+Q_SIGNALS:
+ void durationChanged(int);
+ void easingChanged(const QEasingCurve&);
+
+protected:
+ virtual void transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction);
+ virtual QAbstractAnimation *qtAnimation();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGParentAnimation)
+QML_DECLARE_TYPE(QSGAnchorAnimation)
+
+QT_END_HEADER
+
+#endif // QSGANIMATION_H
diff --git a/src/declarative/items/qsganimation_p_p.h b/src/declarative/items/qsganimation_p_p.h
new file mode 100644
index 0000000000..10457d6b52
--- /dev/null
+++ b/src/declarative/items/qsganimation_p_p.h
@@ -0,0 +1,97 @@
+// Commit: 0ade09152067324f74678f2de4d447b6e0280600
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGANIMATION_P_H
+#define QSGANIMATION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsganimation_p.h"
+
+#include <private/qdeclarativeanimation_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGParentAnimationPrivate : public QDeclarativeAnimationGroupPrivate
+{
+ Q_DECLARE_PUBLIC(QSGParentAnimation)
+public:
+ QSGParentAnimationPrivate()
+ : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0),
+ via(0), topLevelGroup(0), startAction(0), endAction(0) {}
+
+ QSGItem *target;
+ QSGItem *newParent;
+ QSGItem *via;
+
+ QSequentialAnimationGroup *topLevelGroup;
+ QActionAnimation *startAction;
+ QActionAnimation *endAction;
+
+ QPointF computeTransformOrigin(QSGItem::TransformOrigin origin, qreal width, qreal height) const;
+};
+
+class QSGAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
+{
+ Q_DECLARE_PUBLIC(QSGAnchorAnimation)
+public:
+ QSGAnchorAnimationPrivate() : rangeIsSet(false), va(0),
+ interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {}
+
+ bool rangeIsSet;
+ QDeclarativeBulkValueAnimator *va;
+ QVariantAnimation::Interpolator interpolator;
+ QList<QSGItem*> targets;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGANIMATION_P_H
diff --git a/src/declarative/items/qsgborderimage.cpp b/src/declarative/items/qsgborderimage.cpp
new file mode 100644
index 0000000000..81df0a31e7
--- /dev/null
+++ b/src/declarative/items/qsgborderimage.cpp
@@ -0,0 +1,362 @@
+// Commit: 462429f5692f810bdd4e04b916db5f9af428d9e4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgborderimage_p.h"
+#include "qsgborderimage_p_p.h"
+#include "qsgninepatchnode_p.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtCore/qfile.h>
+
+#include <private/qdeclarativeengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGBorderImage::QSGBorderImage(QSGItem *parent)
+: QSGImageBase(*(new QSGBorderImagePrivate), parent)
+{
+}
+
+QSGBorderImage::~QSGBorderImage()
+{
+ Q_D(QSGBorderImage);
+ if (d->sciReply)
+ d->sciReply->deleteLater();
+}
+
+void QSGBorderImage::setSource(const QUrl &url)
+{
+ Q_D(QSGBorderImage);
+ //equality is fairly expensive, so we bypass for simple, common case
+ if ((d->url.isEmpty() == url.isEmpty()) && url == d->url)
+ return;
+
+ if (d->sciReply) {
+ d->sciReply->deleteLater();
+ d->sciReply = 0;
+ }
+
+ d->url = url;
+ d->sciurl = QUrl();
+ emit sourceChanged(d->url);
+
+ if (isComponentComplete())
+ load();
+}
+
+void QSGBorderImage::load()
+{
+ Q_D(QSGBorderImage);
+ if (d->progress != 0.0) {
+ d->progress = 0.0;
+ emit progressChanged(d->progress);
+ }
+
+ if (d->url.isEmpty()) {
+ d->pix.clear(this);
+ d->status = Null;
+ setImplicitWidth(0);
+ setImplicitHeight(0);
+ emit statusChanged(d->status);
+ update();
+ } else {
+ d->status = Loading;
+ if (d->url.path().endsWith(QLatin1String("sci"))) {
+#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML
+ QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url);
+ if (!lf.isEmpty()) {
+ QFile file(lf);
+ file.open(QIODevice::ReadOnly);
+ setGridScaledImage(QSGGridScaledImage(&file));
+ } else
+#endif
+ {
+ QNetworkRequest req(d->url);
+ d->sciReply = qmlEngine(this)->networkAccessManager()->get(req);
+
+ static int sciReplyFinished = -1;
+ static int thisSciRequestFinished = -1;
+ if (sciReplyFinished == -1) {
+ sciReplyFinished =
+ QNetworkReply::staticMetaObject.indexOfSignal("finished()");
+ thisSciRequestFinished =
+ QSGBorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()");
+ }
+
+ QMetaObject::connect(d->sciReply, sciReplyFinished, this,
+ thisSciRequestFinished, Qt::DirectConnection);
+ }
+ } else {
+
+ QDeclarativePixmap::Options options;
+ if (d->async)
+ options |= QDeclarativePixmap::Asynchronous;
+ if (d->cache)
+ options |= QDeclarativePixmap::Cache;
+ d->pix.clear(this);
+ d->pix.load(qmlEngine(this), d->url, options);
+
+ if (d->pix.isLoading()) {
+ d->pix.connectFinished(this, SLOT(requestFinished()));
+ d->pix.connectDownloadProgress(this, SLOT(requestProgress(qint64,qint64)));
+ } else {
+ QSize impsize = d->pix.implicitSize();
+ setImplicitWidth(impsize.width());
+ setImplicitHeight(impsize.height());
+
+ if (d->pix.isReady()) {
+ d->status = Ready;
+ } else {
+ d->status = Error;
+ qmlInfo(this) << d->pix.error();
+ }
+
+ d->progress = 1.0;
+ emit statusChanged(d->status);
+ emit progressChanged(d->progress);
+ update();
+ }
+ }
+ }
+
+ emit statusChanged(d->status);
+}
+
+QSGScaleGrid *QSGBorderImage::border()
+{
+ Q_D(QSGBorderImage);
+ return d->getScaleGrid();
+}
+
+QSGBorderImage::TileMode QSGBorderImage::horizontalTileMode() const
+{
+ Q_D(const QSGBorderImage);
+ return d->horizontalTileMode;
+}
+
+void QSGBorderImage::setHorizontalTileMode(TileMode t)
+{
+ Q_D(QSGBorderImage);
+ if (t != d->horizontalTileMode) {
+ d->horizontalTileMode = t;
+ emit horizontalTileModeChanged();
+ update();
+ }
+}
+
+QSGBorderImage::TileMode QSGBorderImage::verticalTileMode() const
+{
+ Q_D(const QSGBorderImage);
+ return d->verticalTileMode;
+}
+
+void QSGBorderImage::setVerticalTileMode(TileMode t)
+{
+ Q_D(QSGBorderImage);
+ if (t != d->verticalTileMode) {
+ d->verticalTileMode = t;
+ emit verticalTileModeChanged();
+ update();
+ }
+}
+
+void QSGBorderImage::setGridScaledImage(const QSGGridScaledImage& sci)
+{
+ Q_D(QSGBorderImage);
+ if (!sci.isValid()) {
+ d->status = Error;
+ emit statusChanged(d->status);
+ } else {
+ QSGScaleGrid *sg = border();
+ sg->setTop(sci.gridTop());
+ sg->setBottom(sci.gridBottom());
+ sg->setLeft(sci.gridLeft());
+ sg->setRight(sci.gridRight());
+ d->horizontalTileMode = sci.horizontalTileRule();
+ d->verticalTileMode = sci.verticalTileRule();
+
+ d->sciurl = d->url.resolved(QUrl(sci.pixmapUrl()));
+
+ QDeclarativePixmap::Options options;
+ if (d->async)
+ options |= QDeclarativePixmap::Asynchronous;
+ if (d->cache)
+ options |= QDeclarativePixmap::Cache;
+ d->pix.clear(this);
+ d->pix.load(qmlEngine(this), d->sciurl, options);
+
+ if (d->pix.isLoading()) {
+ static int thisRequestProgress = -1;
+ static int thisRequestFinished = -1;
+ if (thisRequestProgress == -1) {
+ thisRequestProgress =
+ QSGBorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
+ thisRequestFinished =
+ QSGBorderImage::staticMetaObject.indexOfSlot("requestFinished()");
+ }
+
+ d->pix.connectFinished(this, thisRequestFinished);
+ d->pix.connectDownloadProgress(this, thisRequestProgress);
+
+ } else {
+
+ QSize impsize = d->pix.implicitSize();
+ setImplicitWidth(impsize.width());
+ setImplicitHeight(impsize.height());
+
+ if (d->pix.isReady()) {
+ d->status = Ready;
+ } else {
+ d->status = Error;
+ qmlInfo(this) << d->pix.error();
+ }
+
+ d->progress = 1.0;
+ emit statusChanged(d->status);
+ emit progressChanged(1.0);
+ update();
+
+ }
+ }
+}
+
+void QSGBorderImage::requestFinished()
+{
+ Q_D(QSGBorderImage);
+
+ QSize impsize = d->pix.implicitSize();
+ if (d->pix.isError()) {
+ d->status = Error;
+ qmlInfo(this) << d->pix.error();
+ } else {
+ d->status = Ready;
+ }
+
+ setImplicitWidth(impsize.width());
+ setImplicitHeight(impsize.height());
+
+ if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height())
+ emit sourceSizeChanged();
+
+ d->progress = 1.0;
+ emit statusChanged(d->status);
+ emit progressChanged(1.0);
+ update();
+}
+
+#define BORDERIMAGE_MAX_REDIRECT 16
+
+void QSGBorderImage::sciRequestFinished()
+{
+ Q_D(QSGBorderImage);
+
+ d->redirectCount++;
+ if (d->redirectCount < BORDERIMAGE_MAX_REDIRECT) {
+ QVariant redirect = d->sciReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ if (redirect.isValid()) {
+ QUrl url = d->sciReply->url().resolved(redirect.toUrl());
+ setSource(url);
+ return;
+ }
+ }
+ d->redirectCount=0;
+
+ if (d->sciReply->error() != QNetworkReply::NoError) {
+ d->status = Error;
+ d->sciReply->deleteLater();
+ d->sciReply = 0;
+ emit statusChanged(d->status);
+ } else {
+ QSGGridScaledImage sci(d->sciReply);
+ d->sciReply->deleteLater();
+ d->sciReply = 0;
+ setGridScaledImage(sci);
+ }
+}
+
+void QSGBorderImage::doUpdate()
+{
+ update();
+}
+
+QSGNode *QSGBorderImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ Q_D(QSGBorderImage);
+
+ QSGTexture *texture = d->pix.texture(d->sceneGraphContext());
+
+ if (!texture || width() <= 0 || height() <= 0) {
+ delete oldNode;
+ return 0;
+ }
+
+ QSGNinePatchNode *node = static_cast<QSGNinePatchNode *>(oldNode);
+
+ if (!node) {
+ node = new QSGNinePatchNode();
+ }
+
+ node->setTexture(texture);
+
+ const QSGScaleGrid *border = d->getScaleGrid();
+ node->setInnerRect(QRectF(border->left(),
+ border->top(),
+ d->pix.width() - border->right() - border->left(),
+ d->pix.height() - border->bottom() - border->top()));
+ node->setRect(QRectF(0, 0, width(), height()));
+ node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+ node->setHorzontalTileMode(d->horizontalTileMode);
+ node->setVerticalTileMode(d->verticalTileMode);
+ node->setMirror(d->mirror);
+ node->update();
+
+ return node;
+}
+
+void QSGBorderImage::pixmapChange()
+{
+ Q_D(QSGBorderImage);
+
+ d->pixmapChanged = true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgborderimage_p.h b/src/declarative/items/qsgborderimage_p.h
new file mode 100644
index 0000000000..1386264779
--- /dev/null
+++ b/src/declarative/items/qsgborderimage_p.h
@@ -0,0 +1,110 @@
+// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGBORDERIMAGE_P_H
+#define QSGBORDERIMAGE_P_H
+
+#include "qsgimagebase_p.h"
+
+QT_BEGIN_HEADER
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGScaleGrid;
+class QSGGridScaledImage;
+class QSGBorderImagePrivate;
+class Q_AUTOTEST_EXPORT QSGBorderImage : public QSGImageBase
+{
+ Q_OBJECT
+ Q_ENUMS(TileMode)
+
+ Q_PROPERTY(QSGScaleGrid *border READ border CONSTANT)
+ Q_PROPERTY(TileMode horizontalTileMode READ horizontalTileMode WRITE setHorizontalTileMode NOTIFY horizontalTileModeChanged)
+ Q_PROPERTY(TileMode verticalTileMode READ verticalTileMode WRITE setVerticalTileMode NOTIFY verticalTileModeChanged)
+ // read-only for BorderImage
+ Q_PROPERTY(QSize sourceSize READ sourceSize NOTIFY sourceSizeChanged)
+
+public:
+ QSGBorderImage(QSGItem *parent=0);
+ ~QSGBorderImage();
+
+ QSGScaleGrid *border();
+
+ enum TileMode { Stretch = Qt::StretchTile, Repeat = Qt::RepeatTile, Round = Qt::RoundTile };
+
+ TileMode horizontalTileMode() const;
+ void setHorizontalTileMode(TileMode);
+
+ TileMode verticalTileMode() const;
+ void setVerticalTileMode(TileMode);
+
+ void setSource(const QUrl &url);
+
+Q_SIGNALS:
+ void horizontalTileModeChanged();
+ void verticalTileModeChanged();
+ void sourceSizeChanged();
+
+protected:
+ virtual void load();
+ virtual void pixmapChange();
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private:
+ void setGridScaledImage(const QSGGridScaledImage& sci);
+
+private Q_SLOTS:
+ void doUpdate();
+ void requestFinished();
+ void sciRequestFinished();
+
+private:
+ Q_DISABLE_COPY(QSGBorderImage)
+ Q_DECLARE_PRIVATE(QSGBorderImage)
+};
+
+QT_END_NAMESPACE
+QML_DECLARE_TYPE(QSGBorderImage)
+QT_END_HEADER
+
+#endif // QSGBORDERIMAGE_P_H
diff --git a/src/declarative/items/qsgborderimage_p_p.h b/src/declarative/items/qsgborderimage_p_p.h
new file mode 100644
index 0000000000..2fb88d9ffd
--- /dev/null
+++ b/src/declarative/items/qsgborderimage_p_p.h
@@ -0,0 +1,109 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGBORDERIMAGE_P_P_H
+#define QSGBORDERIMAGE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgimagebase_p_p.h"
+#include "qsgscalegrid_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkReply;
+class QSGBorderImagePrivate : public QSGImageBasePrivate
+{
+ Q_DECLARE_PUBLIC(QSGBorderImage)
+
+public:
+ QSGBorderImagePrivate()
+ : border(0), sciReply(0),
+ horizontalTileMode(QSGBorderImage::Stretch),
+ verticalTileMode(QSGBorderImage::Stretch),
+ redirectCount(0), pixmapChanged(false)
+ {
+ }
+
+ ~QSGBorderImagePrivate()
+ {
+ }
+
+
+ QSGScaleGrid *getScaleGrid()
+ {
+ Q_Q(QSGBorderImage);
+ if (!border) {
+ border = new QSGScaleGrid(q);
+ static int borderChangedSignalIdx = -1;
+ static int doUpdateSlotIdx = -1;
+ if (borderChangedSignalIdx < 0)
+ borderChangedSignalIdx = QSGScaleGrid::staticMetaObject.indexOfSignal("borderChanged()");
+ if (doUpdateSlotIdx < 0)
+ doUpdateSlotIdx = QSGBorderImage::staticMetaObject.indexOfSlot("doUpdate()");
+ QMetaObject::connect(border, borderChangedSignalIdx, q, doUpdateSlotIdx);
+ }
+ return border;
+ }
+
+ QSGScaleGrid *border;
+ QUrl sciurl;
+ QNetworkReply *sciReply;
+ QSGBorderImage::TileMode horizontalTileMode;
+ QSGBorderImage::TileMode verticalTileMode;
+ int redirectCount;
+
+ bool pixmapChanged : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGBORDERIMAGE_P_P_H
diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp
new file mode 100644
index 0000000000..16bd8ce44c
--- /dev/null
+++ b/src/declarative/items/qsgcanvas.cpp
@@ -0,0 +1,1934 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgcanvas.h"
+#include "qsgcanvas_p.h"
+
+#include "qsgitem.h"
+#include "qsgitem_p.h"
+
+#include <private/qsgrenderer_p.h>
+#include <private/qsgflashnode_p.h>
+
+#include <private/qabstractanimation_p.h>
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qmatrix4x4.h>
+#include <QtGui/qinputcontext.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qabstractanimation.h>
+
+#include <private/qdeclarativedebugtrace_p.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_NO_THREADED_RENDERER)
+DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP)
+
+/*
+Focus behavior
+==============
+
+Prior to being added to a valid canvas items can set and clear focus with no
+effect. Only once items are added to a canvas (by way of having a parent set that
+already belongs to a canvas) do the focus rules apply. Focus goes back to
+having no effect if an item is removed from a canvas.
+
+When an item is moved into a new focus scope (either being added to a canvas
+for the first time, or having its parent changed), if the focus scope already has
+a scope focused item that takes precedence over the item being added. Otherwise,
+the focus of the added tree is used. In the case of of a tree of items being
+added to a canvas for the first time, which may have a conflicted focus state (two
+or more items in one scope having focus set), the same rule is applied item by item -
+thus the first item that has focus will get it (assuming the scope doesn't already
+have a scope focused item), and the other items will have their focus cleared.
+*/
+
+// #define FOCUS_DEBUG
+// #define MOUSE_DEBUG
+// #define TOUCH_DEBUG
+// #define DIRTY_DEBUG
+// #define THREAD_DEBUG
+
+// #define FRAME_TIMING
+
+#ifdef FRAME_TIMING
+static QTime frameTimer;
+int sceneGraphRenderTime;
+int readbackTime;
+#endif
+
+QSGItem::UpdatePaintNodeData::UpdatePaintNodeData()
+: transformNode(0)
+{
+}
+
+QSGRootItem::QSGRootItem()
+{
+}
+
+void QSGCanvasPrivate::stopRenderingThread()
+{
+ if (thread->isRunning()) {
+ mutex.lock();
+ exitThread = true;
+ wait.wakeOne();
+ wait.wait(&mutex);
+ exitThread = false;
+ mutex.unlock();
+ thread->wait();
+ }
+}
+
+void QSGCanvasPrivate::_q_animationStarted()
+{
+#ifdef THREAD_DEBUG
+ qWarning("AnimationDriver: Main Thread: started");
+#endif
+ mutex.lock();
+ animationRunning = true;
+ if (idle)
+ wait.wakeOne();
+ mutex.unlock();
+}
+
+void QSGCanvasPrivate::_q_animationStopped()
+{
+#ifdef THREAD_DEBUG
+ qWarning("AnimationDriver: Main Thread: stopped");
+#endif
+ mutex.lock();
+ animationRunning = false;
+ mutex.unlock();
+}
+
+void QSGCanvas::paintEvent(QPaintEvent *)
+{
+ Q_D(QSGCanvas);
+
+ if (!d->threadedRendering) {
+#ifdef FRAME_TIMING
+ int lastFrame = frameTimer.restart();
+#endif
+
+ if (d->animationDriver->isRunning())
+ d->animationDriver->advance();
+
+#ifdef FRAME_TIMING
+ int animationTime = frameTimer.elapsed();
+#endif
+
+ Q_ASSERT(d->context);
+
+ d->polishItems();
+
+ QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint);
+ QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting);
+
+#ifdef FRAME_TIMING
+ int polishTime = frameTimer.elapsed();
+#endif
+
+ makeCurrent();
+
+#ifdef FRAME_TIMING
+ int makecurrentTime = frameTimer.elapsed();
+#endif
+
+ d->syncSceneGraph();
+
+#ifdef FRAME_TIMING
+ int syncTime = frameTimer.elapsed();
+#endif
+
+ d->renderSceneGraph();
+
+#ifdef FRAME_TIMING
+ printf("FrameTimes, last=%d, animations=%d, polish=%d, makeCurrent=%d, sync=%d, sgrender=%d, readback=%d, total=%d\n",
+ lastFrame,
+ animationTime,
+ polishTime - animationTime,
+ makecurrentTime - polishTime,
+ syncTime - makecurrentTime,
+ sceneGraphRenderTime - syncTime,
+ readbackTime - sceneGraphRenderTime,
+ frameTimer.elapsed());
+#endif
+
+ QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting);
+
+ if (d->animationDriver->isRunning())
+ update();
+ }
+}
+
+void QSGCanvas::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QSGCanvas);
+ if (d->threadedRendering) {
+ d->mutex.lock();
+ QGLWidget::resizeEvent(e);
+ d->widgetSize = e->size();
+ d->mutex.unlock();
+ } else {
+ d->widgetSize = e->size();
+ d->viewportSize = d->widgetSize;
+ QGLWidget::resizeEvent(e);
+ }
+}
+
+void QSGCanvas::showEvent(QShowEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ QGLWidget::showEvent(e);
+
+ if (d->threadedRendering) {
+ d->contextInThread = true;
+ doneCurrent();
+ if (!d->animationDriver) {
+ d->animationDriver = d->context->createAnimationDriver(this);
+ connect(d->animationDriver, SIGNAL(started()), this, SLOT(_q_animationStarted()), Qt::DirectConnection);
+ connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(_q_animationStopped()), Qt::DirectConnection);
+ }
+ d->animationDriver->install();
+ d->mutex.lock();
+ d->thread->start();
+ d->wait.wait(&d->mutex);
+ d->mutex.unlock();
+ } else {
+ makeCurrent();
+
+ if (!d->context || !d->context->isReady()) {
+ d->initializeSceneGraph();
+ d->animationDriver = d->context->createAnimationDriver(this);
+ connect(d->animationDriver, SIGNAL(started()), this, SLOT(update()));
+ }
+
+ d->animationDriver->install();
+ }
+}
+
+void QSGCanvas::hideEvent(QHideEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ if (d->threadedRendering)
+ d->stopRenderingThread();
+
+ d->animationDriver->uninstall();
+
+ QGLWidget::hideEvent(e);
+}
+
+void QSGCanvas::focusOutEvent(QFocusEvent *event)
+{
+ Q_D(QSGCanvas);
+ d->rootItem->setFocus(false);
+ QGLWidget::focusOutEvent(event);
+}
+
+void QSGCanvas::focusInEvent(QFocusEvent *event)
+{
+ Q_D(QSGCanvas);
+ d->rootItem->setFocus(true);
+ QGLWidget::focusInEvent(event);
+}
+
+void QSGCanvasPrivate::initializeSceneGraph()
+{
+ if (!context)
+ context = QSGContext::createDefaultContext();
+
+ if (context->isReady())
+ return;
+
+ QGLContext *glctx = const_cast<QGLContext *>(QGLContext::currentContext());
+ context->initialize(glctx);
+
+ if (!threadedRendering) {
+ Q_Q(QSGCanvas);
+ QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()),
+ Qt::DirectConnection);
+ }
+
+ if (!QSGItemPrivate::get(rootItem)->itemNode()->parent()) {
+ context->rootNode()->appendChildNode(QSGItemPrivate::get(rootItem)->itemNode());
+ }
+
+ emit q_func()->sceneGraphInitialized();
+}
+
+void QSGCanvasPrivate::polishItems()
+{
+ while (!itemsToPolish.isEmpty()) {
+ QSet<QSGItem *>::Iterator iter = itemsToPolish.begin();
+ QSGItem *item = *iter;
+ itemsToPolish.erase(iter);
+ QSGItemPrivate::get(item)->polishScheduled = false;
+ item->updatePolish();
+ }
+}
+
+
+void QSGCanvasPrivate::syncSceneGraph()
+{
+ inSync = true;
+ updateDirtyNodes();
+ inSync = false;
+}
+
+
+void QSGCanvasPrivate::renderSceneGraph()
+{
+ QGLContext *glctx = const_cast<QGLContext *>(QGLContext::currentContext());
+
+ context->renderer()->setDeviceRect(QRect(QPoint(0, 0), viewportSize));
+ context->renderer()->setViewportRect(QRect(QPoint(0, 0), viewportSize));
+ context->renderer()->setProjectMatrixToDeviceRect();
+
+ context->renderNextFrame();
+
+#ifdef FRAME_TIMING
+ sceneGraphRenderTime = frameTimer.elapsed();
+#endif
+
+
+#ifdef FRAME_TIMING
+// int pixel;
+// glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
+ readbackTime = frameTimer.elapsed();
+#endif
+
+ glctx->swapBuffers();
+}
+
+
+void QSGCanvas::sceneGraphChanged()
+{
+ Q_D(QSGCanvas);
+ d->needsRepaint = true;
+}
+
+
+void QSGCanvasPrivate::runThread()
+{
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render thread running");
+#endif
+ Q_Q(QSGCanvas);
+
+ printf("QSGCanvas::runThread(), rendering in a thread...\n");
+
+ q->makeCurrent();
+ initializeSceneGraph();
+
+ QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()),
+ q, SLOT(sceneGraphChanged()),
+ Qt::DirectConnection);
+
+ mutex.lock();
+ wait.wakeOne(); // Wake the main thread waiting for us to start
+
+ while (true) {
+ QSize s;
+ s = widgetSize;
+
+ if (exitThread)
+ break;
+
+ if (s != viewportSize) {
+ glViewport(0, 0, s.width(), s.height());
+ viewportSize = s;
+ }
+
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: Waiting for main thread to stop");
+#endif
+ QCoreApplication::postEvent(q, new QEvent(QEvent::User));
+ wait.wait(&mutex);
+
+ if (exitThread) {
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: Shutting down...");
+#endif
+ break;
+ }
+
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: Main thread has stopped, syncing scene");
+#endif
+
+ // Do processing while main thread is frozen
+ syncSceneGraph();
+
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: Resuming main thread");
+#endif
+
+ // Read animationRunning while inside the locked section
+ bool continous = animationRunning;
+
+ wait.wakeOne();
+ mutex.unlock();
+
+ bool enterIdle = false;
+ if (needsRepaint) {
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: rendering scene");
+#endif
+ renderSceneGraph();
+ needsRepaint = false;
+ } else if (continous) {
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: waiting a while...");
+#endif
+ MyThread::doWait();
+ } else {
+ enterIdle = true;
+ }
+
+ mutex.lock();
+
+ if (enterIdle) {
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: Nothing has changed, going idle...");
+#endif
+ idle = true;
+ wait.wait(&mutex);
+ idle = false;
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: waking up from idle");
+#endif
+ }
+
+ }
+
+
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Render Thread: shutting down, waking up main thread");
+#endif
+ wait.wakeOne();
+ mutex.unlock();
+
+ q->doneCurrent();
+}
+
+QSGCanvasPrivate::QSGCanvasPrivate()
+ : rootItem(0)
+ , activeFocusItem(0)
+ , mouseGrabberItem(0)
+ , hoverItem(0)
+ , dirtyItemList(0)
+ , context(0)
+ , contextInThread(false)
+ , threadedRendering(false)
+ , exitThread(false)
+ , animationRunning(false)
+ , idle(false)
+ , needsRepaint(true)
+ , renderThreadAwakened(false)
+ , inSync(false)
+ , thread(new MyThread(this))
+ , animationDriver(0)
+{
+ threadedRendering = !qmlNoThreadedRenderer();
+}
+
+QSGCanvasPrivate::~QSGCanvasPrivate()
+{
+}
+
+void QSGCanvasPrivate::init(QSGCanvas *c)
+{
+ QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep());
+
+ q_ptr = c;
+
+ Q_Q(QSGCanvas);
+
+ q->setAttribute(Qt::WA_AcceptTouchEvents);
+ q->setFocusPolicy(Qt::StrongFocus);
+
+ rootItem = new QSGRootItem;
+ QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(rootItem);
+ rootItemPrivate->canvas = q;
+ rootItemPrivate->flags |= QSGItem::ItemIsFocusScope;
+
+ context = QSGContext::createDefaultContext();
+}
+
+void QSGCanvasPrivate::sceneMouseEventForTransform(QGraphicsSceneMouseEvent &sceneEvent,
+ const QTransform &transform)
+{
+ sceneEvent.setPos(transform.map(sceneEvent.scenePos()));
+ sceneEvent.setLastPos(transform.map(sceneEvent.lastScenePos()));
+ for (int ii = 0; ii < 5; ++ii) {
+ if (sceneEvent.buttons() & (1 << ii)) {
+ sceneEvent.setButtonDownPos((Qt::MouseButton)(1 << ii),
+ transform.map(sceneEvent.buttonDownScenePos((Qt::MouseButton)(1 << ii))));
+ }
+ }
+}
+
+void QSGCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
+{
+ for (int i=0; i<touchPoints.count(); i++) {
+ QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
+ touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
+ touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
+ touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
+ }
+}
+
+QEvent::Type QSGCanvasPrivate::sceneMouseEventTypeFromMouseEvent(QMouseEvent *event)
+{
+ switch(event->type()) {
+ default:
+ Q_ASSERT(!"Unknown event type");
+ case QEvent::MouseButtonPress:
+ return QEvent::GraphicsSceneMousePress;
+ case QEvent::MouseButtonRelease:
+ return QEvent::GraphicsSceneMouseRelease;
+ case QEvent::MouseButtonDblClick:
+ return QEvent::GraphicsSceneMouseDoubleClick;
+ case QEvent::MouseMove:
+ return QEvent::GraphicsSceneMouseMove;
+ }
+}
+
+/*!
+Fill in the data in \a sceneEvent based on \a event. This method leaves the item local positions in
+\a sceneEvent untouched. Use sceneMouseEventForTransform() to fill in those details.
+*/
+void QSGCanvasPrivate::sceneMouseEventFromMouseEvent(QGraphicsSceneMouseEvent &sceneEvent, QMouseEvent *event)
+{
+ Q_Q(QSGCanvas);
+
+ Q_ASSERT(event);
+
+ if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) {
+ if ((event->button() & event->buttons()) == event->buttons()) {
+ lastMousePosition = event->pos();
+ }
+
+ switch (event->button()) {
+ default:
+ Q_ASSERT(!"Unknown button");
+ case Qt::LeftButton:
+ buttonDownPositions[0] = event->pos();
+ break;
+ case Qt::RightButton:
+ buttonDownPositions[1] = event->pos();
+ break;
+ case Qt::MiddleButton:
+ buttonDownPositions[2] = event->pos();
+ break;
+ case Qt::XButton1:
+ buttonDownPositions[3] = event->pos();
+ break;
+ case Qt::XButton2:
+ buttonDownPositions[4] = event->pos();
+ break;
+ }
+ }
+
+ sceneEvent.setScenePos(event->pos());
+ sceneEvent.setScreenPos(event->globalPos());
+ sceneEvent.setLastScenePos(lastMousePosition);
+ sceneEvent.setLastScreenPos(q->mapToGlobal(lastMousePosition));
+ sceneEvent.setButtons(event->buttons());
+ sceneEvent.setButton(event->button());
+ sceneEvent.setModifiers(event->modifiers());
+ sceneEvent.setWidget(q);
+
+ for (int ii = 0; ii < 5; ++ii) {
+ if (sceneEvent.buttons() & (1 << ii)) {
+ sceneEvent.setButtonDownScenePos((Qt::MouseButton)(1 << ii), buttonDownPositions[ii]);
+ sceneEvent.setButtonDownScreenPos((Qt::MouseButton)(1 << ii), q->mapToGlobal(buttonDownPositions[ii]));
+ }
+ }
+
+ lastMousePosition = event->pos();
+}
+
+/*!
+Fill in the data in \a hoverEvent based on \a mouseEvent. This method leaves the item local positions in
+\a hoverEvent untouched (these are filled in later).
+*/
+void QSGCanvasPrivate::sceneHoverEventFromMouseEvent(QGraphicsSceneHoverEvent &hoverEvent, QMouseEvent *mouseEvent)
+{
+ Q_Q(QSGCanvas);
+ hoverEvent.setWidget(q);
+ hoverEvent.setScenePos(mouseEvent->pos());
+ hoverEvent.setScreenPos(mouseEvent->globalPos());
+ if (lastMousePosition.isNull()) lastMousePosition = mouseEvent->pos();
+ hoverEvent.setLastScenePos(lastMousePosition);
+ hoverEvent.setLastScreenPos(q->mapToGlobal(lastMousePosition));
+ hoverEvent.setModifiers(mouseEvent->modifiers());
+ hoverEvent.setAccepted(mouseEvent->isAccepted());
+
+ lastMousePosition = mouseEvent->pos();
+}
+
+/*!
+Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
+\a touchEvent untouched (these are filled in later).
+*/
+void QSGCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
+{
+ Q_Q(QSGCanvas);
+
+ touchEvent->setWidget(q);
+
+ QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
+ for (int i = 0; i < touchPoints.count(); ++i) {
+ QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
+
+ touchPoint.setScreenRect(touchPoint.sceneRect());
+ touchPoint.setStartScreenPos(touchPoint.startScenePos());
+ touchPoint.setLastScreenPos(touchPoint.lastScenePos());
+
+ touchPoint.setSceneRect(touchPoint.rect());
+ touchPoint.setStartScenePos(touchPoint.startPos());
+ touchPoint.setLastScenePos(touchPoint.lastPos());
+
+ if (touchPoint.isPrimary())
+ lastMousePosition = touchPoint.pos().toPoint();
+ }
+ touchEvent->setTouchPoints(touchPoints);
+}
+
+void QSGCanvasPrivate::setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options)
+{
+ Q_Q(QSGCanvas);
+
+ Q_ASSERT(item);
+ Q_ASSERT(scope || item == rootItem);
+
+#ifdef FOCUS_DEBUG
+ qWarning() << "QSGCanvasPrivate::setFocusInScope():";
+ qWarning() << " scope:" << (QObject *)scope;
+ if (scope)
+ qWarning() << " scopeSubFocusItem:" << (QObject *)QSGItemPrivate::get(scope)->subFocusItem;
+ qWarning() << " item:" << (QObject *)item;
+ qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
+#endif
+
+ QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0;
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+
+ QSGItem *oldActiveFocusItem = 0;
+ QSGItem *newActiveFocusItem = 0;
+
+ QVarLengthArray<QSGItem *, 20> changed;
+
+ // Does this change the active focus?
+ if (item == rootItem || scopePrivate->activeFocus) {
+ oldActiveFocusItem = activeFocusItem;
+ newActiveFocusItem = item;
+ while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem())
+ newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
+
+ if (oldActiveFocusItem) {
+#ifndef QT_NO_IM
+ if (QInputContext *ic = inputContext())
+ ic->reset();
+#endif
+
+ activeFocusItem = 0;
+ QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
+ q->sendEvent(oldActiveFocusItem, &event);
+
+ QSGItem *afi = oldActiveFocusItem;
+ while (afi != scope) {
+ if (QSGItemPrivate::get(afi)->activeFocus) {
+ QSGItemPrivate::get(afi)->activeFocus = false;
+ changed << afi;
+ }
+ afi = afi->parentItem();
+ }
+ }
+ }
+
+ if (item != rootItem) {
+ QSGItem *oldSubFocusItem = scopePrivate->subFocusItem;
+ // Correct focus chain in scope
+ if (oldSubFocusItem) {
+ QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
+ while (sfi != scope) {
+ QSGItemPrivate::get(sfi)->subFocusItem = 0;
+ sfi = sfi->parentItem();
+ }
+ }
+ {
+ scopePrivate->subFocusItem = item;
+ QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
+ while (sfi != scope) {
+ QSGItemPrivate::get(sfi)->subFocusItem = item;
+ sfi = sfi->parentItem();
+ }
+ }
+
+ if (oldSubFocusItem) {
+ QSGItemPrivate::get(oldSubFocusItem)->focus = false;
+ changed << oldSubFocusItem;
+ }
+ }
+
+ if (!(options & DontChangeFocusProperty)) {
+ if (item != rootItem || q->hasFocus()) {
+ itemPrivate->focus = true;
+ changed << item;
+ }
+ }
+
+ if (newActiveFocusItem && q->hasFocus()) {
+ activeFocusItem = newActiveFocusItem;
+
+ QSGItemPrivate::get(newActiveFocusItem)->activeFocus = true;
+ changed << newActiveFocusItem;
+
+ QSGItem *afi = newActiveFocusItem->parentItem();
+ while (afi && afi != scope) {
+ if (afi->isFocusScope()) {
+ QSGItemPrivate::get(afi)->activeFocus = true;
+ changed << afi;
+ }
+ afi = afi->parentItem();
+ }
+
+ updateInputMethodData();
+
+ QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
+ q->sendEvent(newActiveFocusItem, &event);
+ } else {
+ updateInputMethodData();
+ }
+
+ if (!changed.isEmpty())
+ notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+}
+
+void QSGCanvasPrivate::clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options)
+{
+ Q_Q(QSGCanvas);
+
+ Q_UNUSED(item);
+ Q_ASSERT(item);
+ Q_ASSERT(scope || item == rootItem);
+
+#ifdef FOCUS_DEBUG
+ qWarning() << "QSGCanvasPrivate::clearFocusInScope():";
+ qWarning() << " scope:" << (QObject *)scope;
+ qWarning() << " item:" << (QObject *)item;
+ qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
+#endif
+
+ QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0;
+
+ QSGItem *oldActiveFocusItem = 0;
+ QSGItem *newActiveFocusItem = 0;
+
+ QVarLengthArray<QSGItem *, 20> changed;
+
+ Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
+
+ // Does this change the active focus?
+ if (item == rootItem || scopePrivate->activeFocus) {
+ oldActiveFocusItem = activeFocusItem;
+ newActiveFocusItem = scope;
+
+ Q_ASSERT(oldActiveFocusItem);
+
+#ifndef QT_NO_IM
+ if (QInputContext *ic = inputContext())
+ ic->reset();
+#endif
+
+ activeFocusItem = 0;
+ QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
+ q->sendEvent(oldActiveFocusItem, &event);
+
+ QSGItem *afi = oldActiveFocusItem;
+ while (afi != scope) {
+ if (QSGItemPrivate::get(afi)->activeFocus) {
+ QSGItemPrivate::get(afi)->activeFocus = false;
+ changed << afi;
+ }
+ afi = afi->parentItem();
+ }
+ }
+
+ if (item != rootItem) {
+ QSGItem *oldSubFocusItem = scopePrivate->subFocusItem;
+ // Correct focus chain in scope
+ if (oldSubFocusItem) {
+ QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
+ while (sfi != scope) {
+ QSGItemPrivate::get(sfi)->subFocusItem = 0;
+ sfi = sfi->parentItem();
+ }
+ }
+ scopePrivate->subFocusItem = 0;
+
+ if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
+ QSGItemPrivate::get(oldSubFocusItem)->focus = false;
+ changed << oldSubFocusItem;
+ }
+ } else if (!(options & DontChangeFocusProperty)) {
+ QSGItemPrivate::get(item)->focus = false;
+ changed << item;
+ }
+
+ if (newActiveFocusItem) {
+ Q_ASSERT(newActiveFocusItem == scope);
+ activeFocusItem = scope;
+
+ updateInputMethodData();
+
+ QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
+ q->sendEvent(newActiveFocusItem, &event);
+ } else {
+ updateInputMethodData();
+ }
+
+ if (!changed.isEmpty())
+ notifyFocusChangesRecur(changed.data(), changed.count() - 1);
+}
+
+void QSGCanvasPrivate::notifyFocusChangesRecur(QSGItem **items, int remaining)
+{
+ QDeclarativeGuard<QSGItem> item(*items);
+
+ if (remaining)
+ notifyFocusChangesRecur(items + 1, remaining - 1);
+
+ if (item) {
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+
+ if (itemPrivate->notifiedFocus != itemPrivate->focus) {
+ itemPrivate->notifiedFocus = itemPrivate->focus;
+ emit item->focusChanged(itemPrivate->focus);
+ }
+
+ if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
+ itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
+ itemPrivate->itemChange(QSGItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
+ emit item->activeFocusChanged(itemPrivate->activeFocus);
+ }
+ }
+}
+
+void QSGCanvasPrivate::updateInputMethodData()
+{
+ Q_Q(QSGCanvas);
+ bool enabled = activeFocusItem
+ && (QSGItemPrivate::get(activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod);
+ q->setAttribute(Qt::WA_InputMethodEnabled, enabled);
+ q->setInputMethodHints(enabled ? activeFocusItem->inputMethodHints() : Qt::ImhNone);
+}
+
+QVariant QSGCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QSGCanvas);
+ if (!d->activeFocusItem || !(QSGItemPrivate::get(d->activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod))
+ return QVariant();
+ QVariant value = d->activeFocusItem->inputMethodQuery(query);
+
+ //map geometry types
+ QVariant::Type type = value.type();
+ if (type == QVariant::RectF || type == QVariant::Rect) {
+ const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
+ value = transform.mapRect(value.toRectF());
+ } else if (type == QVariant::PointF || type == QVariant::Point) {
+ const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
+ value = transform.map(value.toPointF());
+ }
+ return value;
+}
+
+void QSGCanvasPrivate::dirtyItem(QSGItem *)
+{
+ Q_Q(QSGCanvas);
+ q->maybeUpdate();
+}
+
+void QSGCanvasPrivate::cleanup(QSGNode *n)
+{
+ Q_Q(QSGCanvas);
+
+ Q_ASSERT(!cleanupNodeList.contains(n));
+ cleanupNodeList.append(n);
+ q->maybeUpdate();
+}
+
+static QGLFormat tweakFormat(const QGLFormat &format = QGLFormat::defaultFormat())
+{
+ QGLFormat f = format;
+ f.setSwapInterval(1);
+ return f;
+}
+
+QSGCanvas::QSGCanvas(QWidget *parent, Qt::WindowFlags f)
+ : QGLWidget(*(new QSGCanvasPrivate), tweakFormat(), parent, (QGLWidget *) 0, f)
+{
+ Q_D(QSGCanvas);
+
+ d->init(this);
+}
+
+QSGCanvas::QSGCanvas(const QGLFormat &format, QWidget *parent, Qt::WindowFlags f)
+ : QGLWidget(*(new QSGCanvasPrivate), tweakFormat(format), parent, (QGLWidget *) 0, f)
+{
+ Q_D(QSGCanvas);
+
+ d->init(this);
+}
+
+QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, QWidget *parent, Qt::WindowFlags f)
+: QGLWidget(dd, tweakFormat(), parent, 0, f)
+{
+ Q_D(QSGCanvas);
+
+ d->init(this);
+}
+
+QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, const QGLFormat &format, QWidget *parent, Qt::WindowFlags f)
+: QGLWidget(dd, tweakFormat(format), parent, 0, f)
+{
+ Q_D(QSGCanvas);
+
+ d->init(this);
+}
+
+QSGCanvas::~QSGCanvas()
+{
+ Q_D(QSGCanvas);
+
+ if (d->threadedRendering) {
+ d->stopRenderingThread();
+ delete d->thread;
+ }
+
+ // ### should we change ~QSGItem to handle this better?
+ // manually cleanup for the root item (item destructor only handles these when an item is parented)
+ QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(d->rootItem);
+ rootItemPrivate->removeFromDirtyList();
+ rootItemPrivate->canvas = 0;
+
+ delete d->rootItem; d->rootItem = 0;
+ d->cleanupNodes();
+
+
+ // We need to remove all references to textures pointing to "our" QSGContext
+ // from the QDeclarativePixmapCache. Call into the cache to remove the GL / Scene Graph
+ // part of those cache entries.
+ // To "play nice" with other GL apps that are potentially running in the GUI thread,
+ // We get the current context and only temporarily make our own current
+ QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext());
+ makeCurrent();
+ extern void qt_declarative_pixmapstore_clean(QSGContext *context);
+ qt_declarative_pixmapstore_clean(d->context);
+ delete d->context;
+ if (currentContext)
+ currentContext->makeCurrent();
+}
+
+QSGItem *QSGCanvas::rootItem() const
+{
+ Q_D(const QSGCanvas);
+
+ return d->rootItem;
+}
+
+QSGItem *QSGCanvas::activeFocusItem() const
+{
+ Q_D(const QSGCanvas);
+
+ return d->activeFocusItem;
+}
+
+QSGItem *QSGCanvas::mouseGrabberItem() const
+{
+ Q_D(const QSGCanvas);
+
+ return d->mouseGrabberItem;
+}
+
+
+void QSGCanvasPrivate::clearHover()
+{
+ Q_Q(QSGCanvas);
+ if (!hoverItem)
+ return;
+
+ QGraphicsSceneHoverEvent hoverEvent;
+ hoverEvent.setWidget(q);
+
+ QPoint cursorPos = QCursor::pos();
+ hoverEvent.setScenePos(q->mapFromGlobal(cursorPos));
+ hoverEvent.setLastScenePos(hoverEvent.scenePos());
+ hoverEvent.setScreenPos(cursorPos);
+ hoverEvent.setLastScreenPos(hoverEvent.screenPos());
+
+ QSGItem *item = hoverItem;
+ hoverItem = 0;
+ sendHoverEvent(QEvent::GraphicsSceneHoverLeave, item, &hoverEvent);
+}
+
+
+bool QSGCanvas::event(QEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ if (e->type() == QEvent::User) {
+ Q_ASSERT(d->threadedRendering);
+
+ d->mutex.lock();
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Main Thread: Stopped");
+#endif
+
+ d->polishItems();
+
+ d->renderThreadAwakened = false;
+
+ d->wait.wakeOne();
+
+ // The thread is exited when the widget has been hidden. We then need to
+ // skip the waiting, otherwise we would be waiting for a wakeup that never
+ // comes.
+ if (d->thread->isRunning())
+ d->wait.wait(&d->mutex);
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: Main Thread: Resumed");
+#endif
+ d->mutex.unlock();
+
+ if (d->animationRunning)
+ d->animationDriver->advance();
+ }
+
+ switch (e->type()) {
+
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ {
+ QTouchEvent *touch = static_cast<QTouchEvent *>(e);
+ d->translateTouchEvent(touch);
+ d->deliverTouchEvent(touch);
+ if (!touch->isAccepted())
+ return false;
+ }
+ case QEvent::Leave:
+ d->clearHover();
+ d->lastMousePosition = QPoint();
+ break;
+ default:
+ break;
+ }
+
+ return QGLWidget::event(e);
+}
+
+void QSGCanvas::keyPressEvent(QKeyEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ if (d->activeFocusItem)
+ sendEvent(d->activeFocusItem, e);
+}
+
+void QSGCanvas::keyReleaseEvent(QKeyEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ if (d->activeFocusItem)
+ sendEvent(d->activeFocusItem, e);
+}
+
+void QSGCanvas::inputMethodEvent(QInputMethodEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ if (d->activeFocusItem)
+ sendEvent(d->activeFocusItem, e);
+}
+
+bool QSGCanvasPrivate::deliverInitialMousePressEvent(QSGItem *item, QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGCanvas);
+
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ if (itemPrivate->opacity == 0.0)
+ return false;
+
+ if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(event->scenePos());
+ if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ return false;
+ }
+
+ QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QSGItem *child = children.at(ii);
+ if (!child->isVisible() || !child->isEnabled())
+ continue;
+ if (deliverInitialMousePressEvent(child, event))
+ return true;
+ }
+
+ if (itemPrivate->acceptedMouseButtons & event->button()) {
+ QPointF p = item->mapFromScene(event->scenePos());
+ if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ sceneMouseEventForTransform(*event, itemPrivate->canvasToItemTransform());
+ event->accept();
+ mouseGrabberItem = item;
+ q->sendEvent(item, event);
+ if (event->isAccepted())
+ return true;
+ mouseGrabberItem->ungrabMouse();
+ mouseGrabberItem = 0;
+ }
+ }
+
+ return false;
+}
+
+bool QSGCanvasPrivate::deliverMouseEvent(QGraphicsSceneMouseEvent *sceneEvent)
+{
+ Q_Q(QSGCanvas);
+
+ if (!mouseGrabberItem &&
+ sceneEvent->type() == QEvent::GraphicsSceneMousePress &&
+ (sceneEvent->button() & sceneEvent->buttons()) == sceneEvent->buttons()) {
+
+ return deliverInitialMousePressEvent(rootItem, sceneEvent);
+ }
+
+ if (mouseGrabberItem) {
+ QSGItemPrivate *mgPrivate = QSGItemPrivate::get(mouseGrabberItem);
+ sceneMouseEventForTransform(*sceneEvent, mgPrivate->canvasToItemTransform());
+
+ sceneEvent->accept();
+ q->sendEvent(mouseGrabberItem, sceneEvent);
+ if (sceneEvent->isAccepted())
+ return true;
+ }
+
+ return false;
+}
+
+void QSGCanvas::mousePressEvent(QMouseEvent *event)
+{
+ Q_D(QSGCanvas);
+
+#ifdef MOUSE_DEBUG
+ qWarning() << "QSGCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
+#endif
+
+ QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event));
+ d->sceneMouseEventFromMouseEvent(sceneEvent, event);
+
+ d->deliverMouseEvent(&sceneEvent);
+ event->setAccepted(sceneEvent.isAccepted());
+}
+
+void QSGCanvas::mouseReleaseEvent(QMouseEvent *event)
+{
+ Q_D(QSGCanvas);
+
+#ifdef MOUSE_DEBUG
+ qWarning() << "QSGCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
+#endif
+
+ if (!d->mouseGrabberItem) {
+ QGLWidget::mouseReleaseEvent(event);
+ return;
+ }
+
+ QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event));
+ d->sceneMouseEventFromMouseEvent(sceneEvent, event);
+
+ d->deliverMouseEvent(&sceneEvent);
+ event->setAccepted(sceneEvent.isAccepted());
+
+ d->mouseGrabberItem = 0;
+}
+
+void QSGCanvas::mouseDoubleClickEvent(QMouseEvent *event)
+{
+ Q_D(QSGCanvas);
+
+#ifdef MOUSE_DEBUG
+ qWarning() << "QSGCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
+#endif
+
+ QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event));
+ d->sceneMouseEventFromMouseEvent(sceneEvent, event);
+
+ if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) {
+ if (d->deliverInitialMousePressEvent(d->rootItem, &sceneEvent))
+ event->accept();
+ else
+ event->ignore();
+ return;
+ }
+
+ d->deliverMouseEvent(&sceneEvent);
+ event->setAccepted(sceneEvent.isAccepted());
+}
+
+void QSGCanvasPrivate::sendHoverEvent(QEvent::Type type, QSGItem *item,
+ QGraphicsSceneHoverEvent *event)
+{
+ Q_Q(QSGCanvas);
+ const QTransform transform = QSGItemPrivate::get(item)->canvasToItemTransform();
+
+ //create copy of event
+ QGraphicsSceneHoverEvent hoverEvent(type);
+ hoverEvent.setWidget(event->widget());
+ hoverEvent.setPos(transform.map(event->scenePos()));
+ hoverEvent.setScenePos(event->scenePos());
+ hoverEvent.setScreenPos(event->screenPos());
+ hoverEvent.setLastPos(transform.map(event->lastScenePos()));
+ hoverEvent.setLastScenePos(event->lastScenePos());
+ hoverEvent.setLastScreenPos(event->lastScreenPos());
+ hoverEvent.setModifiers(event->modifiers());
+ hoverEvent.setAccepted(event->isAccepted());
+
+ q->sendEvent(item, &hoverEvent);
+}
+
+void QSGCanvas::mouseMoveEvent(QMouseEvent *event)
+{
+ Q_D(QSGCanvas);
+
+#ifdef MOUSE_DEBUG
+ qWarning() << "QSGCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
+#endif
+
+ if (!d->mouseGrabberItem) {
+ QGraphicsSceneHoverEvent hoverEvent;
+ d->sceneHoverEventFromMouseEvent(hoverEvent, event);
+
+ bool delivered = d->deliverHoverEvent(d->rootItem, &hoverEvent);
+ if (!delivered) {
+ //take care of any exits
+ if (d->hoverItem) {
+ QSGItem *item = d->hoverItem;
+ d->hoverItem = 0;
+ d->sendHoverEvent(QEvent::GraphicsSceneHoverLeave, item, &hoverEvent);
+ }
+ }
+ event->setAccepted(hoverEvent.isAccepted());
+ return;
+ }
+
+ QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event));
+ d->sceneMouseEventFromMouseEvent(sceneEvent, event);
+
+ d->deliverMouseEvent(&sceneEvent);
+ event->setAccepted(sceneEvent.isAccepted());
+}
+
+bool QSGCanvasPrivate::deliverHoverEvent(QSGItem *item, QGraphicsSceneHoverEvent *event)
+{
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ if (itemPrivate->opacity == 0.0)
+ return false;
+
+ if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(event->scenePos());
+ if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ return false;
+ }
+
+ QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QSGItem *child = children.at(ii);
+ if (!child->isEnabled())
+ continue;
+ if (deliverHoverEvent(child, event))
+ return true;
+ }
+
+ if (itemPrivate->hoverEnabled) {
+ QPointF p = item->mapFromScene(event->scenePos());
+ if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ if (hoverItem == item) {
+ //move
+ sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, event);
+ } else {
+ //exit from previous
+ if (hoverItem) {
+ QSGItem *item = hoverItem;
+ hoverItem = 0;
+ sendHoverEvent(QEvent::GraphicsSceneHoverLeave, item, event);
+ }
+
+ //enter new item
+ hoverItem = item;
+ sendHoverEvent(QEvent::GraphicsSceneHoverEnter, item, event);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool QSGCanvasPrivate::deliverWheelEvent(QSGItem *item, QGraphicsSceneWheelEvent *event)
+{
+ Q_Q(QSGCanvas);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ if (itemPrivate->opacity == 0.0)
+ return false;
+
+ if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(event->scenePos());
+ if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ return false;
+ }
+
+ QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QSGItem *child = children.at(ii);
+ if (!child->isEnabled())
+ continue;
+ if (deliverWheelEvent(child, event))
+ return true;
+ }
+
+ QPointF p = item->mapFromScene(event->scenePos());
+ if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ event->setPos(itemPrivate->canvasToItemTransform().map(event->scenePos()));
+ event->accept();
+ q->sendEvent(item, event);
+ if (event->isAccepted())
+ return true;
+ }
+
+ return false;
+}
+
+#ifndef QT_NO_WHEELEVENT
+void QSGCanvas::wheelEvent(QWheelEvent *event)
+{
+ Q_D(QSGCanvas);
+#ifdef MOUSE_DEBUG
+ qWarning() << "QSGCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
+#endif
+ QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel);
+ wheelEvent.setWidget(this);
+ wheelEvent.setScenePos(event->pos());
+ wheelEvent.setScreenPos(event->globalPos());
+ wheelEvent.setButtons(event->buttons());
+ wheelEvent.setModifiers(event->modifiers());
+ wheelEvent.setDelta(event->delta());
+ wheelEvent.setOrientation(event->orientation());
+ wheelEvent.setAccepted(false);
+
+ d->deliverWheelEvent(d->rootItem, &wheelEvent);
+ event->setAccepted(wheelEvent.isAccepted());
+}
+#endif // QT_NO_WHEELEVENT
+
+bool QSGCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
+{
+#ifdef TOUCH_DEBUG
+ if (event->type() == QEvent::TouchBegin)
+ qWarning("touchBeginEvent");
+ else if (event->type() == QEvent::TouchUpdate)
+ qWarning("touchUpdateEvent");
+ else if (event->type() == QEvent::TouchEnd)
+ qWarning("touchEndEvent");
+#endif
+
+ QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
+
+ if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
+ QSet<int> acceptedNewPoints;
+ deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
+ if (acceptedNewPoints.count() > 0)
+ event->accept();
+ return event->isAccepted();
+ }
+
+ const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
+ QList<QTouchEvent::TouchPoint> newPoints;
+ QSGItem *item = 0;
+ for (int i=0; i<touchPoints.count(); i++) {
+ const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
+ switch (touchPoint.state()) {
+ case Qt::TouchPointPressed:
+ newPoints << touchPoint;
+ break;
+ case Qt::TouchPointMoved:
+ case Qt::TouchPointStationary:
+ case Qt::TouchPointReleased:
+ if (itemForTouchPointId.contains(touchPoint.id())) {
+ item = itemForTouchPointId[touchPoint.id()];
+ if (item)
+ updatedPoints[item].append(touchPoint);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (newPoints.count() > 0 || updatedPoints.count() > 0) {
+ QSet<int> acceptedNewPoints;
+ int prevCount = updatedPoints.count();
+ deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
+ if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
+ event->accept();
+ }
+
+ if (event->touchPointStates() & Qt::TouchPointReleased) {
+ for (int i=0; i<touchPoints.count(); i++) {
+ if (touchPoints[i].state() == Qt::TouchPointReleased)
+ itemForTouchPointId.remove(touchPoints[i].id());
+ }
+ }
+
+ return event->isAccepted();
+}
+
+bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
+{
+ Q_Q(QSGCanvas);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+
+ if (itemPrivate->opacity == 0.0)
+ return false;
+
+ if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
+ QRectF bounds(0, 0, item->width(), item->height());
+ for (int i=0; i<newPoints.count(); i++) {
+ QPointF p = item->mapFromScene(newPoints[i].scenePos());
+ if (!bounds.contains(p))
+ return false;
+ }
+ }
+
+ QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QSGItem *child = children.at(ii);
+ if (!child->isEnabled())
+ continue;
+ if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
+ return true;
+ }
+
+ QList<QTouchEvent::TouchPoint> matchingPoints;
+ if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
+ QRectF bounds(0, 0, item->width(), item->height());
+ for (int i=0; i<newPoints.count(); i++) {
+ if (acceptedNewPoints->contains(newPoints[i].id()))
+ continue;
+ QPointF p = item->mapFromScene(newPoints[i].scenePos());
+ if (bounds.contains(p))
+ matchingPoints << newPoints[i];
+ }
+ }
+
+ if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
+ QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
+ eventPoints.append(matchingPoints);
+ transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
+
+ Qt::TouchPointStates eventStates;
+ for (int i=0; i<eventPoints.count(); i++)
+ eventStates |= eventPoints[i].state();
+ // if all points have the same state, set the event type accordingly
+ QEvent::Type eventType;
+ switch (eventStates) {
+ case Qt::TouchPointPressed:
+ eventType = QEvent::TouchBegin;
+ break;
+ case Qt::TouchPointReleased:
+ eventType = QEvent::TouchEnd;
+ break;
+ default:
+ eventType = QEvent::TouchUpdate;
+ break;
+ }
+
+ if (eventStates != Qt::TouchPointStationary) {
+ QTouchEvent touchEvent(eventType);
+ touchEvent.setWidget(q);
+ touchEvent.setDeviceType(event->deviceType());
+ touchEvent.setModifiers(event->modifiers());
+ touchEvent.setTouchPointStates(eventStates);
+ touchEvent.setTouchPoints(eventPoints);
+
+ touchEvent.accept();
+ q->sendEvent(item, &touchEvent);
+
+ if (touchEvent.isAccepted()) {
+ for (int i=0; i<matchingPoints.count(); i++) {
+ itemForTouchPointId[matchingPoints[i].id()] = item;
+ acceptedNewPoints->insert(matchingPoints[i].id());
+ }
+ }
+ }
+ }
+
+ updatedPoints->remove(item);
+ if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
+ return true;
+
+ return false;
+}
+
+bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QGraphicsSceneMouseEvent *event)
+{
+ if (!target)
+ return false;
+
+ if (sendFilteredMouseEvent(target->parentItem(), item, event))
+ return true;
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(target);
+ if (targetPrivate->filtersChildMouseEvents)
+ if (target->childMouseEventFilter(item, event))
+ return true;
+
+ return false;
+}
+
+bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e)
+{
+ Q_D(QSGCanvas);
+
+ if (!item) {
+ qWarning("QSGCanvas::sendEvent: Cannot send event to a null item");
+ return false;
+ }
+
+ Q_ASSERT(e);
+
+ switch (e->type()) {
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ e->accept();
+ QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
+ while (!e->isAccepted() && (item = item->parentItem())) {
+ e->accept();
+ QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
+ }
+ break;
+ case QEvent::InputMethod:
+ e->accept();
+ QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
+ while (!e->isAccepted() && (item = item->parentItem())) {
+ e->accept();
+ QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
+ }
+ break;
+ case QEvent::FocusIn:
+ case QEvent::FocusOut:
+ QSGItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ case QEvent::GraphicsSceneMouseMove:
+ // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
+ {
+ QGraphicsSceneMouseEvent *se = static_cast<QGraphicsSceneMouseEvent *>(e);
+ if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) {
+ se->accept();
+ QSGItemPrivate::get(item)->deliverMouseEvent(se);
+ }
+ }
+ break;
+ case QEvent::GraphicsSceneWheel:
+ QSGItemPrivate::get(item)->deliverWheelEvent(static_cast<QGraphicsSceneWheelEvent *>(e));
+ break;
+ case QEvent::GraphicsSceneHoverEnter:
+ case QEvent::GraphicsSceneHoverLeave:
+ case QEvent::GraphicsSceneHoverMove:
+ QSGItemPrivate::get(item)->deliverHoverEvent(static_cast<QGraphicsSceneHoverEvent *>(e));
+ break;
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ case QEvent::TouchEnd:
+ QSGItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void QSGCanvasPrivate::cleanupNodes()
+{
+ for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
+ delete cleanupNodeList.at(ii);
+ cleanupNodeList.clear();
+}
+
+void QSGCanvasPrivate::updateDirtyNodes()
+{
+#ifdef DIRTY_DEBUG
+ qWarning() << "QSGCanvasPrivate::updateDirtyNodes():";
+#endif
+
+ cleanupNodes();
+
+ QSGItem *updateList = dirtyItemList;
+ dirtyItemList = 0;
+ if (updateList) QSGItemPrivate::get(updateList)->prevDirtyItem = &updateList;
+
+ while (updateList) {
+ QSGItem *item = updateList;
+ QSGItemPrivate *itemPriv = QSGItemPrivate::get(item);
+ itemPriv->removeFromDirtyList();
+
+#ifdef DIRTY_DEBUG
+ qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
+#endif
+ updateDirtyNode(item);
+ }
+}
+
+void QSGCanvasPrivate::updateDirtyNode(QSGItem *item)
+{
+#ifdef QML_RUNTIME_TESTING
+ bool didFlash = false;
+#endif
+
+ QSGItemPrivate *itemPriv = QSGItemPrivate::get(item);
+ quint32 dirty = itemPriv->dirtyAttributes;
+ itemPriv->dirtyAttributes = 0;
+
+ if ((dirty & QSGItemPrivate::TransformUpdateMask) ||
+ (dirty & QSGItemPrivate::Size && itemPriv->origin != QSGItem::TopLeft &&
+ (itemPriv->scale != 1. || itemPriv->rotation != 0.))) {
+
+ QMatrix4x4 matrix;
+
+ if (itemPriv->x != 0. || itemPriv->y != 0.)
+ matrix.translate(itemPriv->x, itemPriv->y);
+
+ if (dirty & QSGItemPrivate::ComplexTransformUpdateMask) {
+ for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
+ itemPriv->transforms.at(ii)->applyTo(&matrix);
+ }
+
+ if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
+ QPointF origin = itemPriv->computeTransformOrigin();
+ matrix.translate(origin.x(), origin.y());
+ if (itemPriv->scale != 1.)
+ matrix.scale(itemPriv->scale, itemPriv->scale);
+ if (itemPriv->rotation != 0.)
+ matrix.rotate(itemPriv->rotation, 0, 0, 1);
+ matrix.translate(-origin.x(), -origin.y());
+ }
+
+ itemPriv->itemNode()->setMatrix(matrix);
+ }
+
+ bool clipEffectivelyChanged = dirty & QSGItemPrivate::Clip &&
+ ((item->clip() == false) != (itemPriv->clipNode == 0));
+ bool effectRefEffectivelyChanged = dirty & QSGItemPrivate::EffectReference &&
+ ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0));
+
+ if (clipEffectivelyChanged) {
+ QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode();
+ QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode;
+
+ if (item->clip()) {
+ Q_ASSERT(itemPriv->clipNode == 0);
+ itemPriv->clipNode = new QSGDefaultClipNode(QRectF(0, 0, itemPriv->width, itemPriv->height));
+
+ if (child)
+ parent->removeChildNode(child);
+ parent->appendChildNode(itemPriv->clipNode);
+ if (child)
+ itemPriv->clipNode->appendChildNode(child);
+
+ } else {
+ Q_ASSERT(itemPriv->clipNode != 0);
+ parent->removeChildNode(itemPriv->clipNode);
+ if (child)
+ itemPriv->clipNode->removeChildNode(child);
+ delete itemPriv->clipNode;
+ itemPriv->clipNode = 0;
+ if (child)
+ parent->appendChildNode(child);
+ }
+ }
+
+ if (dirty & QSGItemPrivate::ChildrenUpdateMask) {
+ while (itemPriv->childContainerNode()->childCount())
+ itemPriv->childContainerNode()->removeChildNode(itemPriv->childContainerNode()->childAtIndex(0));
+ }
+
+ if (effectRefEffectivelyChanged) {
+ QSGNode *parent = itemPriv->clipNode;
+ if (!parent)
+ parent = itemPriv->opacityNode;
+ if (!parent)
+ parent = itemPriv->itemNode();
+ QSGNode *child = itemPriv->groupNode;
+
+ if (itemPriv->effectRefCount) {
+ Q_ASSERT(itemPriv->rootNode == 0);
+ itemPriv->rootNode = new QSGRootNode;
+
+ if (child)
+ parent->removeChildNode(child);
+ parent->appendChildNode(itemPriv->rootNode);
+ if (child)
+ itemPriv->rootNode->appendChildNode(child);
+ } else {
+ Q_ASSERT(itemPriv->rootNode != 0);
+ parent->removeChildNode(itemPriv->rootNode);
+ if (child)
+ itemPriv->rootNode->removeChildNode(child);
+ delete itemPriv->rootNode;
+ itemPriv->rootNode = 0;
+ if (child)
+ parent->appendChildNode(child);
+ }
+ }
+
+ if (dirty & QSGItemPrivate::ChildrenUpdateMask) {
+ QSGNode *groupNode = itemPriv->groupNode;
+ if (groupNode) {
+ for (int count = groupNode->childCount(); count; --count)
+ groupNode->removeChildNode(groupNode->childAtIndex(0));
+ }
+
+ QList<QSGItem *> orderedChildren = itemPriv->paintOrderChildItems();
+ int ii = 0;
+
+ itemPriv->paintNodeIndex = 0;
+ for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
+ QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii));
+ if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
+ continue;
+ if (childPrivate->itemNode()->parent())
+ childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
+
+ itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
+ itemPriv->paintNodeIndex++;
+ }
+
+ if (itemPriv->paintNode)
+ itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
+
+ for (; ii < orderedChildren.count(); ++ii) {
+ QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii));
+ if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
+ continue;
+ if (childPrivate->itemNode()->parent())
+ childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
+
+ itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
+ }
+ }
+
+ if ((dirty & QSGItemPrivate::Size || clipEffectivelyChanged) && itemPriv->clipNode) {
+ itemPriv->clipNode->setRect(QRectF(0, 0, itemPriv->width, itemPriv->height));
+ itemPriv->clipNode->update();
+ }
+
+ if (dirty & (QSGItemPrivate::OpacityValue | QSGItemPrivate::Visible | QSGItemPrivate::HideReference)) {
+ qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0
+ ? itemPriv->opacity : qreal(0);
+
+ if (opacity != 1 && !itemPriv->opacityNode) {
+ itemPriv->opacityNode = new QSGOpacityNode;
+
+ QSGNode *parent = itemPriv->itemNode();
+ QSGNode *child = itemPriv->clipNode;
+ if (!child)
+ child = itemPriv->rootNode;
+ if (!child)
+ child = itemPriv->groupNode;
+
+ if (child)
+ parent->removeChildNode(child);
+ parent->appendChildNode(itemPriv->opacityNode);
+ if (child)
+ itemPriv->opacityNode->appendChildNode(child);
+ }
+ if (itemPriv->opacityNode)
+ itemPriv->opacityNode->setOpacity(opacity);
+ }
+
+ if (dirty & QSGItemPrivate::ContentUpdateMask) {
+
+ if (itemPriv->flags & QSGItem::ItemHasContents) {
+ updatePaintNodeData.transformNode = itemPriv->itemNode();
+ itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
+
+ Q_ASSERT(itemPriv->paintNode == 0 ||
+ itemPriv->paintNode->parent() == 0 ||
+ itemPriv->paintNode->parent() == itemPriv->childContainerNode());
+
+ if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
+ if (itemPriv->childContainerNode()->childCount() == itemPriv->paintNodeIndex)
+ itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
+ else
+ itemPriv->childContainerNode()->insertChildNodeBefore(itemPriv->paintNode, itemPriv->childContainerNode()->childAtIndex(itemPriv->paintNodeIndex));
+ }
+ } else if (itemPriv->paintNode) {
+ delete itemPriv->paintNode;
+ itemPriv->paintNode = 0;
+ }
+ }
+
+#ifndef QT_NO_DEBUG
+ // Check consistency.
+ const QSGNode *nodeChain[] = {
+ itemPriv->itemNodeInstance,
+ itemPriv->opacityNode,
+ itemPriv->clipNode,
+ itemPriv->rootNode,
+ itemPriv->groupNode,
+ itemPriv->paintNode,
+ };
+
+ int ip = 0;
+ for (;;) {
+ while (ip < 5 && nodeChain[ip] == 0)
+ ++ip;
+ if (ip == 5)
+ break;
+ int ic = ip + 1;
+ while (ic < 5 && nodeChain[ic] == 0)
+ ++ic;
+ const QSGNode *parent = nodeChain[ip];
+ const QSGNode *child = nodeChain[ic];
+ if (child == 0) {
+ Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
+ } else {
+ Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
+ Q_ASSERT(child->parent() == parent);
+ bool containsChild = false;
+ for (int i = 0; i < parent->childCount(); ++i)
+ containsChild |= (parent->childAtIndex(i) == child);
+ Q_ASSERT(containsChild);
+ }
+ ip = ic;
+ }
+#endif
+
+#ifdef QML_RUNTIME_TESTING
+ if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
+ QSGFlashNode *flash = new QSGFlashNode();
+ flash->setRect(item->boundingRect());
+ itemPriv->childContainerNode()->appendChildNode(flash);
+ didFlash = true;
+ }
+ Q_Q(QSGCanvas);
+ if (didFlash) {
+ q->maybeUpdate();
+ }
+#endif
+
+}
+
+void QSGCanvas::maybeUpdate()
+{
+ Q_D(QSGCanvas);
+
+ if (d->threadedRendering) {
+ if (!d->renderThreadAwakened) {
+ d->renderThreadAwakened = true;
+ bool locked = d->mutex.tryLock();
+ if (d->idle && locked) {
+#ifdef THREAD_DEBUG
+ qWarning("QSGRenderer: now maybe I should update...");
+#endif
+ d->wait.wakeOne();
+ } else if (d->inSync) {
+ // If we are in sync (on scene graph thread) someone has explicitely asked us
+ // to redraw, hence we tell the render loop to not go idle.
+ // The primary usecase for this is updatePaintNode() calling update() without
+ // changing the scene graph.
+ d->needsRepaint = true;
+ }
+ if (locked)
+ d->mutex.unlock();
+ }
+ } else if (!d->animationDriver || !d->animationDriver->isRunning()) {
+ update();
+ }
+}
+
+/*!
+ \fn void QSGEngine::sceneGraphInitialized();
+
+ This signal is emitted when the scene graph has been initialized.
+
+ This signal will be emitted from the scene graph rendering thread.
+ */
+
+/*!
+ Returns the QSGEngine used for this scene.
+
+ The engine will only be available once the scene graph has been
+ initialized. Register for the sceneGraphEngine() signal to get
+ notification about this.
+ */
+
+QSGEngine *QSGCanvas::sceneGraphEngine() const
+{
+ Q_D(const QSGCanvas);
+ if (d->context->isReady())
+ return d->context->engine();
+ return 0;
+}
+
+#include "moc_qsgcanvas.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgcanvas.h b/src/declarative/items/qsgcanvas.h
new file mode 100644
index 0000000000..6707d24b30
--- /dev/null
+++ b/src/declarative/items/qsgcanvas.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCANVAS_H
+#define QSGCANVAS_H
+
+#include <QtCore/qmetatype.h>
+#include <QtOpenGL/qgl.h>
+#include <QtGui/qwidget.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGItem;
+class QSGEngine;
+class QSGCanvasPrivate;
+class Q_DECLARATIVE_EXPORT QSGCanvas : public QGLWidget
+{
+Q_OBJECT
+Q_DECLARE_PRIVATE(QSGCanvas)
+public:
+ QSGCanvas(QWidget *parent = 0, Qt::WindowFlags f = 0);
+ QSGCanvas(const QGLFormat &format, QWidget *parent = 0, Qt::WindowFlags f = 0);
+ virtual ~QSGCanvas();
+
+ QSGItem *rootItem() const;
+ QSGItem *activeFocusItem() const;
+
+ QSGItem *mouseGrabberItem() const;
+
+ bool sendEvent(QSGItem *, QEvent *);
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ QSGEngine *sceneGraphEngine() const;
+
+Q_SIGNALS:
+ void sceneGraphInitialized();
+
+protected:
+ QSGCanvas(QSGCanvasPrivate &dd, QWidget *parent = 0, Qt::WindowFlags f = 0);
+ QSGCanvas(QSGCanvasPrivate &dd, const QGLFormat &format, QWidget *parent = 0, Qt::WindowFlags f = 0);
+
+ virtual void paintEvent(QPaintEvent *);
+ virtual void resizeEvent(QResizeEvent *);
+
+ virtual void showEvent(QShowEvent *);
+ virtual void hideEvent(QHideEvent *);
+
+ virtual void focusOutEvent(QFocusEvent *);
+ virtual void focusInEvent(QFocusEvent *);
+
+ virtual bool event(QEvent *);
+ virtual void keyPressEvent(QKeyEvent *);
+ virtual void keyReleaseEvent(QKeyEvent *);
+ virtual void inputMethodEvent(QInputMethodEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseDoubleClickEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+#ifndef QT_NO_WHEELEVENT
+ virtual void wheelEvent(QWheelEvent *);
+#endif
+
+private Q_SLOTS:
+ void sceneGraphChanged();
+ void maybeUpdate();
+
+private:
+ Q_DISABLE_COPY(QSGCanvas)
+ Q_PRIVATE_SLOT(d_func(), void _q_animationStarted())
+ Q_PRIVATE_SLOT(d_func(), void _q_animationStopped())
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSGCanvas *);
+
+QT_END_HEADER
+
+#endif // QSGCANVAS_H
+
diff --git a/src/declarative/items/qsgcanvas_p.h b/src/declarative/items/qsgcanvas_p.h
new file mode 100644
index 0000000000..6b8034f922
--- /dev/null
+++ b/src/declarative/items/qsgcanvas_p.h
@@ -0,0 +1,189 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCANVAS_P_H
+#define QSGCANVAS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgitem.h"
+#include "qsgcanvas.h"
+#include <private/qdeclarativeguard_p.h>
+
+#include <private/qsgcontext_p.h>
+
+#include <QtCore/qthread.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <private/qwidget_p.h>
+#include <private/qgl_p.h>
+
+QT_BEGIN_NAMESPACE
+
+//Make it easy to identify and customize the root item if needed
+class QSGRootItem : public QSGItem
+{
+ Q_OBJECT
+public:
+ QSGRootItem();
+};
+
+class QSGCanvasPrivate;
+
+class QTouchEvent;
+class QSGCanvasPrivate : public QGLWidgetPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QSGCanvas)
+
+ static inline QSGCanvasPrivate *get(QSGCanvas *c) { return c->d_func(); }
+
+ QSGCanvasPrivate();
+ virtual ~QSGCanvasPrivate();
+
+ void init(QSGCanvas *);
+
+ QSGRootItem *rootItem;
+
+ QSGItem *activeFocusItem;
+ QSGItem *mouseGrabberItem;
+
+ // Mouse positions are saved in widget coordinates
+ QPoint lastMousePosition;
+ QPoint buttonDownPositions[5]; // Left, Right, Middle, XButton1, XButton2
+ void sceneMouseEventFromMouseEvent(QGraphicsSceneMouseEvent &, QMouseEvent *);
+ void translateTouchEvent(QTouchEvent *touchEvent);
+ static QEvent::Type sceneMouseEventTypeFromMouseEvent(QMouseEvent *);
+ static void sceneMouseEventForTransform(QGraphicsSceneMouseEvent &, const QTransform &);
+ static void transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform);
+ bool deliverInitialMousePressEvent(QSGItem *, QGraphicsSceneMouseEvent *);
+ bool deliverMouseEvent(QGraphicsSceneMouseEvent *);
+ bool sendFilteredMouseEvent(QSGItem *, QSGItem *, QGraphicsSceneMouseEvent *);
+ bool deliverWheelEvent(QSGItem *, QGraphicsSceneWheelEvent *);
+ bool deliverTouchPoints(QSGItem *, QTouchEvent *, const QList<QTouchEvent::TouchPoint> &, QSet<int> *,
+ QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > *);
+ bool deliverTouchEvent(QTouchEvent *);
+ void sceneHoverEventFromMouseEvent(QGraphicsSceneHoverEvent &, QMouseEvent *);
+ bool deliverHoverEvent(QSGItem *, QGraphicsSceneHoverEvent *);
+ void sendHoverEvent(QEvent::Type, QSGItem *, QGraphicsSceneHoverEvent *);
+ void clearHover();
+
+ void stopRenderingThread();
+
+ QDeclarativeGuard<QSGItem> hoverItem;
+ enum FocusOption {
+ DontChangeFocusProperty = 0x01,
+ };
+ Q_DECLARE_FLAGS(FocusOptions, FocusOption)
+
+ void setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions = 0);
+ void clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions = 0);
+ void notifyFocusChangesRecur(QSGItem **item, int remaining);
+
+ void updateInputMethodData();
+
+ void dirtyItem(QSGItem *);
+ void cleanup(QSGNode *);
+
+ void initializeSceneGraph();
+ void polishItems();
+ void syncSceneGraph();
+ void renderSceneGraph();
+ void runThread();
+
+ void _q_animationStarted();
+ void _q_animationStopped();
+
+ QSGItem::UpdatePaintNodeData updatePaintNodeData;
+
+ QSGItem *dirtyItemList;
+ QList<QSGNode *> cleanupNodeList;
+
+ QSet<QSGItem *> itemsToPolish;
+
+ void updateDirtyNodes();
+ void cleanupNodes();
+ bool updateEffectiveOpacity(QSGItem *);
+ void updateEffectiveOpacityRoot(QSGItem *, qreal);
+ void updateDirtyNode(QSGItem *);
+
+ QSGContext *context;
+
+ uint contextInThread : 1;
+ uint threadedRendering : 1;
+ uint exitThread : 1;
+ uint animationRunning: 1;
+ uint idle : 1; // Set to true when render thread sees no change and enters a wait()
+ uint needsRepaint : 1; // Set by callback from render if scene needs repainting.
+ uint renderThreadAwakened : 1;
+ uint inSync: 1;
+
+ struct MyThread : public QThread {
+ MyThread(QSGCanvasPrivate *r) : renderer(r) {}
+ virtual void run() { renderer->runThread(); }
+ static void doWait() { QThread::msleep(16); }
+ QSGCanvasPrivate *renderer;
+ };
+ MyThread *thread;
+ QMutex mutex;
+ QWaitCondition wait;
+ QSize widgetSize;
+ QSize viewportSize;
+
+ QAnimationDriver *animationDriver;
+
+ QHash<int, QSGItem *> itemForTouchPointId;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGCanvasPrivate::FocusOptions)
+
+QT_END_NAMESPACE
+
+#endif // QSGCANVAS_P_H
diff --git a/src/declarative/items/qsgcanvasitem.cpp b/src/declarative/items/qsgcanvasitem.cpp
new file mode 100644
index 0000000000..ba3c4baa11
--- /dev/null
+++ b/src/declarative/items/qsgcanvasitem.cpp
@@ -0,0 +1,441 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtGui/qpainter.h>
+
+#include "private/qsgadaptationlayer_p.h"
+#include "qsgcanvasitem_p.h"
+#include "qsgpainteditem_p.h"
+#include "qsgcontext2d_p.h"
+#include "private/qsgpainternode_p.h"
+#include <qdeclarativeinfo.h>
+#include "qdeclarativeengine_p.h"
+#include <QtCore/QBuffer>
+
+QT_BEGIN_NAMESPACE
+
+class QSGCanvasItemPrivate : public QSGPaintedItemPrivate
+{
+public:
+ QSGCanvasItemPrivate();
+ QSGContext2D* context;
+};
+
+
+/*!
+ \internal
+*/
+QSGCanvasItemPrivate::QSGCanvasItemPrivate()
+ : QSGPaintedItemPrivate()
+ , context(0)
+{
+}
+
+/*!
+ Constructs a QSGCanvasItem with the given \a parent item.
+ */
+QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
+ : QSGPaintedItem(*(new QSGCanvasItemPrivate), parent)
+{
+}
+
+/*!
+ Destroys the QSGCanvasItem.
+*/
+QSGCanvasItem::~QSGCanvasItem()
+{
+}
+
+void QSGCanvasItem::paint(QPainter *painter)
+{
+ Q_D(QSGCanvasItem);
+
+ if (d->context) {
+ d->context->paint(painter);
+ emit canvasUpdated();
+ }
+}
+
+
+QSGContext2D* QSGCanvasItem::getContext(const QString &contextId)
+{
+ Q_D(QSGCanvasItem);
+ if (contextId == QLatin1String("2d")) {
+ if (!d->context) {
+ d->context = new QSGContext2D(this);
+ connect(d->context, SIGNAL(changed()), this, SLOT(requestPaint()));
+ }
+ return d->context;
+ }
+ qDebug("Canvas:requesting unsupported context");
+ return 0;
+}
+
+void QSGCanvasItem::requestPaint()
+{
+ Q_D(QSGCanvasItem);
+ //TODO:update(d->context->dirtyRect());
+ update();
+}
+
+bool QSGCanvasItem::save(const QString &filename) const
+{
+ Q_D(const QSGCanvasItem);
+ QSGPainterNode* node = static_cast<QSGPainterNode*>(d->paintNode);
+ if (node) {
+ QImage image = node->toImage();
+ image.save(filename);
+ }
+ return false;
+}
+
+QString QSGCanvasItem::toDataURL(const QString& mimeType) const
+{
+ Q_D(const QSGCanvasItem);
+
+ QSGPainterNode* node = static_cast<QSGPainterNode*>(d->paintNode);
+ if (node) {
+ QImage image = node->toImage();
+ QByteArray ba;
+ QBuffer buffer(&ba);
+ buffer.open(QIODevice::WriteOnly);
+ QString mime = mimeType;
+ QString type;
+ if (mimeType == QLatin1String("image/bmp"))
+ type = "BMP";
+ else if (mimeType == QLatin1String("image/jpeg"))
+ type = "JPEG";
+ else if (mimeType == QLatin1String("image/x-portable-pixmap"))
+ type = "PPM";
+ else if (mimeType == QLatin1String("image/tiff"))
+ type = "TIFF";
+ else if (mimeType == QLatin1String("image/xbm"))
+ type = "XBM";
+ else if (mimeType == QLatin1String("image/xpm"))
+ type = "XPM";
+ else {
+ type = "PNG";
+ mime = QLatin1String("image/png");
+ }
+ image.save(&buffer, type.toAscii());
+ buffer.close();
+ QString dataUrl = QLatin1String("data:%1;base64,%2");
+ return dataUrl.arg(mime).arg(ba.toBase64().constData());
+ }
+ return QLatin1String("data:,");
+}
+//CanvasItemTextureProvider::CanvasItemTextureProvider(QObject *parent)
+// : QSGTextureProvider(parent)
+// , m_ctx2d(0)
+// , m_fbo(0)
+// , m_multisampledFbo(0)
+// , m_dirtyTexture(true)
+// , m_multisamplingSupportChecked(false)
+// , m_multisampling(false)
+//{
+//}
+
+//CanvasItemTextureProvider::~CanvasItemTextureProvider()
+//{
+// delete m_fbo;
+// delete m_multisampledFbo;
+//}
+
+//void CanvasItemTextureProvider::updateTexture()
+//{
+// if (m_dirtyTexture) {
+// if (!m_ctx2d->isDirty())
+// return;
+// if (m_size.isEmpty()) {
+// m_texture = QSGTextureRef();
+// delete m_fbo;
+// delete m_multisampledFbo;
+// m_multisampledFbo = m_fbo = 0;
+// return;
+// }
+
+//#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+// //create texture
+// if (!m_fbo || m_fbo->size() != m_size )
+// {
+// const QGLContext *ctx = QSGContext::current->glContext();
+// if (!m_multisamplingSupportChecked) {
+// QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
+// m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
+// && extensions.contains("GL_EXT_framebuffer_blit");
+// m_multisamplingSupportChecked = true;
+// }
+
+// if (ctx->format().sampleBuffers() && m_multisampling) {
+// delete m_fbo;
+// delete m_multisampledFbo;
+// QGLFramebufferObjectFormat format;
+
+// format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+// format.setSamples(ctx->format().samples());
+// m_multisampledFbo = new QGLFramebufferObject(m_size, format);
+// {
+// QGLFramebufferObjectFormat format;
+// format.setAttachment(QGLFramebufferObject::NoAttachment);
+// m_fbo = new QGLFramebufferObject(m_size, format);
+// }
+
+// QSGPlainTexture *tex = new QSGPlainTexture;
+// tex->setTextureId(m_fbo->texture());
+// tex->setOwnsTexture(false);
+// tex->setHasAlphaChannel(true);
+// setOpaque(!tex->hasAlphaChannel());
+// m_texture = QSGTextureRef(tex);
+// } else {
+// delete m_fbo;
+// QGLFramebufferObjectFormat format;
+// format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+// m_fbo = new QGLFramebufferObject(m_size, format);
+// QSGPlainTexture *tex = new QSGPlainTexture;
+// tex->setTextureId(m_fbo->texture());
+// tex->setOwnsTexture(false);
+// tex->setHasAlphaChannel(true);
+// setOpaque(!tex->hasAlphaChannel());
+// m_texture = QSGTextureRef(tex);
+// }
+// }
+//#endif
+
+//#ifdef QSGCANVASITEM_DEBUG
+// qDebug() << "painting interval:" << m_elapsedTimer.nsecsElapsed();
+// m_elapsedTimer.restart();
+//#endif
+// //paint 2d
+// if (m_ctx2d) {
+// QPainter p;
+//#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+// if (m_multisampledFbo)
+// p.begin(m_multisampledFbo);
+// else if (m_fbo)
+// p.begin(m_fbo);
+// else
+// return;
+// // move the origin of coordinates to the down left corner and
+// // scale coordinates and turn y-axis up
+// QSize size = m_ctx2d->size();
+// p.translate( 0, size.height());
+// p.scale(1, -1);
+
+// m_ctx2d->paint(&p);
+
+// p.end();
+
+// if (m_multisampledFbo) {
+// QRect r(0, 0, m_fbo->width(), m_fbo->height());
+// QGLFramebufferObject::blitFramebuffer(m_fbo, r, m_multisampledFbo, r);
+// }
+
+// if (m_ctx2d->requireCachedImage())
+// m_ctx2d->setCachedImage(m_fbo->toImage());
+
+//#else
+// m_painter.begin(m_ctx2d->paintDevice());
+// m_ctx2d->paint(&m_painter);
+// m_painter.end();
+
+// if (m_texture.isNull()) {
+// m_texture = QSGContext::current->createTexture(m_ctx2d->toImage());
+// } else {
+// QSGPlainTexture* t =static_cast<QSGPlainTexture*>(m_texture.texture());
+// t->setImage(m_ctx2d->toImage());
+// }
+// m_ctx2d->setCachedImage(m_ctx2d->toImage());
+
+//#endif
+
+//#ifdef QSGCANVASITEM_DEBUG
+// qDebug() << "painting time:" << m_elapsedTimer.nsecsElapsed();
+// m_elapsedTimer.restart();
+//#endif
+// emit painted();
+// }
+// }
+//}
+
+//QSGTextureRef CanvasItemTextureProvider::texture()
+//{
+// return m_texture;
+//}
+//void CanvasItemTextureProvider::setContext2D(QSGContext2D *ctx2d)
+//{
+// if (ctx2d && m_ctx2d != ctx2d) {
+// m_ctx2d = ctx2d;
+// connect(this, SIGNAL(painted()), m_ctx2d, SIGNAL(painted()));
+// }
+//}
+//void CanvasItemTextureProvider::setRect(const QRectF &rect)
+//{
+// if (rect == m_rect)
+// return;
+// m_rect = rect;
+// markDirtyTexture();
+//}
+
+//void CanvasItemTextureProvider::setSize(const QSize &size)
+//{
+// if (size == m_size)
+// return;
+// m_size = size;
+// markDirtyTexture();
+//}
+
+//void CanvasItemTextureProvider::markDirtyTexture()
+//{
+// m_dirtyTexture = true;
+// emit textureChanged();
+//}
+//QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
+// : TextureItem(parent)
+// , m_textureProvider(0)
+// , m_context2dChanged(false)
+// , m_context2d( new QSGContext2D(this))
+// , m_fillMode(QSGCanvasItem::Stretch)
+// , m_color(Qt::white)
+//{
+// m_textureProvider = new CanvasItemTextureProvider(this);
+// m_textureProvider->setContext2D(m_context2d);
+// setTextureProvider(m_textureProvider, true);
+// setFlag(QSGItem::ItemHasContents, true);
+//}
+
+//QSGCanvasItem::~QSGCanvasItem()
+//{
+//}
+
+//void QSGCanvasItem::componentComplete()
+//{
+// m_context2d->setSize(width(), height());
+// qDebug() << "m_context2d.size:" << m_context2d->size();
+// connect(m_context2d, SIGNAL(changed()), this, SLOT(requestPaint()));
+// QScriptEngine* scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this));
+// if (scriptEngine != m_context2d->scriptEngine())
+// m_context2d->setScriptEngine(scriptEngine);
+// QSGItem::componentComplete();
+//}
+
+
+//void QSGCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+//{
+// if (width() == 0 && height()
+// && newGeometry.width() > 0 && newGeometry.height() > 0) {
+// m_context2d->setSize(width(), height());
+// }
+// TextureItem::geometryChanged(newGeometry, oldGeometry);
+//}
+
+//void QSGCanvasItem::setFillMode(FillMode mode)
+//{
+// if (m_fillMode == mode)
+// return;
+
+// m_fillMode = mode;
+// update();
+// emit fillModeChanged();
+//}
+
+//QColor QSGCanvasItem::color()
+//{
+// return m_color;
+//}
+
+//void QSGCanvasItem::setColor(const QColor &color)
+//{
+// if (m_color !=color) {
+// m_color = color;
+// colorChanged();
+// }
+//}
+
+//QSGCanvasItem::FillMode QSGCanvasItem::fillMode() const
+//{
+// return m_fillMode;
+//}
+
+
+
+//Node *QSGCanvasItem::updatePaintNode(Node *oldNode, UpdatePaintNodeData *data)
+//{
+// if (width() <= 0 || height() <= 0) {
+// delete oldNode;
+// return 0;
+// }
+
+// TextureNodeInterface *node = static_cast<TextureNodeInterface *>(oldNode);
+
+// if (node && m_context2d->isDirty()) {
+// QRectF bounds = boundingRect();
+
+// if (m_textureProvider) {
+// m_textureProvider->setRect(QRectF(bounds.x(), bounds.y(), width(), height()));
+
+// m_textureProvider->setSize(QSize(width(), height()));
+// //m_textureProvider->setOpaque(true);
+// m_textureProvider->setHorizontalWrapMode(QSGTextureProvider::ClampToEdge);
+// m_textureProvider->setVerticalWrapMode(QSGTextureProvider::ClampToEdge);
+// node->setTargetRect(bounds);
+// node->setSourceRect(QRectF(0, 0, 1, 1));
+// // node->setTargetRect(image.rect());
+//// node->setSourceRect(QRectF(0, 0, 1, 1));
+//// d->textureProvider->setHorizontalWrapMode(QSGTextureProvider::ClampToEdge);
+//// d->textureProvider->setVerticalWrapMode(QSGTextureProvider::ClampToEdge);
+//// d->textureProvider->setFiltering(d->smooth ? QSGTextureProvider::Linear : QSGTextureProvider::Nearest);
+// }
+
+// if (m_context2dChanged) {
+// //force textnode update the content
+// node->setTexture(0);
+// node->setTexture(m_textureProvider);
+// m_context2dChanged = false;
+// }
+// } else {
+// if (m_context2d->requireCachedImage())
+// m_context2d->setCachedImage(QImage(width(), height(), QImage::Format_ARGB32_Premultiplied));
+// }
+
+// return TextureItem::updatePaintNode(oldNode, data);
+//}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgcanvasitem_p.h b/src/declarative/items/qsgcanvasitem_p.h
new file mode 100644
index 0000000000..a358c353ea
--- /dev/null
+++ b/src/declarative/items/qsgcanvasitem_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCANVASITEM_P_H
+#define QSGCANVASITEM_P_H
+
+#include "qsgpainteditem.h"
+
+#define QSGCANVASITEM_DEBUG //enable this for just DEBUG purpose!
+
+#ifdef QSGCANVASITEM_DEBUG
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class QSGContext2D;
+class QSGCanvasItemPrivate;
+class QSGCanvasItem : public QSGPaintedItem
+{
+ Q_OBJECT
+public:
+ QSGCanvasItem(QSGItem *parent = 0);
+ ~QSGCanvasItem();
+
+signals:
+ void canvasUpdated();
+public Q_SLOTS:
+ QString toDataURL(const QString& type = QLatin1String("image/png")) const;
+ QSGContext2D* getContext(const QString & = QLatin1String("2d"));
+ void requestPaint();
+
+ // Save current canvas to disk
+ bool save(const QString& filename) const;
+
+protected:
+ void paint(QPainter *painter);
+private:
+ Q_DECLARE_PRIVATE(QSGCanvasItem)
+ friend class QSGContext2D;
+};
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGCanvasItem)
+
+QT_END_HEADER
+
+#endif //QSGCANVASITEM_P_H
diff --git a/src/declarative/items/qsgclipnode.cpp b/src/declarative/items/qsgclipnode.cpp
new file mode 100644
index 0000000000..2e40972620
--- /dev/null
+++ b/src/declarative/items/qsgclipnode.cpp
@@ -0,0 +1,121 @@
+
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsgclipnode_p.h"
+
+#include <QtGui/qvector2d.h>
+#include <QtCore/qmath.h>
+
+QSGDefaultClipNode::QSGDefaultClipNode(const QRectF &rect)
+ : m_rect(rect)
+ , m_radius(0)
+ , m_dirty_geometry(true)
+ , m_geometry(QSGGeometry::defaultAttributes_Point2D(), 0)
+{
+ setGeometry(&m_geometry);
+ setIsRectangular(true);
+}
+
+void QSGDefaultClipNode::setRect(const QRectF &rect)
+{
+ m_rect = rect;
+ m_dirty_geometry = true;
+}
+
+void QSGDefaultClipNode::setRadius(qreal radius)
+{
+ m_radius = radius;
+ m_dirty_geometry = true;
+ setIsRectangular(radius == 0);
+}
+
+void QSGDefaultClipNode::update()
+{
+ if (m_dirty_geometry) {
+ updateGeometry();
+ m_dirty_geometry = false;
+ }
+}
+
+void QSGDefaultClipNode::updateGeometry()
+{
+ QSGGeometry *g = geometry();
+
+ if (qFuzzyIsNull(m_radius)) {
+ g->allocate(4);
+ QSGGeometry::updateRectGeometry(g, m_rect);
+
+ } else {
+ int vertexCount = 0;
+
+ // Radius should never exceeds half of the width or half of the height
+ qreal radius = qMin(qMin(m_rect.width() / 2, m_rect.height() / 2), m_radius);
+ QRectF rect = m_rect;
+ rect.adjust(radius, radius, -radius, -radius);
+
+ int segments = qMin(30, qCeil(radius)); // Number of segments per corner.
+
+ g->allocate((segments + 1) * 2);
+
+ QVector2D *vertices = (QVector2D *)g->vertexData();
+
+ for (int part = 0; part < 2; ++part) {
+ for (int i = 0; i <= segments; ++i) {
+ //### Should change to calculate sin/cos only once.
+ qreal angle = qreal(0.5 * M_PI) * (part + i / qreal(segments));
+ qreal s = qFastSin(angle);
+ qreal c = qFastCos(angle);
+ qreal y = (part ? rect.bottom() : rect.top()) - radius * c; // current inner y-coordinate.
+ qreal lx = rect.left() - radius * s; // current inner left x-coordinate.
+ qreal rx = rect.right() + radius * s; // current inner right x-coordinate.
+
+ vertices[vertexCount++] = QVector2D(rx, y);
+ vertices[vertexCount++] = QVector2D(lx, y);
+ }
+ }
+
+ markDirty(DirtyGeometry);
+ }
+ setClipRect(m_rect);
+}
+
diff --git a/src/declarative/items/qsgclipnode_p.h b/src/declarative/items/qsgclipnode_p.h
new file mode 100644
index 0000000000..aa1d01efdd
--- /dev/null
+++ b/src/declarative/items/qsgclipnode_p.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCLIPNODE_P_H
+#define QSGCLIPNODE_P_H
+
+#include <qsgnode.h>
+
+class QSGDefaultClipNode : public QSGClipNode
+{
+public:
+ QSGDefaultClipNode(const QRectF &);
+
+ void setRect(const QRectF &);
+ QRectF rect() const { return m_rect; }
+
+ void setRadius(qreal radius);
+ qreal radius() const { return m_radius; }
+
+ virtual void update();
+
+private:
+ void updateGeometry();
+ QRectF m_rect;
+ qreal m_radius;
+
+ uint m_dirty_geometry : 1;
+ uint m_reserved : 31;
+
+ QSGGeometry m_geometry;
+};
+
+#endif // QSGCLIPNODE_P_H
diff --git a/src/declarative/items/qsgcontext2d.cpp b/src/declarative/items/qsgcontext2d.cpp
new file mode 100644
index 0000000000..6f7121ac30
--- /dev/null
+++ b/src/declarative/items/qsgcontext2d.cpp
@@ -0,0 +1,2716 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgcontext2d_p.h"
+#include "qsgcontext2d_p_p.h"
+#include "private/qsgadaptationlayer_p.h"
+#include "qsgcanvasitem_p.h"
+#include <QtOpenGL/qglframebufferobject.h>
+#include <QtCore/qdebug.h>
+#include "private/qsgcontext_p.h"
+
+#include <QtGui/qgraphicsitem.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicseffect.h>
+#include <qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+#include "qdeclarativepixmapcache_p.h"
+#include <QtScript/QScriptEngine>
+
+QT_BEGIN_NAMESPACE
+
+static const double Q_PI = 3.14159265358979323846; // pi
+template <class T>
+void memcpy_vector(QVector<T>* dst, const QVector<T>& src)
+{
+ int pos = dst->size();
+ dst->resize(pos + src.size());
+ memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size());
+}
+
+template <class T>
+void copy_vector(QVector<T>* dst, const QVector<T>& src)
+{
+ int pos = dst->size();
+ dst->resize(pos + src.size());
+ for (int i = 0; i < src.size(); i++) {
+ (*dst)[pos + i] = src[i];
+ }
+}
+
+// Note, this is exported but in a private header as qtopengl depends on it.
+// But it really should be considered private API
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+
+#define DEGREES(t) ((t) * 180.0 / Q_PI)
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
+
+static inline int extractInt(const char **name)
+{
+ int result = 0;
+ bool negative = false;
+
+ //eat leading whitespace
+ while (isspace(*name[0]))
+ ++*name;
+
+ if (*name[0] == '-') {
+ ++*name;
+ negative = true;
+ } /*else if (name[0] == '+')
+ ++name; //ignore*/
+
+ //construct number
+ while (isdigit(*name[0])) {
+ result = result * 10 + (*name[0] - '0');
+ ++*name;
+ }
+ if (negative)
+ result = -result;
+
+ //handle optional percentage
+ if (*name[0] == '%')
+ result *= qreal(255)/100; //### floor or round?
+
+ //eat trailing whitespace
+ while (isspace(*name[0]))
+ ++*name;
+
+ return result;
+}
+
+static bool qt_get_rgb(const QString &string, QRgb *rgb)
+{
+ const char *name = string.toLatin1().constData();
+ int len = qstrlen(name);
+
+ if (len < 5)
+ return false;
+
+ bool handleAlpha = false;
+
+ if (name[0] != 'r')
+ return false;
+ if (name[1] != 'g')
+ return false;
+ if (name[2] != 'b')
+ return false;
+ if (name[3] == 'a') {
+ handleAlpha = true;
+ if(name[3] != '(')
+ return false;
+ } else if (name[3] != '(')
+ return false;
+
+ name += 4;
+
+ int r, g, b, a = 1;
+ int result;
+
+ //red
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ r = result;
+ ++name;
+ } else
+ return false;
+
+ //green
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ g = result;
+ ++name;
+ } else
+ return false;
+
+ char nextChar = handleAlpha ? ',' : ')';
+
+ //blue
+ result = extractInt(&name);
+ if (name[0] == nextChar) {
+ b = result;
+ ++name;
+ } else
+ return false;
+
+ //alpha
+ if (handleAlpha) {
+ result = extractInt(&name);
+ if (name[0] == ')') {
+ a = result * 255; //map 0-1 to 0-255
+ ++name;
+ } else
+ return false;
+ }
+
+ if (name[0] != '\0')
+ return false;
+
+ *rgb = qRgba(qClamp(r,0,255), qClamp(g,0,255), qClamp(b,0,255), qClamp(a,0,255));
+ return true;
+}
+
+//### unify with qt_get_rgb?
+static bool qt_get_hsl(const QString &string, QColor *color)
+{
+ const char *name = string.toLatin1().constData();
+ int len = qstrlen(name);
+
+ if (len < 5)
+ return false;
+
+ bool handleAlpha = false;
+
+ if (name[0] != 'h')
+ return false;
+ if (name[1] != 's')
+ return false;
+ if (name[2] != 'l')
+ return false;
+ if (name[3] == 'a') {
+ handleAlpha = true;
+ if(name[3] != '(')
+ return false;
+ } else if (name[3] != '(')
+ return false;
+
+ name += 4;
+
+ int h, s, l, a = 1;
+ int result;
+
+ //hue
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ h = result;
+ ++name;
+ } else
+ return false;
+
+ //saturation
+ result = extractInt(&name);
+ if (name[0] == ',') {
+ s = result;
+ ++name;
+ } else
+ return false;
+
+ char nextChar = handleAlpha ? ',' : ')';
+
+ //lightness
+ result = extractInt(&name);
+ if (name[0] == nextChar) {
+ l = result;
+ ++name;
+ } else
+ return false;
+
+ //alpha
+ if (handleAlpha) {
+ result = extractInt(&name);
+ if (name[0] == ')') {
+ a = result * 255; //map 0-1 to 0-255
+ ++name;
+ } else
+ return false;
+ }
+
+ if (name[0] != '\0')
+ return false;
+
+ *color = QColor::fromHsl(qClamp(h,0,255), qClamp(s,0,255), qClamp(l,0,255), qClamp(a,0,255));
+ return true;
+}
+
+//### optimize further
+QColor colorFromString(const QString &name)
+{
+ if (name.startsWith(QLatin1String("rgb"))) {
+ QRgb rgb;
+ if (qt_get_rgb(name, &rgb))
+ return QColor(rgb);
+ } else if (name.startsWith(QLatin1String("hsl"))) {
+ QColor color;
+ if (qt_get_hsl(name, &color))
+ return color;
+ }
+
+ return QColor(name);
+}
+
+
+static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
+{
+ if (compositeOperator == QLatin1String("source-over")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("source-out")) {
+ return QPainter::CompositionMode_SourceOut;
+ } else if (compositeOperator == QLatin1String("source-in")) {
+ return QPainter::CompositionMode_SourceIn;
+ } else if (compositeOperator == QLatin1String("source-atop")) {
+ return QPainter::CompositionMode_SourceAtop;
+ } else if (compositeOperator == QLatin1String("destination-atop")) {
+ return QPainter::CompositionMode_DestinationAtop;
+ } else if (compositeOperator == QLatin1String("destination-in")) {
+ return QPainter::CompositionMode_DestinationIn;
+ } else if (compositeOperator == QLatin1String("destination-out")) {
+ return QPainter::CompositionMode_DestinationOut;
+ } else if (compositeOperator == QLatin1String("destination-over")) {
+ return QPainter::CompositionMode_DestinationOver;
+ } else if (compositeOperator == QLatin1String("darker")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("lighter")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("copy")) {
+ return QPainter::CompositionMode_Source;
+ } else if (compositeOperator == QLatin1String("xor")) {
+ return QPainter::CompositionMode_Xor;
+ }
+
+ return QPainter::CompositionMode_SourceOver;
+}
+
+static QString compositeOperatorToString(QPainter::CompositionMode op)
+{
+ switch (op) {
+ case QPainter::CompositionMode_SourceOver:
+ return QLatin1String("source-over");
+ case QPainter::CompositionMode_DestinationOver:
+ return QLatin1String("destination-over");
+ case QPainter::CompositionMode_Clear:
+ return QLatin1String("clear");
+ case QPainter::CompositionMode_Source:
+ return QLatin1String("source");
+ case QPainter::CompositionMode_Destination:
+ return QLatin1String("destination");
+ case QPainter::CompositionMode_SourceIn:
+ return QLatin1String("source-in");
+ case QPainter::CompositionMode_DestinationIn:
+ return QLatin1String("destination-in");
+ case QPainter::CompositionMode_SourceOut:
+ return QLatin1String("source-out");
+ case QPainter::CompositionMode_DestinationOut:
+ return QLatin1String("destination-out");
+ case QPainter::CompositionMode_SourceAtop:
+ return QLatin1String("source-atop");
+ case QPainter::CompositionMode_DestinationAtop:
+ return QLatin1String("destination-atop");
+ case QPainter::CompositionMode_Xor:
+ return QLatin1String("xor");
+ case QPainter::CompositionMode_Plus:
+ return QLatin1String("plus");
+ case QPainter::CompositionMode_Multiply:
+ return QLatin1String("multiply");
+ case QPainter::CompositionMode_Screen:
+ return QLatin1String("screen");
+ case QPainter::CompositionMode_Overlay:
+ return QLatin1String("overlay");
+ case QPainter::CompositionMode_Darken:
+ return QLatin1String("darken");
+ case QPainter::CompositionMode_Lighten:
+ return QLatin1String("lighten");
+ case QPainter::CompositionMode_ColorDodge:
+ return QLatin1String("color-dodge");
+ case QPainter::CompositionMode_ColorBurn:
+ return QLatin1String("color-burn");
+ case QPainter::CompositionMode_HardLight:
+ return QLatin1String("hard-light");
+ case QPainter::CompositionMode_SoftLight:
+ return QLatin1String("soft-light");
+ case QPainter::CompositionMode_Difference:
+ return QLatin1String("difference");
+ case QPainter::CompositionMode_Exclusion:
+ return QLatin1String("exclusion");
+ default:
+ break;
+ }
+ return QString();
+}
+
+bool QSGContext2DPrivate::hasShadow() const
+{
+ return state.shadowColor.isValid()
+ && state.shadowColor.alpha()
+ && (state.shadowBlur || state.shadowOffsetX || state.shadowOffsetY);
+}
+
+void QSGContext2DPrivate::clearShadow()
+{
+ state.shadowOffsetX = 0;
+ state.shadowOffsetY = 0;
+ state.shadowBlur = 0;
+ state.shadowColor = QColor();
+}
+
+QImage QSGContext2DPrivate::makeShadowImage(const QPixmap& pix)
+{
+ QImage shadowImg(pix.width() + state.shadowBlur * 2 + qAbs(state.shadowOffsetX),
+ pix.height() + state.shadowBlur *2 + qAbs(state.shadowOffsetY),
+ QImage::Format_ARGB32);
+ shadowImg.fill(0);
+ QPainter tmpPainter(&shadowImg);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
+ qreal shadowX = state.shadowOffsetX > 0? state.shadowOffsetX : 0;
+ qreal shadowY = state.shadowOffsetY > 0? state.shadowOffsetY : 0;
+
+ tmpPainter.drawPixmap(shadowX, shadowY, pix);
+ tmpPainter.end();
+
+ // blur the alpha channel
+ if (state.shadowBlur > 0) {
+ QImage blurred(shadowImg.size(), QImage::Format_ARGB32);
+ blurred.fill(0);
+ QPainter blurPainter(&blurred);
+ qt_blurImage(&blurPainter, shadowImg, state.shadowBlur, false, true);
+ blurPainter.end();
+ shadowImg = blurred;
+ }
+
+ // blacken the image with shadow color...
+ tmpPainter.begin(&shadowImg);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ tmpPainter.fillRect(shadowImg.rect(), state.shadowColor);
+ tmpPainter.end();
+ return shadowImg;
+}
+
+void QSGContext2DPrivate::fillRectShadow(QPainter* p, QRectF shadowRect)
+{
+ QRectF r = shadowRect;
+ r.moveTo(0, 0);
+
+ QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32);
+ QPainter tp;
+ tp.begin(&shadowImage);
+ tp.fillRect(r, p->brush());
+ tp.end();
+ shadowImage = makeShadowImage(QPixmap::fromImage(shadowImage));
+
+ qreal dx = shadowRect.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+ qreal dy = shadowRect.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+
+ p->drawImage(dx, dy, shadowImage);
+ p->fillRect(shadowRect, p->brush());
+}
+
+void QSGContext2DPrivate::fillShadowPath(QPainter* p, const QPainterPath& path)
+{
+ QRectF r = path.boundingRect();
+ QImage img(r.size().width() + r.left() + 1,
+ r.size().height() + r.top() + 1,
+ QImage::Format_ARGB32);
+ img.fill(0);
+ QPainter tp(&img);
+ tp.fillPath(path.translated(0, 0), p->brush());
+ tp.end();
+
+ QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
+ qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+ qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+
+ p->drawImage(dx, dy, shadowImage);
+ p->fillPath(path, p->brush());
+}
+
+void QSGContext2DPrivate::strokeShadowPath(QPainter* p, const QPainterPath& path)
+{
+ QRectF r = path.boundingRect();
+ QImage img(r.size().width() + r.left() + 1,
+ r.size().height() + r.top() + 1,
+ QImage::Format_ARGB32);
+ img.fill(0);
+ QPainter tp(&img);
+ tp.strokePath(path, p->pen());
+ tp.end();
+
+ QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
+ qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
+ qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+ p->drawImage(dx, dy, shadowImage);
+ p->strokePath(path, p->pen());
+}
+
+void QSGContext2DPrivate::clear()
+{
+ clearRect(0, 0, size.width(), size.height());
+}
+
+void QSGContext2DPrivate::reset()
+{
+ stateStack.clear();
+ state.matrix = QMatrix();
+ state.clipPath = QPainterPath();
+ state.strokeStyle = Qt::black;
+ state.fillStyle = Qt::black;
+ state.globalAlpha = 1.0;
+ state.lineWidth = 1;
+ state.lineCap = Qt::FlatCap;
+ state.lineJoin = Qt::MiterJoin;
+ state.miterLimit = 10;
+ state.shadowOffsetX = 0;
+ state.shadowOffsetY = 0;
+ state.shadowBlur = 0;
+ state.shadowColor = qRgba(0, 0, 0, 0);
+ state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
+ state.font = QFont();
+ state.textAlign = QSGContext2D::Start;
+ state.textBaseline = QSGContext2D::Alphabetic;
+ clear();
+}
+
+
+void QSGContext2DPrivate::updateMatrix(const QMatrix& m)
+{
+ commands.push_back(QSGContext2D::UpdateMatrix);
+ matrixes.push_back(m);
+}
+
+
+
+
+void QSGContext2DPrivate::save()
+{
+ stateStack.push(state);
+}
+
+void QSGContext2DPrivate::restore()
+{
+ if (!stateStack.isEmpty()) {
+ bool update = false;
+ QSGContext2D::State s = stateStack.pop();
+ if (state.matrix != s.matrix) {
+ updateMatrix(s.matrix);
+ update = true;
+ }
+
+ if (s.pen != state.pen) {
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(s.pen);
+ update = true;
+ }
+
+ if (s.globalAlpha != state.globalAlpha) {
+ commands.push_back(QSGContext2D::GlobalAlpha);
+ reals.push_back(s.globalAlpha);
+ update = true;
+ }
+
+ if (s.globalCompositeOperation != state.globalCompositeOperation) {
+ commands.push_back(QSGContext2D::GlobalCompositeOperation);
+ ints.push_back(s.globalCompositeOperation);
+ update = true;
+ }
+
+ if (s.font != state.font) {
+ commands.push_back(QSGContext2D::Font);
+ fonts.push_back(s.font);
+ update = true;
+ }
+
+ if (s.fillStyle != state.fillStyle) {
+ commands.push_back(QSGContext2D::FillStyle);
+ brushes.push_back(s.fillStyle);
+ update = true;
+ }
+
+ if (s.clipPath != state.clipPath) {
+ //commands.push_back(QSGContext2D::ClipPath);
+ update = true;
+ }
+
+ if (s.textAlign != state.textAlign) {
+ commands.push_back(QSGContext2D::TextAlign);
+ update = true;
+ }
+
+ if (s.textBaseline != state.textBaseline) {
+ commands.push_back(QSGContext2D::TextBaseline);
+ update = true;
+ }
+
+ if (s.shadowBlur != state.shadowBlur
+ || s.shadowColor != state.shadowColor
+ || s.shadowOffsetX != state.shadowOffsetX
+ || s.shadowOffsetY != state.shadowOffsetY) {
+ update = true;
+ }
+
+ if (update)
+ state = s;
+ }
+}
+
+void QSGContext2DPrivate::scale(qreal x, qreal y)
+{
+ state.matrix.scale(x, y);
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::rotate(qreal angle)
+{
+ state.matrix.rotate(DEGREES(angle));
+ updateMatrix(state.matrix);
+}
+
+
+void QSGContext2DPrivate::translate(qreal x, qreal y)
+{
+ state.matrix.translate(x, y);
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::transform(
+ qreal m11, qreal m12,
+ qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix matrix(m11, m12, m21, m22, dx, dy);
+ state.matrix *= matrix;
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::setTransform(
+ qreal m11, qreal m12,
+ qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ QMatrix matrix(m11, m12, m21, m22, dx, dy);
+ state.matrix = matrix;
+ updateMatrix(state.matrix);
+}
+
+void QSGContext2DPrivate::clearRect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ commands.push_back(QSGContext2D::ClearRect);
+ reals.push_back(x);
+ reals.push_back(y);
+ reals.push_back(w);
+ reals.push_back(h);
+}
+
+void QSGContext2DPrivate::fillRect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ commands.push_back(QSGContext2D::FillRect);
+ reals.push_back(x);
+ reals.push_back(y);
+ reals.push_back(w);
+ reals.push_back(h);
+}
+
+void QSGContext2DPrivate::strokeRect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addRect(x, y, w, h);
+ commands.push_back(QSGContext2D::Stroke);
+ pathes.push_back(path);
+}
+
+void QSGContext2DPrivate::beginPath()
+{
+ path = QPainterPath();
+}
+
+void QSGContext2DPrivate::closePath()
+{
+ path.closeSubpath();
+}
+
+void QSGContext2DPrivate::moveTo( qreal x, qreal y)
+{
+ path.moveTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::lineTo( qreal x, qreal y)
+{
+ path.lineTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::quadraticCurveTo(qreal cpx, qreal cpy,
+ qreal x, qreal y)
+{
+ path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
+ state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y,
+ qreal x, qreal y)
+{
+ path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
+ state.matrix.map(QPointF(cp2x, cp2y)),
+ state.matrix.map(QPointF(x, y)));
+}
+
+void QSGContext2DPrivate::arcTo(qreal x1, qreal y1,
+ qreal x2, qreal y2,
+ qreal radius)
+{
+ QPointF st = state.matrix.map(QPoint(x1, y1));
+ QPointF end = state.matrix.map(QPoint(x2, y2));
+
+ path.arcTo(st.x(), st.y(),
+ end.x()-st.x(), end.y()-st.y(),
+ radius, 90);
+}
+
+void QSGContext2DPrivate::rect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addRect(QRectF(x, y, w, h));
+ path.addPath(state.matrix.map(path));
+}
+
+void QSGContext2DPrivate::arc(qreal xc,
+ qreal yc,
+ qreal radius,
+ qreal sar,
+ qreal ear,
+ bool antiClockWise)
+{
+ QPainterPath path;
+
+ //### HACK
+
+ // In Qt we don't switch the coordinate system for degrees
+ // and still use the 0,0 as bottom left for degrees so we need
+ // to switch
+ sar = -sar;
+ ear = -ear;
+ antiClockWise = !antiClockWise;
+ //end hack
+
+ float sa = DEGREES(sar);
+ float ea = DEGREES(ear);
+
+ double span = 0;
+
+ double xs = xc - radius;
+ double ys = yc - radius;
+ double width = radius*2;
+ double height = radius*2;
+
+ if (!antiClockWise && (ea < sa)) {
+ span += 360;
+ } else if (antiClockWise && (sa < ea)) {
+ span -= 360;
+ }
+
+ //### this is also due to switched coordinate system
+ // we would end up with a 0 span instead of 360
+ if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
+ qFuzzyCompare(qAbs(span), 360))) {
+ span += ea - sa;
+ }
+
+ path.moveTo(QPointF(xc + radius * qCos(sar),
+ yc - radius * qSin(sar)));
+
+ path.arcTo(xs, ys, width, height, sa, span);
+
+ path.addPath(state.matrix.map(path));
+}
+
+void QSGContext2DPrivate::fill()
+{
+ commands.push_back(QSGContext2D::Fill);
+ pathes.push_back(path);
+}
+
+void QSGContext2DPrivate::stroke()
+{
+ commands.push_back(QSGContext2D::Stroke);
+ pathes.push_back(path);
+// painter->setMatrix(state.matrix, false);
+// QPainterPath tmp = state.matrix.inverted().map(path); //why?
+// painter->strokePath(tmp, painter->pen());
+}
+
+void QSGContext2DPrivate::clip()
+{
+ state.clipPath = path;
+ pathes.push_back(state.clipPath);
+ commands.push_back(QSGContext2D::Clip);
+}
+
+void QSGContext2DPrivate::setGlobalAlpha( qreal alpha)
+{
+ state.globalAlpha = alpha;
+ commands.push_back(QSGContext2D::GlobalAlpha);
+ reals.push_back(state.globalAlpha);
+}
+
+void QSGContext2DPrivate::setGlobalCompositeOperation( const QString &op)
+{
+ state.globalCompositeOperation = compositeOperatorFromString(op);
+ commands.push_back(QSGContext2D::GlobalCompositeOperation);
+ ints.push_back(state.globalCompositeOperation);
+}
+
+void QSGContext2DPrivate::setStrokeStyle( const QVariant &style)
+{
+ QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
+ QBrush b;
+ if (gradient) {
+ b = gradient->value();
+ } else {
+ b = colorFromString(style.toString());
+ }
+
+ if (state.strokeStyle != b) {
+ state.strokeStyle = b;
+ state.pen.setBrush(state.strokeStyle);
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+void QSGContext2DPrivate::setStrokeColor(const QColor& color)
+{
+ if (state.strokeStyle != color) {
+ state.strokeStyle = color;
+ commands.push_back(QSGContext2D::UpdatePen);
+ QPen pen;
+ pen.setBrush(state.strokeStyle);
+ pens.push_back(pen);
+ }
+}
+
+void QSGContext2DPrivate::setFillColor(const QColor& color)
+{
+ if (state.fillStyle != color) {
+ state.fillStyle = color;
+ commands.push_back(QSGContext2D::UpdateBrush);
+ brushes.push_back(state.fillStyle);
+ }
+}
+
+void QSGContext2DPrivate::setFillStyle( const QVariant &style)
+{
+ QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
+ QBrush b;
+ if (gradient) {
+ b = gradient->value();
+ } else {
+ b = colorFromString(style.toString());
+ }
+
+ if (state.fillStyle != b) {
+ state.fillStyle = b;
+ commands.push_back(QSGContext2D::UpdateBrush);
+ brushes.push_back(b);
+ }
+}
+
+
+void QSGContext2DPrivate::setLineWidth( qreal w)
+{
+ if (state.lineWidth != w) {
+ state.pen.setWidthF(w);
+ state.lineWidth = w;
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setLineCap( const QString& cap)
+{
+ Qt::PenCapStyle style;
+ if (cap == QLatin1String("round"))
+ style = Qt::RoundCap;
+ else if (cap == QLatin1String("square"))
+ style = Qt::SquareCap;
+ else //if (capString == "butt")
+ style = Qt::FlatCap;
+
+
+ if (state.lineCap != style) {
+ state.pen.setCapStyle(style);
+ state.lineCap = style;
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setLineJoin( const QString& join)
+{
+ Qt::PenJoinStyle style;
+ if (join == QLatin1String("round"))
+ style = Qt::RoundJoin;
+ else if (join == QLatin1String("bevel"))
+ style = Qt::BevelJoin;
+ else //if (joinString == "miter")
+ style = Qt::MiterJoin;
+ if (state.lineJoin != style) {
+ state.lineJoin = style;
+ state.pen.setJoinStyle(style);
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setMiterLimit( qreal limit)
+{
+ if (state.miterLimit != limit) {
+ state.pen.setMiterLimit(limit);
+ state.miterLimit = limit;
+ commands.push_back(QSGContext2D::UpdatePen);
+ pens.push_back(state.pen);
+ }
+}
+
+void QSGContext2DPrivate::setShadowOffsetX( qreal x)
+{
+ if (state.shadowOffsetX != x) {
+ state.shadowOffsetX = x;
+ commands.push_back(QSGContext2D::ShadowOffsetX);
+ reals.push_back(x);
+ }
+}
+
+void QSGContext2DPrivate::setShadowOffsetY( qreal y)
+{
+ if (state.shadowOffsetY != y) {
+ state.shadowOffsetY = y;
+ commands.push_back(QSGContext2D::ShadowOffsetY);
+ reals.push_back(y);
+ }
+}
+
+void QSGContext2DPrivate::setShadowBlur( qreal b)
+{
+ if (state.shadowBlur != b) {
+ state.shadowBlur = b;
+ commands.push_back(QSGContext2D::ShadowBlur);
+ reals.push_back(b);
+ }
+}
+
+void QSGContext2DPrivate::setShadowColor( const QString& color)
+{
+ QColor c = colorFromString(color);
+ if (state.shadowColor != c) {
+ state.shadowColor = c;
+ commands.push_back(QSGContext2D::ShadowColor);
+ colors.push_back(c);
+ }
+}
+
+void QSGContext2DPrivate::setFont( const QString& fontString)
+{
+ QFont font;
+ // ### this is simplified and incomplete
+ // ### TODO:get code from Qt webkit
+ QStringList tokens = fontString.split(QLatin1String(" "));
+ foreach (const QString &token, tokens) {
+ if (token == QLatin1String("italic"))
+ font.setItalic(true);
+ else if (token == QLatin1String("bold"))
+ font.setBold(true);
+ else if (token.endsWith(QLatin1String("px"))) {
+ QString number = token;
+ number.remove(QLatin1String("px"));
+ font.setPointSizeF(number.trimmed().toFloat());
+ } else
+ font.setFamily(token);
+ }
+
+ if (state.font != font) {
+ state.font = font;
+ commands.push_back(QSGContext2D::Font);
+ fonts.push_back(font);
+ }
+}
+
+void QSGContext2DPrivate::setTextBaseline( const QString& baseline)
+{
+ QSGContext2D::TextBaseLineType tbl;
+ if (baseline==QLatin1String("alphabetic"))
+ tbl = QSGContext2D::Alphabetic;
+ else if (baseline == QLatin1String("hanging"))
+ tbl = QSGContext2D::Hanging;
+ else if (baseline == QLatin1String("top"))
+ tbl = QSGContext2D::Top;
+ else if (baseline == QLatin1String("bottom"))
+ tbl = QSGContext2D::Bottom;
+ else if (baseline == QLatin1String("middle"))
+ tbl = QSGContext2D::Middle;
+ else {
+ tbl = QSGContext2D::Alphabetic;
+ Q_Q(QSGContext2D);
+ qmlInfo(q) << "QSGContext2D: invalid baseline:" << baseline;
+ }
+ if (state.textBaseline != tbl) {
+ state.textBaseline = tbl;
+ commands.push_back(QSGContext2D::TextBaseline);
+ ints.push_back(tbl);
+ }
+}
+
+void QSGContext2DPrivate::setTextAlign(const QString& align)
+{
+ QSGContext2D::TextAlignType ta;
+ if (align==QLatin1String("start"))
+ ta = QSGContext2D::Start;
+ else if (align == QLatin1String("end"))
+ ta = QSGContext2D::End;
+ else if (align == QLatin1String("left"))
+ ta = QSGContext2D::Left;
+ else if (align == QLatin1String("right"))
+ ta = QSGContext2D::Right;
+ else if (align == QLatin1String("center"))
+ ta = QSGContext2D::Center;
+ else {
+ ta = QSGContext2D::Start;
+ Q_Q(QSGContext2D);
+ qmlInfo(q) << "QSGContext2D: invalid text align:" << align;
+ }
+ if (state.textAlign != ta) {
+ state.textAlign = ta;
+ commands.push_back(QSGContext2D::TextAlign);
+ ints.push_back(ta);
+ }
+}
+
+void QSGContext2DPrivate::fillText(const QString& text, qreal x, qreal y)
+{
+ commands.push_back(QSGContext2D::FillText);
+ strings.push_back(text);
+ reals.push_back(x);
+ reals.push_back(y);
+ ints.push_back(state.textAlign);
+ ints.push_back(state.textBaseline);
+}
+
+
+void QSGContext2DPrivate::strokeText( const QString& text, qreal x, qreal y)
+{
+ commands.push_back(QSGContext2D::StrokeText);
+ strings.push_back(text);
+ reals.push_back(x);
+ reals.push_back(y);
+ ints.push_back(state.textAlign);
+ ints.push_back(state.textBaseline);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy)
+{
+ commands.push_back(QSGContext2D::DrawImage1);
+ strings.push_back(url);
+ reals.push_back(dx);
+ reals.push_back(dy);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+ commands.push_back(QSGContext2D::DrawImage2);
+ strings.push_back(url);
+ reals.push_back(dx);
+ reals.push_back(dy);
+ reals.push_back(dw);
+ reals.push_back(dh);
+}
+
+void QSGContext2DPrivate::drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+ commands.push_back(QSGContext2D::DrawImage3);
+ strings.push_back(url);
+ reals.push_back(sx);
+ reals.push_back(sy);
+ reals.push_back(sw);
+ reals.push_back(sh);
+ reals.push_back(dx);
+ reals.push_back(dy);
+ reals.push_back(dw);
+ reals.push_back(dh);
+}
+
+QList<int> QSGContext2DPrivate::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+ Q_Q(QSGContext2D);
+ waitingForPainting = true;
+ commands.push_back(QSGContext2D::GetImageData);
+ reals.push_back(sx);
+ reals.push_back(sy);
+ reals.push_back(sw);
+ reals.push_back(sh);
+ q->sync();
+ return imageData;
+}
+
+void QSGContext2DPrivate::putImageData(const QVariantList& imageData, qreal dx, qreal dy, qreal w, qreal h)
+{
+ QImage image = cachedImage.copy(dx, dy, w, h);
+ uchar* data = image.bits();
+ int i = 0;
+ while(i< imageData.size() && i < image.byteCount()) {
+ //the stored order in QImage:BGRA
+ //the stored order in Canvas:RGBA
+ *(data+i) = imageData[i+2].toInt();//B
+ *(data+i+1) = imageData[i+1].toInt();//G
+ *(data+i+2) = imageData[i].toInt();//R
+ *(data+i+3) = imageData[i+3].toInt();//A
+ i+=4;
+ }
+ commands.push_back(QSGContext2D::PutImageData);
+ images.push_back(image);
+ reals.push_back(dx);
+ reals.push_back(dy);
+}
+
+void QSGContext2D::save()
+{
+ Q_D(QSGContext2D);
+ d->save();
+}
+
+
+void QSGContext2D::restore()
+{
+ Q_D(QSGContext2D);
+ d->restore();
+}
+
+
+void QSGContext2D::scale(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->scale(x, y);
+}
+
+
+void QSGContext2D::rotate(qreal angle)
+{
+ Q_D(QSGContext2D);
+ d->rotate(angle);
+}
+
+
+void QSGContext2D::translate(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->translate(x, y);
+}
+
+void QSGContext2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ Q_D(QSGContext2D);
+ d->transform(m11, m12, m21, m22, dx, dy);
+}
+
+
+void QSGContext2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy)
+{
+ Q_D(QSGContext2D);
+ d->setTransform(m11, m12, m21, m22, dx, dy);
+}
+
+QString QSGContext2D::globalCompositeOperation() const
+{
+ Q_D(const QSGContext2D);
+ return compositeOperatorToString(d->state.globalCompositeOperation);
+}
+
+void QSGContext2D::setGlobalCompositeOperation(const QString &op)
+{
+ Q_D(QSGContext2D);
+ d->setGlobalCompositeOperation(op);
+}
+
+QVariant QSGContext2D::strokeStyle() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.strokeStyle;
+}
+
+void QSGContext2D::setStrokeStyle(const QVariant &style)
+{
+ Q_D(QSGContext2D);
+ d->setStrokeStyle(style);
+}
+
+QVariant QSGContext2D::fillStyle() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.fillStyle;
+}
+
+QColor QSGContext2D::strokeColor() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.strokeStyle.color();
+}
+
+QColor QSGContext2D::fillColor() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.fillStyle.color();
+}
+
+void QSGContext2D::setFillStyle(const QVariant &style)
+{
+ Q_D(QSGContext2D);
+ d->setFillStyle(style);
+}
+void QSGContext2D::setStrokeColor(const QColor& color)
+{
+ Q_D(QSGContext2D);
+ d->setStrokeColor(color);
+}
+
+void QSGContext2D::setFillColor(const QColor& color)
+{
+ Q_D(QSGContext2D);
+ d->setFillColor(color);
+}
+
+qreal QSGContext2D::globalAlpha() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.globalAlpha;
+}
+
+void QSGContext2D::setGlobalAlpha(qreal alpha)
+{
+ Q_D(QSGContext2D);
+ d->setGlobalAlpha(alpha);
+}
+
+QSGImage *QSGContext2D::createImage(const QString &url)
+{
+ Q_D(QSGContext2D);
+//### cache image
+ QSGImage* img = new QSGImage(d->canvas);
+ img->setSource(QUrl(url));
+ return img;
+}
+
+QSGCanvasGradient *QSGContext2D::createLinearGradient(qreal x0, qreal y0,
+ qreal x1, qreal y1)
+{
+ QLinearGradient g(x0, y0, x1, y1);
+ return new QSGCanvasGradient(g);
+}
+
+
+QSGCanvasGradient *QSGContext2D::createRadialGradient(qreal x0, qreal y0,
+ qreal r0, qreal x1,
+ qreal y1, qreal r1)
+{
+ QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ return new QSGCanvasGradient(g);
+}
+
+qreal QSGContext2D::lineWidth() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.lineWidth;
+}
+
+void QSGContext2D::setLineWidth(qreal w)
+{
+ Q_D(QSGContext2D);
+ d->setLineWidth(w);
+}
+
+QString QSGContext2D::lineCap() const
+{
+ Q_D(const QSGContext2D);
+ switch(d->state.lineCap) {
+ case Qt::RoundCap:
+ return QLatin1String("round");
+ case Qt::FlatCap:
+ return QLatin1String("butt");
+ case Qt::SquareCap:
+ return QLatin1String("square");
+ default:
+ break;
+ }
+ return QLatin1String("");
+}
+
+void QSGContext2D::setLineCap(const QString &capString)
+{
+ Q_D(QSGContext2D);
+ d->setLineCap(capString);
+}
+
+QString QSGContext2D::lineJoin() const
+{
+ Q_D(const QSGContext2D);
+ switch (d->state.lineJoin) {
+ case Qt::RoundJoin:
+ return QLatin1String("round");
+ case Qt::BevelJoin:
+ return QLatin1String("bevel");
+ case Qt::MiterJoin:
+ return QLatin1String("miter");
+ default:
+ break;
+ }
+ return QLatin1String("");
+}
+
+void QSGContext2D::setLineJoin(const QString &joinString)
+{
+ Q_D(QSGContext2D);
+ d->setLineJoin(joinString);
+}
+
+qreal QSGContext2D::miterLimit() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.miterLimit;
+}
+
+void QSGContext2D::setMiterLimit(qreal m)
+{
+ Q_D(QSGContext2D);
+ d->setMiterLimit(m);
+}
+
+void QSGContext2D::setShadowOffsetX(qreal x)
+{
+ Q_D(QSGContext2D);
+ d->setShadowOffsetX(x);
+}
+
+void QSGContext2D::setShadowOffsetY(qreal y)
+{
+ Q_D(QSGContext2D);
+ d->setShadowOffsetY(y);
+}
+
+void QSGContext2D::setShadowBlur(qreal b)
+{
+ Q_D(QSGContext2D);
+ d->setShadowBlur(b);
+}
+
+void QSGContext2D::setShadowColor(const QString &str)
+{
+ Q_D(QSGContext2D);
+ d->setShadowColor(str);
+}
+
+QString QSGContext2D::textBaseline() const
+{
+ Q_D(const QSGContext2D);
+ switch(d->state.textBaseline) {
+ case QSGContext2D::Alphabetic:
+ return QLatin1String("alphabetic");
+ case QSGContext2D::Hanging:
+ return QLatin1String("hanging");
+ case QSGContext2D::Top:
+ return QLatin1String("top");
+ case QSGContext2D::Bottom:
+ return QLatin1String("bottom");
+ case QSGContext2D::Middle:
+ return QLatin1String("middle");
+ default:
+ break;
+ }
+ return QLatin1String("alphabetic");
+}
+
+void QSGContext2D::setTextBaseline(const QString &baseline)
+{
+ Q_D(QSGContext2D);
+ d->setTextBaseline(baseline);
+}
+
+QString QSGContext2D::textAlign() const
+{
+ Q_D(const QSGContext2D);
+ switch(d->state.textAlign) {
+ case QSGContext2D::Start:
+ return QLatin1String("start");
+ case QSGContext2D::End:
+ return QLatin1String("end");
+ case QSGContext2D::Left:
+ return QLatin1String("left");
+ case QSGContext2D::Right:
+ return QLatin1String("right");
+ case QSGContext2D::Center:
+ return QLatin1String("center");
+ default:
+ break;
+ }
+ return QLatin1String("start");
+}
+
+void QSGContext2D::setTextAlign(const QString &align)
+{
+ Q_D(QSGContext2D);
+ d->setTextAlign(align);
+
+ d->commands.push_back(QSGContext2D::TextAlign);
+ d->ints.push_back(d->state.textAlign);
+}
+
+void QSGContext2D::setFont(const QString &fontString)
+{
+ Q_D(QSGContext2D);
+ d->setFont(fontString);
+}
+
+QString QSGContext2D::font() const
+{
+ //### TODO
+ Q_D(const QSGContext2D);
+ return d->state.font.toString();
+}
+
+qreal QSGContext2D::shadowOffsetX() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowOffsetX;
+}
+
+qreal QSGContext2D::shadowOffsetY() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowOffsetY;
+}
+
+
+qreal QSGContext2D::shadowBlur() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowBlur;
+}
+
+
+QString QSGContext2D::shadowColor() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.shadowColor.name();
+}
+
+
+void QSGContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->clearRect(x, y, w, h);
+}
+
+void QSGContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->fillRect(x, y, w, h);
+}
+
+int QSGContext2DPrivate::baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics)
+{
+ int offset = 0;
+ switch (value) {
+ case QSGContext2D::Top:
+ break;
+ case QSGContext2D::Alphabetic:
+ case QSGContext2D::Middle:
+ case QSGContext2D::Hanging:
+ offset = metrics.ascent();
+ break;
+ case QSGContext2D::Bottom:
+ offset = metrics.height();
+ break;
+ }
+ return offset;
+}
+
+int QSGContext2DPrivate::textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
+{
+ int offset = 0;
+ if (value == QSGContext2D::Start)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right;
+ else if (value == QSGContext2D::End)
+ value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left;
+ switch (value) {
+ case QSGContext2D::Center:
+ offset = metrics.width(text)/2;
+ break;
+ case QSGContext2D::Right:
+ offset = metrics.width(text);
+ case QSGContext2D::Left:
+ default:
+ break;
+ }
+ return offset;
+}
+
+void QSGContext2D::fillText(const QString &text, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->fillText(text, x, y);
+}
+
+void QSGContext2D::strokeText(const QString &text, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->strokeText(text, x, y);
+}
+
+void QSGContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->strokeRect(x, y, w, h);
+}
+
+void QSGContext2D::beginPath()
+{
+ Q_D(QSGContext2D);
+ d->beginPath();
+}
+
+
+void QSGContext2D::closePath()
+{
+ Q_D(QSGContext2D);
+ d->closePath();
+}
+
+
+void QSGContext2D::moveTo(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->moveTo(x, y);
+}
+
+
+void QSGContext2D::lineTo(qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->lineTo(x, y);
+}
+
+
+void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->quadraticCurveTo(cpx, cpy, x, y);
+}
+
+
+void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y)
+{
+ Q_D(QSGContext2D);
+ d->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+}
+
+
+void QSGContext2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
+{
+ Q_D(QSGContext2D);
+ d->arcTo(x1, y1, x2, y2, radius);
+}
+
+
+void QSGContext2D::rect(qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ d->rect(x, y, w, h);
+}
+
+void QSGContext2D::arc(qreal xc, qreal yc, qreal radius,
+ qreal sar, qreal ear,
+ bool anticlockwise)
+{
+ Q_D(QSGContext2D);
+ d->arc(xc, yc, radius, sar, ear, anticlockwise);
+}
+
+
+void QSGContext2D::fill()
+{
+ Q_D(QSGContext2D);
+ d->fill();
+}
+
+
+void QSGContext2D::stroke()
+{
+ Q_D(QSGContext2D);
+ d->stroke();
+}
+
+
+void QSGContext2D::clip()
+{
+ Q_D(QSGContext2D);
+ d->clip();
+}
+
+
+bool QSGContext2D::isPointInPath(qreal x, qreal y) const
+{
+ Q_D(const QSGContext2D);
+ return d->path.contains(QPointF(x, y));
+}
+
+
+QList<int> QSGContext2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
+{
+ Q_D(QSGContext2D);
+ return d->getImageData(sx, sy, sw, sh);
+}
+
+void QSGContext2D::putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h)
+{
+ Q_D(QSGContext2D);
+ return d->putImageData(imageData.toList(), x, y, w, h);
+}
+
+QSGContext2D::QSGContext2D(QObject *parent)
+ : QObject(*(new QSGContext2DPrivate()), parent)
+{
+ Q_D(QSGContext2D);
+ d->canvas = qobject_cast<QSGCanvasItem*>(parent);
+}
+
+QSGContext2D::QSGContext2D(QSGContext2D *orig, QSGContext2DWorkerAgent* agentData)
+ : QObject(*(new QSGContext2DPrivate()), 0)
+{
+ Q_D(QSGContext2D);
+ d->agent = 0;
+ d->agentData = agentData;
+ if (d->agentData) {
+ d->agentData->orig = orig;
+ }
+ d->canvas = qobject_cast<QSGCanvasItem*>(orig);
+}
+
+QSGContext2D::~QSGContext2D()
+{
+ Q_D(QSGContext2D);
+ if (d->agent) {
+ d->agentData->syncDone.wakeAll();
+ d->agent->release();
+ }
+}
+
+bool QSGContext2D::isDirty() const
+{
+ Q_D(const QSGContext2D);
+ return !d->commands.isEmpty();
+}
+
+QScriptValue QSGContext2D::scriptValue() const
+{
+ Q_D(const QSGContext2D);
+ return d->scriptValue;
+}
+
+void QSGContext2D::setScriptEngine(QScriptEngine *eng)
+{
+ Q_D(QSGContext2D);
+ if (d->scriptEngine != eng) {
+ d->scriptEngine = eng;
+// QScriptValue agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent"));
+// if (!agent.isValid()) {
+// d->scriptEngine->evaluate(QLatin1String(
+// "(function CanvasImageData(w, h, d) {"
+// " this.widht = w;"
+// " this.height = h;"
+// " this.data = d;"
+// " })"));
+// d->scriptEngine->evaluate(agentScript());
+// agent = d->scriptEngine->globalObject().property(QLatin1String("Context2DAgent"));
+// if (!agent.isValid()) {
+// qWarning() << "QSGContext2D:error when evaluating context2d script value!";
+// d->scriptValue = QScriptValue();
+// return;
+// }
+// }
+// QScriptValue o = d->scriptEngine->newQObject(this);
+// d->scriptValue = agent.construct(QScriptValueList() << o);
+ }
+}
+
+QScriptEngine *QSGContext2D::scriptEngine() const
+{
+ Q_D(const QSGContext2D);
+ return d->scriptEngine;
+}
+
+void QSGContext2D::addref()
+{
+ Q_D(QSGContext2D);
+ Q_ASSERT(d->agentData);
+ d->agentData->ref.ref();
+}
+
+void QSGContext2D::release()
+{
+ Q_D(QSGContext2D);
+ Q_ASSERT(d->agentData);
+ if (!d->agentData->ref.deref()) {
+ deleteLater();
+ }
+}
+
+
+bool QSGContext2D::inWorkerThread() const
+{
+ Q_D(const QSGContext2D);
+ return d->agentData != 0;
+}
+const QString& QSGContext2D::agentScript() const
+{
+ static QString script;
+ if (script.isEmpty()) {
+ script = QString::fromLatin1(
+ "function CanvasImageData(w, h, d) {"
+ " this.width = w;"
+ " this.height = h;"
+ " this.data = d;"
+ "}"
+ "function Context2DAgent(_ctx2d) {"
+ " this._ctx = _ctx2d;"
+ " this._fillColor = '#000000';"
+ " this._fillStyle = '#000000';"
+ " this._strokeColor = '#000000';"
+ " this._strokeStyle = '#000000';"
+ " this._globalCompositeOperation = \"source-over\";"
+ " this._commands = [];"
+ " this.createImageData = function() {"
+ " var d = null;"
+ " if (arguments.length == 1 && arguments[0] instanceof CanvasImageData) {"
+ " d = new CanvasImageData(arguments[0].width,"
+ " arguments[0].height,"
+ " new Array(arguments[0].width * arguments[0].height * 4));"
+ " } else if (arguments.length == 2) {"
+ " d = new CanvasImageData(arguments[0], arguments[1], new Array(arguments[0] * arguments[1] * 4));"
+ " }"
+ " if (d)"
+ " for (var i=0; i<d.data.length; i++)"
+ " d.data[i] = 255;"
+ " return d;"
+ " };"
+ " this.getImageData = function(sx, sy, sw, sh) {"
+ " var imageData = new CanvasImageData(sw, sh, this._ctx.getImageData(sx, sy, sw, sh));"
+ " return imageData;"
+ " };"
+ " this.sync = function() {"
+ " this._ctx.processCommands(this._commands);"
+ " this._commands.length = 0;"
+ " };");
+
+ script.append(QString::fromLatin1(
+ "this.save = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Save));
+
+ script.append(QString::fromLatin1(
+ "this.restore = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Restore));
+
+ script.append(QString::fromLatin1(
+ "this.scale = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(Scale));
+
+ script.append(QString::fromLatin1(
+ "this.createImage = function(url) {"
+ " return this._ctx.createImage(url);"
+ "};"));
+
+ script.append(QString::fromLatin1(
+ "this.rotate = function(x) {"
+ " this._commands.push([%1, x]);"
+ "};").arg(Rotate));
+
+ script.append(QString::fromLatin1(
+ "this.translate = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(Translate));
+
+ script.append(QString::fromLatin1(
+ "this.transform = function(a1, a2, a3, a4, a5, a6) {"
+ " this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+ "};").arg(Transform));
+
+ script.append(QString::fromLatin1(
+ "this.setTransform = function(a1, a2, a3, a4, a5, a6) {"
+ " this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+ "};").arg(SetTransform));
+
+ script.append(QString::fromLatin1(
+ "this.clearRect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(ClearRect));
+
+ script.append(QString::fromLatin1(
+ "this.fillRect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(FillRect));
+
+ script.append(QString::fromLatin1(
+ "this.strokeRect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(StrokeRect));
+
+ script.append(QString::fromLatin1(
+ "this.beginPath = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(BeginPath));
+
+ script.append(QString::fromLatin1(
+ "this.closePath = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(ClosePath));
+
+ script.append(QString::fromLatin1(
+ "this.moveTo = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(MoveTo));
+
+ script.append(QString::fromLatin1(
+ "this.lineTo = function(x, y) {"
+ " this._commands.push([%1, x, y]);"
+ "};").arg(LineTo));
+
+ script.append(QString::fromLatin1(
+ "this.quadraticCurveTo = function(a1, a2, a3, a4) {"
+ " this._commands.push([%1, a1, a2, a3, a4]);"
+ "};").arg(QuadraticCurveTo));
+
+ script.append(QString::fromLatin1(
+ "this.bezierCurveTo = function(a1, a2, a3, a4, a5, a6) {"
+ " this._commands.push([%1, a1, a2, a3, a4, a5, a6]);"
+ "};").arg(BezierCurveTo));
+
+ script.append(QString::fromLatin1(
+ "this.arcTo = function(x1, y1, x2, y2, radius) {"
+ " this._commands.push([%1, x1, y1, x2, y2, radius]);"
+ "};").arg(ArcTo));
+
+ script.append(QString::fromLatin1(
+ "this.rect = function(x, y, w, h) {"
+ " this._commands.push([%1, x, y, w, h]);"
+ "};").arg(Rect));
+
+ script.append(QString::fromLatin1(
+ "this.rect = function(x, y, radius, startAngle, endAngle, anticlockwise) {"
+ " this._commands.push([%1, x, y, radius, startAngle, endAngle, anticlockwise]);"
+ "};").arg(Arc));
+
+ script.append(QString::fromLatin1(
+ "this.fill = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Fill));
+
+ script.append(QString::fromLatin1(
+ "this.stroke = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Stroke));
+
+ script.append(QString::fromLatin1(
+ "this.clip = function() {"
+ " this._commands.push([%1]);"
+ "};").arg(Clip));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"globalAlpha\", function() {"
+ " return this._globalAlpha;"
+ " });"
+ " this.__defineSetter__(\"globalAlpha\", function(v) {"
+ " this._globalAlpha = v;"
+ " this._commands.push([%1, v]);"
+ " });").arg(GlobalAlpha));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"globalCompositeOperation\", function() {"
+ " return this._globalCompositeOperation;"
+ " });"
+ " this.__defineSetter__(\"globalCompositeOperation\", function(v) {"
+ " this._globalCompositeOperation = v;"
+ " this._commands.push([%1, v]);"
+ " });").arg(GlobalCompositeOperation));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"strokeStyle\", function() {return this._strokeStyle; });"
+ " this.__defineSetter__(\"strokeStyle\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._strokeStyle = v;"
+ " });").arg(StrokeStyle));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"fillStyle\", function() {return this._fillStyle; });"
+ " this.__defineSetter__(\"fillStyle\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._fillStyle = v;"
+ " });").arg(FillStyle));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"strokeColor\", function() {return this._strokeColor; });"
+ " this.__defineSetter__(\"strokeColor\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._strokeColor = v;"
+ " });").arg(StrokeColor));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"fillColor\", function() {return this._fillColor; });"
+ " this.__defineSetter__(\"fillColor\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._fillColor = v;"
+ " });").arg(FillColor));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"lineWidth\", function() {return this._lineWidth; });"
+ " this.__defineSetter__(\"lineWidth\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._lineWidth = v;"
+ " });").arg(LineWidth));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"lineCap\", function() {return this._lineCap; });"
+ " this.__defineSetter__(\"lineCap\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._lineCap = v;"
+ " });").arg(LineCap));
+
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"lineJoin\", function() {return this._lineJoin; });"
+ " this.__defineSetter__(\"lineJoin\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._lineJoin = v;"
+ " });").arg(LineJoin));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"miterLimit\", function() {return this._miterLimit; });"
+ " this.__defineSetter__(\"miterLimit\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._miterLimit = v;"
+ " });").arg(MiterLimit));
+
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowOffsetX\", function() {return this._shadowOffsetX; });"
+ " this.__defineSetter__(\"shadowOffsetX\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowOffsetX = v;"
+ " });").arg(ShadowOffsetX));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowOffsetY\", function() {return this._shadowOffsetY; });"
+ " this.__defineSetter__(\"shadowOffsetY\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowOffsetY = v;"
+ " });").arg(ShadowOffsetY));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowBlur\", function() {return this._shadowBlur; });"
+ " this.__defineSetter__(\"shadowBlur\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowBlur = v;"
+ " });").arg(ShadowBlur));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"shadowColor\", function() {return this._shadowColor; });"
+ " this.__defineSetter__(\"shadowColor\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._shadowColor = v;"
+ " });").arg(ShadowColor));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"font\", function() {return this._font; });"
+ " this.__defineSetter__(\"font\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._font = v;"
+ " });").arg(Font));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"textBaseline\", function() {return this._textBaseline; });"
+ " this.__defineSetter__(\"textBaseline\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._textBaseline = v;"
+ " });").arg(TextBaseline));
+
+ script.append(QString::fromLatin1(
+ " this.__defineGetter__(\"textAlign\", function() {return this._textAlign; });"
+ " this.__defineSetter__(\"textAlign\", function(v) {"
+ " this._commands.push([%1, v]);"
+ " this._textAlign = v;"
+ " });").arg(TextAlign));
+
+ script.append(QString::fromLatin1(
+ "this.fillText = function(text, x, y) {"
+ " this._commands.push([%1, text, x, y]);"
+ "};").arg(FillText));
+
+ script.append(QString::fromLatin1(
+ "this.strokeText = function(text, x, y) {"
+ " this._commands.push([%1, text, x, y]);"
+ "};").arg(StrokeText));
+
+ script.append(QString::fromLatin1(
+ "this.drawImage = function() {"
+ " if (arguments.length == 3) {"
+ " this._commands.push([%1, arguments[0], arguments[1], arguments[2]]);"
+ " } else if (arguments.length == 5) {"
+ " this._commands.push([%2, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4]]);"
+ " } else if (arguments.length == 9) {"
+ " this._commands.push([%3, arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7], arguments[8]]);}"
+ "};").arg(DrawImage1).arg(DrawImage2).arg(DrawImage3));
+
+ script.append(QString::fromLatin1(
+ "this.putImageData = function() {"
+ " var dx = arguments[1];"
+ " var dy = arguments[2];"
+ " if (arguments.length == 3) {"
+ " this._commands.push([%1, arguments[0].data, dx, dy, arguments[0].width, arguments[0].height]);"
+ " } else if (arguments.length == 7) {"
+ " var dirtyX = arguments[3];"
+ " var dirtyY = arguments[4];"
+ " var dirtyWidth = arguments[5];"
+ " var dirtyHeight = arguments[6];"
+ " var width = arguments[0].width;"
+ " var height = arguments[0].heigh;"
+ " var filteredData = arguments[0].data.filter(function(element, index, array){"
+ " var x=index/width;"
+ " var y=index%width-1;"
+ " return x >= dirtyX && x < dirtyX+dirtyWidth"
+ " && y >= dirtyY && y < dirtyY+dirtyHeight;"
+ " });"
+ " this._commands.push([%2, filteredData, dx, dy, dirtyWidth, dirtyHeight]);"
+ " }"
+ "};").arg(PutImageData).arg(PutImageData));
+ script.append(QString::fromLatin1("}"));
+ }
+ return script;
+}
+
+QSGContext2D *QSGContext2D::agent()
+{
+ Q_D(QSGContext2D);
+
+ if (d->agent)
+ return d->agent;
+
+ d->agent = new QSGContext2D(this, new QSGContext2DWorkerAgent);
+ connect(this, SIGNAL(painted()), d->agent, SIGNAL(painted()));
+ d->agent->setSize(size());
+ return d->agent;
+
+}
+void QSGContext2D::processCommands(const QScriptValue& commands)
+{
+#ifdef QSGCANVASITEM_DEBUG
+ QElapsedTimer t;
+ t.start();
+#endif
+ int ii = 0;
+ if (commands.isArray()) {
+ QScriptValue cmd = commands.property(ii);
+ while(cmd.isValid()) {
+ processCommand(cmd);
+ ii++;
+ cmd = commands.property(ii);
+ }
+ }
+
+#ifdef QSGCANVASITEM_DEBUG
+ qDebug() << "processed" << ii << "commands in " << t.nsecsElapsed() << "nsecs";
+#endif
+ sync();
+}
+
+void QSGContext2D::sync()
+{
+ Q_D(QSGContext2D);
+
+#ifdef QSGCANVASITEM_DEBUG
+ QElapsedTimer t;
+ t.start();
+#endif
+ if (d->agentData) {
+ if (d->agentData->ref == 1) return;
+
+ Sync *s = new Sync;
+ s->data = d->agentData;
+
+ d->agentData->mutex.lock();
+ QCoreApplication::postEvent(this, s);
+ d->agentData->syncDone.wait(&d->agentData->mutex);
+ d->agentData->mutex.unlock();
+ } else {
+ //qmlInfo(this) << "Context2D sync() can only be called from a WorkerScript;";
+ emit changed();
+ }
+
+#ifdef QSGCANVASITEM_DEBUG
+ qDebug() << "syncing time:" << t.nsecsElapsed();
+#endif
+}
+
+
+bool QSGContext2D::event(QEvent *e)
+{
+ Q_D(QSGContext2D);
+ if (e->type() == QEvent::User && d->agentData) {
+ QMutexLocker locker(&d->agentData->mutex);
+ Sync *s = static_cast<Sync *>(e);
+
+ QSGContext2DPrivate* origin_d = static_cast<QSGContext2DPrivate*>(s->data->orig->d_func());
+
+ //quick copy
+ memcpy_vector<PaintCommand>(&origin_d->commands, d->commands);
+ memcpy_vector<int>(&origin_d->ints, d->ints);
+ memcpy_vector<qreal>(&origin_d->reals, d->reals);
+ memcpy_vector<QColor>(&origin_d->colors, d->colors);
+ memcpy_vector<QMatrix>(&origin_d->matrixes, d->matrixes);
+ memcpy_vector<QSize>(&origin_d->sizes, d->sizes);
+
+ //slow copy
+ copy_vector<QString>(&origin_d->strings, d->strings);
+ copy_vector<QVariant>(&origin_d->variants, d->variants);
+ copy_vector<QPen>(&origin_d->pens, d->pens);
+ copy_vector<QBrush>(&origin_d->brushes, d->brushes);
+ copy_vector<QPainterPath>(&origin_d->pathes, d->pathes);
+ copy_vector<QFont>(&origin_d->fonts, d->fonts);
+ copy_vector<QImage>(&origin_d->images, d->images);
+ origin_d->state = d->state;
+ d->clearCommands();
+
+ if (d->waitingForPainting) {
+ d->imageData.clear();
+ origin_d->imageData.clear();
+ emit s->data->orig->changed();
+ while(origin_d->imageData.isEmpty()) {
+ QCoreApplication::processEvents();
+ }
+ d->imageData = origin_d->imageData;
+ d->waitingForPainting = false;
+ qDebug() << "imageData size:" << d->imageData.size();
+ } else {
+ emit s->data->orig->changed();
+ }
+
+ d->agentData->syncDone.wakeAll();
+ return true;
+ }
+ return QObject::event(e);
+}
+
+void QSGContext2D::processCommand(const QScriptValue& cmd)
+{
+ int action = cmd.property(0).toInt32();
+ switch (action) {
+ case QSGContext2D::Save:
+ save();
+ break;
+ case QSGContext2D::Restore:
+ restore();
+ break;
+ case QSGContext2D::Scale:
+ scale(cmd.property(1).toNumber(), cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::Rotate:
+ rotate(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::Translate:
+ translate(cmd.property(1).toNumber(), cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::Transform:
+ transform(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber());
+ break;
+ case QSGContext2D::SetTransform:
+ setTransform(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber());
+ break;
+ case QSGContext2D::ClearRect:
+ clearRect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::FillRect:
+ fillRect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::StrokeRect:
+ strokeRect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::BeginPath:
+ beginPath();
+ break;
+ case QSGContext2D::ClosePath:
+ closePath();
+ break;
+ case QSGContext2D::MoveTo:
+ moveTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::LineTo:
+ lineTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber());
+ break;
+ case QSGContext2D::QuadraticCurveTo:
+ quadraticCurveTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::BezierCurveTo:
+ bezierCurveTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber());
+ break;
+ case QSGContext2D::ArcTo:
+ arcTo(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber());
+ break;
+ case QSGContext2D::Rect:
+ rect(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber());
+ break;
+ case QSGContext2D::Arc:
+ arc(cmd.property(1).toNumber(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toBool());
+ break;
+ case QSGContext2D::Fill:
+ fill();
+ break;
+ case QSGContext2D::Stroke:
+ stroke();
+ break;
+ case QSGContext2D::Clip:
+ clip();
+ break;
+ case QSGContext2D::GlobalAlpha:
+ setGlobalAlpha(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::GlobalCompositeOperation:
+ setGlobalCompositeOperation(cmd.property(1).toString());
+ break;
+ case QSGContext2D::StrokeStyle:
+ setStrokeStyle(cmd.property(1).toVariant());
+ break;
+ case QSGContext2D::FillStyle:
+ setFillStyle(cmd.property(1).toVariant());
+ break;
+ case QSGContext2D::FillColor:
+ setFillColor(cmd.property(1).toVariant().value<QColor>());
+ break;
+ case QSGContext2D::StrokeColor:
+ setStrokeColor(cmd.property(1).toVariant().value<QColor>());
+ break;
+ case QSGContext2D::LineWidth:
+ setLineWidth(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::LineCap:
+ setLineCap(cmd.property(1).toString());
+ break;
+ case QSGContext2D::LineJoin:
+ setLineJoin(cmd.property(1).toString());
+ break;
+ case QSGContext2D::MiterLimit:
+ setMiterLimit(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowOffsetX:
+ setShadowOffsetX(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowOffsetY:
+ setShadowOffsetY(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowBlur:
+ setShadowBlur(cmd.property(1).toNumber());
+ break;
+ case QSGContext2D::ShadowColor:
+ setShadowColor(cmd.property(1).toString());
+ break;
+ case QSGContext2D::Font:
+ setFont(cmd.property(1).toString());
+ break;
+ case QSGContext2D::TextBaseline:
+ setTextBaseline(cmd.property(1).toString());
+ break;
+ case QSGContext2D::TextAlign:
+ setTextAlign(cmd.property(1).toString());
+ break;
+ case QSGContext2D::FillText:
+ fillText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
+ break;
+ case QSGContext2D::StrokeText:
+ strokeText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
+ break;
+ case QSGContext2D::DrawImage1:
+ {
+ drawImage(cmd.property(1).toString(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber());
+ break;
+ }
+ case QSGContext2D::DrawImage2:
+ drawImage(cmd.property(1).toString(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber());
+ break;
+ case QSGContext2D::DrawImage3:
+ drawImage(cmd.property(1).toString(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber(),
+ cmd.property(6).toNumber(),
+ cmd.property(7).toNumber(),
+ cmd.property(8).toNumber(),
+ cmd.property(9).toNumber());
+ break;
+ case QSGContext2D::PutImageData:
+ putImageData(cmd.property(1).toVariant(),
+ cmd.property(2).toNumber(),
+ cmd.property(3).toNumber(),
+ cmd.property(4).toNumber(),
+ cmd.property(5).toNumber());
+ break;
+ default:
+ break;
+ }
+}
+
+void QSGContext2D::paint(QPainter* p)
+{
+ Q_D(QSGContext2D);
+
+ QTransform transform = p->worldTransform();
+ if (!d->commands.isEmpty()) {
+ int matrix_idx, real_idx, int_idx, variant_idx, string_idx,color_idx,cmd_idx,
+ pen_idx, brush_idx, font_idx, path_idx, image_idx, size_idx;
+
+ matrix_idx = real_idx = int_idx = variant_idx = string_idx =color_idx = cmd_idx
+ = pen_idx = brush_idx = font_idx = path_idx = image_idx = size_idx = 0;
+
+ foreach(PaintCommand cmd, d->commands) {
+ switch (cmd) {
+ case UpdateMatrix:
+ {
+// qDebug() << "update matrix from " << d->state.matrix << " to " << d->matrixes[matrix_idx];
+ //p->setWorldTransform(transform * QTransform(d->matrixes[matrix_idx++]), false);
+ //p->setMatrix(d->matrixes[matrix_idx++]);
+ d->state.matrix = d->matrixes[matrix_idx++];
+ break;
+ }
+ case ClearRect:
+ {
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ qreal w = d->reals[real_idx++];
+ qreal h = d->reals[real_idx++];
+ p->eraseRect(QRectF(x, y, w, h));
+ break;
+ }
+ case FillRect:
+ {
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ qreal w = d->reals[real_idx++];
+ qreal h = d->reals[real_idx++];
+ if (d->hasShadow())
+ d->fillRectShadow(p, QRectF(x, y, w, h));
+ else
+ p->fillRect(QRectF(x, y, w, h), p->brush());
+ break;
+ }
+ case ShadowColor:
+ {
+ QColor c = d->colors[color_idx++];
+ d->state.shadowColor = c;
+ break;
+ }
+ case ShadowBlur:
+ {
+ qreal blur = d->reals[real_idx++];
+ d->state.shadowBlur = blur;
+ break;
+ }
+ case ShadowOffsetX:
+ {
+ qreal x = d->reals[real_idx++];
+ d->state.shadowOffsetX = x;
+ break;
+ }
+ case ShadowOffsetY:
+ {
+ qreal y = d->reals[real_idx++];
+ d->state.shadowOffsetY = y;
+ break;
+ }
+ case Fill:
+ {
+ QPainterPath path = d->pathes[path_idx++];
+ if (d->hasShadow())
+ d->fillShadowPath(p,path);
+ else
+ p->fillPath(path, p->brush());
+ break;
+ }
+ case Stroke:
+ {
+ p->setMatrix(d->state.matrix);
+ QPainterPath path = d->state.matrix.inverted().map(d->pathes[path_idx++]);
+ if (d->hasShadow())
+ d->strokeShadowPath(p,path);
+ else
+ p->strokePath(path, p->pen());
+ break;
+ }
+ case Clip:
+ {
+ QPainterPath clipPath = d->pathes[path_idx++];
+ p->setClipPath(clipPath);
+ p->setClipping(true);
+ break;
+ }
+ case UpdateBrush:
+ {
+ p->setBrush(d->brushes[brush_idx++]);
+ break;
+ }
+ case UpdatePen:
+ {
+ p->setPen(d->pens[pen_idx++]);
+ break;
+ }
+ case GlobalAlpha:
+ {
+ p->setOpacity(d->reals[real_idx++]);
+ break;
+ }
+ case GlobalCompositeOperation:
+ {
+ p->setCompositionMode(static_cast<QPainter::CompositionMode>(d->ints[int_idx++]));
+ break;
+ }
+ case Font:
+ {
+ p->setFont(d->fonts[font_idx++]);
+ break;
+ }
+ case StrokeText:
+ {
+ QString text = d->strings[string_idx++];
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ int align = d->ints[int_idx++];
+ int baseline = d->ints[int_idx++];
+
+ QPen oldPen = p->pen();
+ p->setPen(QPen(p->brush(),0));
+ //p->setMatrix(state.matrix, false); // always set?
+
+ QPainterPath textPath;
+ QFont oldFont = p->font();
+ QFont font = p->font();
+ font.setStyleStrategy(QFont::ForceOutline);
+ p->setFont(font);
+ const QFontMetrics &metrics = p->fontMetrics();
+ int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), metrics);
+ int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), metrics, text);
+ textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
+ if (d->hasShadow())
+ d->strokeShadowPath(p,textPath);
+
+ p->strokePath(textPath, QPen(p->brush(), p->pen().widthF()));
+
+ //reset old font
+ p->setFont(oldFont);
+ p->setPen(oldPen);
+ break;
+ }
+ case FillText:
+ {
+ QString text = d->strings[string_idx++];
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ int align = d->ints[int_idx++];
+ int baseline = d->ints[int_idx++];
+
+ QFont oldFont = p->font();
+ QPen oldPen = p->pen();
+ p->setPen(QPen(p->brush(), p->pen().widthF()));
+ //p->setMatrix(state.matrix, false);
+ //QFont font = p->font();
+ QFont font = d->state.font;
+ font.setBold(true);
+
+ p->setFont(font);
+ int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), p->fontMetrics());
+ int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), p->fontMetrics(), text);
+ QTextOption opt; // Adjust baseLine etc
+ if (d->hasShadow()) {
+ const QFontMetrics &metrics = p->fontMetrics();
+ QPainterPath textPath;
+ textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
+ d->fillShadowPath(p,textPath);
+ }
+ //p->drawText(QRectF(x - xoffset, y - yoffset, QWIDGETSIZE_MAX, p->fontMetrics().height()), text, opt);
+ p->setFont(oldFont);
+ p->setPen(oldPen);
+ break;
+ }
+ case DrawImage1:
+ {
+ QUrl url(d->strings[string_idx++]);
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ QDeclarativePixmap px(qmlEngine(d->canvas), url);
+ qDebug() << "draw image:" << url << px.pixmap().size();
+ if (px.isReady()) {
+ QPixmap pixmap = px.pixmap();
+ if (d->hasShadow()) {
+ QImage shadow = d->makeShadowImage(pixmap);
+ qreal dx = x + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+ qreal dy = y + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+ p->drawImage(QPointF(dx, dy), shadow);
+ }
+ p->drawPixmap(QPointF(x, y), pixmap);
+ }
+ break;
+ }
+ case DrawImage2:
+ {
+ qreal dx = d->reals[real_idx++];
+ qreal dy = d->reals[real_idx++];
+ qreal dw = d->reals[real_idx++];
+ qreal dh = d->reals[real_idx++];
+ QUrl url(d->strings[string_idx++]);
+ QDeclarativePixmap px(qmlEngine(d->canvas), url);
+ if (px.isReady()) {
+ QPixmap pixmap = px.pixmap().scaled(dw, dh);
+ if (d->hasShadow()) {
+ QImage shadow = d->makeShadowImage(pixmap);
+ qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+ qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+ p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
+ }
+ p->drawPixmap(QPointF(dx, dy), pixmap);
+ }
+ break;
+ }
+ case DrawImage3:
+ {
+ qreal sx = d->reals[real_idx++];
+ qreal sy = d->reals[real_idx++];
+ qreal sw = d->reals[real_idx++];
+ qreal sh = d->reals[real_idx++];
+ qreal dx = d->reals[real_idx++];
+ qreal dy = d->reals[real_idx++];
+ qreal dw = d->reals[real_idx++];
+ qreal dh = d->reals[real_idx++];
+ QUrl url(d->strings[string_idx++]);
+ QDeclarativePixmap px(qmlEngine(d->canvas), url);
+ if (px.isReady()) {
+ QPixmap pixmap = px.pixmap().copy(sx, sy, sw, sh).scaled(dw, dh);
+ if (d->hasShadow()) {
+ QImage shadow = d->makeShadowImage(pixmap);
+ qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
+ qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
+ p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
+ }
+ p->drawPixmap(QPointF(dx, dy), pixmap);
+ }
+ break;
+ }
+ case GetImageData:
+ {
+ qreal sx = d->reals[real_idx++];
+ qreal sy = d->reals[real_idx++];
+ qreal sw = d->reals[real_idx++];
+ qreal sh = d->reals[real_idx++];
+ QImage img = toImage().copy(sx, sy, sw, sh);
+ const uchar* data = img.constBits();
+ int i = 0;
+
+ while(i< img.byteCount()) {
+ //the stored order in QImage:BGRA
+ d->imageData << *(data+i+2);//R
+ d->imageData << *(data+i+1);//G
+ d->imageData << *(data+i);//B
+ d->imageData << *(data+i+3);//A
+ i+=4;
+ }
+ break;
+ }
+ case PutImageData:
+ {
+ QImage image = d->images[image_idx++];
+ qreal x = d->reals[real_idx++];
+ qreal y = d->reals[real_idx++];
+ p->drawImage(QPointF(x, y), image);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ d->clearCommands();
+ }
+}
+
+QPaintDevice* QSGContext2D::paintDevice()
+{
+ Q_D(QSGContext2D);
+ return &d->cachedImage;
+}
+const QImage& QSGContext2D::toImage() const
+{
+ Q_D(const QSGContext2D);
+ return d->cachedImage;
+}
+bool QSGContext2D::requireCachedImage() const
+{
+ Q_D(const QSGContext2D);
+ return d->waitingForPainting;
+}
+void QSGContext2D::setCachedImage(const QImage& image)
+{
+ Q_D(QSGContext2D);
+#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
+ if (d->waitingForPainting) {
+ d->cachedImage = image;
+ d->waitingForPainting = false;
+ }
+#endif
+ if (inWorkerThread()) {
+ d->agent->setCachedImage(image);
+ }
+}
+
+void QSGContext2D::clear()
+{
+ Q_D(QSGContext2D);
+ d->clear();
+}
+
+void QSGContext2D::reset()
+{
+ Q_D(QSGContext2D);
+ d->reset();
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy)
+{
+ Q_D(QSGContext2D);
+ if (!imgUrl.isEmpty())
+ d->drawImage(imgUrl, dx, dy);
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+{
+ Q_D(QSGContext2D);
+ if (!imgUrl.isEmpty())
+ d->drawImage(imgUrl, sx, sy, sw, sh, dx, dy, dw, dh);
+}
+
+void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy,
+ qreal dw, qreal dh)
+{
+ Q_D(QSGContext2D);
+ if (!imgUrl.isEmpty())
+ d->drawImage(imgUrl, dx, dy, dw, dh);
+}
+
+void QSGContext2D::setSize(int width, int height)
+{
+ QSize size(width, height);
+ setSize(size);
+}
+
+void QSGContext2D::setSize(const QSize &size)
+{
+ Q_D(QSGContext2D);
+
+ if (d->size == size)
+ return;
+ d->setSize(size);
+ emit changed();
+}
+
+QSize QSGContext2D::size() const
+{
+ Q_D(const QSGContext2D);
+ return d->size;
+}
+
+QMatrix QSGContext2D::worldMatrix() const
+{
+ Q_D(const QSGContext2D);
+ return d->state.matrix;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgcontext2d_p.h b/src/declarative/items/qsgcontext2d_p.h
new file mode 100644
index 0000000000..1a900100de
--- /dev/null
+++ b/src/declarative/items/qsgcontext2d_p.h
@@ -0,0 +1,409 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCONTEXT2D_P_H
+#define QSGCONTEXT2D_P_H
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+
+#include "qsgtexturematerial.h"
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtGui/qpixmap.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qvariant.h>
+#include <QtScript/qscriptvalue.h>
+#include <QMutex>
+#include <QWaitCondition>
+#include "qsgimage_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+QColor colorFromString(const QString &name);
+
+class QSGCanvasGradient : public QObject
+{
+ Q_OBJECT
+public:
+ QSGCanvasGradient(const QGradient &gradient) : m_gradient(gradient) {}
+
+public slots:
+ QGradient value() { return m_gradient; }
+ void addColorStop(float pos, const QString &color) { m_gradient.setColorAt(pos, colorFromString(color));}
+
+public:
+ QGradient m_gradient;
+};
+
+Q_DECLARE_METATYPE(QSGCanvasGradient*)
+
+
+//class QSGCanvasImage: public QObject
+//{
+// Q_OBJECT
+// Q_PROPERTY(QString src READ src WRITE setSrc NOTIFY sourceChanged)
+//public:
+// QSGCanvasImage() {}
+// QSGCanvasImage(const QString &url) : m_image(url), m_src(url) {
+// }
+// QSGCanvasImage(const QImage &img) {m_image = img;}
+
+//public slots:
+// QImage &value() { return m_image; }
+// const QImage &value() const{ return m_image; }
+// QString src() { return m_src; }
+// void setSrc(const QString &src) { m_src = src; m_image.load(src); emit sourceChanged();}
+//signals:
+// void sourceChanged();
+
+//private:
+// QSGImage* img;
+// QString src;
+//};
+
+//Q_DECLARE_METATYPE(QSGCanvasImage*)
+
+
+/*
+
+ */
+
+class QSGContext2DWorkerAgent;
+class QSGContext2DPrivate;
+class QSGContext2D : public QObject
+{
+ Q_OBJECT
+ // compositing
+ Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha)
+ Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation)
+ Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle)
+ Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle)
+ Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor)
+ Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor)
+ // line caps/joins
+ Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth)
+ Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap)
+ Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin)
+ Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit)
+ // shadows
+ Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX)
+ Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY)
+ Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur)
+ Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor)
+ // fonts
+ Q_PROPERTY(QString font READ font WRITE setFont)
+ Q_PROPERTY(QString textBaseline READ textBaseline WRITE setTextBaseline)
+ Q_PROPERTY(QString textAlign READ textAlign WRITE setTextAlign)
+ Q_ENUMS(PaintCommand)
+public:
+ enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging};
+ enum TextAlignType { Start=0, End, Left, Right, Center};
+ enum PaintCommand {
+ Invalid = 0,
+ Save,
+ Restore,
+ //matrix operations
+ UpdateMatrix,
+ Scale,
+ Rotate,
+ Translate,
+ Transform,
+ SetTransform,
+
+ ClearRect,
+ FillRect,
+
+ //path operations
+ UpdatePath,
+ BeginPath,
+ ClosePath,
+ MoveTo,
+ LineTo,
+ QuadraticCurveTo,
+ BezierCurveTo,
+ ArcTo,
+ Rect,
+ Arc,
+ Fill,
+ Stroke,
+ Clip,
+ StrokeRect,
+
+ //brushes and pens
+ UpdateBrush,
+ UpdatePen,
+ GlobalAlpha,
+ GlobalCompositeOperation,
+ StrokeStyle,
+ FillStyle,
+ StrokeColor,
+ FillColor,
+ LineWidth,
+ LineCap,
+ LineJoin,
+ MiterLimit,
+
+ //shadows
+ UpdateShadow,
+ ShadowOffsetX,
+ ShadowOffsetY,
+ ShadowBlur,
+ ShadowColor,
+
+ //font&text
+ Font,
+ TextBaseline,
+ TextAlign,
+ FillText,
+ StrokeText,
+
+ //image
+ DrawImage1,
+ DrawImage2,
+ DrawImage3,
+ GetImageData,
+ PutImageData
+ };
+
+ QSGContext2D(QObject *parent = 0);
+ QSGContext2D(QSGContext2D *ctx2d, QSGContext2DWorkerAgent* agentData);
+ ~QSGContext2D();
+ void setSize(int width, int height);
+ void setSize(const QSize &size);
+ QSize size() const;
+
+ void clear();
+ void reset();
+ QPaintDevice* paintDevice();
+ const QImage& toImage() const;
+ bool requireCachedImage() const;
+ void setCachedImage(const QImage& image);
+ // compositing
+ qreal globalAlpha() const; // (default 1.0)
+ QString globalCompositeOperation() const; // (default over)
+ QVariant strokeStyle() const; // (default black)
+ QVariant fillStyle() const; // (default black)
+ QColor strokeColor() const; // (default black)
+ QColor fillColor() const; // (default black)
+
+ void setGlobalAlpha(qreal alpha);
+ void setGlobalCompositeOperation(const QString &op);
+ void setStrokeStyle(const QVariant &style);
+ void setFillStyle(const QVariant &style);
+ void setStrokeColor(const QColor& color);
+ void setFillColor(const QColor& color);
+
+ // line caps/joins
+ qreal lineWidth() const; // (default 1)
+ QString lineCap() const; // "butt", "round", "square" (default "butt")
+ QString lineJoin() const; // "round", "bevel", "miter" (default "miter")
+ qreal miterLimit() const; // (default 10)
+
+ void setLineWidth(qreal w);
+ void setLineCap(const QString &s);
+ void setLineJoin(const QString &s);
+ void setMiterLimit(qreal m);
+
+ void setFont(const QString &font);
+ QString font() const;
+ void setTextBaseline(const QString &font);
+ QString textBaseline() const;
+ void setTextAlign(const QString &font);
+ QString textAlign() const;
+
+
+ // shadows
+ qreal shadowOffsetX() const; // (default 0)
+ qreal shadowOffsetY() const; // (default 0)
+ qreal shadowBlur() const; // (default 0)
+ QString shadowColor() const; // (default black)
+
+ void setShadowOffsetX(qreal x);
+ void setShadowOffsetY(qreal y);
+ void setShadowBlur(qreal b);
+ void setShadowColor(const QString &str);
+
+public slots:
+ void save(); // push state on state stack
+ void restore(); // pop state stack and restore state
+
+ // QTextMetrics measureText(const QString& text);
+
+ void fillText(const QString &text, qreal x, qreal y);
+ void strokeText(const QString &text, qreal x, qreal y);
+
+ void scale(qreal x, qreal y);
+ void rotate(qreal angle);
+ void translate(qreal x, qreal y);
+ void transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+ void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+
+ QSGCanvasGradient *createLinearGradient(qreal x0, qreal y0,
+ qreal x1, qreal y1);
+ QSGCanvasGradient *createRadialGradient(qreal x0, qreal y0,
+ qreal r0, qreal x1,
+ qreal y1, qreal r1);
+
+ // rects
+ void clearRect(qreal x, qreal y, qreal w, qreal h);
+ void fillRect(qreal x, qreal y, qreal w, qreal h);
+ void strokeRect(qreal x, qreal y, qreal w, qreal h);
+
+ // path API
+ void beginPath();
+ void closePath();
+ void moveTo(qreal x, qreal y);
+ void lineTo(qreal x, qreal y);
+ void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y);
+ void bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y);
+ void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius);
+ void rect(qreal x, qreal y, qreal w, qreal h);
+ void arc(qreal x, qreal y, qreal radius,
+ qreal startAngle, qreal endAngle,
+ bool anticlockwise);
+ void fill();
+ void stroke();
+ void clip();
+ bool isPointInPath(qreal x, qreal y) const;
+
+
+ QSGImage *createImage(const QString &url);
+
+ void drawImage(const QString& imgUrl, qreal dx, qreal dy);
+ void drawImage(const QString& imgUrl, qreal dx, qreal dy, qreal dw, qreal dh);
+ void drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh);
+
+ // pixel manipulation
+ QList<int> getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
+ void putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h);
+
+ void paint(QPainter* painter);
+ void sync();
+ void processCommands(const QScriptValue& commands);
+signals:
+ void changed();
+ void painted();
+public:
+ bool isDirty() const;
+ QScriptValue scriptValue() const;
+ void setScriptEngine(QScriptEngine *eng);
+ QScriptEngine *scriptEngine() const;
+
+ void addref();
+ void release();
+
+ struct VariantRef
+ {
+ VariantRef() : a(0) {}
+ VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); }
+ VariantRef(QSGContext2D *_a) : a(_a) { if (a) a->addref(); }
+ ~VariantRef() { if (a) a->release(); }
+
+ VariantRef &operator=(const VariantRef &o) {
+ if (o.a) o.a->addref();
+ if (a) a->release(); a = o.a;
+ return *this;
+ }
+
+ QSGContext2D *a;
+ };
+ struct Sync : public QEvent {
+ Sync() : QEvent(QEvent::User) {}
+ QSGContext2DWorkerAgent *data;
+ };
+ inline bool inWorkerThread() const;
+ QSGContext2D *agent();
+ const QString& agentScript() const;
+
+
+ struct State {
+ QMatrix matrix;
+ QPainterPath clipPath;
+ QBrush strokeStyle;
+ QBrush fillStyle;
+ qreal globalAlpha;
+ qreal lineWidth;
+ Qt::PenCapStyle lineCap;
+ Qt::PenJoinStyle lineJoin;
+ qreal miterLimit;
+ qreal shadowOffsetX;
+ qreal shadowOffsetY;
+ qreal shadowBlur;
+ QColor shadowColor;
+ QPainter::CompositionMode globalCompositeOperation;
+ QFont font;
+ QSGContext2D::TextAlignType textAlign;
+ QSGContext2D::TextBaseLineType textBaseline;
+ QPen pen;
+ };
+
+ QMatrix worldMatrix() const;
+
+protected:
+ virtual bool event(QEvent *);
+
+private:
+ void processCommand(const QScriptValue& command);
+
+ Q_DECLARE_PRIVATE(QSGContext2D)
+};
+
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSGContext2D::VariantRef)
+QML_DECLARE_TYPE(QSGContext2D)
+
+QT_END_HEADER
+
+#endif // QSGCONTEXT2D_P_H
diff --git a/src/declarative/items/qsgcontext2d_p_p.h b/src/declarative/items/qsgcontext2d_p_p.h
new file mode 100644
index 0000000000..36f26c99df
--- /dev/null
+++ b/src/declarative/items/qsgcontext2d_p_p.h
@@ -0,0 +1,227 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGCONTEXT2D_P_P_H
+#define QSGCONTEXT2D_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgcontext2d_p.h"
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+class QSGCanvasItem;
+struct QSGContext2DWorkerAgent {
+ QSGContext2DWorkerAgent()
+ :ref(1)
+ , orig(0)
+ {}
+
+ QAtomicInt ref;
+ QSGContext2D *orig;
+ QMutex mutex;
+ QWaitCondition syncDone;
+};
+
+class QSGContext2DPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGContext2D)
+
+public:
+ QSGContext2DPrivate()
+ : agent(0)
+ , agentData(0)
+ , scriptEngine(0)
+ , cachedImage(1,1, QImage::Format_ARGB32)
+ , canvas(0)
+ , waitingForPainting(false)
+ {
+ }
+ void updateMatrix(const QMatrix& m);
+
+ void setSize(const QSize &s)
+ {
+ size = s;
+ cachedImage = QImage(s, QImage::Format_ARGB32);
+ }
+ void clear();
+ void reset();
+
+ // compositing
+ void setGlobalAlpha(qreal alpha);
+ void setGlobalCompositeOperation(const QString &op);
+ void setStrokeStyle(const QVariant &style);
+ void setFillStyle(const QVariant &style);
+ void setStrokeColor(const QColor& color);
+ void setFillColor(const QColor& color);
+
+ // line caps/joins
+ void setLineWidth(qreal w);
+ void setLineCap(const QString &s);
+ void setLineJoin(const QString &s);
+ void setMiterLimit(qreal m);
+
+ void setFont(const QString &font);
+ void setTextBaseline(const QString &font);
+ void setTextAlign(const QString &font);
+
+
+ // shadows
+ void setShadowOffsetX(qreal x);
+ void setShadowOffsetY(qreal y);
+ void setShadowBlur(qreal b);
+ void setShadowColor(const QString &str);
+
+ bool hasShadow() const;
+ void clearShadow();
+ QImage makeShadowImage(const QPixmap& pix);
+ void fillRectShadow(QPainter* p, QRectF shadowRect);
+ void fillShadowPath(QPainter* p, const QPainterPath& path);
+ void strokeShadowPath(QPainter* p, const QPainterPath& path);
+ void save();
+ void restore();
+
+ // QTextMetrics measureText(const QString& text);
+
+ void fillText(const QString &text, qreal x, qreal y);
+ void strokeText(const QString &text, qreal x, qreal y);
+
+ void scale(qreal x, qreal y);
+ void rotate(qreal angle);
+ void translate(qreal x, qreal y);
+ void transform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+ void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
+ qreal dx, qreal dy);
+
+ // rects
+ void clearRect(qreal x, qreal y, qreal w, qreal h);
+ void fillRect(qreal x, qreal y, qreal w, qreal h);
+ void strokeRect(qreal x, qreal y, qreal w, qreal h);
+
+ // path API
+ void beginPath();
+ void closePath();
+ void moveTo(qreal x, qreal y);
+ void lineTo(qreal x, qreal y);
+ void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y);
+ void bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y);
+ void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius);
+ void rect(qreal x, qreal y, qreal w, qreal h);
+ void arc(qreal x, qreal y, qreal radius,
+ qreal startAngle, qreal endAngle,
+ bool anticlockwise);
+ void fill();
+ void stroke();
+ void clip();
+
+ void drawImage(const QString& url, qreal dx, qreal dy);
+ void drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh);
+ void drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh);
+
+ QList<int> getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
+ void putImageData(const QVariantList& imageData, qreal x, qreal y, qreal w, qreal h);
+
+ int baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics);
+ int textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &string);
+
+ void clearCommands()
+ {
+ commands.remove(0, commands.size());
+ variants.remove(0, variants.size());
+ pens.remove(0, pens.size());
+ ints.remove(0, ints.size());
+ reals.remove(0, reals.size());
+ strings.remove(0, strings.size());
+ colors.remove(0, colors.size());
+ matrixes.remove(0, matrixes.size());
+ brushes.remove(0, brushes.size());
+ pathes.remove(0, pathes.size());
+ fonts.remove(0, fonts.size());
+ images.remove(0, images.size());
+ sizes.remove(0, sizes.size());
+ }
+
+ //current context2d variables
+ QPainterPath path;
+ QSize size;
+ QSGContext2D::State state;
+ QStack<QSGContext2D::State> stateStack;
+
+ //variables for actual painting
+ QVector<QSGContext2D::PaintCommand> commands;
+ QVector<QVariant> variants;
+ QVector<int> ints;
+ QVector<qreal> reals;
+ QVector<QString> strings;
+ QVector<QColor> colors;
+ QVector<QMatrix> matrixes;
+ QVector<QPen> pens;
+ QVector<QBrush> brushes;
+ QVector<QPainterPath> pathes;
+ QVector<QFont> fonts;
+ QVector<QImage> images;
+ QVector<QSize> sizes;
+ QList<int> imageData;
+
+ //workerscript agent
+ QSGContext2D* agent;
+ QSGContext2DWorkerAgent* agentData;
+ QScriptEngine* scriptEngine;
+ QScriptValue scriptValue;
+ QImage cachedImage;
+ QSGCanvasItem* canvas;
+ bool waitingForPainting;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGCONTEXT2D_P_P_H
diff --git a/src/declarative/items/qsgevents.cpp b/src/declarative/items/qsgevents.cpp
new file mode 100644
index 0000000000..44ef38b037
--- /dev/null
+++ b/src/declarative/items/qsgevents.cpp
@@ -0,0 +1,47 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgevents_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgevents_p_p.h b/src/declarative/items/qsgevents_p_p.h
new file mode 100644
index 0000000000..f0d434db32
--- /dev/null
+++ b/src/declarative/items/qsgevents_p_p.h
@@ -0,0 +1,142 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGEVENTS_P_P_H
+#define QSGEVENTS_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qdeclarative.h>
+
+#include <QtCore/qobject.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGKeyEvent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int key READ key)
+ Q_PROPERTY(QString text READ text)
+ Q_PROPERTY(int modifiers READ modifiers)
+ Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat)
+ Q_PROPERTY(int count READ count)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
+
+public:
+ QSGKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers, const QString &text=QString(), bool autorep=false, ushort count=1)
+ : event(type, key, modifiers, text, autorep, count) { event.setAccepted(false); }
+ QSGKeyEvent(const QKeyEvent &ke)
+ : event(ke) { event.setAccepted(false); }
+
+ int key() const { return event.key(); }
+ QString text() const { return event.text(); }
+ int modifiers() const { return event.modifiers(); }
+ bool isAutoRepeat() const { return event.isAutoRepeat(); }
+ int count() const { return event.count(); }
+
+ bool isAccepted() { return event.isAccepted(); }
+ void setAccepted(bool accepted) { event.setAccepted(accepted); }
+
+private:
+ QKeyEvent event;
+};
+
+class QSGMouseEvent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int x READ x)
+ Q_PROPERTY(int y READ y)
+ Q_PROPERTY(int button READ button)
+ Q_PROPERTY(int buttons READ buttons)
+ Q_PROPERTY(int modifiers READ modifiers)
+ Q_PROPERTY(bool wasHeld READ wasHeld)
+ Q_PROPERTY(bool isClick READ isClick)
+ Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
+
+public:
+ QSGMouseEvent(int x, int y, Qt::MouseButton button, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers
+ , bool isClick=false, bool wasHeld=false)
+ : _x(x), _y(y), _button(button), _buttons(buttons), _modifiers(modifiers)
+ , _wasHeld(wasHeld), _isClick(isClick), _accepted(true) {}
+
+ int x() const { return _x; }
+ int y() const { return _y; }
+ int button() const { return _button; }
+ int buttons() const { return _buttons; }
+ int modifiers() const { return _modifiers; }
+ bool wasHeld() const { return _wasHeld; }
+ bool isClick() const { return _isClick; }
+
+ // only for internal usage
+ void setX(int x) { _x = x; }
+ void setY(int y) { _y = y; }
+
+ bool isAccepted() { return _accepted; }
+ void setAccepted(bool accepted) { _accepted = accepted; }
+
+private:
+ int _x;
+ int _y;
+ Qt::MouseButton _button;
+ Qt::MouseButtons _buttons;
+ Qt::KeyboardModifiers _modifiers;
+ bool _wasHeld;
+ bool _isClick;
+ bool _accepted;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGKeyEvent)
+QML_DECLARE_TYPE(QSGMouseEvent)
+
+#endif // QSGEVENTS_P_P_H
diff --git a/src/declarative/items/qsgflickable.cpp b/src/declarative/items/qsgflickable.cpp
new file mode 100644
index 0000000000..be0ac13bbf
--- /dev/null
+++ b/src/declarative/items/qsgflickable.cpp
@@ -0,0 +1,1495 @@
+// Commit: d4fa1878ff1e7628d3e984d54f8a93810353c71b
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgflickable_p.h"
+#include "qsgflickable_p_p.h"
+#include "qsgcanvas.h"
+#include "qsgcanvas_p.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qapplication.h>
+#include "qplatformdefs.h"
+
+QT_BEGIN_NAMESPACE
+
+// The maximum number of pixels a flick can overshoot
+#ifndef QML_FLICK_OVERSHOOT
+#define QML_FLICK_OVERSHOOT 200
+#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 1
+#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
+
+// FlickThreshold determines how far the "mouse" must have moved
+// before we perform a flick.
+static const int FlickThreshold = 20;
+
+// RetainGrabVelocity is the maxmimum instantaneous velocity that
+// will ensure the Flickable retains the grab on consecutive flicks.
+static const int RetainGrabVelocity = 15;
+
+QSGFlickableVisibleArea::QSGFlickableVisibleArea(QSGFlickable *parent)
+ : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
+ , m_yPosition(0.), m_heightRatio(0.)
+{
+}
+
+qreal QSGFlickableVisibleArea::widthRatio() const
+{
+ return m_widthRatio;
+}
+
+qreal QSGFlickableVisibleArea::xPosition() const
+{
+ return m_xPosition;
+}
+
+qreal QSGFlickableVisibleArea::heightRatio() const
+{
+ return m_heightRatio;
+}
+
+qreal QSGFlickableVisibleArea::yPosition() const
+{
+ return m_yPosition;
+}
+
+void QSGFlickableVisibleArea::updateVisible()
+{
+ QSGFlickablePrivate *p = QSGFlickablePrivate::get(flickable);
+
+ bool changeX = false;
+ bool changeY = false;
+ bool changeWidth = false;
+ bool changeHeight = false;
+
+ // Vertical
+ const qreal viewheight = flickable->height();
+ const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
+ qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
+ qreal pageSize = viewheight / (maxyextent + viewheight);
+
+ if (pageSize != m_heightRatio) {
+ m_heightRatio = pageSize;
+ changeHeight = true;
+ }
+ if (pagePos != m_yPosition) {
+ m_yPosition = pagePos;
+ changeY = true;
+ }
+
+ // Horizontal
+ const qreal viewwidth = flickable->width();
+ const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
+ pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
+ pageSize = viewwidth / (maxxextent + viewwidth);
+
+ if (pageSize != m_widthRatio) {
+ m_widthRatio = pageSize;
+ changeWidth = true;
+ }
+ if (pagePos != m_xPosition) {
+ m_xPosition = pagePos;
+ changeX = true;
+ }
+
+ if (changeX)
+ emit xPositionChanged(m_xPosition);
+ if (changeY)
+ emit yPositionChanged(m_yPosition);
+ if (changeWidth)
+ emit widthRatioChanged(m_widthRatio);
+ if (changeHeight)
+ emit heightRatioChanged(m_heightRatio);
+}
+
+
+QSGFlickablePrivate::QSGFlickablePrivate()
+ : contentItem(new QSGItem)
+ , hData(this, &QSGFlickablePrivate::setViewportX)
+ , vData(this, &QSGFlickablePrivate::setViewportY)
+ , flickingHorizontally(false), flickingVertically(false)
+ , hMoved(false), vMoved(false)
+ , movingHorizontally(false), movingVertically(false)
+ , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
+ , deceleration(QML_FLICK_DEFAULTDECELERATION)
+ , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
+ , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
+ , fixupMode(Normal), vTime(0), visibleArea(0)
+ , flickableDirection(QSGFlickable::AutoFlickDirection)
+ , boundsBehavior(QSGFlickable::DragAndOvershootBounds)
+{
+}
+
+void QSGFlickablePrivate::init()
+{
+ Q_Q(QSGFlickable);
+ QDeclarative_setParent_noEvent(contentItem, q);
+ contentItem->setParentItem(q);
+ static int timelineUpdatedIdx = -1;
+ static int timelineCompletedIdx = -1;
+ static int flickableTickedIdx = -1;
+ static int flickableMovementEndingIdx = -1;
+ if (timelineUpdatedIdx == -1) {
+ timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
+ timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
+ flickableTickedIdx = QSGFlickable::staticMetaObject.indexOfSlot("ticked()");
+ flickableMovementEndingIdx = QSGFlickable::staticMetaObject.indexOfSlot("movementEnding()");
+ }
+ QMetaObject::connect(&timeline, timelineUpdatedIdx,
+ q, flickableTickedIdx, Qt::DirectConnection);
+ QMetaObject::connect(&timeline, timelineCompletedIdx,
+ q, flickableMovementEndingIdx, Qt::DirectConnection);
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFiltersChildMouseEvents(true);
+ QSGItemPrivate *viewportPrivate = QSGItemPrivate::get(contentItem);
+ viewportPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ lastPosTime.invalidate();
+}
+
+/*
+ Returns the amount to overshoot by given a velocity.
+ Will be roughly in range 0 - size/4
+*/
+qreal QSGFlickablePrivate::overShootDistance(qreal size)
+{
+ if (maxVelocity <= 0)
+ return 0.0;
+
+ return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
+}
+
+void QSGFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
+{
+ if (v > maxVelocity)
+ v = maxVelocity;
+ else if (v < -maxVelocity)
+ v = -maxVelocity;
+ velocityBuffer.append(v);
+ if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
+ velocityBuffer.remove(0);
+}
+
+void QSGFlickablePrivate::AxisData::updateVelocity()
+{
+ if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
+ velocity = 0;
+ int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
+ for (int i = 0; i < count; ++i) {
+ qreal v = velocityBuffer.at(i);
+ velocity += v;
+ }
+ velocity /= count;
+ }
+}
+
+void QSGFlickablePrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeom, const QRectF &oldGeom)
+{
+ Q_Q(QSGFlickable);
+ if (item == contentItem) {
+ if (newGeom.x() != oldGeom.x())
+ emit q->contentXChanged();
+ if (newGeom.y() != oldGeom.y())
+ emit q->contentYChanged();
+ }
+}
+
+void QSGFlickablePrivate::flickX(qreal velocity)
+{
+ Q_Q(QSGFlickable);
+ flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
+}
+
+void QSGFlickablePrivate::flickY(qreal velocity)
+{
+ Q_Q(QSGFlickable);
+ flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
+}
+
+void QSGFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
+ QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
+{
+ Q_Q(QSGFlickable);
+ qreal maxDistance = -1;
+ data.fixingUp = false;
+ // -ve velocity means list is moving up
+ if (velocity > 0) {
+ maxDistance = qAbs(minExtent - data.move.value());
+ data.flickTarget = minExtent;
+ } else {
+ maxDistance = qAbs(maxExtent - data.move.value());
+ data.flickTarget = maxExtent;
+ }
+ if (maxDistance > 0) {
+ qreal v = velocity;
+ if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
+ if (v < 0)
+ v = -maxVelocity;
+ else
+ v = maxVelocity;
+ }
+ timeline.reset(data.move);
+ if (boundsBehavior == QSGFlickable::DragAndOvershootBounds)
+ timeline.accel(data.move, v, deceleration);
+ else
+ timeline.accel(data.move, v, deceleration, maxDistance);
+ timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
+ if (!flickingHorizontally && q->xflick()) {
+ flickingHorizontally = true;
+ emit q->flickingChanged();
+ emit q->flickingHorizontallyChanged();
+ if (!flickingVertically)
+ emit q->flickStarted();
+ }
+ if (!flickingVertically && q->yflick()) {
+ flickingVertically = true;
+ emit q->flickingChanged();
+ emit q->flickingVerticallyChanged();
+ if (!flickingHorizontally)
+ emit q->flickStarted();
+ }
+ } else {
+ timeline.reset(data.move);
+ fixup(data, minExtent, maxExtent);
+ }
+}
+
+void QSGFlickablePrivate::fixupY_callback(void *data)
+{
+ ((QSGFlickablePrivate *)data)->fixupY();
+}
+
+void QSGFlickablePrivate::fixupX_callback(void *data)
+{
+ ((QSGFlickablePrivate *)data)->fixupX();
+}
+
+void QSGFlickablePrivate::fixupX()
+{
+ Q_Q(QSGFlickable);
+ fixup(hData, q->minXExtent(), q->maxXExtent());
+}
+
+void QSGFlickablePrivate::fixupY()
+{
+ Q_Q(QSGFlickable);
+ fixup(vData, q->minYExtent(), q->maxYExtent());
+}
+
+void QSGFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
+{
+ if (data.move.value() > minExtent || maxExtent > minExtent) {
+ timeline.reset(data.move);
+ if (data.move.value() != minExtent) {
+ switch (fixupMode) {
+ case Immediate:
+ timeline.set(data.move, minExtent);
+ break;
+ case ExtentChanged:
+ // The target has changed. Don't start from the beginning; just complete the
+ // second half of the animation using the new extent.
+ timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
+ data.fixingUp = true;
+ break;
+ default: {
+ qreal dist = minExtent - data.move;
+ timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
+ timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
+ data.fixingUp = true;
+ }
+ }
+ }
+ } else if (data.move.value() < maxExtent) {
+ timeline.reset(data.move);
+ switch (fixupMode) {
+ case Immediate:
+ timeline.set(data.move, maxExtent);
+ break;
+ case ExtentChanged:
+ // The target has changed. Don't start from the beginning; just complete the
+ // second half of the animation using the new extent.
+ timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
+ data.fixingUp = true;
+ break;
+ default: {
+ qreal dist = maxExtent - data.move;
+ timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
+ timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
+ data.fixingUp = true;
+ }
+ }
+ }
+ data.inOvershoot = false;
+ fixupMode = Normal;
+ vTime = timeline.time();
+}
+
+void QSGFlickablePrivate::updateBeginningEnd()
+{
+ Q_Q(QSGFlickable);
+ bool atBoundaryChange = false;
+
+ // Vertical
+ const int maxyextent = int(-q->maxYExtent());
+ const qreal ypos = -vData.move.value();
+ bool atBeginning = (ypos <= -q->minYExtent());
+ bool atEnd = (maxyextent <= ypos);
+
+ if (atBeginning != vData.atBeginning) {
+ vData.atBeginning = atBeginning;
+ atBoundaryChange = true;
+ }
+ if (atEnd != vData.atEnd) {
+ vData.atEnd = atEnd;
+ atBoundaryChange = true;
+ }
+
+ // Horizontal
+ const int maxxextent = int(-q->maxXExtent());
+ const qreal xpos = -hData.move.value();
+ atBeginning = (xpos <= -q->minXExtent());
+ atEnd = (maxxextent <= xpos);
+
+ if (atBeginning != hData.atBeginning) {
+ hData.atBeginning = atBeginning;
+ atBoundaryChange = true;
+ }
+ if (atEnd != hData.atEnd) {
+ hData.atEnd = atEnd;
+ atBoundaryChange = true;
+ }
+
+ if (atBoundaryChange)
+ emit q->isAtBoundaryChanged();
+
+ if (visibleArea)
+ visibleArea->updateVisible();
+}
+
+QSGFlickable::QSGFlickable(QSGItem *parent)
+ : QSGItem(*(new QSGFlickablePrivate), parent)
+{
+ Q_D(QSGFlickable);
+ d->init();
+}
+
+QSGFlickable::QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent)
+ : QSGItem(dd, parent)
+{
+ Q_D(QSGFlickable);
+ d->init();
+}
+
+QSGFlickable::~QSGFlickable()
+{
+}
+
+qreal QSGFlickable::contentX() const
+{
+ Q_D(const QSGFlickable);
+ return -d->contentItem->x();
+}
+
+void QSGFlickable::setContentX(qreal pos)
+{
+ Q_D(QSGFlickable);
+ d->timeline.reset(d->hData.move);
+ d->vTime = d->timeline.time();
+ movementXEnding();
+ if (-pos != d->hData.move.value()) {
+ d->hData.move.setValue(-pos);
+ viewportMoved();
+ }
+}
+
+qreal QSGFlickable::contentY() const
+{
+ Q_D(const QSGFlickable);
+ return -d->contentItem->y();
+}
+
+void QSGFlickable::setContentY(qreal pos)
+{
+ Q_D(QSGFlickable);
+ d->timeline.reset(d->vData.move);
+ d->vTime = d->timeline.time();
+ movementYEnding();
+ if (-pos != d->vData.move.value()) {
+ d->vData.move.setValue(-pos);
+ viewportMoved();
+ }
+}
+
+bool QSGFlickable::isInteractive() const
+{
+ Q_D(const QSGFlickable);
+ return d->interactive;
+}
+
+void QSGFlickable::setInteractive(bool interactive)
+{
+ Q_D(QSGFlickable);
+ if (interactive != d->interactive) {
+ d->interactive = interactive;
+ if (!interactive && (d->flickingHorizontally || d->flickingVertically)) {
+ d->timeline.clear();
+ d->vTime = d->timeline.time();
+ d->flickingHorizontally = false;
+ d->flickingVertically = false;
+ emit flickingChanged();
+ emit flickingHorizontallyChanged();
+ emit flickingVerticallyChanged();
+ emit flickEnded();
+ }
+ emit interactiveChanged();
+ }
+}
+
+qreal QSGFlickable::horizontalVelocity() const
+{
+ Q_D(const QSGFlickable);
+ return d->hData.smoothVelocity.value();
+}
+
+qreal QSGFlickable::verticalVelocity() const
+{
+ Q_D(const QSGFlickable);
+ return d->vData.smoothVelocity.value();
+}
+
+bool QSGFlickable::isAtXEnd() const
+{
+ Q_D(const QSGFlickable);
+ return d->hData.atEnd;
+}
+
+bool QSGFlickable::isAtXBeginning() const
+{
+ Q_D(const QSGFlickable);
+ return d->hData.atBeginning;
+}
+
+bool QSGFlickable::isAtYEnd() const
+{
+ Q_D(const QSGFlickable);
+ return d->vData.atEnd;
+}
+
+bool QSGFlickable::isAtYBeginning() const
+{
+ Q_D(const QSGFlickable);
+ return d->vData.atBeginning;
+}
+
+void QSGFlickable::ticked()
+{
+ viewportMoved();
+}
+
+QSGItem *QSGFlickable::contentItem()
+{
+ Q_D(QSGFlickable);
+ return d->contentItem;
+}
+
+QSGFlickableVisibleArea *QSGFlickable::visibleArea()
+{
+ Q_D(QSGFlickable);
+ if (!d->visibleArea)
+ d->visibleArea = new QSGFlickableVisibleArea(this);
+ return d->visibleArea;
+}
+
+QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const
+{
+ Q_D(const QSGFlickable);
+ return d->flickableDirection;
+}
+
+void QSGFlickable::setFlickableDirection(FlickableDirection direction)
+{
+ Q_D(QSGFlickable);
+ if (direction != d->flickableDirection) {
+ d->flickableDirection = direction;
+ emit flickableDirectionChanged();
+ }
+}
+
+void QSGFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGFlickable);
+ if (interactive && timeline.isActive()
+ && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
+ || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
+ stealMouse = true; // If we've been flicked then steal the click.
+ } else {
+ stealMouse = false;
+ }
+ q->setKeepMouseGrab(stealMouse);
+ pressed = true;
+ timeline.clear();
+ hData.reset();
+ vData.reset();
+ hData.dragMinBound = q->minXExtent();
+ vData.dragMinBound = q->minYExtent();
+ hData.dragMaxBound = q->maxXExtent();
+ vData.dragMaxBound = q->maxYExtent();
+ fixupMode = Normal;
+ lastPos = QPoint();
+ QSGItemPrivate::start(lastPosTime);
+ pressPos = event->pos();
+ hData.pressPos = hData.move.value();
+ vData.pressPos = vData.move.value();
+ flickingHorizontally = false;
+ flickingVertically = false;
+ QSGItemPrivate::start(pressTime);
+ QSGItemPrivate::start(velocityTime);
+}
+
+void QSGFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGFlickable);
+ if (!interactive || !lastPosTime.isValid())
+ return;
+ bool rejectY = false;
+ bool rejectX = false;
+
+ bool stealY = stealMouse;
+ bool stealX = stealMouse;
+
+ if (q->yflick()) {
+ int dy = int(event->pos().y() - pressPos.y());
+ if (qAbs(dy) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
+ if (!vMoved)
+ vData.dragStartOffset = dy;
+ qreal newY = dy + vData.pressPos - vData.dragStartOffset;
+ const qreal minY = vData.dragMinBound;
+ const qreal maxY = vData.dragMaxBound;
+ if (newY > minY)
+ newY = minY + (newY - minY) / 2;
+ if (newY < maxY && maxY - minY <= 0)
+ newY = maxY + (newY - maxY) / 2;
+ if (boundsBehavior == QSGFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
+ rejectY = true;
+ if (newY < maxY) {
+ newY = maxY;
+ rejectY = false;
+ }
+ if (newY > minY) {
+ newY = minY;
+ rejectY = false;
+ }
+ }
+ if (!rejectY && stealMouse) {
+ vData.move.setValue(qRound(newY));
+ vMoved = true;
+ }
+ if (qAbs(dy) > QApplication::startDragDistance())
+ stealY = true;
+ }
+ }
+
+ if (q->xflick()) {
+ int dx = int(event->pos().x() - pressPos.x());
+ if (qAbs(dx) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
+ if (!hMoved)
+ hData.dragStartOffset = dx;
+ qreal newX = dx + hData.pressPos - hData.dragStartOffset;
+ const qreal minX = hData.dragMinBound;
+ const qreal maxX = hData.dragMaxBound;
+ if (newX > minX)
+ newX = minX + (newX - minX) / 2;
+ if (newX < maxX && maxX - minX <= 0)
+ newX = maxX + (newX - maxX) / 2;
+ if (boundsBehavior == QSGFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
+ rejectX = true;
+ if (newX < maxX) {
+ newX = maxX;
+ rejectX = false;
+ }
+ if (newX > minX) {
+ newX = minX;
+ rejectX = false;
+ }
+ }
+ if (!rejectX && stealMouse) {
+ hData.move.setValue(qRound(newX));
+ hMoved = true;
+ }
+
+ if (qAbs(dx) > QApplication::startDragDistance())
+ stealX = true;
+ }
+ }
+
+ stealMouse = stealX || stealY;
+ if (stealMouse)
+ q->setKeepMouseGrab(true);
+
+ if (rejectY) {
+ vData.velocityBuffer.clear();
+ vData.velocity = 0;
+ }
+ if (rejectX) {
+ hData.velocityBuffer.clear();
+ hData.velocity = 0;
+ }
+
+ if (hMoved || vMoved) {
+ q->movementStarting();
+ q->viewportMoved();
+ }
+
+ if (!lastPos.isNull()) {
+ qreal elapsed = qreal(QSGItemPrivate::elapsed(lastPosTime)) / 1000.;
+ if (elapsed <= 0)
+ return;
+ QSGItemPrivate::restart(lastPosTime);
+ qreal dy = event->pos().y()-lastPos.y();
+ if (q->yflick() && !rejectY)
+ vData.addVelocitySample(dy/elapsed, maxVelocity);
+ qreal dx = event->pos().x()-lastPos.x();
+ if (q->xflick() && !rejectX)
+ hData.addVelocitySample(dx/elapsed, maxVelocity);
+ }
+
+ lastPos = event->pos();
+}
+
+void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGFlickable);
+ stealMouse = false;
+ q->setKeepMouseGrab(false);
+ pressed = false;
+ if (!lastPosTime.isValid())
+ return;
+
+ // if we drag then pause before release we should not cause a flick.
+ if (QSGItemPrivate::elapsed(lastPosTime) < 100) {
+ vData.updateVelocity();
+ hData.updateVelocity();
+ } else {
+ hData.velocity = 0.0;
+ vData.velocity = 0.0;
+ }
+
+ vTime = timeline.time();
+
+ qreal velocity = vData.velocity;
+ if (vData.atBeginning || vData.atEnd)
+ velocity /= 2;
+ if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
+ flickY(velocity);
+ else
+ fixupY();
+
+ velocity = hData.velocity;
+ if (hData.atBeginning || hData.atEnd)
+ velocity /= 2;
+ if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
+ flickX(velocity);
+ else
+ fixupX();
+
+ if (!timeline.isActive())
+ q->movementEnding();
+}
+
+void QSGFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGFlickable);
+ if (d->interactive) {
+ if (!d->pressed)
+ d->handleMousePressEvent(event);
+ event->accept();
+ } else {
+ QSGItem::mousePressEvent(event);
+ }
+}
+
+void QSGFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGFlickable);
+ if (d->interactive) {
+ d->handleMouseMoveEvent(event);
+ event->accept();
+ } else {
+ QSGItem::mouseMoveEvent(event);
+ }
+}
+
+void QSGFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGFlickable);
+ if (d->interactive) {
+ d->clearDelayedPress();
+ d->handleMouseReleaseEvent(event);
+ event->accept();
+ ungrabMouse();
+ } else {
+ QSGItem::mouseReleaseEvent(event);
+ }
+}
+
+void QSGFlickable::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ Q_D(QSGFlickable);
+ if (!d->interactive) {
+ QSGItem::wheelEvent(event);
+ } else if (yflick() && event->orientation() == Qt::Vertical) {
+ bool valid = false;
+ if (event->delta() > 0 && contentY() > -minYExtent()) {
+ d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
+ valid = true;
+ } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
+ d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
+ valid = true;
+ }
+ if (valid) {
+ d->flickingVertically = false;
+ d->flickY(d->vData.velocity);
+ if (d->flickingVertically) {
+ d->vMoved = true;
+ movementStarting();
+ }
+ event->accept();
+ }
+ } else if (xflick() && event->orientation() == Qt::Horizontal) {
+ bool valid = false;
+ if (event->delta() > 0 && contentX() > -minXExtent()) {
+ d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
+ valid = true;
+ } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
+ d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
+ valid = true;
+ }
+ if (valid) {
+ d->flickingHorizontally = false;
+ d->flickX(d->hData.velocity);
+ if (d->flickingHorizontally) {
+ d->hMoved = true;
+ movementStarting();
+ }
+ event->accept();
+ }
+ } else {
+ QSGItem::wheelEvent(event);
+ }
+}
+
+bool QSGFlickablePrivate::isOutermostPressDelay() const
+{
+ Q_Q(const QSGFlickable);
+ QSGItem *item = q->parentItem();
+ while (item) {
+ QSGFlickable *flick = qobject_cast<QSGFlickable*>(item);
+ if (flick && flick->pressDelay() > 0 && flick->isInteractive())
+ return false;
+ item = item->parentItem();
+ }
+
+ return true;
+}
+
+void QSGFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGFlickable);
+ if (!q->canvas() || pressDelay <= 0)
+ return;
+ if (!isOutermostPressDelay())
+ return;
+ delayedPressTarget = q->canvas()->mouseGrabberItem();
+ delayedPressEvent = new QGraphicsSceneMouseEvent(event->type());
+ delayedPressEvent->setAccepted(false);
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button));
+ delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button));
+ delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button));
+ }
+ }
+ delayedPressEvent->setButtons(event->buttons());
+ delayedPressEvent->setButton(event->button());
+ delayedPressEvent->setPos(event->pos());
+ delayedPressEvent->setScenePos(event->scenePos());
+ delayedPressEvent->setScreenPos(event->screenPos());
+ delayedPressEvent->setLastPos(event->lastPos());
+ delayedPressEvent->setLastScenePos(event->lastScenePos());
+ delayedPressEvent->setLastScreenPos(event->lastScreenPos());
+ delayedPressEvent->setModifiers(event->modifiers());
+ delayedPressTimer.start(pressDelay, q);
+}
+
+void QSGFlickablePrivate::clearDelayedPress()
+{
+ if (delayedPressEvent) {
+ delayedPressTimer.stop();
+ delete delayedPressEvent;
+ delayedPressEvent = 0;
+ }
+}
+
+void QSGFlickablePrivate::setViewportX(qreal x)
+{
+ contentItem->setX(x);
+}
+
+void QSGFlickablePrivate::setViewportY(qreal y)
+{
+ contentItem->setY(y);
+}
+
+void QSGFlickable::timerEvent(QTimerEvent *event)
+{
+ Q_D(QSGFlickable);
+ if (event->timerId() == d->delayedPressTimer.timerId()) {
+ d->delayedPressTimer.stop();
+ if (d->delayedPressEvent) {
+ QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
+ if (!grabber || grabber != this) {
+ // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
+ // so we reset the grabber
+ if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
+ d->delayedPressTarget->ungrabMouse();
+ // Use the event handler that will take care of finding the proper item to propagate the event
+ QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
+ }
+ delete d->delayedPressEvent;
+ d->delayedPressEvent = 0;
+ }
+ }
+}
+
+qreal QSGFlickable::minYExtent() const
+{
+ return 0.0;
+}
+
+qreal QSGFlickable::minXExtent() const
+{
+ return 0.0;
+}
+
+/* returns -ve */
+qreal QSGFlickable::maxXExtent() const
+{
+ return width() - vWidth();
+}
+/* returns -ve */
+qreal QSGFlickable::maxYExtent() const
+{
+ return height() - vHeight();
+}
+
+void QSGFlickable::viewportMoved()
+{
+ Q_D(QSGFlickable);
+
+ qreal prevX = d->lastFlickablePosition.x();
+ qreal prevY = d->lastFlickablePosition.y();
+ d->velocityTimeline.clear();
+ if (d->pressed || d->calcVelocity) {
+ int elapsed = QSGItemPrivate::restart(d->velocityTime);
+ if (elapsed > 0) {
+ qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
+ qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
+ d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
+ d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
+ d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
+ d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
+ }
+ } else {
+ if (d->timeline.time() > d->vTime) {
+ qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
+ qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
+ d->hData.smoothVelocity.setValue(horizontalVelocity);
+ d->vData.smoothVelocity.setValue(verticalVelocity);
+ }
+ }
+
+ if (!d->vData.inOvershoot && !d->vData.fixingUp && d->flickingVertically
+ && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
+ && qAbs(d->vData.smoothVelocity.value()) > 100) {
+ // Increase deceleration if we've passed a bound
+ d->vData.inOvershoot = true;
+ qreal maxDistance = d->overShootDistance(height());
+ d->timeline.reset(d->vData.move);
+ d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
+ d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
+ }
+ if (!d->hData.inOvershoot && !d->hData.fixingUp && d->flickingHorizontally
+ && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
+ && qAbs(d->hData.smoothVelocity.value()) > 100) {
+ // Increase deceleration if we've passed a bound
+ d->hData.inOvershoot = true;
+ qreal maxDistance = d->overShootDistance(width());
+ d->timeline.reset(d->hData.move);
+ d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
+ d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
+ }
+
+ d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
+
+ d->vTime = d->timeline.time();
+ d->updateBeginningEnd();
+}
+
+void QSGFlickable::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ Q_D(QSGFlickable);
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+
+ bool changed = false;
+ if (newGeometry.width() != oldGeometry.width()) {
+ if (xflick())
+ changed = true;
+ if (d->hData.viewSize < 0) {
+ d->contentItem->setWidth(width());
+ emit contentWidthChanged();
+ }
+ // Make sure that we're entirely in view.
+ if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
+ d->fixupMode = QSGFlickablePrivate::Immediate;
+ d->fixupX();
+ }
+ }
+ if (newGeometry.height() != oldGeometry.height()) {
+ if (yflick())
+ changed = true;
+ if (d->vData.viewSize < 0) {
+ d->contentItem->setHeight(height());
+ emit contentHeightChanged();
+ }
+ // Make sure that we're entirely in view.
+ if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
+ d->fixupMode = QSGFlickablePrivate::Immediate;
+ d->fixupY();
+ }
+ }
+
+ if (changed)
+ d->updateBeginningEnd();
+}
+
+void QSGFlickable::cancelFlick()
+{
+ Q_D(QSGFlickable);
+ d->timeline.reset(d->hData.move);
+ d->timeline.reset(d->vData.move);
+ movementEnding();
+}
+
+void QSGFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
+{
+ QSGItem *i = qobject_cast<QSGItem *>(o);
+ if (i) {
+ i->setParentItem(static_cast<QSGFlickablePrivate*>(prop->data)->contentItem);
+ } else {
+ o->setParent(prop->object); // XXX todo - do we want this?
+ }
+}
+
+int QSGFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
+{
+ // XXX todo
+ return 0;
+}
+
+QObject *QSGFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
+{
+ // XXX todo
+ return 0;
+}
+
+void QSGFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
+{
+ // XXX todo
+}
+
+QDeclarativeListProperty<QObject> QSGFlickable::flickableData()
+{
+ Q_D(QSGFlickable);
+ return QDeclarativeListProperty<QObject>(this, (void *)d, QSGFlickablePrivate::data_append,
+ QSGFlickablePrivate::data_count,
+ QSGFlickablePrivate::data_at,
+ QSGFlickablePrivate::data_clear);
+}
+
+QDeclarativeListProperty<QSGItem> QSGFlickable::flickableChildren()
+{
+ Q_D(QSGFlickable);
+ return QSGItemPrivate::get(d->contentItem)->children();
+}
+
+QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const
+{
+ Q_D(const QSGFlickable);
+ return d->boundsBehavior;
+}
+
+void QSGFlickable::setBoundsBehavior(BoundsBehavior b)
+{
+ Q_D(QSGFlickable);
+ if (b == d->boundsBehavior)
+ return;
+ d->boundsBehavior = b;
+ emit boundsBehaviorChanged();
+}
+
+qreal QSGFlickable::contentWidth() const
+{
+ Q_D(const QSGFlickable);
+ return d->hData.viewSize;
+}
+
+void QSGFlickable::setContentWidth(qreal w)
+{
+ Q_D(QSGFlickable);
+ if (d->hData.viewSize == w)
+ return;
+ d->hData.viewSize = w;
+ if (w < 0)
+ d->contentItem->setWidth(width());
+ else
+ d->contentItem->setWidth(w);
+ // Make sure that we're entirely in view.
+ if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
+ d->fixupMode = QSGFlickablePrivate::Immediate;
+ d->fixupX();
+ } else if (!d->pressed && d->hData.fixingUp) {
+ d->fixupMode = QSGFlickablePrivate::ExtentChanged;
+ d->fixupX();
+ }
+ emit contentWidthChanged();
+ d->updateBeginningEnd();
+}
+
+qreal QSGFlickable::contentHeight() const
+{
+ Q_D(const QSGFlickable);
+ return d->vData.viewSize;
+}
+
+void QSGFlickable::setContentHeight(qreal h)
+{
+ Q_D(QSGFlickable);
+ if (d->vData.viewSize == h)
+ return;
+ d->vData.viewSize = h;
+ if (h < 0)
+ d->contentItem->setHeight(height());
+ else
+ d->contentItem->setHeight(h);
+ // Make sure that we're entirely in view.
+ if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
+ d->fixupMode = QSGFlickablePrivate::Immediate;
+ d->fixupY();
+ } else if (!d->pressed && d->vData.fixingUp) {
+ d->fixupMode = QSGFlickablePrivate::ExtentChanged;
+ d->fixupY();
+ }
+ emit contentHeightChanged();
+ d->updateBeginningEnd();
+}
+
+void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
+{
+ Q_D(QSGFlickable);
+ if (w != d->hData.viewSize) {
+ qreal oldSize = d->hData.viewSize;
+ d->hData.viewSize = w;
+ d->contentItem->setWidth(w);
+ emit contentWidthChanged();
+ if (center.x() != 0) {
+ qreal pos = center.x() * w / oldSize;
+ setContentX(contentX() + pos - center.x());
+ }
+ }
+ if (h != d->vData.viewSize) {
+ qreal oldSize = d->vData.viewSize;
+ d->vData.viewSize = h;
+ d->contentItem->setHeight(h);
+ emit contentHeightChanged();
+ if (center.y() != 0) {
+ qreal pos = center.y() * h / oldSize;
+ setContentY(contentY() + pos - center.y());
+ }
+ }
+ d->updateBeginningEnd();
+}
+
+void QSGFlickable::returnToBounds()
+{
+ Q_D(QSGFlickable);
+ d->fixupX();
+ d->fixupY();
+}
+
+qreal QSGFlickable::vWidth() const
+{
+ Q_D(const QSGFlickable);
+ if (d->hData.viewSize < 0)
+ return width();
+ else
+ return d->hData.viewSize;
+}
+
+qreal QSGFlickable::vHeight() const
+{
+ Q_D(const QSGFlickable);
+ if (d->vData.viewSize < 0)
+ return height();
+ else
+ return d->vData.viewSize;
+}
+
+bool QSGFlickable::xflick() const
+{
+ Q_D(const QSGFlickable);
+ if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
+ return vWidth() != width();
+ return d->flickableDirection & QSGFlickable::HorizontalFlick;
+}
+
+bool QSGFlickable::yflick() const
+{
+ Q_D(const QSGFlickable);
+ if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
+ return vHeight() != height();
+ return d->flickableDirection & QSGFlickable::VerticalFlick;
+}
+
+void QSGFlickable::mouseUngrabEvent()
+{
+ Q_D(QSGFlickable);
+ if (d->pressed) {
+ // if our mouse grab has been removed (probably by another Flickable),
+ // fix our state
+ d->pressed = false;
+ d->stealMouse = false;
+ setKeepMouseGrab(false);
+ }
+}
+
+bool QSGFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGFlickable);
+ QGraphicsSceneMouseEvent mouseEvent(event->type());
+ QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+
+ QSGCanvas *c = canvas();
+ QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
+ bool disabledItem = grabber && !grabber->isEnabled();
+ bool stealThisEvent = d->stealMouse;
+ if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
+ mouseEvent.setAccepted(false);
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
+ }
+ }
+ mouseEvent.setScenePos(event->scenePos());
+ mouseEvent.setLastScenePos(event->lastScenePos());
+ mouseEvent.setPos(mapFromScene(event->scenePos()));
+ mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
+
+ switch(mouseEvent.type()) {
+ case QEvent::GraphicsSceneMouseMove:
+ d->handleMouseMoveEvent(&mouseEvent);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ if (d->pressed) // we are already pressed - this is a delayed replay
+ return false;
+
+ d->handleMousePressEvent(&mouseEvent);
+ d->captureDelayedPress(event);
+ stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ if (d->delayedPressEvent) {
+ // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
+ // so we reset the grabber
+ if (c->mouseGrabberItem() == d->delayedPressTarget)
+ d->delayedPressTarget->ungrabMouse();
+ //Use the event handler that will take care of finding the proper item to propagate the event
+ QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
+ d->clearDelayedPress();
+ // We send the release
+ canvas()->sendEvent(c->mouseGrabberItem(), event);
+ // And the event has been consumed
+ d->stealMouse = false;
+ d->pressed = false;
+ return true;
+ }
+ d->handleMouseReleaseEvent(&mouseEvent);
+ break;
+ default:
+ break;
+ }
+ grabber = qobject_cast<QSGItem*>(c->mouseGrabberItem());
+ if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
+ d->clearDelayedPress();
+ grabMouse();
+ }
+
+ return stealThisEvent || d->delayedPressEvent || disabledItem;
+ } else if (d->lastPosTime.isValid()) {
+ d->lastPosTime.invalidate();
+ }
+ if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
+ d->clearDelayedPress();
+ d->stealMouse = false;
+ d->pressed = false;
+ }
+ return false;
+}
+
+
+bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e)
+{
+ Q_D(QSGFlickable);
+ if (!isVisible() || !d->interactive)
+ return QSGItem::childMouseEventFilter(i, e);
+ switch (e->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
+ default:
+ break;
+ }
+
+ return QSGItem::childMouseEventFilter(i, e);
+}
+
+qreal QSGFlickable::maximumFlickVelocity() const
+{
+ Q_D(const QSGFlickable);
+ return d->maxVelocity;
+}
+
+void QSGFlickable::setMaximumFlickVelocity(qreal v)
+{
+ Q_D(QSGFlickable);
+ if (v == d->maxVelocity)
+ return;
+ d->maxVelocity = v;
+ emit maximumFlickVelocityChanged();
+}
+
+qreal QSGFlickable::flickDeceleration() const
+{
+ Q_D(const QSGFlickable);
+ return d->deceleration;
+}
+
+void QSGFlickable::setFlickDeceleration(qreal deceleration)
+{
+ Q_D(QSGFlickable);
+ if (deceleration == d->deceleration)
+ return;
+ d->deceleration = deceleration;
+ emit flickDecelerationChanged();
+}
+
+bool QSGFlickable::isFlicking() const
+{
+ Q_D(const QSGFlickable);
+ return d->flickingHorizontally || d->flickingVertically;
+}
+
+bool QSGFlickable::isFlickingHorizontally() const
+{
+ Q_D(const QSGFlickable);
+ return d->flickingHorizontally;
+}
+
+bool QSGFlickable::isFlickingVertically() const
+{
+ Q_D(const QSGFlickable);
+ return d->flickingVertically;
+}
+
+int QSGFlickable::pressDelay() const
+{
+ Q_D(const QSGFlickable);
+ return d->pressDelay;
+}
+
+void QSGFlickable::setPressDelay(int delay)
+{
+ Q_D(QSGFlickable);
+ if (d->pressDelay == delay)
+ return;
+ d->pressDelay = delay;
+ emit pressDelayChanged();
+}
+
+
+bool QSGFlickable::isMoving() const
+{
+ Q_D(const QSGFlickable);
+ return d->movingHorizontally || d->movingVertically;
+}
+
+bool QSGFlickable::isMovingHorizontally() const
+{
+ Q_D(const QSGFlickable);
+ return d->movingHorizontally;
+}
+
+bool QSGFlickable::isMovingVertically() const
+{
+ Q_D(const QSGFlickable);
+ return d->movingVertically;
+}
+
+void QSGFlickable::movementStarting()
+{
+ Q_D(QSGFlickable);
+ if (d->hMoved && !d->movingHorizontally) {
+ d->movingHorizontally = true;
+ emit movingChanged();
+ emit movingHorizontallyChanged();
+ if (!d->movingVertically)
+ emit movementStarted();
+ }
+ else if (d->vMoved && !d->movingVertically) {
+ d->movingVertically = true;
+ emit movingChanged();
+ emit movingVerticallyChanged();
+ if (!d->movingHorizontally)
+ emit movementStarted();
+ }
+}
+
+void QSGFlickable::movementEnding()
+{
+ Q_D(QSGFlickable);
+ movementXEnding();
+ movementYEnding();
+ d->hData.smoothVelocity.setValue(0);
+ d->vData.smoothVelocity.setValue(0);
+}
+
+void QSGFlickable::movementXEnding()
+{
+ Q_D(QSGFlickable);
+ if (d->flickingHorizontally) {
+ d->flickingHorizontally = false;
+ emit flickingChanged();
+ emit flickingHorizontallyChanged();
+ if (!d->flickingVertically)
+ emit flickEnded();
+ }
+ if (!d->pressed && !d->stealMouse) {
+ if (d->movingHorizontally) {
+ d->movingHorizontally = false;
+ d->hMoved = false;
+ emit movingChanged();
+ emit movingHorizontallyChanged();
+ if (!d->movingVertically)
+ emit movementEnded();
+ }
+ }
+ d->hData.fixingUp = false;
+}
+
+void QSGFlickable::movementYEnding()
+{
+ Q_D(QSGFlickable);
+ if (d->flickingVertically) {
+ d->flickingVertically = false;
+ emit flickingChanged();
+ emit flickingVerticallyChanged();
+ if (!d->flickingHorizontally)
+ emit flickEnded();
+ }
+ if (!d->pressed && !d->stealMouse) {
+ if (d->movingVertically) {
+ d->movingVertically = false;
+ d->vMoved = false;
+ emit movingChanged();
+ emit movingVerticallyChanged();
+ if (!d->movingHorizontally)
+ emit movementEnded();
+ }
+ }
+ d->vData.fixingUp = false;
+}
+
+void QSGFlickablePrivate::updateVelocity()
+{
+ Q_Q(QSGFlickable);
+ emit q->horizontalVelocityChanged();
+ emit q->verticalVelocityChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgflickable_p.h b/src/declarative/items/qsgflickable_p.h
new file mode 100644
index 0000000000..c1ed024527
--- /dev/null
+++ b/src/declarative/items/qsgflickable_p.h
@@ -0,0 +1,230 @@
+// Commit: 1bcddaaf318fc37c71c5191913f3487c49444ec6
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGFLICKABLE_P_H
+#define QSGFLICKABLE_P_H
+
+#include "qsgitem.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGFlickablePrivate;
+class QSGFlickableVisibleArea;
+class Q_AUTOTEST_EXPORT QSGFlickable : public QSGItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
+ Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentHeight NOTIFY contentHeightChanged)
+ Q_PROPERTY(qreal contentX READ contentX WRITE setContentX NOTIFY contentXChanged)
+ Q_PROPERTY(qreal contentY READ contentY WRITE setContentY NOTIFY contentYChanged)
+ Q_PROPERTY(QSGItem *contentItem READ contentItem CONSTANT)
+
+ Q_PROPERTY(qreal horizontalVelocity READ horizontalVelocity NOTIFY horizontalVelocityChanged)
+ Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged)
+
+ Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged)
+ Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged)
+ Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged)
+ Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged)
+ Q_PROPERTY(bool movingHorizontally READ isMovingHorizontally NOTIFY movingHorizontallyChanged)
+ Q_PROPERTY(bool movingVertically READ isMovingVertically NOTIFY movingVerticallyChanged)
+ Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged)
+ Q_PROPERTY(bool flickingHorizontally READ isFlickingHorizontally NOTIFY flickingHorizontallyChanged)
+ Q_PROPERTY(bool flickingVertically READ isFlickingVertically NOTIFY flickingVerticallyChanged)
+ Q_PROPERTY(FlickableDirection flickableDirection READ flickableDirection WRITE setFlickableDirection NOTIFY flickableDirectionChanged)
+
+ Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
+ Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged)
+
+ Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged)
+ Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged)
+ Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged)
+ Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged)
+
+ Q_PROPERTY(QSGFlickableVisibleArea *visibleArea READ visibleArea CONSTANT)
+
+ Q_PROPERTY(QDeclarativeListProperty<QObject> flickableData READ flickableData)
+ Q_PROPERTY(QDeclarativeListProperty<QSGItem> flickableChildren READ flickableChildren)
+ Q_CLASSINFO("DefaultProperty", "flickableData")
+
+ Q_ENUMS(FlickableDirection)
+ Q_ENUMS(BoundsBehavior)
+
+public:
+ QSGFlickable(QSGItem *parent=0);
+ ~QSGFlickable();
+
+ QDeclarativeListProperty<QObject> flickableData();
+ QDeclarativeListProperty<QSGItem> flickableChildren();
+
+ enum BoundsBehavior { StopAtBounds, DragOverBounds, DragAndOvershootBounds };
+ BoundsBehavior boundsBehavior() const;
+ void setBoundsBehavior(BoundsBehavior);
+
+ qreal contentWidth() const;
+ void setContentWidth(qreal);
+
+ qreal contentHeight() const;
+ void setContentHeight(qreal);
+
+ qreal contentX() const;
+ virtual void setContentX(qreal pos);
+
+ qreal contentY() const;
+ virtual void setContentY(qreal pos);
+
+ bool isMoving() const;
+ bool isMovingHorizontally() const;
+ bool isMovingVertically() const;
+ bool isFlicking() const;
+ bool isFlickingHorizontally() const;
+ bool isFlickingVertically() const;
+
+ int pressDelay() const;
+ void setPressDelay(int delay);
+
+ qreal maximumFlickVelocity() const;
+ void setMaximumFlickVelocity(qreal);
+
+ qreal flickDeceleration() const;
+ void setFlickDeceleration(qreal);
+
+ bool isInteractive() const;
+ void setInteractive(bool);
+
+ qreal horizontalVelocity() const;
+ qreal verticalVelocity() const;
+
+ bool isAtXEnd() const;
+ bool isAtXBeginning() const;
+ bool isAtYEnd() const;
+ bool isAtYBeginning() const;
+
+ QSGItem *contentItem();
+
+ enum FlickableDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 };
+ FlickableDirection flickableDirection() const;
+ void setFlickableDirection(FlickableDirection);
+
+ Q_INVOKABLE void resizeContent(qreal w, qreal h, QPointF center);
+ Q_INVOKABLE void returnToBounds();
+
+Q_SIGNALS:
+ void contentWidthChanged();
+ void contentHeightChanged();
+ void contentXChanged();
+ void contentYChanged();
+ void movingChanged();
+ void movingHorizontallyChanged();
+ void movingVerticallyChanged();
+ void flickingChanged();
+ void flickingHorizontallyChanged();
+ void flickingVerticallyChanged();
+ void horizontalVelocityChanged();
+ void verticalVelocityChanged();
+ void isAtBoundaryChanged();
+ void flickableDirectionChanged();
+ void interactiveChanged();
+ void boundsBehaviorChanged();
+ void maximumFlickVelocityChanged();
+ void flickDecelerationChanged();
+ void pressDelayChanged();
+ void movementStarted();
+ void movementEnded();
+ void flickStarted();
+ void flickEnded();
+
+protected:
+ virtual bool childMouseEventFilter(QSGItem *, QEvent *);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void wheelEvent(QGraphicsSceneWheelEvent *event);
+ virtual void timerEvent(QTimerEvent *event);
+
+ QSGFlickableVisibleArea *visibleArea();
+
+protected Q_SLOTS:
+ virtual void ticked();
+ void movementStarting();
+ void movementEnding();
+
+protected:
+ void movementXEnding();
+ void movementYEnding();
+ virtual qreal minXExtent() const;
+ virtual qreal minYExtent() const;
+ virtual qreal maxXExtent() const;
+ virtual qreal maxYExtent() const;
+ qreal vWidth() const;
+ qreal vHeight() const;
+ virtual void viewportMoved();
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+ void mouseUngrabEvent();
+ bool sendMouseEvent(QGraphicsSceneMouseEvent *event);
+
+ bool xflick() const;
+ bool yflick() const;
+ void cancelFlick();
+
+protected:
+ QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent);
+
+private:
+ Q_DISABLE_COPY(QSGFlickable)
+ Q_DECLARE_PRIVATE(QSGFlickable)
+ friend class QSGFlickableVisibleArea;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGFlickable)
+
+QT_END_HEADER
+
+#endif // QSGFLICKABLE_P_H
diff --git a/src/declarative/items/qsgflickable_p_p.h b/src/declarative/items/qsgflickable_p_p.h
new file mode 100644
index 0000000000..114db53668
--- /dev/null
+++ b/src/declarative/items/qsgflickable_p_p.h
@@ -0,0 +1,243 @@
+// Commit: 160f1867868cdea916923652b00484ed11f90aaa
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGFLICKABLE_P_P_H
+#define QSGFLICKABLE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgflickable_p.h"
+#include "qsgitem_p.h"
+#include "qsgitemchangelistener_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtCore/qdatetime.h>
+
+#include <private/qdeclarativetimeline_p_p.h>
+#include <private/qdeclarativeanimation_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// Really slow flicks can be annoying.
+const qreal MinimumFlickVelocity = 75.0;
+
+class QSGFlickableVisibleArea;
+class QSGFlickablePrivate : public QSGItemPrivate, public QSGItemChangeListener
+{
+ Q_DECLARE_PUBLIC(QSGFlickable)
+
+public:
+ static inline QSGFlickablePrivate *get(QSGFlickable *o) { return o->d_func(); }
+
+ QSGFlickablePrivate();
+ void init();
+
+ struct Velocity : public QDeclarativeTimeLineValue
+ {
+ Velocity(QSGFlickablePrivate *p)
+ : parent(p) {}
+ virtual void setValue(qreal v) {
+ if (v != value()) {
+ QDeclarativeTimeLineValue::setValue(v);
+ parent->updateVelocity();
+ }
+ }
+ QSGFlickablePrivate *parent;
+ };
+
+ struct AxisData {
+ AxisData(QSGFlickablePrivate *fp, void (QSGFlickablePrivate::*func)(qreal))
+ : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true)
+ , fixingUp(false), inOvershoot(false)
+ {}
+
+ void reset() {
+ velocityBuffer.clear();
+ dragStartOffset = 0;
+ fixingUp = false;
+ inOvershoot = false;
+ }
+
+ void addVelocitySample(qreal v, qreal maxVelocity);
+ void updateVelocity();
+
+ QDeclarativeTimeLineValueProxy<QSGFlickablePrivate> move;
+ qreal viewSize;
+ qreal pressPos;
+ qreal dragStartOffset;
+ qreal dragMinBound;
+ qreal dragMaxBound;
+ qreal velocity;
+ qreal flickTarget;
+ QSGFlickablePrivate::Velocity smoothVelocity;
+ QPODVector<qreal,10> velocityBuffer;
+ bool atEnd : 1;
+ bool atBeginning : 1;
+ bool fixingUp : 1;
+ bool inOvershoot : 1;
+ };
+
+ void flickX(qreal velocity);
+ void flickY(qreal velocity);
+ virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+ QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
+
+ void fixupX();
+ void fixupY();
+ virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
+
+ void updateBeginningEnd();
+
+ bool isOutermostPressDelay() const;
+ void captureDelayedPress(QGraphicsSceneMouseEvent *event);
+ void clearDelayedPress();
+
+ void setViewportX(qreal x);
+ void setViewportY(qreal y);
+
+ qreal overShootDistance(qreal size);
+
+ void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &);
+
+public:
+ QSGItem *contentItem;
+
+ AxisData hData;
+ AxisData vData;
+
+ QDeclarativeTimeLine timeline;
+ bool flickingHorizontally : 1;
+ bool flickingVertically : 1;
+ bool hMoved : 1;
+ bool vMoved : 1;
+ bool movingHorizontally : 1;
+ bool movingVertically : 1;
+ bool stealMouse : 1;
+ bool pressed : 1;
+ bool interactive : 1;
+ bool calcVelocity : 1;
+ QElapsedTimer lastPosTime;
+ QPointF lastPos;
+ QPointF pressPos;
+ QElapsedTimer pressTime;
+ qreal deceleration;
+ qreal maxVelocity;
+ QElapsedTimer velocityTime;
+ QPointF lastFlickablePosition;
+ qreal reportedVelocitySmoothing;
+ QGraphicsSceneMouseEvent *delayedPressEvent;
+ QSGItem *delayedPressTarget;
+ QBasicTimer delayedPressTimer;
+ int pressDelay;
+ int fixupDuration;
+
+ enum FixupMode { Normal, Immediate, ExtentChanged };
+ FixupMode fixupMode;
+
+ static void fixupY_callback(void *);
+ static void fixupX_callback(void *);
+
+ void updateVelocity();
+ int vTime;
+ QDeclarativeTimeLine velocityTimeline;
+ QSGFlickableVisibleArea *visibleArea;
+ QSGFlickable::FlickableDirection flickableDirection;
+ QSGFlickable::BoundsBehavior boundsBehavior;
+
+ void handleMousePressEvent(QGraphicsSceneMouseEvent *);
+ void handleMouseMoveEvent(QGraphicsSceneMouseEvent *);
+ void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *);
+
+ // flickableData property
+ static void data_append(QDeclarativeListProperty<QObject> *, QObject *);
+ static int data_count(QDeclarativeListProperty<QObject> *);
+ static QObject *data_at(QDeclarativeListProperty<QObject> *, int);
+ static void data_clear(QDeclarativeListProperty<QObject> *);
+};
+
+class QSGFlickableVisibleArea : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal xPosition READ xPosition NOTIFY xPositionChanged)
+ Q_PROPERTY(qreal yPosition READ yPosition NOTIFY yPositionChanged)
+ Q_PROPERTY(qreal widthRatio READ widthRatio NOTIFY widthRatioChanged)
+ Q_PROPERTY(qreal heightRatio READ heightRatio NOTIFY heightRatioChanged)
+
+public:
+ QSGFlickableVisibleArea(QSGFlickable *parent=0);
+
+ qreal xPosition() const;
+ qreal widthRatio() const;
+ qreal yPosition() const;
+ qreal heightRatio() const;
+
+ void updateVisible();
+
+signals:
+ void xPositionChanged(qreal xPosition);
+ void yPositionChanged(qreal yPosition);
+ void widthRatioChanged(qreal widthRatio);
+ void heightRatioChanged(qreal heightRatio);
+
+private:
+ QSGFlickable *flickable;
+ qreal m_xPosition;
+ qreal m_widthRatio;
+ qreal m_yPosition;
+ qreal m_heightRatio;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGFlickableVisibleArea)
+
+#endif // QSGFLICKABLE_P_P_H
diff --git a/src/declarative/items/qsgflipable.cpp b/src/declarative/items/qsgflipable.cpp
new file mode 100644
index 0000000000..a856d6360b
--- /dev/null
+++ b/src/declarative/items/qsgflipable.cpp
@@ -0,0 +1,255 @@
+// Commit: caee66da925949cf7aef2ff8e1a86c38dd6e6efd
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgflipable_p.h"
+#include "qsgitem_p.h"
+
+#include <private/qdeclarativeguard_p.h>
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+// XXX todo - i think this needs work and a bit of a re-think
+
+class QSGLocalTransform : public QSGTransform
+{
+ Q_OBJECT
+public:
+ QSGLocalTransform(QObject *parent) : QSGTransform(parent) {}
+
+ void setTransform(const QTransform &t) {
+ transform = t;
+ update();
+ }
+ virtual void applyTo(QMatrix4x4 *matrix) const {
+ *matrix *= transform;
+ }
+private:
+ QTransform transform;
+};
+
+class QSGFlipablePrivate : public QSGItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGFlipable)
+public:
+ QSGFlipablePrivate() : current(QSGFlipable::Front), front(0), back(0), sideDirty(false) {}
+
+ virtual void transformChanged();
+ void updateSide();
+ void setBackTransform();
+
+ QSGFlipable::Side current;
+ QDeclarativeGuard<QSGLocalTransform> backTransform;
+ QDeclarativeGuard<QSGItem> front;
+ QDeclarativeGuard<QSGItem> back;
+
+ bool sideDirty;
+ bool wantBackXFlipped;
+ bool wantBackYFlipped;
+};
+
+QSGFlipable::QSGFlipable(QSGItem *parent)
+: QSGItem(*(new QSGFlipablePrivate), parent)
+{
+}
+
+QSGFlipable::~QSGFlipable()
+{
+}
+
+QSGItem *QSGFlipable::front()
+{
+ Q_D(const QSGFlipable);
+ return d->front;
+}
+
+void QSGFlipable::setFront(QSGItem *front)
+{
+ Q_D(QSGFlipable);
+ if (d->front) {
+ qmlInfo(this) << tr("front is a write-once property");
+ return;
+ }
+ d->front = front;
+ d->front->setParentItem(this);
+ if (Back == d->current)
+ d->front->setOpacity(0.);
+ emit frontChanged();
+}
+
+QSGItem *QSGFlipable::back()
+{
+ Q_D(const QSGFlipable);
+ return d->back;
+}
+
+void QSGFlipable::setBack(QSGItem *back)
+{
+ Q_D(QSGFlipable);
+ if (d->back) {
+ qmlInfo(this) << tr("back is a write-once property");
+ return;
+ }
+ if (back == 0)
+ return;
+ d->back = back;
+ d->back->setParentItem(this);
+
+ d->backTransform = new QSGLocalTransform(d->back);
+ d->backTransform->prependToItem(d->back);
+
+ if (Front == d->current)
+ d->back->setOpacity(0.);
+ connect(back, SIGNAL(widthChanged()),
+ this, SLOT(retransformBack()));
+ connect(back, SIGNAL(heightChanged()),
+ this, SLOT(retransformBack()));
+ emit backChanged();
+}
+
+void QSGFlipable::retransformBack()
+{
+ Q_D(QSGFlipable);
+ if (d->current == QSGFlipable::Back && d->back)
+ d->setBackTransform();
+}
+
+QSGFlipable::Side QSGFlipable::side() const
+{
+ Q_D(const QSGFlipable);
+
+ const_cast<QSGFlipablePrivate *>(d)->updateSide();
+ return d->current;
+}
+
+void QSGFlipablePrivate::transformChanged()
+{
+ Q_Q(QSGFlipable);
+
+ if (!sideDirty) {
+ sideDirty = true;
+ q->polish();
+ }
+
+ QSGItemPrivate::transformChanged();
+}
+
+void QSGFlipable::updatePolish()
+{
+ Q_D(QSGFlipable);
+ d->updateSide();
+}
+
+// determination on the currently visible side of the flipable
+// has to be done on the complete scene transform to give
+// correct results.
+void QSGFlipablePrivate::updateSide()
+{
+ Q_Q(QSGFlipable);
+
+ if (!sideDirty)
+ return;
+
+ sideDirty = false;
+
+ QTransform sceneTransform;
+ itemToParentTransform(sceneTransform);
+
+ QPointF p1(0, 0);
+ QPointF p2(1, 0);
+ QPointF p3(1, 1);
+
+ QPointF scenep1 = sceneTransform.map(p1);
+ QPointF scenep2 = sceneTransform.map(p2);
+ QPointF scenep3 = sceneTransform.map(p3);
+#if 0
+ p1 = q->mapToParent(p1);
+ p2 = q->mapToParent(p2);
+ p3 = q->mapToParent(p3);
+#endif
+
+ qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) -
+ (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x());
+
+ wantBackYFlipped = scenep1.x() >= scenep2.x();
+ wantBackXFlipped = scenep2.y() >= scenep3.y();
+
+ QSGFlipable::Side newSide;
+ if (cross > 0) {
+ newSide = QSGFlipable::Back;
+ } else {
+ newSide = QSGFlipable::Front;
+ }
+
+ if (newSide != current) {
+ current = newSide;
+ if (current == QSGFlipable::Back && back)
+ setBackTransform();
+ if (front)
+ front->setOpacity((current==QSGFlipable::Front)?1.:0.);
+ if (back)
+ back->setOpacity((current==QSGFlipable::Back)?1.:0.);
+ emit q->sideChanged();
+ }
+}
+
+/* Depends on the width/height of the back item, and so needs reevaulating
+ if those change.
+*/
+void QSGFlipablePrivate::setBackTransform()
+{
+ QTransform mat;
+ mat.translate(back->width()/2,back->height()/2);
+ if (back->width() && wantBackYFlipped)
+ mat.rotate(180, Qt::YAxis);
+ if (back->height() && wantBackXFlipped)
+ mat.rotate(180, Qt::XAxis);
+ mat.translate(-back->width()/2,-back->height()/2);
+
+ if (backTransform)
+ backTransform->setTransform(mat);
+}
+
+QT_END_NAMESPACE
+
+#include "qsgflipable.moc"
diff --git a/src/declarative/items/qsgflipable_p.h b/src/declarative/items/qsgflipable_p.h
new file mode 100644
index 0000000000..02178adca8
--- /dev/null
+++ b/src/declarative/items/qsgflipable_p.h
@@ -0,0 +1,104 @@
+// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGFLIPABLE_P_H
+#define QSGFLIPABLE_P_H
+
+#include "qsgitem.h"
+
+#include <QtGui/qtransform.h>
+#include <QtGui/qvector3d.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGFlipablePrivate;
+class Q_AUTOTEST_EXPORT QSGFlipable : public QSGItem
+{
+ Q_OBJECT
+
+ Q_ENUMS(Side)
+ Q_PROPERTY(QSGItem *front READ front WRITE setFront NOTIFY frontChanged)
+ Q_PROPERTY(QSGItem *back READ back WRITE setBack NOTIFY backChanged)
+ Q_PROPERTY(Side side READ side NOTIFY sideChanged)
+ //### flipAxis
+ //### flipRotation
+public:
+ QSGFlipable(QSGItem *parent=0);
+ ~QSGFlipable();
+
+ QSGItem *front();
+ void setFront(QSGItem *);
+
+ QSGItem *back();
+ void setBack(QSGItem *);
+
+ enum Side { Front, Back };
+ Side side() const;
+
+Q_SIGNALS:
+ void frontChanged();
+ void backChanged();
+ void sideChanged();
+
+protected:
+ virtual void updatePolish();
+
+private Q_SLOTS:
+ void retransformBack();
+
+private:
+ Q_DISABLE_COPY(QSGFlipable)
+ Q_DECLARE_PRIVATE(QSGFlipable)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGFlipable)
+
+QT_END_HEADER
+
+#endif // QSGFLIPABLE_P_H
diff --git a/src/declarative/items/qsgfocusscope.cpp b/src/declarative/items/qsgfocusscope.cpp
new file mode 100644
index 0000000000..84f19b1671
--- /dev/null
+++ b/src/declarative/items/qsgfocusscope.cpp
@@ -0,0 +1,57 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgfocusscope_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGFocusScope::QSGFocusScope(QSGItem *parent)
+: QSGItem(parent)
+{
+ setFlag(ItemIsFocusScope);
+}
+
+QSGFocusScope::~QSGFocusScope()
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgfocusscope_p.h b/src/declarative/items/qsgfocusscope_p.h
new file mode 100644
index 0000000000..ceffd9f089
--- /dev/null
+++ b/src/declarative/items/qsgfocusscope_p.h
@@ -0,0 +1,68 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGFOCUSSCOPE_P_H
+#define QSGFOCUSSCOPE_P_H
+
+#include "qsgitem.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_AUTOTEST_EXPORT QSGFocusScope : public QSGItem
+{
+ Q_OBJECT
+public:
+ QSGFocusScope(QSGItem *parent=0);
+ virtual ~QSGFocusScope();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGFocusScope)
+
+QT_END_HEADER
+
+#endif // QSGFOCUSSCOPE_P_H
diff --git a/src/declarative/items/qsggridview.cpp b/src/declarative/items/qsggridview.cpp
new file mode 100644
index 0000000000..b65cd39e8e
--- /dev/null
+++ b/src/declarative/items/qsggridview.cpp
@@ -0,0 +1,2661 @@
+// Commit: 806f031efeda71d3f4d7d2f949b437493e79cf52
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsggridview_p.h"
+#include "qsgvisualitemmodel_p.h"
+#include "qsgflickable_p_p.h"
+
+#include <private/qdeclarativesmoothedanimation_p_p.h>
+#include <private/qlistmodelinterface_p.h>
+
+#include <QtGui/qevent.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qcoreapplication.h>
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+//----------------------------------------------------------------------------
+
+class FxGridItemSG
+{
+public:
+ FxGridItemSG(QSGItem *i, QSGGridView *v) : item(i), view(v) {
+ attached = static_cast<QSGGridViewAttached*>(qmlAttachedPropertiesObject<QSGGridView>(item));
+ if (attached)
+ attached->setView(view);
+ }
+ ~FxGridItemSG() {}
+
+ qreal rowPos() const {
+ qreal rowPos = 0;
+ if (view->flow() == QSGGridView::LeftToRight) {
+ rowPos = item->y();
+ } else {
+ if (view->effectiveLayoutDirection() == Qt::RightToLeft)
+ rowPos = -view->cellWidth()-item->x();
+ else
+ rowPos = item->x();
+ }
+ return rowPos;
+ }
+ qreal colPos() const {
+ qreal colPos = 0;
+ if (view->flow() == QSGGridView::LeftToRight) {
+ if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
+ int colSize = view->cellWidth();
+ int columns = view->width()/colSize;
+ colPos = colSize * (columns-1) - item->x();
+ } else {
+ colPos = item->x();
+ }
+ } else {
+ colPos = item->y();
+ }
+
+ return colPos;
+ }
+ qreal endRowPos() const {
+ if (view->flow() == QSGGridView::LeftToRight) {
+ return item->y() + view->cellHeight() - 1;
+ } else {
+ if (view->effectiveLayoutDirection() == Qt::RightToLeft)
+ return -item->x() - 1;
+ else
+ return item->x() + view->cellWidth() - 1;
+ }
+ }
+ void setPosition(qreal col, qreal row) {
+ if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
+ if (view->flow() == QSGGridView::LeftToRight) {
+ int columns = view->width()/view->cellWidth();
+ item->setPos(QPointF((view->cellWidth() * (columns-1) - col), row));
+ } else {
+ item->setPos(QPointF(-view->cellWidth()-row, col));
+ }
+ } else {
+ if (view->flow() == QSGGridView::LeftToRight)
+ item->setPos(QPointF(col, row));
+ else
+ item->setPos(QPointF(row, col));
+ }
+ }
+ bool contains(qreal x, qreal y) const {
+ return (x >= item->x() && x < item->x() + view->cellWidth() &&
+ y >= item->y() && y < item->y() + view->cellHeight());
+ }
+
+ QSGItem *item;
+ QSGGridView *view;
+ QSGGridViewAttached *attached;
+ int index;
+};
+
+//----------------------------------------------------------------------------
+
+class QSGGridViewPrivate : public QSGFlickablePrivate
+{
+ Q_DECLARE_PUBLIC(QSGGridView)
+
+public:
+ QSGGridViewPrivate()
+ : currentItem(0), layoutDirection(Qt::LeftToRight), flow(QSGGridView::LeftToRight)
+ , visibleIndex(0) , currentIndex(-1)
+ , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0)
+ , highlightRangeStart(0), highlightRangeEnd(0)
+ , highlightRange(QSGGridView::NoHighlightRange)
+ , highlightComponent(0), highlight(0), trackedItem(0)
+ , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0)
+ , highlightMoveDuration(150)
+ , footerComponent(0), footer(0), headerComponent(0), header(0)
+ , bufferMode(BufferBefore | BufferAfter), snapMode(QSGGridView::NoSnap)
+ , ownModel(false), wrap(false), autoHighlight(true)
+ , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false)
+ , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false)
+ , highlightRangeStartValid(false), highlightRangeEndValid(false) {}
+
+ void init();
+ void clear();
+ FxGridItemSG *createItem(int modelIndex);
+ void releaseItem(FxGridItemSG *item);
+ void refill(qreal from, qreal to, bool doBuffer=false);
+
+ void updateGrid();
+ void scheduleLayout();
+ void layout();
+ void updateUnrequestedIndexes();
+ void updateUnrequestedPositions();
+ void updateTrackedItem();
+ void createHighlight();
+ void updateHighlight();
+ void updateCurrent(int modelIndex);
+ void updateHeader();
+ void updateFooter();
+ void fixupPosition();
+
+ FxGridItemSG *visibleItem(int modelIndex) const {
+ if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
+ for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
+ FxGridItemSG *item = visibleItems.at(i);
+ if (item->index == modelIndex)
+ return item;
+ }
+ }
+ return 0;
+ }
+
+ bool isRightToLeftTopToBottom() const {
+ Q_Q(const QSGGridView);
+ return flow == QSGGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft;
+ }
+
+ void regenerate() {
+ Q_Q(QSGGridView);
+ if (q->isComponentComplete()) {
+ clear();
+ updateGrid();
+ setPosition(0);
+ q->refill();
+ updateCurrent(currentIndex);
+ }
+ }
+
+ void mirrorChange() {
+ Q_Q(QSGGridView);
+ regenerate();
+ emit q->effectiveLayoutDirectionChanged();
+ }
+
+ qreal position() const {
+ Q_Q(const QSGGridView);
+ return flow == QSGGridView::LeftToRight ? q->contentY() : q->contentX();
+ }
+ void setPosition(qreal pos) {
+ Q_Q(QSGGridView);
+ if (flow == QSGGridView::LeftToRight) {
+ q->QSGFlickable::setContentY(pos);
+ q->QSGFlickable::setContentX(0);
+ } else {
+ if (q->effectiveLayoutDirection() == Qt::LeftToRight)
+ q->QSGFlickable::setContentX(pos);
+ else
+ q->QSGFlickable::setContentX(-pos-size());
+ q->QSGFlickable::setContentY(0);
+ }
+ }
+ int size() const {
+ Q_Q(const QSGGridView);
+ return flow == QSGGridView::LeftToRight ? q->height() : q->width();
+ }
+ qreal originPosition() const {
+ qreal pos = 0;
+ if (!visibleItems.isEmpty())
+ pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize();
+ return pos;
+ }
+
+ qreal lastPosition() const {
+ qreal pos = 0;
+ if (model && model->count())
+ pos = rowPosAt(model->count() - 1) + rowSize();
+ return pos;
+ }
+
+ qreal startPosition() const {
+ return isRightToLeftTopToBottom() ? -lastPosition()+1 : originPosition();
+ }
+
+ qreal endPosition() const {
+ return isRightToLeftTopToBottom() ? -originPosition()+1 : lastPosition();
+
+ }
+
+ bool isValid() const {
+ return model && model->count() && model->isValid();
+ }
+
+ int rowSize() const {
+ return flow == QSGGridView::LeftToRight ? cellHeight : cellWidth;
+ }
+ int colSize() const {
+ return flow == QSGGridView::LeftToRight ? cellWidth : cellHeight;
+ }
+
+ qreal colPosAt(int modelIndex) const {
+ if (FxGridItemSG *item = visibleItem(modelIndex))
+ return item->colPos();
+ if (!visibleItems.isEmpty()) {
+ if (modelIndex < visibleIndex) {
+ int count = (visibleIndex - modelIndex) % columns;
+ int col = visibleItems.first()->colPos() / colSize();
+ col = (columns - count + col) % columns;
+ return col * colSize();
+ } else {
+ int count = columns - 1 - (modelIndex - visibleItems.last()->index - 1) % columns;
+ return visibleItems.last()->colPos() - count * colSize();
+ }
+ } else {
+ return (modelIndex % columns) * colSize();
+ }
+ return 0;
+ }
+ qreal rowPosAt(int modelIndex) const {
+ if (FxGridItemSG *item = visibleItem(modelIndex))
+ return item->rowPos();
+ if (!visibleItems.isEmpty()) {
+ if (modelIndex < visibleIndex) {
+ int firstCol = visibleItems.first()->colPos() / colSize();
+ int col = visibleIndex - modelIndex + (columns - firstCol - 1);
+ int rows = col / columns;
+ return visibleItems.first()->rowPos() - rows * rowSize();
+ } else {
+ int count = modelIndex - visibleItems.last()->index;
+ int col = visibleItems.last()->colPos() + count * colSize();
+ int rows = col / (columns * colSize());
+ return visibleItems.last()->rowPos() + rows * rowSize();
+ }
+ } else {
+ qreal pos = (modelIndex / columns) * rowSize();
+ if (header)
+ pos += headerSize();
+ return pos;
+ }
+ return 0;
+ }
+
+ FxGridItemSG *firstVisibleItem() const {
+ const qreal pos = isRightToLeftTopToBottom() ? -position()-size() : position();
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxGridItemSG *item = visibleItems.at(i);
+ if (item->index != -1 && item->endRowPos() > pos)
+ return item;
+ }
+ return visibleItems.count() ? visibleItems.first() : 0;
+ }
+
+ int lastVisibleIndex() const {
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxGridItemSG *item = visibleItems.at(i);
+ if (item->index != -1)
+ return item->index;
+ }
+ return -1;
+ }
+
+ // Map a model index to visibleItems list index.
+ // These may differ if removed items are still present in the visible list,
+ // e.g. doing a removal animation
+ int mapFromModel(int modelIndex) const {
+ if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
+ return -1;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxGridItemSG *listItem = visibleItems.at(i);
+ if (listItem->index == modelIndex)
+ return i + visibleIndex;
+ if (listItem->index > modelIndex)
+ return -1;
+ }
+ return -1; // Not in visibleList
+ }
+
+ qreal snapPosAt(qreal pos) const {
+ Q_Q(const QSGGridView);
+ qreal snapPos = 0;
+ if (!visibleItems.isEmpty()) {
+ pos += rowSize()/2;
+ snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize();
+ snapPos = pos - fmodf(pos - snapPos, qreal(rowSize()));
+ qreal maxExtent;
+ qreal minExtent;
+ if (isRightToLeftTopToBottom()) {
+ maxExtent = q->minXExtent();
+ minExtent = q->maxXExtent();
+ } else {
+ maxExtent = flow == QSGGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent();
+ minExtent = flow == QSGGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent();
+ }
+ if (snapPos > maxExtent)
+ snapPos = maxExtent;
+ if (snapPos < minExtent)
+ snapPos = minExtent;
+ }
+ return snapPos;
+ }
+
+ FxGridItemSG *snapItemAt(qreal pos) {
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxGridItemSG *item = visibleItems[i];
+ if (item->index == -1)
+ continue;
+ qreal itemTop = item->rowPos();
+ if (itemTop+rowSize()/2 >= pos && itemTop - rowSize()/2 <= pos)
+ return item;
+ }
+ return 0;
+ }
+
+ int snapIndex() {
+ int index = currentIndex;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxGridItemSG *item = visibleItems[i];
+ if (item->index == -1)
+ continue;
+ qreal itemTop = item->rowPos();
+ if (itemTop >= highlight->rowPos()-rowSize()/2 && itemTop < highlight->rowPos()+rowSize()/2) {
+ index = item->index;
+ if (item->colPos() >= highlight->colPos()-colSize()/2 && item->colPos() < highlight->colPos()+colSize()/2)
+ return item->index;
+ }
+ }
+ return index;
+ }
+
+ qreal headerSize() const {
+ if (!header)
+ return 0.0;
+
+ return flow == QSGGridView::LeftToRight
+ ? header->item->height()
+ : header->item->width();
+ }
+
+
+ virtual void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
+ Q_Q(const QSGGridView);
+ QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
+ if (item == q) {
+ if (newGeometry.height() != oldGeometry.height()
+ || newGeometry.width() != oldGeometry.width()) {
+ if (q->isComponentComplete()) {
+ updateGrid();
+ scheduleLayout();
+ }
+ }
+ } else if ((header && header->item == item) || (footer && footer->item == item)) {
+ if (header)
+ updateHeader();
+ if (footer)
+ updateFooter();
+ }
+ }
+
+ void positionViewAtIndex(int index, int mode);
+ virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
+ virtual void flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+ QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
+
+ // for debugging only
+ void checkVisible() const {
+ int skip = 0;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxGridItemSG *listItem = visibleItems.at(i);
+ if (listItem->index == -1) {
+ ++skip;
+ } else if (listItem->index != visibleIndex + i - skip) {
+ for (int j = 0; j < visibleItems.count(); j++)
+ qDebug() << " index" << j << "item index" << visibleItems.at(j)->index;
+ qFatal("index %d %d %d", visibleIndex, i, listItem->index);
+ }
+ }
+ }
+
+ QDeclarativeGuard<QSGVisualModel> model;
+ QVariant modelVariant;
+ QList<FxGridItemSG*> visibleItems;
+ QHash<QSGItem*,int> unrequestedItems;
+ FxGridItemSG *currentItem;
+ Qt::LayoutDirection layoutDirection;
+ QSGGridView::Flow flow;
+ int visibleIndex;
+ int currentIndex;
+ int cellWidth;
+ int cellHeight;
+ int columns;
+ int requestedIndex;
+ int itemCount;
+ qreal highlightRangeStart;
+ qreal highlightRangeEnd;
+ QSGGridView::HighlightRangeMode highlightRange;
+ QDeclarativeComponent *highlightComponent;
+ FxGridItemSG *highlight;
+ FxGridItemSG *trackedItem;
+ enum MovementReason { Other, SetIndex, Mouse };
+ MovementReason moveReason;
+ int buffer;
+ QSmoothedAnimation *highlightXAnimator;
+ QSmoothedAnimation *highlightYAnimator;
+ int highlightMoveDuration;
+ QDeclarativeComponent *footerComponent;
+ FxGridItemSG *footer;
+ QDeclarativeComponent *headerComponent;
+ FxGridItemSG *header;
+ enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
+ int bufferMode;
+ QSGGridView::SnapMode snapMode;
+
+ bool ownModel : 1;
+ bool wrap : 1;
+ bool autoHighlight : 1;
+ bool fixCurrentVisibility : 1;
+ bool lazyRelease : 1;
+ bool layoutScheduled : 1;
+ bool deferredRelease : 1;
+ bool haveHighlightRange : 1;
+ bool currentIndexCleared : 1;
+ bool highlightRangeStartValid : 1;
+ bool highlightRangeEndValid : 1;
+};
+
+void QSGGridViewPrivate::init()
+{
+ Q_Q(QSGGridView);
+ QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
+ QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
+ q->setFlag(QSGItem::ItemIsFocusScope);
+ q->setFlickableDirection(QSGFlickable::VerticalFlick);
+ addItemChangeListener(this, Geometry);
+}
+
+void QSGGridViewPrivate::clear()
+{
+ for (int i = 0; i < visibleItems.count(); ++i)
+ releaseItem(visibleItems.at(i));
+ visibleItems.clear();
+ visibleIndex = 0;
+ releaseItem(currentItem);
+ currentItem = 0;
+ createHighlight();
+ trackedItem = 0;
+ itemCount = 0;
+}
+
+FxGridItemSG *QSGGridViewPrivate::createItem(int modelIndex)
+{
+ Q_Q(QSGGridView);
+ // create object
+ requestedIndex = modelIndex;
+ FxGridItemSG *listItem = 0;
+ if (QSGItem *item = model->item(modelIndex, false)) {
+ listItem = new FxGridItemSG(item, q);
+ listItem->index = modelIndex;
+ if (model->completePending()) {
+ // complete
+ listItem->item->setZ(1);
+ listItem->item->setParentItem(q->contentItem());
+ model->completeItem();
+ } else {
+ listItem->item->setParentItem(q->contentItem());
+ }
+ unrequestedItems.remove(listItem->item);
+ }
+ requestedIndex = -1;
+ return listItem;
+}
+
+
+void QSGGridViewPrivate::releaseItem(FxGridItemSG *item)
+{
+ Q_Q(QSGGridView);
+ if (!item || !model)
+ return;
+ if (trackedItem == item) {
+ QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged()));
+ QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged()));
+ trackedItem = 0;
+ }
+ if (model->release(item->item) == 0) {
+ // item was not destroyed, and we no longer reference it.
+ unrequestedItems.insert(item->item, model->indexOf(item->item, q));
+ }
+ delete item;
+}
+
+void QSGGridViewPrivate::refill(qreal from, qreal to, bool doBuffer)
+{
+ Q_Q(QSGGridView);
+ if (!isValid() || !q->isComponentComplete())
+ return;
+ itemCount = model->count();
+ qreal bufferFrom = from - buffer;
+ qreal bufferTo = to + buffer;
+ qreal fillFrom = from;
+ qreal fillTo = to;
+ if (doBuffer && (bufferMode & BufferAfter))
+ fillTo = bufferTo;
+ if (doBuffer && (bufferMode & BufferBefore))
+ fillFrom = bufferFrom;
+
+ bool changed = false;
+
+ int colPos = colPosAt(visibleIndex);
+ int rowPos = rowPosAt(visibleIndex);
+ int modelIndex = visibleIndex;
+ if (visibleItems.count()) {
+ rowPos = visibleItems.last()->rowPos();
+ colPos = visibleItems.last()->colPos() + colSize();
+ if (colPos > colSize() * (columns-1)) {
+ colPos = 0;
+ rowPos += rowSize();
+ }
+ int i = visibleItems.count() - 1;
+ while (i > 0 && visibleItems.at(i)->index == -1)
+ --i;
+ modelIndex = visibleItems.at(i)->index + 1;
+ }
+
+ if (visibleItems.count() && (fillFrom > rowPos + rowSize()*2
+ || fillTo < rowPosAt(visibleIndex) - rowSize())) {
+ // We've jumped more than a page. Estimate which items are now
+ // visible and fill from there.
+ int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
+ for (int i = 0; i < visibleItems.count(); ++i)
+ releaseItem(visibleItems.at(i));
+ visibleItems.clear();
+ modelIndex += count;
+ if (modelIndex >= model->count())
+ modelIndex = model->count() - 1;
+ else if (modelIndex < 0)
+ modelIndex = 0;
+ modelIndex = modelIndex / columns * columns;
+ visibleIndex = modelIndex;
+ colPos = colPosAt(visibleIndex);
+ rowPos = rowPosAt(visibleIndex);
+ }
+
+ int colNum = colPos / colSize();
+
+ FxGridItemSG *item = 0;
+
+ // Item creation and release is staggered in order to avoid
+ // creating/releasing multiple items in one frame
+ // while flicking (as much as possible).
+ while (modelIndex < model->count() && rowPos <= fillTo + rowSize()*(columns - colNum)/(columns+1)) {
+// qDebug() << "refill: append item" << modelIndex;
+ if (!(item = createItem(modelIndex)))
+ break;
+ item->setPosition(colPos, rowPos);
+ visibleItems.append(item);
+ colPos += colSize();
+ colNum++;
+ if (colPos > colSize() * (columns-1)) {
+ colPos = 0;
+ colNum = 0;
+ rowPos += rowSize();
+ }
+ ++modelIndex;
+ changed = true;
+ if (doBuffer) // never buffer more than one item per frame
+ break;
+ }
+
+ if (visibleItems.count()) {
+ rowPos = visibleItems.first()->rowPos();
+ colPos = visibleItems.first()->colPos() - colSize();
+ if (colPos < 0) {
+ colPos = colSize() * (columns - 1);
+ rowPos -= rowSize();
+ }
+ }
+ colNum = colPos / colSize();
+ while (visibleIndex > 0 && rowPos + rowSize() - 1 >= fillFrom - rowSize()*(colNum+1)/(columns+1)){
+// qDebug() << "refill: prepend item" << visibleIndex-1 << "top pos" << rowPos << colPos;
+ if (!(item = createItem(visibleIndex-1)))
+ break;
+ --visibleIndex;
+ item->setPosition(colPos, rowPos);
+ visibleItems.prepend(item);
+ colPos -= colSize();
+ colNum--;
+ if (colPos < 0) {
+ colPos = colSize() * (columns - 1);
+ colNum = columns-1;
+ rowPos -= rowSize();
+ }
+ changed = true;
+ if (doBuffer) // never buffer more than one item per frame
+ break;
+ }
+
+ if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
+ while (visibleItems.count() > 1
+ && (item = visibleItems.first())
+ && item->rowPos()+rowSize()-1 < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) {
+ if (item->attached->delayRemove())
+ break;
+// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endRowPos();
+ if (item->index != -1)
+ visibleIndex++;
+ visibleItems.removeFirst();
+ releaseItem(item);
+ changed = true;
+ }
+ while (visibleItems.count() > 1
+ && (item = visibleItems.last())
+ && item->rowPos() > bufferTo + rowSize()*(columns - item->colPos()/colSize())/(columns+1)) {
+ if (item->attached->delayRemove())
+ break;
+// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1;
+ visibleItems.removeLast();
+ releaseItem(item);
+ changed = true;
+ }
+ deferredRelease = false;
+ } else {
+ deferredRelease = true;
+ }
+ if (changed) {
+ if (header)
+ updateHeader();
+ if (footer)
+ updateFooter();
+ if (flow == QSGGridView::LeftToRight)
+ q->setContentHeight(endPosition() - startPosition());
+ else
+ q->setContentWidth(endPosition() - startPosition());
+ } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
+ refill(from, to, true);
+ }
+ lazyRelease = false;
+}
+
+void QSGGridViewPrivate::updateGrid()
+{
+ Q_Q(QSGGridView);
+ columns = (int)qMax((flow == QSGGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.));
+ if (isValid()) {
+ if (flow == QSGGridView::LeftToRight)
+ q->setContentHeight(endPosition() - startPosition());
+ else
+ q->setContentWidth(lastPosition() - originPosition());
+ }
+}
+
+void QSGGridViewPrivate::scheduleLayout()
+{
+ Q_Q(QSGGridView);
+ if (!layoutScheduled) {
+ layoutScheduled = true;
+ q->polish();
+ }
+}
+
+void QSGGridViewPrivate::layout()
+{
+ Q_Q(QSGGridView);
+ layoutScheduled = false;
+ if (!isValid() && !visibleItems.count()) {
+ clear();
+ return;
+ }
+ if (visibleItems.count()) {
+ qreal rowPos = visibleItems.first()->rowPos();
+ qreal colPos = visibleItems.first()->colPos();
+ int col = visibleIndex % columns;
+ if (colPos != col * colSize()) {
+ colPos = col * colSize();
+ visibleItems.first()->setPosition(colPos, rowPos);
+ }
+ for (int i = 1; i < visibleItems.count(); ++i) {
+ FxGridItemSG *item = visibleItems.at(i);
+ colPos += colSize();
+ if (colPos > colSize() * (columns-1)) {
+ colPos = 0;
+ rowPos += rowSize();
+ }
+ item->setPosition(colPos, rowPos);
+ }
+ }
+ if (header)
+ updateHeader();
+ if (footer)
+ updateFooter();
+ q->refill();
+ updateHighlight();
+ moveReason = Other;
+ if (flow == QSGGridView::LeftToRight) {
+ q->setContentHeight(endPosition() - startPosition());
+ fixupY();
+ } else {
+ q->setContentWidth(endPosition() - startPosition());
+ fixupX();
+ }
+ updateUnrequestedPositions();
+}
+
+void QSGGridViewPrivate::updateUnrequestedIndexes()
+{
+ Q_Q(QSGGridView);
+ QHash<QSGItem*,int>::iterator it;
+ for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
+ *it = model->indexOf(it.key(), q);
+}
+
+void QSGGridViewPrivate::updateUnrequestedPositions()
+{
+ QHash<QSGItem*,int>::const_iterator it;
+ for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
+ QSGItem *item = it.key();
+ if (flow == QSGGridView::LeftToRight) {
+ item->setPos(QPointF(colPosAt(*it), rowPosAt(*it)));
+ } else {
+ if (isRightToLeftTopToBottom())
+ item->setPos(QPointF(-rowPosAt(*it)-item->width(), colPosAt(*it)));
+ else
+ item->setPos(QPointF(rowPosAt(*it), colPosAt(*it)));
+ }
+ }
+}
+
+void QSGGridViewPrivate::updateTrackedItem()
+{
+ Q_Q(QSGGridView);
+ FxGridItemSG *item = currentItem;
+ if (highlight)
+ item = highlight;
+
+ if (trackedItem && item != trackedItem) {
+ QObject::disconnect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged()));
+ QObject::disconnect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged()));
+ trackedItem = 0;
+ }
+
+ if (!trackedItem && item) {
+ trackedItem = item;
+ QObject::connect(trackedItem->item, SIGNAL(yChanged()), q, SLOT(trackedPositionChanged()));
+ QObject::connect(trackedItem->item, SIGNAL(xChanged()), q, SLOT(trackedPositionChanged()));
+ }
+ if (trackedItem)
+ q->trackedPositionChanged();
+}
+
+void QSGGridViewPrivate::createHighlight()
+{
+ Q_Q(QSGGridView);
+ bool changed = false;
+ if (highlight) {
+ if (trackedItem == highlight)
+ trackedItem = 0;
+ highlight->item->setParentItem(0);
+ highlight->item->deleteLater();
+ delete highlight;
+ highlight = 0;
+ delete highlightXAnimator;
+ delete highlightYAnimator;
+ highlightXAnimator = 0;
+ highlightYAnimator = 0;
+ changed = true;
+ }
+
+ if (currentItem) {
+ QSGItem *item = 0;
+ if (highlightComponent) {
+ QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = highlightComponent->create(highlightContext);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(highlightContext, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete highlightContext;
+ }
+ } else {
+ item = new QSGItem;
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ highlight = new FxGridItemSG(item, q);
+ if (currentItem && autoHighlight)
+ highlight->setPosition(currentItem->colPos(), currentItem->rowPos());
+ highlightXAnimator = new QSmoothedAnimation(q);
+ highlightXAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("x"));
+ highlightXAnimator->userDuration = highlightMoveDuration;
+ highlightYAnimator = new QSmoothedAnimation(q);
+ highlightYAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("y"));
+ highlightYAnimator->userDuration = highlightMoveDuration;
+ if (autoHighlight) {
+ highlightXAnimator->restart();
+ highlightYAnimator->restart();
+ }
+ changed = true;
+ }
+ }
+ if (changed)
+ emit q->highlightItemChanged();
+}
+
+void QSGGridViewPrivate::updateHighlight()
+{
+ if ((!currentItem && highlight) || (currentItem && !highlight))
+ createHighlight();
+ if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
+ // auto-update highlight
+ highlightXAnimator->to = currentItem->item->x();
+ highlightYAnimator->to = currentItem->item->y();
+ highlight->item->setWidth(currentItem->item->width());
+ highlight->item->setHeight(currentItem->item->height());
+ highlightXAnimator->restart();
+ highlightYAnimator->restart();
+ }
+ updateTrackedItem();
+}
+
+void QSGGridViewPrivate::updateCurrent(int modelIndex)
+{
+ Q_Q(QSGGridView);
+ if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
+ if (currentItem) {
+ currentItem->attached->setIsCurrentItem(false);
+ releaseItem(currentItem);
+ currentItem = 0;
+ currentIndex = modelIndex;
+ emit q->currentIndexChanged();
+ updateHighlight();
+ } else if (currentIndex != modelIndex) {
+ currentIndex = modelIndex;
+ emit q->currentIndexChanged();
+ }
+ return;
+ }
+
+ if (currentItem && currentIndex == modelIndex) {
+ updateHighlight();
+ return;
+ }
+
+ FxGridItemSG *oldCurrentItem = currentItem;
+ currentIndex = modelIndex;
+ currentItem = createItem(modelIndex);
+ fixCurrentVisibility = true;
+ if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
+ oldCurrentItem->attached->setIsCurrentItem(false);
+ if (currentItem) {
+ currentItem->setPosition(colPosAt(modelIndex), rowPosAt(modelIndex));
+ currentItem->item->setFocus(true);
+ currentItem->attached->setIsCurrentItem(true);
+ }
+ updateHighlight();
+ emit q->currentIndexChanged();
+ releaseItem(oldCurrentItem);
+}
+
+void QSGGridViewPrivate::updateFooter()
+{
+ Q_Q(QSGGridView);
+ if (!footer && footerComponent) {
+ QSGItem *item = 0;
+ QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = footerComponent->create(context);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(context, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete context;
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ item->setZ(1);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ footer = new FxGridItemSG(item, q);
+ }
+ }
+ if (footer) {
+ qreal colOffset = 0;
+ qreal rowOffset;
+ if (isRightToLeftTopToBottom()) {
+ rowOffset = footer->item->width()-cellWidth;
+ } else {
+ rowOffset = 0;
+ if (q->effectiveLayoutDirection() == Qt::RightToLeft)
+ colOffset = footer->item->width()-cellWidth;
+ }
+ if (visibleItems.count()) {
+ qreal endPos = lastPosition();
+ if (lastVisibleIndex() == model->count()-1) {
+ footer->setPosition(colOffset, endPos + rowOffset);
+ } else {
+ qreal visiblePos = isRightToLeftTopToBottom() ? -position() : position() + size();
+ if (endPos <= visiblePos || footer->endRowPos() < endPos + rowOffset)
+ footer->setPosition(colOffset, endPos + rowOffset);
+ }
+ } else {
+ qreal endPos = 0;
+ if (header) {
+ endPos += (flow == QSGGridView::LeftToRight) ? header->item->height() : header->item->width();
+ }
+ footer->setPosition(colOffset, endPos);
+ }
+ }
+}
+
+void QSGGridViewPrivate::updateHeader()
+{
+ Q_Q(QSGGridView);
+ if (!header && headerComponent) {
+ QSGItem *item = 0;
+ QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = headerComponent->create(context);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(context, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete context;
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ item->setZ(1);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ header = new FxGridItemSG(item, q);
+ }
+ }
+ if (header) {
+ qreal colOffset = 0;
+ qreal rowOffset;
+ if (isRightToLeftTopToBottom()) {
+ rowOffset = -cellWidth;
+ } else {
+ rowOffset = -headerSize();
+ if (q->effectiveLayoutDirection() == Qt::RightToLeft)
+ colOffset = header->item->width()-cellWidth;
+ }
+ if (visibleItems.count()) {
+ qreal startPos = originPosition();
+ if (visibleIndex == 0) {
+ header->setPosition(colOffset, startPos + rowOffset);
+ } else {
+ qreal tempPos = isRightToLeftTopToBottom() ? -position()-size() : position();
+ qreal headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos();
+ if (tempPos <= startPos || headerPos > startPos + rowOffset)
+ header->setPosition(colOffset, startPos + rowOffset);
+ }
+ } else {
+ header->setPosition(colOffset, 0);
+ }
+ }
+}
+
+void QSGGridViewPrivate::fixupPosition()
+{
+ moveReason = Other;
+ if (flow == QSGGridView::LeftToRight)
+ fixupY();
+ else
+ fixupX();
+}
+
+void QSGGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
+{
+ if ((flow == QSGGridView::TopToBottom && &data == &vData)
+ || (flow == QSGGridView::LeftToRight && &data == &hData))
+ return;
+
+ fixupMode = moveReason == Mouse ? fixupMode : Immediate;
+
+ qreal highlightStart;
+ qreal highlightEnd;
+ qreal viewPos;
+ if (isRightToLeftTopToBottom()) {
+ // Handle Right-To-Left exceptions
+ viewPos = -position()-size();
+ highlightStart = highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
+ highlightEnd = highlightRangeEndValid ? size()-highlightRangeStart : highlightRangeEnd;
+ } else {
+ viewPos = position();
+ highlightStart = highlightRangeStart;
+ highlightEnd = highlightRangeEnd;
+ }
+
+ if (snapMode != QSGGridView::NoSnap) {
+ qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position();
+ FxGridItemSG *topItem = snapItemAt(tempPosition+highlightStart);
+ FxGridItemSG *bottomItem = snapItemAt(tempPosition+highlightEnd);
+ qreal pos;
+ if (topItem && bottomItem && haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) {
+ qreal topPos = qMin(topItem->rowPos() - highlightStart, -maxExtent);
+ qreal bottomPos = qMax(bottomItem->rowPos() - highlightEnd, -minExtent);
+ pos = qAbs(data.move + topPos) < qAbs(data.move + bottomPos) ? topPos : bottomPos;
+ } else if (topItem) {
+ qreal headerPos = 0;
+ if (header)
+ headerPos = isRightToLeftTopToBottom() ? header->rowPos() + cellWidth - headerSize() : header->rowPos();
+ if (topItem->index == 0 && header && tempPosition+highlightStart < headerPos+headerSize()/2) {
+ pos = isRightToLeftTopToBottom() ? - headerPos + highlightStart - size() : headerPos - highlightStart;
+ } else {
+ if (isRightToLeftTopToBottom())
+ pos = qMax(qMin(-topItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent);
+ else
+ pos = qMax(qMin(topItem->rowPos() - highlightStart, -maxExtent), -minExtent);
+ }
+ } else if (bottomItem) {
+ if (isRightToLeftTopToBottom())
+ pos = qMax(qMin(-bottomItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent);
+ else
+ pos = qMax(qMin(bottomItem->rowPos() - highlightStart, -maxExtent), -minExtent);
+ } else {
+ QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
+ return;
+ }
+ if (currentItem && haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) {
+ updateHighlight();
+ qreal currPos = currentItem->rowPos();
+ if (isRightToLeftTopToBottom())
+ pos = -pos-size(); // Transform Pos if required
+ if (pos < currPos + rowSize() - highlightEnd)
+ pos = currPos + rowSize() - highlightEnd;
+ if (pos > currPos - highlightStart)
+ pos = currPos - highlightStart;
+ if (isRightToLeftTopToBottom())
+ pos = -pos-size(); // Untransform
+ }
+
+ qreal dist = qAbs(data.move + pos);
+ if (dist > 0) {
+ timeline.reset(data.move);
+ if (fixupMode != Immediate) {
+ timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
+ data.fixingUp = true;
+ } else {
+ timeline.set(data.move, -pos);
+ }
+ vTime = timeline.time();
+ }
+ } else if (haveHighlightRange && highlightRange == QSGGridView::StrictlyEnforceRange) {
+ if (currentItem) {
+ updateHighlight();
+ qreal pos = currentItem->rowPos();
+ if (viewPos < pos + rowSize() - highlightEnd)
+ viewPos = pos + rowSize() - highlightEnd;
+ if (viewPos > pos - highlightStart)
+ viewPos = pos - highlightStart;
+ if (isRightToLeftTopToBottom())
+ viewPos = -viewPos-size();
+ timeline.reset(data.move);
+ if (viewPos != position()) {
+ if (fixupMode != Immediate) {
+ timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
+ data.fixingUp = true;
+ } else {
+ timeline.set(data.move, -viewPos);
+ }
+ }
+ vTime = timeline.time();
+ }
+ } else {
+ QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
+ }
+ data.inOvershoot = false;
+ fixupMode = Normal;
+}
+
+void QSGGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+ QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
+{
+ Q_Q(QSGGridView);
+ data.fixingUp = false;
+ moveReason = Mouse;
+ if ((!haveHighlightRange || highlightRange != QSGGridView::StrictlyEnforceRange)
+ && snapMode == QSGGridView::NoSnap) {
+ QSGFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
+ return;
+ }
+ qreal maxDistance = 0;
+ qreal dataValue = isRightToLeftTopToBottom() ? -data.move.value()+size() : data.move.value();
+ // -ve velocity means list is moving up/left
+ if (velocity > 0) {
+ if (data.move.value() < minExtent) {
+ if (snapMode == QSGGridView::SnapOneRow) {
+ if (FxGridItemSG *item = firstVisibleItem())
+ maxDistance = qAbs(item->rowPos() + dataValue);
+ } else {
+ maxDistance = qAbs(minExtent - data.move.value());
+ }
+ }
+ if (snapMode == QSGGridView::NoSnap && highlightRange != QSGGridView::StrictlyEnforceRange)
+ data.flickTarget = minExtent;
+ } else {
+ if (data.move.value() > maxExtent) {
+ if (snapMode == QSGGridView::SnapOneRow) {
+ qreal pos = snapPosAt(-dataValue) + (isRightToLeftTopToBottom() ? 0 : rowSize());
+ maxDistance = qAbs(pos + dataValue);
+ } else {
+ maxDistance = qAbs(maxExtent - data.move.value());
+ }
+ }
+ if (snapMode == QSGGridView::NoSnap && highlightRange != QSGGridView::StrictlyEnforceRange)
+ data.flickTarget = maxExtent;
+ }
+ bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
+ qreal highlightStart = isRightToLeftTopToBottom() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
+ if (maxDistance > 0 || overShoot) {
+ // This mode requires the grid to stop exactly on a row boundary.
+ qreal v = velocity;
+ if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
+ if (v < 0)
+ v = -maxVelocity;
+ else
+ v = maxVelocity;
+ }
+ qreal accel = deceleration;
+ qreal v2 = v * v;
+ qreal overshootDist = 0.0;
+ if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGGridView::SnapOneRow) {
+ // + rowSize()/4 to encourage moving at least one item in the flick direction
+ qreal dist = v2 / (accel * 2.0) + rowSize()/4;
+ dist = qMin(dist, maxDistance);
+ if (v > 0)
+ dist = -dist;
+ qreal distTemp = isRightToLeftTopToBottom() ? -dist : dist;
+ data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
+ data.flickTarget = isRightToLeftTopToBottom() ? -data.flickTarget+size() : data.flickTarget;
+ qreal adjDist = -data.flickTarget + data.move.value();
+ if (qAbs(adjDist) > qAbs(dist)) {
+ // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
+ qreal adjv2 = accel * 2.0f * qAbs(adjDist);
+ if (adjv2 > v2) {
+ v2 = adjv2;
+ v = qSqrt(v2);
+ if (dist > 0)
+ v = -v;
+ }
+ }
+ dist = adjDist;
+ accel = v2 / (2.0f * qAbs(dist));
+ } else {
+ data.flickTarget = velocity > 0 ? minExtent : maxExtent;
+ overshootDist = overShoot ? overShootDistance(vSize) : 0;
+ }
+ timeline.reset(data.move);
+ timeline.accel(data.move, v, accel, maxDistance + overshootDist);
+ timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
+ if (!flickingHorizontally && q->xflick()) {
+ flickingHorizontally = true;
+ emit q->flickingChanged();
+ emit q->flickingHorizontallyChanged();
+ emit q->flickStarted();
+ }
+ if (!flickingVertically && q->yflick()) {
+ flickingVertically = true;
+ emit q->flickingChanged();
+ emit q->flickingVerticallyChanged();
+ emit q->flickStarted();
+ }
+ } else {
+ timeline.reset(data.move);
+ fixup(data, minExtent, maxExtent);
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+QSGGridView::QSGGridView(QSGItem *parent)
+ : QSGFlickable(*(new QSGGridViewPrivate), parent)
+{
+ Q_D(QSGGridView);
+ d->init();
+}
+
+QSGGridView::~QSGGridView()
+{
+ Q_D(QSGGridView);
+ d->clear();
+ if (d->ownModel)
+ delete d->model;
+ delete d->header;
+ delete d->footer;
+}
+
+// For internal use
+int QSGGridView::modelCount() const
+{
+ Q_D(const QSGGridView);
+ return d->model->count();
+}
+
+QVariant QSGGridView::model() const
+{
+ Q_D(const QSGGridView);
+ return d->modelVariant;
+}
+
+void QSGGridView::setModel(const QVariant &model)
+{
+ Q_D(QSGGridView);
+ if (d->modelVariant == model)
+ return;
+ if (d->model) {
+ disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
+ }
+ d->clear();
+ d->modelVariant = model;
+ QObject *object = qvariant_cast<QObject*>(model);
+ QSGVisualModel *vim = 0;
+ if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
+ if (d->ownModel) {
+ delete d->model;
+ d->ownModel = false;
+ }
+ d->model = vim;
+ } else {
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this), this);
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ dataModel->setModel(model);
+ }
+ if (d->model) {
+ d->bufferMode = QSGGridViewPrivate::BufferBefore | QSGGridViewPrivate::BufferAfter;
+ if (isComponentComplete()) {
+ refill();
+ if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
+ setCurrentIndex(0);
+ } else {
+ d->moveReason = QSGGridViewPrivate::SetIndex;
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
+ d->updateTrackedItem();
+ }
+ d->moveReason = QSGGridViewPrivate::Other;
+ }
+ }
+ connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
+ emit countChanged();
+ }
+ emit modelChanged();
+}
+
+QDeclarativeComponent *QSGGridView::delegate() const
+{
+ Q_D(const QSGGridView);
+ if (d->model) {
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ return dataModel->delegate();
+ }
+
+ return 0;
+}
+
+void QSGGridView::setDelegate(QDeclarativeComponent *delegate)
+{
+ Q_D(QSGGridView);
+ if (delegate == this->delegate())
+ return;
+
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this));
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
+ int oldCount = dataModel->count();
+ dataModel->setDelegate(delegate);
+ if (isComponentComplete()) {
+ for (int i = 0; i < d->visibleItems.count(); ++i)
+ d->releaseItem(d->visibleItems.at(i));
+ d->visibleItems.clear();
+ d->releaseItem(d->currentItem);
+ d->currentItem = 0;
+ refill();
+ d->moveReason = QSGGridViewPrivate::SetIndex;
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
+ d->updateTrackedItem();
+ }
+ d->moveReason = QSGGridViewPrivate::Other;
+ }
+ if (oldCount != dataModel->count())
+ emit countChanged();
+ emit delegateChanged();
+ }
+}
+
+int QSGGridView::currentIndex() const
+{
+ Q_D(const QSGGridView);
+ return d->currentIndex;
+}
+
+void QSGGridView::setCurrentIndex(int index)
+{
+ Q_D(QSGGridView);
+ if (d->requestedIndex >= 0) // currently creating item
+ return;
+ d->currentIndexCleared = (index == -1);
+ if (index == d->currentIndex)
+ return;
+ if (isComponentComplete() && d->isValid()) {
+ d->moveReason = QSGGridViewPrivate::SetIndex;
+ d->updateCurrent(index);
+ } else {
+ d->currentIndex = index;
+ emit currentIndexChanged();
+ }
+}
+
+QSGItem *QSGGridView::currentItem()
+{
+ Q_D(QSGGridView);
+ if (!d->currentItem)
+ return 0;
+ return d->currentItem->item;
+}
+
+QSGItem *QSGGridView::highlightItem()
+{
+ Q_D(QSGGridView);
+ if (!d->highlight)
+ return 0;
+ return d->highlight->item;
+}
+
+int QSGGridView::count() const
+{
+ Q_D(const QSGGridView);
+ if (d->model)
+ return d->model->count();
+ return 0;
+}
+
+QDeclarativeComponent *QSGGridView::highlight() const
+{
+ Q_D(const QSGGridView);
+ return d->highlightComponent;
+}
+
+void QSGGridView::setHighlight(QDeclarativeComponent *highlight)
+{
+ Q_D(QSGGridView);
+ if (highlight != d->highlightComponent) {
+ d->highlightComponent = highlight;
+ d->updateCurrent(d->currentIndex);
+ emit highlightChanged();
+ }
+}
+
+bool QSGGridView::highlightFollowsCurrentItem() const
+{
+ Q_D(const QSGGridView);
+ return d->autoHighlight;
+}
+
+void QSGGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
+{
+ Q_D(QSGGridView);
+ if (d->autoHighlight != autoHighlight) {
+ d->autoHighlight = autoHighlight;
+ if (autoHighlight) {
+ d->updateHighlight();
+ } else if (d->highlightXAnimator) {
+ d->highlightXAnimator->stop();
+ d->highlightYAnimator->stop();
+ }
+ }
+}
+
+int QSGGridView::highlightMoveDuration() const
+{
+ Q_D(const QSGGridView);
+ return d->highlightMoveDuration;
+}
+
+void QSGGridView::setHighlightMoveDuration(int duration)
+{
+ Q_D(QSGGridView);
+ if (d->highlightMoveDuration != duration) {
+ d->highlightMoveDuration = duration;
+ if (d->highlightYAnimator) {
+ d->highlightXAnimator->userDuration = d->highlightMoveDuration;
+ d->highlightYAnimator->userDuration = d->highlightMoveDuration;
+ }
+ emit highlightMoveDurationChanged();
+ }
+}
+
+qreal QSGGridView::preferredHighlightBegin() const
+{
+ Q_D(const QSGGridView);
+ return d->highlightRangeStart;
+}
+
+void QSGGridView::setPreferredHighlightBegin(qreal start)
+{
+ Q_D(QSGGridView);
+ d->highlightRangeStartValid = true;
+ if (d->highlightRangeStart == start)
+ return;
+ d->highlightRangeStart = start;
+ d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit preferredHighlightBeginChanged();
+}
+
+void QSGGridView::resetPreferredHighlightBegin()
+{
+ Q_D(QSGGridView);
+ d->highlightRangeStartValid = false;
+ if (d->highlightRangeStart == 0)
+ return;
+ d->highlightRangeStart = 0;
+ emit preferredHighlightBeginChanged();
+}
+
+qreal QSGGridView::preferredHighlightEnd() const
+{
+ Q_D(const QSGGridView);
+ return d->highlightRangeEnd;
+}
+
+void QSGGridView::setPreferredHighlightEnd(qreal end)
+{
+ Q_D(QSGGridView);
+ d->highlightRangeEndValid = true;
+ if (d->highlightRangeEnd == end)
+ return;
+ d->highlightRangeEnd = end;
+ d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit preferredHighlightEndChanged();
+}
+
+void QSGGridView::resetPreferredHighlightEnd()
+{
+ Q_D(QSGGridView);
+ d->highlightRangeEndValid = false;
+ if (d->highlightRangeEnd == 0)
+ return;
+ d->highlightRangeEnd = 0;
+ emit preferredHighlightEndChanged();
+}
+
+QSGGridView::HighlightRangeMode QSGGridView::highlightRangeMode() const
+{
+ Q_D(const QSGGridView);
+ return d->highlightRange;
+}
+
+void QSGGridView::setHighlightRangeMode(HighlightRangeMode mode)
+{
+ Q_D(QSGGridView);
+ if (d->highlightRange == mode)
+ return;
+ d->highlightRange = mode;
+ d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit highlightRangeModeChanged();
+}
+
+Qt::LayoutDirection QSGGridView::layoutDirection() const
+{
+ Q_D(const QSGGridView);
+ return d->layoutDirection;
+}
+
+void QSGGridView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
+{
+ Q_D(QSGGridView);
+ if (d->layoutDirection != layoutDirection) {
+ d->layoutDirection = layoutDirection;
+ d->regenerate();
+ emit layoutDirectionChanged();
+ emit effectiveLayoutDirectionChanged();
+ }
+}
+
+Qt::LayoutDirection QSGGridView::effectiveLayoutDirection() const
+{
+ Q_D(const QSGGridView);
+ if (d->effectiveLayoutMirror)
+ return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
+ else
+ return d->layoutDirection;
+}
+
+QSGGridView::Flow QSGGridView::flow() const
+{
+ Q_D(const QSGGridView);
+ return d->flow;
+}
+
+void QSGGridView::setFlow(Flow flow)
+{
+ Q_D(QSGGridView);
+ if (d->flow != flow) {
+ d->flow = flow;
+ if (d->flow == LeftToRight) {
+ setContentWidth(-1);
+ setFlickableDirection(QSGFlickable::VerticalFlick);
+ } else {
+ setContentHeight(-1);
+ setFlickableDirection(QSGFlickable::HorizontalFlick);
+ }
+ setContentX(0);
+ setContentY(0);
+ d->regenerate();
+ emit flowChanged();
+ }
+}
+
+bool QSGGridView::isWrapEnabled() const
+{
+ Q_D(const QSGGridView);
+ return d->wrap;
+}
+
+void QSGGridView::setWrapEnabled(bool wrap)
+{
+ Q_D(QSGGridView);
+ if (d->wrap == wrap)
+ return;
+ d->wrap = wrap;
+ emit keyNavigationWrapsChanged();
+}
+
+int QSGGridView::cacheBuffer() const
+{
+ Q_D(const QSGGridView);
+ return d->buffer;
+}
+
+void QSGGridView::setCacheBuffer(int buffer)
+{
+ Q_D(QSGGridView);
+ if (d->buffer != buffer) {
+ d->buffer = buffer;
+ if (isComponentComplete())
+ refill();
+ emit cacheBufferChanged();
+ }
+}
+
+int QSGGridView::cellWidth() const
+{
+ Q_D(const QSGGridView);
+ return d->cellWidth;
+}
+
+void QSGGridView::setCellWidth(int cellWidth)
+{
+ Q_D(QSGGridView);
+ if (cellWidth != d->cellWidth && cellWidth > 0) {
+ d->cellWidth = qMax(1, cellWidth);
+ d->updateGrid();
+ emit cellWidthChanged();
+ d->layout();
+ }
+}
+
+int QSGGridView::cellHeight() const
+{
+ Q_D(const QSGGridView);
+ return d->cellHeight;
+}
+
+void QSGGridView::setCellHeight(int cellHeight)
+{
+ Q_D(QSGGridView);
+ if (cellHeight != d->cellHeight && cellHeight > 0) {
+ d->cellHeight = qMax(1, cellHeight);
+ d->updateGrid();
+ emit cellHeightChanged();
+ d->layout();
+ }
+}
+
+QSGGridView::SnapMode QSGGridView::snapMode() const
+{
+ Q_D(const QSGGridView);
+ return d->snapMode;
+}
+
+void QSGGridView::setSnapMode(SnapMode mode)
+{
+ Q_D(QSGGridView);
+ if (d->snapMode != mode) {
+ d->snapMode = mode;
+ emit snapModeChanged();
+ }
+}
+
+QDeclarativeComponent *QSGGridView::footer() const
+{
+ Q_D(const QSGGridView);
+ return d->footerComponent;
+}
+
+void QSGGridView::setFooter(QDeclarativeComponent *footer)
+{
+ Q_D(QSGGridView);
+ if (d->footerComponent != footer) {
+ if (d->footer) {
+ // XXX todo - the original did scene()->removeItem(). Why?
+ d->footer->item->setParentItem(0);
+ d->footer->item->deleteLater();
+ delete d->footer;
+ d->footer = 0;
+ }
+ d->footerComponent = footer;
+ if (isComponentComplete()) {
+ d->updateFooter();
+ d->updateGrid();
+ d->fixupPosition();
+ }
+ emit footerChanged();
+ }
+}
+
+QDeclarativeComponent *QSGGridView::header() const
+{
+ Q_D(const QSGGridView);
+ return d->headerComponent;
+}
+
+void QSGGridView::setHeader(QDeclarativeComponent *header)
+{
+ Q_D(QSGGridView);
+ if (d->headerComponent != header) {
+ if (d->header) {
+ // XXX todo - the original did scene()->removeItem(). Why?
+ d->header->item->setParentItem(0);
+ d->header->item->deleteLater();
+ delete d->header;
+ d->header = 0;
+ }
+ d->headerComponent = header;
+ if (isComponentComplete()) {
+ d->updateHeader();
+ d->updateFooter();
+ d->updateGrid();
+ d->fixupPosition();
+ }
+ emit headerChanged();
+ }
+}
+
+void QSGGridView::setContentX(qreal pos)
+{
+ Q_D(QSGGridView);
+ // Positioning the view manually should override any current movement state
+ d->moveReason = QSGGridViewPrivate::Other;
+ QSGFlickable::setContentX(pos);
+}
+
+void QSGGridView::setContentY(qreal pos)
+{
+ Q_D(QSGGridView);
+ // Positioning the view manually should override any current movement state
+ d->moveReason = QSGGridViewPrivate::Other;
+ QSGFlickable::setContentY(pos);
+}
+
+void QSGGridView::updatePolish()
+{
+ Q_D(QSGGridView);
+ QSGFlickable::updatePolish();
+ d->layout();
+}
+
+void QSGGridView::viewportMoved()
+{
+ Q_D(QSGGridView);
+ QSGFlickable::viewportMoved();
+ if (!d->itemCount)
+ return;
+ d->lazyRelease = true;
+ if (d->flickingHorizontally || d->flickingVertically) {
+ if (yflick()) {
+ if (d->vData.velocity > 0)
+ d->bufferMode = QSGGridViewPrivate::BufferBefore;
+ else if (d->vData.velocity < 0)
+ d->bufferMode = QSGGridViewPrivate::BufferAfter;
+ }
+
+ if (xflick()) {
+ if (d->hData.velocity > 0)
+ d->bufferMode = QSGGridViewPrivate::BufferBefore;
+ else if (d->hData.velocity < 0)
+ d->bufferMode = QSGGridViewPrivate::BufferAfter;
+ }
+ }
+ refill();
+ if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
+ d->moveReason = QSGGridViewPrivate::Mouse;
+ if (d->moveReason != QSGGridViewPrivate::SetIndex) {
+ if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
+ // reposition highlight
+ qreal pos = d->highlight->rowPos();
+ qreal viewPos;
+ qreal highlightStart;
+ qreal highlightEnd;
+ if (d->isRightToLeftTopToBottom()) {
+ highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
+ viewPos = -d->position()-d->size();
+ } else {
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ viewPos = d->position();
+ }
+ if (pos > viewPos + highlightEnd - d->rowSize())
+ pos = viewPos + highlightEnd - d->rowSize();
+ if (pos < viewPos + highlightStart)
+ pos = viewPos + highlightStart;
+ d->highlight->setPosition(d->highlight->colPos(), qRound(pos));
+
+ // update current index
+ int idx = d->snapIndex();
+ if (idx >= 0 && idx != d->currentIndex) {
+ d->updateCurrent(idx);
+ if (d->currentItem && d->currentItem->colPos() != d->highlight->colPos() && d->autoHighlight) {
+ if (d->flow == LeftToRight)
+ d->highlightXAnimator->to = d->currentItem->item->x();
+ else
+ d->highlightYAnimator->to = d->currentItem->item->y();
+ }
+ }
+ }
+ }
+}
+
+qreal QSGGridView::minYExtent() const
+{
+ Q_D(const QSGGridView);
+ if (d->flow == QSGGridView::TopToBottom)
+ return QSGFlickable::minYExtent();
+ qreal extent = -d->startPosition();
+ if (d->header && d->visibleItems.count())
+ extent += d->header->item->height();
+ if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ extent += d->highlightRangeStart;
+ extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd));
+ }
+ return extent;
+}
+
+qreal QSGGridView::maxYExtent() const
+{
+ Q_D(const QSGGridView);
+ if (d->flow == QSGGridView::TopToBottom)
+ return QSGFlickable::maxYExtent();
+ qreal extent;
+ if (!d->model || !d->model->count()) {
+ extent = 0;
+ } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ extent = -(d->rowPosAt(d->model->count()-1) - d->highlightRangeStart);
+ if (d->highlightRangeEnd != d->highlightRangeStart)
+ extent = qMin(extent, -(d->endPosition() - d->highlightRangeEnd + 1));
+ } else {
+ extent = -(d->endPosition() - height());
+ }
+ if (d->footer)
+ extent -= d->footer->item->height();
+ const qreal minY = minYExtent();
+ if (extent > minY)
+ extent = minY;
+ return extent;
+}
+
+qreal QSGGridView::minXExtent() const
+{
+ Q_D(const QSGGridView);
+ if (d->flow == QSGGridView::LeftToRight)
+ return QSGFlickable::minXExtent();
+ qreal extent = -d->startPosition();
+ qreal highlightStart;
+ qreal highlightEnd;
+ qreal endPositionFirstItem;
+ if (d->isRightToLeftTopToBottom()) {
+ endPositionFirstItem = d->rowPosAt(d->model->count()-1);
+ highlightStart = d->highlightRangeStartValid
+ ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
+ : d->size() - (d->lastPosition()-endPositionFirstItem);
+ highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
+ if (d->footer && d->visibleItems.count())
+ extent += d->footer->item->width();
+ } else {
+ endPositionFirstItem = d->rowPosAt(0)+d->rowSize();
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ if (d->header && d->visibleItems.count())
+ extent += d->header->item->width();
+ }
+ if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ extent += highlightStart;
+ extent = qMax(extent, -(endPositionFirstItem - highlightEnd));
+ }
+ return extent;
+}
+
+qreal QSGGridView::maxXExtent() const
+{
+ Q_D(const QSGGridView);
+ if (d->flow == QSGGridView::LeftToRight)
+ return QSGFlickable::maxXExtent();
+ qreal extent;
+ qreal highlightStart;
+ qreal highlightEnd;
+ qreal lastItemPosition = 0;
+ if (d->isRightToLeftTopToBottom()){
+ highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
+ highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
+ lastItemPosition = d->endPosition();
+ } else {
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ lastItemPosition = 0;
+ if (d->model && d->model->count())
+ lastItemPosition = d->rowPosAt(d->model->count()-1);
+ }
+ if (!d->model || !d->model->count()) {
+ extent = 0;
+ } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ extent = -(lastItemPosition - highlightStart);
+ if (highlightEnd != highlightStart)
+ extent = d->isRightToLeftTopToBottom()
+ ? qMax(extent, -(d->endPosition() - highlightEnd + 1))
+ : qMin(extent, -(d->endPosition() - highlightEnd + 1));
+ } else {
+ extent = -(d->endPosition() - width());
+ }
+ if (d->isRightToLeftTopToBottom()) {
+ if (d->header)
+ extent -= d->header->item->width();
+ } else {
+ if (d->footer)
+ extent -= d->footer->item->width();
+ }
+
+ const qreal minX = minXExtent();
+ if (extent > minX)
+ extent = minX;
+ return extent;
+}
+
+void QSGGridView::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QSGGridView);
+ if (d->model && d->model->count() && d->interactive) {
+ d->moveReason = QSGGridViewPrivate::SetIndex;
+ int oldCurrent = currentIndex();
+ switch (event->key()) {
+ case Qt::Key_Up:
+ moveCurrentIndexUp();
+ break;
+ case Qt::Key_Down:
+ moveCurrentIndexDown();
+ break;
+ case Qt::Key_Left:
+ moveCurrentIndexLeft();
+ break;
+ case Qt::Key_Right:
+ moveCurrentIndexRight();
+ break;
+ default:
+ break;
+ }
+ if (oldCurrent != currentIndex()) {
+ event->accept();
+ return;
+ }
+ }
+ d->moveReason = QSGGridViewPrivate::Other;
+ event->ignore();
+ QSGFlickable::keyPressEvent(event);
+}
+
+void QSGGridView::moveCurrentIndexUp()
+{
+ Q_D(QSGGridView);
+ const int count = d->model ? d->model->count() : 0;
+ if (!count)
+ return;
+ if (d->flow == QSGGridView::LeftToRight) {
+ if (currentIndex() >= d->columns || d->wrap) {
+ int index = currentIndex() - d->columns;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+ } else {
+ if (currentIndex() > 0 || d->wrap) {
+ int index = currentIndex() - 1;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+ }
+}
+
+void QSGGridView::moveCurrentIndexDown()
+{
+ Q_D(QSGGridView);
+ const int count = d->model ? d->model->count() : 0;
+ if (!count)
+ return;
+ if (d->flow == QSGGridView::LeftToRight) {
+ if (currentIndex() < count - d->columns || d->wrap) {
+ int index = currentIndex()+d->columns;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+ } else {
+ if (currentIndex() < count - 1 || d->wrap) {
+ int index = currentIndex() + 1;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+ }
+}
+
+void QSGGridView::moveCurrentIndexLeft()
+{
+ Q_D(QSGGridView);
+ const int count = d->model ? d->model->count() : 0;
+ if (!count)
+ return;
+ if (effectiveLayoutDirection() == Qt::LeftToRight) {
+ if (d->flow == QSGGridView::LeftToRight) {
+ if (currentIndex() > 0 || d->wrap) {
+ int index = currentIndex() - 1;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+ } else {
+ if (currentIndex() >= d->columns || d->wrap) {
+ int index = currentIndex() - d->columns;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+ }
+ } else {
+ if (d->flow == QSGGridView::LeftToRight) {
+ if (currentIndex() < count - 1 || d->wrap) {
+ int index = currentIndex() + 1;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+ } else {
+ if (currentIndex() < count - d->columns || d->wrap) {
+ int index = currentIndex() + d->columns;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+ }
+ }
+}
+
+void QSGGridView::moveCurrentIndexRight()
+{
+ Q_D(QSGGridView);
+ const int count = d->model ? d->model->count() : 0;
+ if (!count)
+ return;
+ if (effectiveLayoutDirection() == Qt::LeftToRight) {
+ if (d->flow == QSGGridView::LeftToRight) {
+ if (currentIndex() < count - 1 || d->wrap) {
+ int index = currentIndex() + 1;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+ } else {
+ if (currentIndex() < count - d->columns || d->wrap) {
+ int index = currentIndex()+d->columns;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+ }
+ } else {
+ if (d->flow == QSGGridView::LeftToRight) {
+ if (currentIndex() > 0 || d->wrap) {
+ int index = currentIndex() - 1;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+ } else {
+ if (currentIndex() >= d->columns || d->wrap) {
+ int index = currentIndex() - d->columns;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+ }
+ }
+}
+
+void QSGGridViewPrivate::positionViewAtIndex(int index, int mode)
+{
+ Q_Q(QSGGridView);
+ if (!isValid())
+ return;
+ if (mode < QSGGridView::Beginning || mode > QSGGridView::Contain)
+ return;
+
+ int idx = qMax(qMin(index, model->count()-1), 0);
+
+ if (layoutScheduled)
+ layout();
+ qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position();
+ FxGridItemSG *item = visibleItem(idx);
+ qreal maxExtent;
+ if (flow == QSGGridView::LeftToRight)
+ maxExtent = -q->maxYExtent();
+ else
+ maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent();
+ if (!item) {
+ int itemPos = rowPosAt(idx);
+ // save the currently visible items in case any of them end up visible again
+ QList<FxGridItemSG*> oldVisible = visibleItems;
+ visibleItems.clear();
+ visibleIndex = idx - idx % columns;
+ if (flow == QSGGridView::LeftToRight)
+ maxExtent = -q->maxYExtent();
+ else
+ maxExtent = isRightToLeftTopToBottom() ? q->minXExtent()-size() : -q->maxXExtent();
+ setPosition(qMin(qreal(itemPos), maxExtent));
+ // now release the reference to all the old visible items.
+ for (int i = 0; i < oldVisible.count(); ++i)
+ releaseItem(oldVisible.at(i));
+ item = visibleItem(idx);
+ }
+ if (item) {
+ qreal itemPos = item->rowPos();
+ switch (mode) {
+ case QSGGridView::Beginning:
+ pos = itemPos;
+ if (index < 0 && header) {
+ pos -= flow == QSGGridView::LeftToRight
+ ? header->item->height()
+ : header->item->width();
+ }
+ break;
+ case QSGGridView::Center:
+ pos = itemPos - (size() - rowSize())/2;
+ break;
+ case QSGGridView::End:
+ pos = itemPos - size() + rowSize();
+ if (index >= model->count() && footer) {
+ pos += flow == QSGGridView::LeftToRight
+ ? footer->item->height()
+ : footer->item->width();
+ }
+ break;
+ case QSGGridView::Visible:
+ if (itemPos > pos + size())
+ pos = itemPos - size() + rowSize();
+ else if (item->endRowPos() < pos)
+ pos = itemPos;
+ break;
+ case QSGGridView::Contain:
+ if (item->endRowPos() > pos + size())
+ pos = itemPos - size() + rowSize();
+ if (itemPos < pos)
+ pos = itemPos;
+ }
+ pos = qMin(pos, maxExtent);
+ qreal minExtent;
+ if (flow == QSGGridView::LeftToRight)
+ minExtent = -q->minYExtent();
+ else
+ minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent();
+ pos = qMax(pos, minExtent);
+ moveReason = QSGGridViewPrivate::Other;
+ q->cancelFlick();
+ setPosition(pos);
+ }
+ fixupPosition();
+}
+
+void QSGGridView::positionViewAtIndex(int index, int mode)
+{
+ Q_D(QSGGridView);
+ if (!d->isValid() || index < 0 || index >= d->model->count())
+ return;
+ d->positionViewAtIndex(index, mode);
+}
+
+void QSGGridView::positionViewAtBeginning()
+{
+ Q_D(QSGGridView);
+ if (!d->isValid())
+ return;
+ d->positionViewAtIndex(-1, Beginning);
+}
+
+void QSGGridView::positionViewAtEnd()
+{
+ Q_D(QSGGridView);
+ if (!d->isValid())
+ return;
+ d->positionViewAtIndex(d->model->count(), End);
+}
+
+int QSGGridView::indexAt(qreal x, qreal y) const
+{
+ Q_D(const QSGGridView);
+ for (int i = 0; i < d->visibleItems.count(); ++i) {
+ const FxGridItemSG *listItem = d->visibleItems.at(i);
+ if(listItem->contains(x, y))
+ return listItem->index;
+ }
+
+ return -1;
+}
+
+void QSGGridView::componentComplete()
+{
+ Q_D(QSGGridView);
+ QSGFlickable::componentComplete();
+ d->updateHeader();
+ d->updateFooter();
+ d->updateGrid();
+ if (d->isValid()) {
+ refill();
+ d->moveReason = QSGGridViewPrivate::SetIndex;
+ if (d->currentIndex < 0 && !d->currentIndexCleared)
+ d->updateCurrent(0);
+ else
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
+ d->updateTrackedItem();
+ }
+ d->moveReason = QSGGridViewPrivate::Other;
+ d->fixupPosition();
+ }
+}
+
+void QSGGridView::trackedPositionChanged()
+{
+ Q_D(QSGGridView);
+ if (!d->trackedItem || !d->currentItem)
+ return;
+ if (d->moveReason == QSGGridViewPrivate::SetIndex) {
+ const qreal trackedPos = d->trackedItem->rowPos();
+ qreal viewPos;
+ qreal highlightStart;
+ qreal highlightEnd;
+ if (d->isRightToLeftTopToBottom()) {
+ viewPos = -d->position()-d->size();
+ highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
+ } else {
+ viewPos = d->position();
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ }
+ qreal pos = viewPos;
+ if (d->haveHighlightRange) {
+ if (d->highlightRange == StrictlyEnforceRange) {
+ if (trackedPos > pos + highlightEnd - d->rowSize())
+ pos = trackedPos - highlightEnd + d->rowSize();
+ if (trackedPos < pos + highlightStart)
+ pos = trackedPos - highlightStart;
+ } else {
+ if (trackedPos < d->startPosition() + highlightStart) {
+ pos = d->startPosition();
+ } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + highlightEnd) {
+ pos = d->endPosition() - d->size() + 1;
+ if (pos < d->startPosition())
+ pos = d->startPosition();
+ } else {
+ if (trackedPos < viewPos + highlightStart) {
+ pos = trackedPos - highlightStart;
+ } else if (trackedPos > viewPos + highlightEnd - d->rowSize()) {
+ pos = trackedPos - highlightEnd + d->rowSize();
+ }
+ }
+ }
+ } else {
+ if (trackedPos < viewPos && d->currentItem->rowPos() < viewPos) {
+ pos = qMax(trackedPos, d->currentItem->rowPos());
+ } else if (d->trackedItem->endRowPos() >= viewPos + d->size()
+ && d->currentItem->endRowPos() >= viewPos + d->size()) {
+ if (d->trackedItem->endRowPos() <= d->currentItem->endRowPos()) {
+ pos = d->trackedItem->endRowPos() - d->size() + 1;
+ if (d->rowSize() > d->size())
+ pos = trackedPos;
+ } else {
+ pos = d->currentItem->endRowPos() - d->size() + 1;
+ if (d->rowSize() > d->size())
+ pos = d->currentItem->rowPos();
+ }
+ }
+ }
+ if (viewPos != pos) {
+ cancelFlick();
+ d->calcVelocity = true;
+ d->setPosition(pos);
+ d->calcVelocity = false;
+ }
+ }
+}
+
+void QSGGridView::itemsInserted(int modelIndex, int count)
+{
+ Q_D(QSGGridView);
+ if (!isComponentComplete())
+ return;
+
+ int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
+ if (index < 0) {
+ int i = d->visibleItems.count() - 1;
+ while (i > 0 && d->visibleItems.at(i)->index == -1)
+ --i;
+ if (d->visibleItems.at(i)->index + 1 == modelIndex) {
+ // Special case of appending an item to the model.
+ index = d->visibleIndex + d->visibleItems.count();
+ } else {
+ if (modelIndex <= d->visibleIndex) {
+ // Insert before visible items
+ d->visibleIndex += count;
+ for (int i = 0; i < d->visibleItems.count(); ++i) {
+ FxGridItemSG *listItem = d->visibleItems.at(i);
+ if (listItem->index != -1 && listItem->index >= modelIndex)
+ listItem->index += count;
+ }
+ }
+ if (d->currentIndex >= modelIndex) {
+ // adjust current item index
+ d->currentIndex += count;
+ if (d->currentItem)
+ d->currentItem->index = d->currentIndex;
+ emit currentIndexChanged();
+ }
+ d->scheduleLayout();
+ d->itemCount += count;
+ emit countChanged();
+ return;
+ }
+ }
+
+ int insertCount = count;
+ if (index < d->visibleIndex && d->visibleItems.count()) {
+ insertCount -= d->visibleIndex - index;
+ index = d->visibleIndex;
+ modelIndex = d->visibleIndex;
+ }
+
+ qreal tempPos = d->isRightToLeftTopToBottom() ? -d->position()-d->size()+width()+1 : d->position();
+ int to = d->buffer+tempPos+d->size()-1;
+ int colPos = 0;
+ int rowPos = 0;
+ if (d->visibleItems.count()) {
+ index -= d->visibleIndex;
+ if (index < d->visibleItems.count()) {
+ colPos = d->visibleItems.at(index)->colPos();
+ rowPos = d->visibleItems.at(index)->rowPos();
+ } else {
+ // appending items to visible list
+ colPos = d->visibleItems.at(index-1)->colPos() + d->colSize();
+ rowPos = d->visibleItems.at(index-1)->rowPos();
+ if (colPos > d->colSize() * (d->columns-1)) {
+ colPos = 0;
+ rowPos += d->rowSize();
+ }
+ }
+ } else if (d->itemCount == 0 && d->header) {
+ rowPos = d->headerSize();
+ }
+
+ // Update the indexes of the following visible items.
+ for (int i = 0; i < d->visibleItems.count(); ++i) {
+ FxGridItemSG *listItem = d->visibleItems.at(i);
+ if (listItem->index != -1 && listItem->index >= modelIndex)
+ listItem->index += count;
+ }
+
+ bool addedVisible = false;
+ QList<FxGridItemSG*> added;
+ int i = 0;
+ while (i < insertCount && rowPos <= to + d->rowSize()*(d->columns - (colPos/d->colSize()))/qreal(d->columns)) {
+ if (!addedVisible) {
+ d->scheduleLayout();
+ addedVisible = true;
+ }
+ FxGridItemSG *item = d->createItem(modelIndex + i);
+ d->visibleItems.insert(index, item);
+ item->setPosition(colPos, rowPos);
+ added.append(item);
+ colPos += d->colSize();
+ if (colPos > d->colSize() * (d->columns-1)) {
+ colPos = 0;
+ rowPos += d->rowSize();
+ }
+ ++index;
+ ++i;
+ }
+ if (i < insertCount) {
+ // We didn't insert all our new items, which means anything
+ // beyond the current index is not visible - remove it.
+ while (d->visibleItems.count() > index) {
+ d->releaseItem(d->visibleItems.takeLast());
+ }
+ }
+
+ // update visibleIndex
+ d->visibleIndex = 0;
+ for (QList<FxGridItemSG*>::Iterator it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
+ if ((*it)->index != -1) {
+ d->visibleIndex = (*it)->index;
+ break;
+ }
+ }
+
+ if (d->itemCount && d->currentIndex >= modelIndex) {
+ // adjust current item index
+ d->currentIndex += count;
+ if (d->currentItem) {
+ d->currentItem->index = d->currentIndex;
+ d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex));
+ }
+ emit currentIndexChanged();
+ } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
+ setCurrentIndex(0);
+ }
+
+ // everything is in order now - emit add() signal
+ for (int j = 0; j < added.count(); ++j)
+ added.at(j)->attached->emitAdd();
+
+ d->itemCount += count;
+ emit countChanged();
+}
+
+void QSGGridView::itemsRemoved(int modelIndex, int count)
+{
+ Q_D(QSGGridView);
+ if (!isComponentComplete())
+ return;
+
+ d->itemCount -= count;
+ bool currentRemoved = d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count;
+ bool removedVisible = false;
+
+ // Remove the items from the visible list, skipping anything already marked for removal
+ QList<FxGridItemSG*>::Iterator it = d->visibleItems.begin();
+ while (it != d->visibleItems.end()) {
+ FxGridItemSG *item = *it;
+ if (item->index == -1 || item->index < modelIndex) {
+ // already removed, or before removed items
+ if (item->index < modelIndex && !removedVisible) {
+ d->scheduleLayout();
+ removedVisible = true;
+ }
+ ++it;
+ } else if (item->index >= modelIndex + count) {
+ // after removed items
+ item->index -= count;
+ ++it;
+ } else {
+ // removed item
+ if (!removedVisible) {
+ d->scheduleLayout();
+ removedVisible = true;
+ }
+ item->attached->emitRemove();
+ if (item->attached->delayRemove()) {
+ item->index = -1;
+ connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
+ ++it;
+ } else {
+ it = d->visibleItems.erase(it);
+ d->releaseItem(item);
+ }
+ }
+ }
+
+ // fix current
+ if (d->currentIndex >= modelIndex + count) {
+ d->currentIndex -= count;
+ if (d->currentItem)
+ d->currentItem->index -= count;
+ emit currentIndexChanged();
+ } else if (currentRemoved) {
+ // current item has been removed.
+ d->releaseItem(d->currentItem);
+ d->currentItem = 0;
+ d->currentIndex = -1;
+ if (d->itemCount)
+ d->updateCurrent(qMin(modelIndex, d->itemCount-1));
+ else
+ emit currentIndexChanged();
+ }
+
+ // update visibleIndex
+ d->visibleIndex = 0;
+ for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
+ if ((*it)->index != -1) {
+ d->visibleIndex = (*it)->index;
+ break;
+ }
+ }
+
+ if (removedVisible && d->visibleItems.isEmpty()) {
+ d->timeline.clear();
+ if (d->itemCount == 0) {
+ d->setPosition(0);
+ d->updateHeader();
+ d->updateFooter();
+ }
+ }
+
+ emit countChanged();
+}
+
+void QSGGridView::destroyRemoved()
+{
+ Q_D(QSGGridView);
+ for (QList<FxGridItemSG*>::Iterator it = d->visibleItems.begin();
+ it != d->visibleItems.end();) {
+ FxGridItemSG *listItem = *it;
+ if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
+ d->releaseItem(listItem);
+ it = d->visibleItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Correct the positioning of the items
+ d->layout();
+}
+
+void QSGGridView::itemsMoved(int from, int to, int count)
+{
+ Q_D(QSGGridView);
+ if (!isComponentComplete())
+ return;
+ QHash<int,FxGridItemSG*> moved;
+
+ FxGridItemSG *firstItem = d->firstVisibleItem();
+
+ QList<FxGridItemSG*>::Iterator it = d->visibleItems.begin();
+ while (it != d->visibleItems.end()) {
+ FxGridItemSG *item = *it;
+ if (item->index >= from && item->index < from + count) {
+ // take the items that are moving
+ item->index += (to-from);
+ moved.insert(item->index, item);
+ it = d->visibleItems.erase(it);
+ } else {
+ if (item->index > from && item->index != -1) {
+ // move everything after the moved items.
+ item->index -= count;
+ if (item->index < d->visibleIndex)
+ d->visibleIndex = item->index;
+ }
+ ++it;
+ }
+ }
+
+ int remaining = count;
+ int endIndex = d->visibleIndex;
+ it = d->visibleItems.begin();
+ while (it != d->visibleItems.end()) {
+ FxGridItemSG *item = *it;
+ if (remaining && item->index >= to && item->index < to + count) {
+ // place items in the target position, reusing any existing items
+ FxGridItemSG *movedItem = moved.take(item->index);
+ if (!movedItem)
+ movedItem = d->createItem(item->index);
+ it = d->visibleItems.insert(it, movedItem);
+ if (it == d->visibleItems.begin() && firstItem)
+ movedItem->setPosition(firstItem->colPos(), firstItem->rowPos());
+ ++it;
+ --remaining;
+ } else {
+ if (item->index != -1) {
+ if (item->index >= to) {
+ // update everything after the moved items.
+ item->index += count;
+ }
+ endIndex = item->index;
+ }
+ ++it;
+ }
+ }
+
+ // If we have moved items to the end of the visible items
+ // then add any existing moved items that we have
+ while (FxGridItemSG *item = moved.take(endIndex+1)) {
+ d->visibleItems.append(item);
+ ++endIndex;
+ }
+
+ // update visibleIndex
+ for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
+ if ((*it)->index != -1) {
+ d->visibleIndex = (*it)->index;
+ break;
+ }
+ }
+
+ // Fix current index
+ if (d->currentIndex >= 0 && d->currentItem) {
+ int oldCurrent = d->currentIndex;
+ d->currentIndex = d->model->indexOf(d->currentItem->item, this);
+ if (oldCurrent != d->currentIndex) {
+ d->currentItem->index = d->currentIndex;
+ emit currentIndexChanged();
+ }
+ }
+
+ // Whatever moved items remain are no longer visible items.
+ while (moved.count()) {
+ int idx = moved.begin().key();
+ FxGridItemSG *item = moved.take(idx);
+ if (d->currentItem && item->item == d->currentItem->item)
+ item->setPosition(d->colPosAt(idx), d->rowPosAt(idx));
+ d->releaseItem(item);
+ }
+
+ d->layout();
+}
+
+void QSGGridView::modelReset()
+{
+ Q_D(QSGGridView);
+ d->clear();
+ refill();
+ d->moveReason = QSGGridViewPrivate::SetIndex;
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->colPos(), d->currentItem->rowPos());
+ d->updateTrackedItem();
+ }
+ d->moveReason = QSGGridViewPrivate::Other;
+
+ emit countChanged();
+}
+
+void QSGGridView::createdItem(int index, QSGItem *item)
+{
+ Q_D(QSGGridView);
+ if (d->requestedIndex != index) {
+ item->setParentItem(this);
+ d->unrequestedItems.insert(item, index);
+ if (d->flow == QSGGridView::LeftToRight) {
+ item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index)));
+ } else {
+ item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index)));
+ }
+ }
+}
+
+void QSGGridView::destroyingItem(QSGItem *item)
+{
+ Q_D(QSGGridView);
+ d->unrequestedItems.remove(item);
+}
+
+void QSGGridView::animStopped()
+{
+ Q_D(QSGGridView);
+ d->bufferMode = QSGGridViewPrivate::NoBuffer;
+ if (d->haveHighlightRange && d->highlightRange == QSGGridView::StrictlyEnforceRange)
+ d->updateHighlight();
+}
+
+void QSGGridView::refill()
+{
+ Q_D(QSGGridView);
+ if (d->isRightToLeftTopToBottom())
+ d->refill(-d->position()-d->size()+1, -d->position());
+ else
+ d->refill(d->position(), d->position()+d->size()-1);
+}
+
+
+QSGGridViewAttached *QSGGridView::qmlAttachedProperties(QObject *obj)
+{
+ return new QSGGridViewAttached(obj);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsggridview_p.h b/src/declarative/items/qsggridview_p.h
new file mode 100644
index 0000000000..8eca17df55
--- /dev/null
+++ b/src/declarative/items/qsggridview_p.h
@@ -0,0 +1,290 @@
+// Commit: 95814418f9d6adeba365c795462e8afb00138211
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGGRIDVIEW_P_H
+#define QSGGRIDVIEW_P_H
+
+#include "qsgflickable_p.h"
+
+#include <private/qdeclarativeguard_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class QSGVisualModel;
+class QSGGridViewAttached;
+class QSGGridViewPrivate;
+class Q_AUTOTEST_EXPORT QSGGridView : public QSGFlickable
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGGridView)
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged)
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+
+ Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
+ Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged)
+ Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem)
+ Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged)
+
+ Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin)
+ Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd)
+ Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged)
+
+ Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
+ Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
+ Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged)
+ Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
+ Q_PROPERTY(int cellWidth READ cellWidth WRITE setCellWidth NOTIFY cellWidthChanged)
+ Q_PROPERTY(int cellHeight READ cellHeight WRITE setCellHeight NOTIFY cellHeightChanged)
+
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged)
+
+ Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged)
+ Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged)
+
+ Q_ENUMS(HighlightRangeMode)
+ Q_ENUMS(SnapMode)
+ Q_ENUMS(Flow)
+ Q_ENUMS(PositionMode)
+ Q_CLASSINFO("DefaultProperty", "data")
+
+public:
+ QSGGridView(QSGItem *parent=0);
+ ~QSGGridView();
+
+ QVariant model() const;
+ int modelCount() const;
+ void setModel(const QVariant &);
+
+ QDeclarativeComponent *delegate() const;
+ void setDelegate(QDeclarativeComponent *);
+
+ int currentIndex() const;
+ void setCurrentIndex(int idx);
+
+ QSGItem *currentItem();
+ QSGItem *highlightItem();
+ int count() const;
+
+ QDeclarativeComponent *highlight() const;
+ void setHighlight(QDeclarativeComponent *highlight);
+
+ bool highlightFollowsCurrentItem() const;
+ void setHighlightFollowsCurrentItem(bool);
+
+ int highlightMoveDuration() const;
+ void setHighlightMoveDuration(int);
+
+ enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange };
+ HighlightRangeMode highlightRangeMode() const;
+ void setHighlightRangeMode(HighlightRangeMode mode);
+
+ qreal preferredHighlightBegin() const;
+ void setPreferredHighlightBegin(qreal);
+ void resetPreferredHighlightBegin();
+
+ qreal preferredHighlightEnd() const;
+ void setPreferredHighlightEnd(qreal);
+ void resetPreferredHighlightEnd();
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection(Qt::LayoutDirection);
+ Qt::LayoutDirection effectiveLayoutDirection() const;
+
+ enum Flow { LeftToRight, TopToBottom };
+ Flow flow() const;
+ void setFlow(Flow);
+
+ bool isWrapEnabled() const;
+ void setWrapEnabled(bool);
+
+ int cacheBuffer() const;
+ void setCacheBuffer(int);
+
+ int cellWidth() const;
+ void setCellWidth(int);
+
+ int cellHeight() const;
+ void setCellHeight(int);
+
+ enum SnapMode { NoSnap, SnapToRow, SnapOneRow };
+ SnapMode snapMode() const;
+ void setSnapMode(SnapMode mode);
+
+ QDeclarativeComponent *footer() const;
+ void setFooter(QDeclarativeComponent *);
+
+ QDeclarativeComponent *header() const;
+ void setHeader(QDeclarativeComponent *);
+
+ virtual void setContentX(qreal pos);
+ virtual void setContentY(qreal pos);
+
+ enum PositionMode { Beginning, Center, End, Visible, Contain };
+
+ Q_INVOKABLE void positionViewAtIndex(int index, int mode);
+ Q_INVOKABLE int indexAt(qreal x, qreal y) const;
+ Q_INVOKABLE void positionViewAtBeginning();
+ Q_INVOKABLE void positionViewAtEnd();
+
+ static QSGGridViewAttached *qmlAttachedProperties(QObject *);
+
+public Q_SLOTS:
+ void moveCurrentIndexUp();
+ void moveCurrentIndexDown();
+ void moveCurrentIndexLeft();
+ void moveCurrentIndexRight();
+
+Q_SIGNALS:
+ void countChanged();
+ void currentIndexChanged();
+ void cellWidthChanged();
+ void cellHeightChanged();
+ void highlightChanged();
+ void highlightItemChanged();
+ void preferredHighlightBeginChanged();
+ void preferredHighlightEndChanged();
+ void highlightRangeModeChanged();
+ void highlightMoveDurationChanged();
+ void modelChanged();
+ void delegateChanged();
+ void flowChanged();
+ void layoutDirectionChanged();
+ void effectiveLayoutDirectionChanged();
+ void keyNavigationWrapsChanged();
+ void cacheBufferChanged();
+ void snapModeChanged();
+ void headerChanged();
+ void footerChanged();
+
+protected:
+ virtual void updatePolish();
+ virtual void viewportMoved();
+ virtual qreal minYExtent() const;
+ virtual qreal maxYExtent() const;
+ virtual qreal minXExtent() const;
+ virtual qreal maxXExtent() const;
+ virtual void keyPressEvent(QKeyEvent *);
+ virtual void componentComplete();
+
+private Q_SLOTS:
+ void trackedPositionChanged();
+ void itemsInserted(int index, int count);
+ void itemsRemoved(int index, int count);
+ void itemsMoved(int from, int to, int count);
+ void modelReset();
+ void destroyRemoved();
+ void createdItem(int index, QSGItem *item);
+ void destroyingItem(QSGItem *item);
+ void animStopped();
+
+private:
+ void refill();
+};
+
+class QSGGridViewAttached : public QObject
+{
+ Q_OBJECT
+public:
+ QSGGridViewAttached(QObject *parent)
+ : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {}
+ ~QSGGridViewAttached() {}
+
+ Q_PROPERTY(QSGGridView *view READ view NOTIFY viewChanged)
+ QSGGridView *view() { return m_view; }
+ void setView(QSGGridView *view) {
+ if (view != m_view) {
+ m_view = view;
+ emit viewChanged();
+ }
+ }
+
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
+ bool isCurrentItem() const { return m_isCurrent; }
+ void setIsCurrentItem(bool c) {
+ if (m_isCurrent != c) {
+ m_isCurrent = c;
+ emit currentItemChanged();
+ }
+ }
+
+ Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged)
+ bool delayRemove() const { return m_delayRemove; }
+ void setDelayRemove(bool delay) {
+ if (m_delayRemove != delay) {
+ m_delayRemove = delay;
+ emit delayRemoveChanged();
+ }
+ }
+
+ void emitAdd() { emit add(); }
+ void emitRemove() { emit remove(); }
+
+Q_SIGNALS:
+ void currentItemChanged();
+ void delayRemoveChanged();
+ void add();
+ void remove();
+ void viewChanged();
+
+public:
+ QDeclarativeGuard<QSGGridView> m_view;
+ bool m_isCurrent : 1;
+ bool m_delayRemove : 1;
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGGridView)
+QML_DECLARE_TYPEINFO(QSGGridView, QML_HAS_ATTACHED_PROPERTIES)
+
+QT_END_HEADER
+
+#endif // QSGGRIDVIEW_P_H
diff --git a/src/declarative/items/qsgimage.cpp b/src/declarative/items/qsgimage.cpp
new file mode 100644
index 0000000000..706aaa7d5d
--- /dev/null
+++ b/src/declarative/items/qsgimage.cpp
@@ -0,0 +1,306 @@
+// Commit: 051a76c1d65d698f71dc75c89f91ae9021357eae
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgimage_p.h"
+#include "qsgimage_p_p.h"
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QtGui/qpainter.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGImagePrivate::QSGImagePrivate()
+ : fillMode(QSGImage::Stretch)
+ , paintedWidth(0)
+ , paintedHeight(0)
+ , pixmapChanged(false)
+{
+}
+
+QSGImage::QSGImage(QSGItem *parent)
+ : QSGImageBase(*(new QSGImagePrivate), parent)
+{
+}
+
+QSGImage::QSGImage(QSGImagePrivate &dd, QSGItem *parent)
+ : QSGImageBase(dd, parent)
+{
+}
+
+QSGImage::~QSGImage()
+{
+}
+
+void QSGImagePrivate::setPixmap(const QPixmap &pixmap)
+{
+ Q_Q(QSGImage);
+ pix.setPixmap(pixmap);
+
+ q->pixmapChange();
+ status = pix.isNull() ? QSGImageBase::Null : QSGImageBase::Ready;
+
+ q->update();
+}
+
+QSGImage::FillMode QSGImage::fillMode() const
+{
+ Q_D(const QSGImage);
+ return d->fillMode;
+}
+
+void QSGImage::setFillMode(FillMode mode)
+{
+ Q_D(QSGImage);
+ if (d->fillMode == mode)
+ return;
+ d->fillMode = mode;
+ update();
+ updatePaintedGeometry();
+ emit fillModeChanged();
+}
+
+qreal QSGImage::paintedWidth() const
+{
+ Q_D(const QSGImage);
+ return d->paintedWidth;
+}
+
+qreal QSGImage::paintedHeight() const
+{
+ Q_D(const QSGImage);
+ return d->paintedHeight;
+}
+
+void QSGImage::updatePaintedGeometry()
+{
+ Q_D(QSGImage);
+
+ if (d->fillMode == PreserveAspectFit) {
+ if (!d->pix.width() || !d->pix.height()) {
+ setImplicitWidth(0);
+ setImplicitHeight(0);
+ return;
+ }
+ qreal w = widthValid() ? width() : d->pix.width();
+ qreal widthScale = w / qreal(d->pix.width());
+ qreal h = heightValid() ? height() : d->pix.height();
+ qreal heightScale = h / qreal(d->pix.height());
+ if (widthScale <= heightScale) {
+ d->paintedWidth = w;
+ d->paintedHeight = widthScale * qreal(d->pix.height());
+ } else if(heightScale < widthScale) {
+ d->paintedWidth = heightScale * qreal(d->pix.width());
+ d->paintedHeight = h;
+ }
+ if (widthValid() && !heightValid()) {
+ setImplicitHeight(d->paintedHeight);
+ } else {
+ setImplicitHeight(d->pix.height());
+ }
+ if (heightValid() && !widthValid()) {
+ setImplicitWidth(d->paintedWidth);
+ } else {
+ setImplicitWidth(d->pix.width());
+ }
+ } else if (d->fillMode == PreserveAspectCrop) {
+ if (!d->pix.width() || !d->pix.height())
+ return;
+ qreal widthScale = width() / qreal(d->pix.width());
+ qreal heightScale = height() / qreal(d->pix.height());
+ if (widthScale < heightScale) {
+ widthScale = heightScale;
+ } else if(heightScale < widthScale) {
+ heightScale = widthScale;
+ }
+
+ d->paintedHeight = heightScale * qreal(d->pix.height());
+ d->paintedWidth = widthScale * qreal(d->pix.width());
+ } else {
+ d->paintedWidth = width();
+ d->paintedHeight = height();
+ }
+ emit paintedGeometryChanged();
+}
+
+void QSGImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ QSGImageBase::geometryChanged(newGeometry, oldGeometry);
+ updatePaintedGeometry();
+}
+
+QRectF QSGImage::boundingRect() const
+{
+ Q_D(const QSGImage);
+ return QRectF(0, 0, qMax(width(), d->paintedWidth), qMax(height(), d->paintedHeight));
+}
+
+QSGTexture *QSGImage::texture() const
+{
+ Q_D(const QSGImage);
+ QSGTexture *t = d->pix.texture(d->sceneGraphContext());
+ if (t) {
+ t->setFiltering(QSGItemPrivate::get(this)->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+ t->setMipmapFiltering(QSGTexture::None);
+ t->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ t->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ }
+ return t;
+}
+
+QSGNode *QSGImage::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ Q_D(QSGImage);
+
+ QSGTexture *texture = d->pix.texture(d->sceneGraphContext());
+
+ if (!texture || width() <= 0 || height() <= 0) {
+ delete oldNode;
+ return 0;
+ }
+
+ QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
+ if (!node) {
+ d->pixmapChanged = true;
+ node = d->sceneGraphContext()->createImageNode();
+ node->setTexture(texture);
+ }
+
+ if (d->pixmapChanged) {
+ // force update the texture in the node to trigger reconstruction of
+ // geometry and the likes when a atlas segment has changed.
+ node->setTexture(0);
+ node->setTexture(texture);
+ d->pixmapChanged = false;
+ }
+
+ QRectF targetRect;
+ QRectF sourceRect;
+ QSGTexture::WrapMode hWrap = QSGTexture::ClampToEdge;
+ QSGTexture::WrapMode vWrap = QSGTexture::ClampToEdge;
+
+ switch (d->fillMode) {
+ default:
+ case Stretch:
+ targetRect = QRectF(0, 0, width(), height());
+ sourceRect = d->pix.rect();
+ break;
+
+ case PreserveAspectFit:
+ targetRect = QRectF((width() - d->paintedWidth) / 2., (height() - d->paintedHeight) / 2.,
+ d->paintedWidth, d->paintedHeight);
+ sourceRect = d->pix.rect();
+ break;
+
+ case PreserveAspectCrop: {
+ targetRect = QRect(0, 0, width(), height());
+ qreal wscale = width() / qreal(d->pix.width());
+ qreal hscale = height() / qreal(d->pix.height());
+
+ if (wscale > hscale) {
+ int src = (hscale / wscale) * qreal(d->pix.height());
+ sourceRect = QRectF(0, (d->pix.height() - src) / 2, d->pix.width(), src);
+ } else {
+ int src = (wscale / hscale) * qreal(d->pix.width());
+ sourceRect = QRectF((d->pix.width() - src) / 2, 0, src, d->pix.height());
+ }
+ }
+ break;
+
+ case Tile:
+ targetRect = QRectF(0, 0, width(), height());
+ sourceRect = QRectF(0, 0, width(), height());
+ hWrap = QSGTexture::Repeat;
+ vWrap = QSGTexture::Repeat;
+ break;
+
+ case TileHorizontally:
+ targetRect = QRectF(0, 0, width(), height());
+ sourceRect = QRectF(0, 0, width(), d->pix.height());
+ hWrap = QSGTexture::Repeat;
+ break;
+
+ case TileVertically:
+ targetRect = QRectF(0, 0, width(), height());
+ sourceRect = QRectF(0, 0, d->pix.width(), height());
+ vWrap = QSGTexture::Repeat;
+ break;
+
+ };
+
+ QRectF nsrect(sourceRect.x() / d->pix.width(),
+ sourceRect.y() / d->pix.height(),
+ sourceRect.width() / d->pix.width(),
+ sourceRect.height() / d->pix.height());
+
+ if (d->mirror) {
+ qreal oldLeft = nsrect.left();
+ nsrect.setLeft(nsrect.right());
+ nsrect.setRight(oldLeft);
+ }
+
+ node->setHorizontalWrapMode(hWrap);
+ node->setVerticalWrapMode(vWrap);
+ node->setFiltering(d->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+
+ node->setTargetRect(targetRect);
+ node->setSourceRect(nsrect);
+ node->update();
+
+ return node;
+}
+
+void QSGImage::pixmapChange()
+{
+ Q_D(QSGImage);
+ // PreserveAspectFit calculates the implicit size differently so we
+ // don't call our superclass pixmapChange(), since that would
+ // result in the implicit size being set incorrectly, then updated
+ // in updatePaintedGeometry()
+ if (d->fillMode != PreserveAspectFit)
+ QSGImageBase::pixmapChange();
+ updatePaintedGeometry();
+ d->pixmapChanged = true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgimage_p.h b/src/declarative/items/qsgimage_p.h
new file mode 100644
index 0000000000..aad63d42c0
--- /dev/null
+++ b/src/declarative/items/qsgimage_p.h
@@ -0,0 +1,104 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGIMAGE_P_H
+#define QSGIMAGE_P_H
+
+#include "qsgimagebase_p.h"
+#include <private/qsgtextureprovider_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGImagePrivate;
+class Q_AUTOTEST_EXPORT QSGImage : public QSGImageBase, public QSGTextureProvider
+{
+ Q_OBJECT
+ Q_ENUMS(FillMode)
+
+ Q_PROPERTY(FillMode fillMode READ fillMode WRITE setFillMode NOTIFY fillModeChanged)
+ Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedGeometryChanged)
+ Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedGeometryChanged)
+ Q_PROPERTY(QSGTexture *texture READ texture)
+
+ Q_INTERFACES(QSGTextureProvider)
+
+public:
+ QSGImage(QSGItem *parent=0);
+ ~QSGImage();
+
+ enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally };
+ FillMode fillMode() const;
+ void setFillMode(FillMode);
+
+ qreal paintedWidth() const;
+ qreal paintedHeight() const;
+
+ QRectF boundingRect() const;
+
+ virtual QSGTexture *texture() const;
+
+Q_SIGNALS:
+ void fillModeChanged();
+ void paintedGeometryChanged();
+
+protected:
+ QSGImage(QSGImagePrivate &dd, QSGItem *parent);
+ void pixmapChange();
+ void updatePaintedGeometry();
+
+ virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private:
+ Q_DISABLE_COPY(QSGImage)
+ Q_DECLARE_PRIVATE(QSGImage)
+};
+
+QT_END_NAMESPACE
+QML_DECLARE_TYPE(QSGImage)
+QT_END_HEADER
+
+#endif // QSGIMAGE_P_H
diff --git a/src/declarative/items/qsgimage_p_p.h b/src/declarative/items/qsgimage_p_p.h
new file mode 100644
index 0000000000..01b549df1f
--- /dev/null
+++ b/src/declarative/items/qsgimage_p_p.h
@@ -0,0 +1,81 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGIMAGE_P_P_H
+#define QSGIMAGE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgimagebase_p_p.h"
+#include "qsgimage_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGImagePrivate;
+
+class QSGImagePrivate : public QSGImageBasePrivate
+{
+ Q_DECLARE_PUBLIC(QSGImage)
+
+public:
+ QSGImagePrivate();
+
+ QSGImage::FillMode fillMode;
+ qreal paintedWidth;
+ qreal paintedHeight;
+ void setPixmap(const QPixmap &pix);
+
+ bool pixmapChanged : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGIMAGE_P_P_H
diff --git a/src/declarative/items/qsgimagebase.cpp b/src/declarative/items/qsgimagebase.cpp
new file mode 100644
index 0000000000..9f2de03bbe
--- /dev/null
+++ b/src/declarative/items/qsgimagebase.cpp
@@ -0,0 +1,285 @@
+// Commit: 051a76c1d65d698f71dc75c89f91ae9021357eae
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgimagebase_p.h"
+#include "qsgimagebase_p_p.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGImageBase::QSGImageBase(QSGItem *parent)
+: QSGImplicitSizeItem(*(new QSGImageBasePrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QSGImageBase::QSGImageBase(QSGImageBasePrivate &dd, QSGItem *parent)
+: QSGImplicitSizeItem(dd, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QSGImageBase::~QSGImageBase()
+{
+}
+
+QSGImageBase::Status QSGImageBase::status() const
+{
+ Q_D(const QSGImageBase);
+ return d->status;
+}
+
+
+qreal QSGImageBase::progress() const
+{
+ Q_D(const QSGImageBase);
+ return d->progress;
+}
+
+
+bool QSGImageBase::asynchronous() const
+{
+ Q_D(const QSGImageBase);
+ return d->async;
+}
+
+void QSGImageBase::setAsynchronous(bool async)
+{
+ Q_D(QSGImageBase);
+ if (d->async != async) {
+ d->async = async;
+ emit asynchronousChanged();
+ }
+}
+
+QUrl QSGImageBase::source() const
+{
+ Q_D(const QSGImageBase);
+ return d->url;
+}
+
+void QSGImageBase::setSource(const QUrl &url)
+{
+ Q_D(QSGImageBase);
+ //equality is fairly expensive, so we bypass for simple, common case
+ if ((d->url.isEmpty() == url.isEmpty()) && url == d->url)
+ return;
+
+ d->url = url;
+ emit sourceChanged(d->url);
+
+ if (isComponentComplete())
+ load();
+}
+
+void QSGImageBase::setSourceSize(const QSize& size)
+{
+ Q_D(QSGImageBase);
+ if (d->sourcesize == size)
+ return;
+
+ d->sourcesize = size;
+ d->explicitSourceSize = true;
+ emit sourceSizeChanged();
+ if (isComponentComplete())
+ load();
+}
+
+QSize QSGImageBase::sourceSize() const
+{
+ Q_D(const QSGImageBase);
+
+ int width = d->sourcesize.width();
+ int height = d->sourcesize.height();
+ return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height());
+}
+
+void QSGImageBase::resetSourceSize()
+{
+ Q_D(QSGImageBase);
+ if (!d->explicitSourceSize)
+ return;
+ d->explicitSourceSize = false;
+ d->sourcesize = QSize();
+ emit sourceSizeChanged();
+ if (isComponentComplete())
+ load();
+}
+
+bool QSGImageBase::cache() const
+{
+ Q_D(const QSGImageBase);
+ return d->cache;
+}
+
+void QSGImageBase::setCache(bool cache)
+{
+ Q_D(QSGImageBase);
+ if (d->cache == cache)
+ return;
+
+ d->cache = cache;
+ emit cacheChanged();
+ if (isComponentComplete())
+ load();
+}
+
+void QSGImageBase::setMirror(bool mirror)
+{
+ Q_D(QSGImageBase);
+ if (mirror == d->mirror)
+ return;
+
+ d->mirror = mirror;
+
+ if (isComponentComplete())
+ update();
+
+ emit mirrorChanged();
+}
+
+bool QSGImageBase::mirror() const
+{
+ Q_D(const QSGImageBase);
+ return d->mirror;
+}
+
+void QSGImageBase::load()
+{
+ Q_D(QSGImageBase);
+
+ if (d->url.isEmpty()) {
+ d->pix.clear(this);
+ d->status = Null;
+ d->progress = 0.0;
+ pixmapChange();
+ emit progressChanged(d->progress);
+ emit statusChanged(d->status);
+ update();
+ } else {
+ QDeclarativePixmap::Options options;
+ if (d->async)
+ options |= QDeclarativePixmap::Asynchronous;
+ if (d->cache)
+ options |= QDeclarativePixmap::Cache;
+ d->pix.clear(this);
+ d->pix.load(qmlEngine(this), d->url, d->explicitSourceSize ? sourceSize() : QSize(), options);
+
+ if (d->pix.isLoading()) {
+ d->progress = 0.0;
+ d->status = Loading;
+ emit progressChanged(d->progress);
+ emit statusChanged(d->status);
+
+ static int thisRequestProgress = -1;
+ static int thisRequestFinished = -1;
+ if (thisRequestProgress == -1) {
+ thisRequestProgress =
+ QSGImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)");
+ thisRequestFinished =
+ QSGImageBase::staticMetaObject.indexOfSlot("requestFinished()");
+ }
+
+ d->pix.connectFinished(this, thisRequestFinished);
+ d->pix.connectDownloadProgress(this, thisRequestProgress);
+
+ } else {
+ requestFinished();
+ }
+ }
+}
+
+void QSGImageBase::requestFinished()
+{
+ Q_D(QSGImageBase);
+
+ QSGImageBase::Status oldStatus = d->status;
+ qreal oldProgress = d->progress;
+
+ if (d->pix.isError()) {
+ d->status = Error;
+ qmlInfo(this) << d->pix.error();
+ } else {
+ d->status = Ready;
+ }
+
+ d->progress = 1.0;
+
+ pixmapChange();
+
+ if (d->sourcesize.width() != d->pix.width() || d->sourcesize.height() != d->pix.height())
+ emit sourceSizeChanged();
+
+ if (d->status != oldStatus)
+ emit statusChanged(d->status);
+ if (d->progress != oldProgress)
+ emit progressChanged(d->progress);
+
+ update();
+}
+
+void QSGImageBase::requestProgress(qint64 received, qint64 total)
+{
+ Q_D(QSGImageBase);
+ if (d->status == Loading && total > 0) {
+ d->progress = qreal(received)/total;
+ emit progressChanged(d->progress);
+ }
+}
+
+void QSGImageBase::componentComplete()
+{
+ Q_D(QSGImageBase);
+ QSGItem::componentComplete();
+ if (d->url.isValid())
+ load();
+}
+
+void QSGImageBase::pixmapChange()
+{
+ Q_D(QSGImageBase);
+ setImplicitWidth(d->pix.width());
+ setImplicitHeight(d->pix.height());
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgimagebase_p.h b/src/declarative/items/qsgimagebase_p.h
new file mode 100644
index 0000000000..00b14525f2
--- /dev/null
+++ b/src/declarative/items/qsgimagebase_p.h
@@ -0,0 +1,117 @@
+// Commit: af05f64d3edc860c3cf79c7f0bdf2377faae5f40
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGIMAGEBASE_P_H
+#define QSGIMAGEBASE_P_H
+
+#include "qsgimplicitsizeitem_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QSGImageBasePrivate;
+class Q_AUTOTEST_EXPORT QSGImageBase : public QSGImplicitSizeItem
+{
+ Q_OBJECT
+ Q_ENUMS(Status)
+
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
+ Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
+ Q_PROPERTY(bool cache READ cache WRITE setCache NOTIFY cacheChanged)
+ Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged)
+ Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged)
+
+public:
+ QSGImageBase(QSGItem *parent=0);
+ ~QSGImageBase();
+ enum Status { Null, Ready, Loading, Error };
+ Status status() const;
+ qreal progress() const;
+
+ QUrl source() const;
+ virtual void setSource(const QUrl &url);
+
+ bool asynchronous() const;
+ void setAsynchronous(bool);
+
+ bool cache() const;
+ void setCache(bool);
+
+ virtual void setSourceSize(const QSize&);
+ QSize sourceSize() const;
+ void resetSourceSize();
+
+ virtual void setMirror(bool mirror);
+ bool mirror() const;
+
+Q_SIGNALS:
+ void sourceChanged(const QUrl &);
+ void sourceSizeChanged();
+ void statusChanged(QSGImageBase::Status);
+ void progressChanged(qreal progress);
+ void asynchronousChanged();
+ void cacheChanged();
+ void mirrorChanged();
+
+protected:
+ virtual void load();
+ virtual void componentComplete();
+ virtual void pixmapChange();
+ QSGImageBase(QSGImageBasePrivate &dd, QSGItem *parent);
+
+private Q_SLOTS:
+ virtual void requestFinished();
+ void requestProgress(qint64,qint64);
+
+private:
+ Q_DISABLE_COPY(QSGImageBase)
+ Q_DECLARE_PRIVATE(QSGImageBase)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSGIMAGEBASE_P_H
diff --git a/src/declarative/items/qsgimagebase_p_p.h b/src/declarative/items/qsgimagebase_p_p.h
new file mode 100644
index 0000000000..8c67b41e8b
--- /dev/null
+++ b/src/declarative/items/qsgimagebase_p_p.h
@@ -0,0 +1,93 @@
+// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGIMAGEBASE_P_P_H
+#define QSGIMAGEBASE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgimplicitsizeitem_p_p.h"
+#include "qsgimagebase_p.h"
+
+#include <private/qdeclarativepixmapcache_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkReply;
+class QSGImageBasePrivate : public QSGImplicitSizeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGImageBase)
+
+public:
+ QSGImageBasePrivate()
+ : status(QSGImageBase::Null),
+ progress(0.0),
+ explicitSourceSize(false),
+ async(false),
+ cache(true),
+ mirror(false)
+ {
+ }
+
+ QDeclarativePixmap pix;
+ QSGImageBase::Status status;
+ QUrl url;
+ qreal progress;
+ QSize sourcesize;
+ bool explicitSourceSize : 1;
+ bool async : 1;
+ bool cache : 1;
+ bool mirror: 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGIMAGEBASE_P_P_H
diff --git a/src/declarative/items/qsgimplicitsizeitem.cpp b/src/declarative/items/qsgimplicitsizeitem.cpp
new file mode 100644
index 0000000000..f2cc9bcbdb
--- /dev/null
+++ b/src/declarative/items/qsgimplicitsizeitem.cpp
@@ -0,0 +1,93 @@
+// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qsgimplicitsizeitem_p.h"
+#include "private/qsgimplicitsizeitem_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QSGImplicitSizeItemPrivate::implicitWidthChanged()
+{
+ Q_Q(QSGImplicitSizeItem);
+ emit q->implicitWidthChanged();
+}
+
+void QSGImplicitSizeItemPrivate::implicitHeightChanged()
+{
+ Q_Q(QSGImplicitSizeItem);
+ emit q->implicitHeightChanged();
+}
+
+QSGImplicitSizeItem::QSGImplicitSizeItem(QSGItem *parent)
+ : QSGItem(*(new QSGImplicitSizeItemPrivate), parent)
+{
+}
+
+QSGImplicitSizeItem::QSGImplicitSizeItem(QSGImplicitSizeItemPrivate &dd, QSGItem *parent)
+ : QSGItem(dd, parent)
+{
+}
+
+
+void QSGImplicitSizePaintedItemPrivate::implicitWidthChanged()
+{
+ Q_Q(QSGImplicitSizePaintedItem);
+ emit q->implicitWidthChanged();
+}
+
+void QSGImplicitSizePaintedItemPrivate::implicitHeightChanged()
+{
+ Q_Q(QSGImplicitSizePaintedItem);
+ emit q->implicitHeightChanged();
+}
+
+QSGImplicitSizePaintedItem::QSGImplicitSizePaintedItem(QSGItem *parent)
+ : QSGPaintedItem(*(new QSGImplicitSizePaintedItemPrivate), parent)
+{
+}
+
+QSGImplicitSizePaintedItem::QSGImplicitSizePaintedItem(QSGImplicitSizePaintedItemPrivate &dd, QSGItem *parent)
+ : QSGPaintedItem(dd, parent)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgimplicitsizeitem_p.h b/src/declarative/items/qsgimplicitsizeitem_p.h
new file mode 100644
index 0000000000..36ef0e7f8b
--- /dev/null
+++ b/src/declarative/items/qsgimplicitsizeitem_p.h
@@ -0,0 +1,101 @@
+// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGIMPLICITSIZEITEM_H
+#define QSGIMPLICITSIZEITEM_H
+
+#include "qsgpainteditem.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QSGImplicitSizeItemPrivate;
+class Q_AUTOTEST_EXPORT QSGImplicitSizeItem : public QSGItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
+ Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged)
+
+public:
+ QSGImplicitSizeItem(QSGItem *parent = 0);
+
+protected:
+ QSGImplicitSizeItem(QSGImplicitSizeItemPrivate &dd, QSGItem *parent);
+
+Q_SIGNALS:
+ void implicitWidthChanged();
+ void implicitHeightChanged();
+
+private:
+ Q_DISABLE_COPY(QSGImplicitSizeItem)
+ Q_DECLARE_PRIVATE(QSGImplicitSizeItem)
+};
+
+class QSGImplicitSizePaintedItemPrivate;
+class Q_AUTOTEST_EXPORT QSGImplicitSizePaintedItem : public QSGPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
+ Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged)
+
+public:
+ QSGImplicitSizePaintedItem(QSGItem *parent = 0);
+
+protected:
+ QSGImplicitSizePaintedItem(QSGImplicitSizePaintedItemPrivate &dd, QSGItem *parent);
+ virtual void drawContents(QPainter *, const QRect &) {};
+
+Q_SIGNALS:
+ void implicitWidthChanged();
+ void implicitHeightChanged();
+
+private:
+ Q_DISABLE_COPY(QSGImplicitSizePaintedItem)
+ Q_DECLARE_PRIVATE(QSGImplicitSizePaintedItem)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSGIMPLICITSIZEITEM_H
diff --git a/src/declarative/items/qsgimplicitsizeitem_p_p.h b/src/declarative/items/qsgimplicitsizeitem_p_p.h
new file mode 100644
index 0000000000..f67ecfab9f
--- /dev/null
+++ b/src/declarative/items/qsgimplicitsizeitem_p_p.h
@@ -0,0 +1,92 @@
+// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGIMPLICITSIZEITEM_P_H
+#define QSGIMPLICITSIZEITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qsgitem_p.h"
+#include "private/qsgpainteditem_p.h"
+#include "private/qsgimplicitsizeitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGImplicitSizeItemPrivate : public QSGItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGImplicitSizeItem)
+
+public:
+ QSGImplicitSizeItemPrivate()
+ {
+ }
+
+ virtual void implicitWidthChanged();
+ virtual void implicitHeightChanged();
+};
+
+
+class QSGImplicitSizePaintedItemPrivate : public QSGPaintedItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGImplicitSizePaintedItem)
+
+public:
+ QSGImplicitSizePaintedItemPrivate()
+ {
+ }
+
+ virtual void implicitWidthChanged();
+ virtual void implicitHeightChanged();
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGIMPLICITSIZEITEM_P_H
diff --git a/src/declarative/items/qsgitem.cpp b/src/declarative/items/qsgitem.cpp
new file mode 100644
index 0000000000..f2d26955aa
--- /dev/null
+++ b/src/declarative/items/qsgitem.cpp
@@ -0,0 +1,3143 @@
+// Commit: c44be8c0b27756a2025ebad1945632f3f7e4bebc
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgitem.h"
+
+#include "qsgcanvas.h"
+#include <QtScript/qscriptengine.h>
+#include "qsgcanvas_p.h"
+
+#include "qsgevents_p_p.h"
+
+#include <QtDeclarative/qdeclarativeitem.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativeview.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qgraphicstransform.h>
+#include <QtGui/qpen.h>
+#include <QtGui/qinputcontext.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qnumeric.h>
+
+#include <private/qdeclarativeengine_p.h>
+#include <private/qdeclarativestategroup_p.h>
+#include <private/qdeclarativeopenmetaobject_p.h>
+#include <private/qdeclarativestate_p.h>
+#include <private/qlistmodelinterface_p.h>
+#include <private/qsgitem_p.h>
+
+#include <float.h>
+
+// XXX todo Readd parentNotifier for faster parent bindings
+// XXX todo Check that elements that create items handle memory correctly after visual ownership change
+
+QT_BEGIN_NAMESPACE
+
+QSGTransformPrivate::QSGTransformPrivate()
+{
+}
+
+QSGTransform::QSGTransform(QObject *parent)
+: QObject(*(new QSGTransformPrivate), parent)
+{
+}
+
+QSGTransform::QSGTransform(QSGTransformPrivate &dd, QObject *parent)
+: QObject(dd, parent)
+{
+}
+
+QSGTransform::~QSGTransform()
+{
+ Q_D(QSGTransform);
+ for (int ii = 0; ii < d->items.count(); ++ii) {
+ QSGItemPrivate *p = QSGItemPrivate::get(d->items.at(ii));
+ p->transforms.removeOne(this);
+ p->dirty(QSGItemPrivate::Transform);
+ }
+}
+
+void QSGTransform::update()
+{
+ Q_D(QSGTransform);
+ for (int ii = 0; ii < d->items.count(); ++ii) {
+ QSGItemPrivate *p = QSGItemPrivate::get(d->items.at(ii));
+ p->dirty(QSGItemPrivate::Transform);
+ }
+}
+
+QSGContents::QSGContents(QSGItem *item)
+: m_item(item), m_x(0), m_y(0), m_width(0), m_height(0)
+{
+ //### optimize
+ connect(this, SIGNAL(rectChanged(QRectF)), m_item, SIGNAL(childrenRectChanged(QRectF)));
+}
+
+QSGContents::~QSGContents()
+{
+ QList<QSGItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QSGItem *child = children.at(i);
+ QSGItemPrivate::get(child)->removeItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed);
+ }
+}
+
+QRectF QSGContents::rectF() const
+{
+ return QRectF(m_x, m_y, m_width, m_height);
+}
+
+void QSGContents::calcHeight(QSGItem *changed)
+{
+ qreal oldy = m_y;
+ qreal oldheight = m_height;
+
+ if (changed) {
+ qreal top = oldy;
+ qreal bottom = oldy + oldheight;
+ qreal y = changed->y();
+ if (y + changed->height() > bottom)
+ bottom = y + changed->height();
+ if (y < top)
+ top = y;
+ m_y = top;
+ m_height = bottom - top;
+ } else {
+ qreal top = FLT_MAX;
+ qreal bottom = 0;
+ QList<QSGItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QSGItem *child = children.at(i);
+ qreal y = child->y();
+ if (y + child->height() > bottom)
+ bottom = y + child->height();
+ if (y < top)
+ top = y;
+ }
+ if (!children.isEmpty())
+ m_y = top;
+ m_height = qMax(bottom - top, qreal(0.0));
+ }
+
+ if (m_height != oldheight || m_y != oldy)
+ emit rectChanged(rectF());
+}
+
+void QSGContents::calcWidth(QSGItem *changed)
+{
+ qreal oldx = m_x;
+ qreal oldwidth = m_width;
+
+ if (changed) {
+ qreal left = oldx;
+ qreal right = oldx + oldwidth;
+ qreal x = changed->x();
+ if (x + changed->width() > right)
+ right = x + changed->width();
+ if (x < left)
+ left = x;
+ m_x = left;
+ m_width = right - left;
+ } else {
+ qreal left = FLT_MAX;
+ qreal right = 0;
+ QList<QSGItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QSGItem *child = children.at(i);
+ qreal x = child->x();
+ if (x + child->width() > right)
+ right = x + child->width();
+ if (x < left)
+ left = x;
+ }
+ if (!children.isEmpty())
+ m_x = left;
+ m_width = qMax(right - left, qreal(0.0));
+ }
+
+ if (m_width != oldwidth || m_x != oldx)
+ emit rectChanged(rectF());
+}
+
+void QSGContents::complete()
+{
+ QList<QSGItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QSGItem *child = children.at(i);
+ QSGItemPrivate::get(child)->addItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed);
+ //###what about changes to visibility?
+ }
+
+ calcGeometry();
+}
+
+void QSGContents::itemGeometryChanged(QSGItem *changed, const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_UNUSED(changed)
+ //### we can only pass changed if the left edge has moved left, or the right edge has moved right
+ if (newGeometry.width() != oldGeometry.width() || newGeometry.x() != oldGeometry.x())
+ calcWidth(/*changed*/);
+ if (newGeometry.height() != oldGeometry.height() || newGeometry.y() != oldGeometry.y())
+ calcHeight(/*changed*/);
+}
+
+void QSGContents::itemDestroyed(QSGItem *item)
+{
+ if (item)
+ QSGItemPrivate::get(item)->removeItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed);
+ calcGeometry();
+}
+
+void QSGContents::childRemoved(QSGItem *item)
+{
+ if (item)
+ QSGItemPrivate::get(item)->removeItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed);
+ calcGeometry();
+}
+
+void QSGContents::childAdded(QSGItem *item)
+{
+ if (item)
+ QSGItemPrivate::get(item)->addItemChangeListener(this, QSGItemPrivate::Geometry | QSGItemPrivate::Destroyed);
+ calcWidth(item);
+ calcHeight(item);
+}
+
+QSGItemKeyFilter::QSGItemKeyFilter(QSGItem *item)
+: m_processPost(false), m_next(0)
+{
+ QSGItemPrivate *p = item?QSGItemPrivate::get(item):0;
+ if (p) {
+ m_next = p->keyHandler;
+ p->keyHandler = this;
+ }
+}
+
+QSGItemKeyFilter::~QSGItemKeyFilter()
+{
+}
+
+void QSGItemKeyFilter::keyPressed(QKeyEvent *event, bool post)
+{
+ if (m_next) m_next->keyPressed(event, post);
+}
+
+void QSGItemKeyFilter::keyReleased(QKeyEvent *event, bool post)
+{
+ if (m_next) m_next->keyReleased(event, post);
+}
+
+void QSGItemKeyFilter::inputMethodEvent(QInputMethodEvent *event, bool post)
+{
+ if (m_next)
+ m_next->inputMethodEvent(event, post);
+ else
+ event->ignore();
+}
+
+QVariant QSGItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ if (m_next) return m_next->inputMethodQuery(query);
+ return QVariant();
+}
+
+void QSGItemKeyFilter::componentComplete()
+{
+ if (m_next) m_next->componentComplete();
+}
+
+QSGKeyNavigationAttached::QSGKeyNavigationAttached(QObject *parent)
+: QObject(*(new QSGKeyNavigationAttachedPrivate), parent),
+ QSGItemKeyFilter(qobject_cast<QSGItem*>(parent))
+{
+ m_processPost = true;
+}
+
+QSGKeyNavigationAttached *
+QSGKeyNavigationAttached::qmlAttachedProperties(QObject *obj)
+{
+ return new QSGKeyNavigationAttached(obj);
+}
+
+QSGItem *QSGKeyNavigationAttached::left() const
+{
+ Q_D(const QSGKeyNavigationAttached);
+ return d->left;
+}
+
+void QSGKeyNavigationAttached::setLeft(QSGItem *i)
+{
+ Q_D(QSGKeyNavigationAttached);
+ if (d->left == i)
+ return;
+ d->left = i;
+ d->leftSet = true;
+ QSGKeyNavigationAttached* other =
+ qobject_cast<QSGKeyNavigationAttached*>(qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(i));
+ if (other && !other->d_func()->rightSet){
+ other->d_func()->right = qobject_cast<QSGItem*>(parent());
+ emit other->rightChanged();
+ }
+ emit leftChanged();
+}
+
+QSGItem *QSGKeyNavigationAttached::right() const
+{
+ Q_D(const QSGKeyNavigationAttached);
+ return d->right;
+}
+
+void QSGKeyNavigationAttached::setRight(QSGItem *i)
+{
+ Q_D(QSGKeyNavigationAttached);
+ if (d->right == i)
+ return;
+ d->right = i;
+ d->rightSet = true;
+ QSGKeyNavigationAttached* other =
+ qobject_cast<QSGKeyNavigationAttached*>(qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(i));
+ if (other && !other->d_func()->leftSet){
+ other->d_func()->left = qobject_cast<QSGItem*>(parent());
+ emit other->leftChanged();
+ }
+ emit rightChanged();
+}
+
+QSGItem *QSGKeyNavigationAttached::up() const
+{
+ Q_D(const QSGKeyNavigationAttached);
+ return d->up;
+}
+
+void QSGKeyNavigationAttached::setUp(QSGItem *i)
+{
+ Q_D(QSGKeyNavigationAttached);
+ if (d->up == i)
+ return;
+ d->up = i;
+ d->upSet = true;
+ QSGKeyNavigationAttached* other =
+ qobject_cast<QSGKeyNavigationAttached*>(qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(i));
+ if (other && !other->d_func()->downSet){
+ other->d_func()->down = qobject_cast<QSGItem*>(parent());
+ emit other->downChanged();
+ }
+ emit upChanged();
+}
+
+QSGItem *QSGKeyNavigationAttached::down() const
+{
+ Q_D(const QSGKeyNavigationAttached);
+ return d->down;
+}
+
+void QSGKeyNavigationAttached::setDown(QSGItem *i)
+{
+ Q_D(QSGKeyNavigationAttached);
+ if (d->down == i)
+ return;
+ d->down = i;
+ d->downSet = true;
+ QSGKeyNavigationAttached* other =
+ qobject_cast<QSGKeyNavigationAttached*>(qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(i));
+ if(other && !other->d_func()->upSet){
+ other->d_func()->up = qobject_cast<QSGItem*>(parent());
+ emit other->upChanged();
+ }
+ emit downChanged();
+}
+
+QSGItem *QSGKeyNavigationAttached::tab() const
+{
+ Q_D(const QSGKeyNavigationAttached);
+ return d->tab;
+}
+
+void QSGKeyNavigationAttached::setTab(QSGItem *i)
+{
+ Q_D(QSGKeyNavigationAttached);
+ if (d->tab == i)
+ return;
+ d->tab = i;
+ d->tabSet = true;
+ QSGKeyNavigationAttached* other =
+ qobject_cast<QSGKeyNavigationAttached*>(qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(i));
+ if(other && !other->d_func()->backtabSet){
+ other->d_func()->backtab = qobject_cast<QSGItem*>(parent());
+ emit other->backtabChanged();
+ }
+ emit tabChanged();
+}
+
+QSGItem *QSGKeyNavigationAttached::backtab() const
+{
+ Q_D(const QSGKeyNavigationAttached);
+ return d->backtab;
+}
+
+void QSGKeyNavigationAttached::setBacktab(QSGItem *i)
+{
+ Q_D(QSGKeyNavigationAttached);
+ if (d->backtab == i)
+ return;
+ d->backtab = i;
+ d->backtabSet = true;
+ QSGKeyNavigationAttached* other =
+ qobject_cast<QSGKeyNavigationAttached*>(qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(i));
+ if(other && !other->d_func()->tabSet){
+ other->d_func()->tab = qobject_cast<QSGItem*>(parent());
+ emit other->tabChanged();
+ }
+ emit backtabChanged();
+}
+
+QSGKeyNavigationAttached::Priority QSGKeyNavigationAttached::priority() const
+{
+ return m_processPost ? AfterItem : BeforeItem;
+}
+
+void QSGKeyNavigationAttached::setPriority(Priority order)
+{
+ bool processPost = order == AfterItem;
+ if (processPost != m_processPost) {
+ m_processPost = processPost;
+ emit priorityChanged();
+ }
+}
+
+void QSGKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post)
+{
+ Q_D(QSGKeyNavigationAttached);
+ event->ignore();
+
+ if (post != m_processPost) {
+ QSGItemKeyFilter::keyPressed(event, post);
+ return;
+ }
+
+ bool mirror = false;
+ switch(event->key()) {
+ case Qt::Key_Left: {
+ if (QSGItem *parentItem = qobject_cast<QSGItem*>(parent()))
+ mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror;
+ QSGItem* leftItem = mirror ? d->right : d->left;
+ if (leftItem) {
+ setFocusNavigation(leftItem, mirror ? "right" : "left");
+ event->accept();
+ }
+ break;
+ }
+ case Qt::Key_Right: {
+ if (QSGItem *parentItem = qobject_cast<QSGItem*>(parent()))
+ mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror;
+ QSGItem* rightItem = mirror ? d->left : d->right;
+ if (rightItem) {
+ setFocusNavigation(rightItem, mirror ? "left" : "right");
+ event->accept();
+ }
+ break;
+ }
+ case Qt::Key_Up:
+ if (d->up) {
+ setFocusNavigation(d->up, "up");
+ event->accept();
+ }
+ break;
+ case Qt::Key_Down:
+ if (d->down) {
+ setFocusNavigation(d->down, "down");
+ event->accept();
+ }
+ break;
+ case Qt::Key_Tab:
+ if (d->tab) {
+ setFocusNavigation(d->tab, "tab");
+ event->accept();
+ }
+ break;
+ case Qt::Key_Backtab:
+ if (d->backtab) {
+ setFocusNavigation(d->backtab, "backtab");
+ event->accept();
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!event->isAccepted()) QSGItemKeyFilter::keyPressed(event, post);
+}
+
+void QSGKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post)
+{
+ Q_D(QSGKeyNavigationAttached);
+ event->ignore();
+
+ if (post != m_processPost) {
+ QSGItemKeyFilter::keyReleased(event, post);
+ return;
+ }
+
+ bool mirror = false;
+ switch(event->key()) {
+ case Qt::Key_Left:
+ if (QSGItem *parentItem = qobject_cast<QSGItem*>(parent()))
+ mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror;
+ if (mirror ? d->right : d->left)
+ event->accept();
+ break;
+ case Qt::Key_Right:
+ if (QSGItem *parentItem = qobject_cast<QSGItem*>(parent()))
+ mirror = QSGItemPrivate::get(parentItem)->effectiveLayoutMirror;
+ if (mirror ? d->left : d->right)
+ event->accept();
+ break;
+ case Qt::Key_Up:
+ if (d->up) {
+ event->accept();
+ }
+ break;
+ case Qt::Key_Down:
+ if (d->down) {
+ event->accept();
+ }
+ break;
+ case Qt::Key_Tab:
+ if (d->tab) {
+ event->accept();
+ }
+ break;
+ case Qt::Key_Backtab:
+ if (d->backtab) {
+ event->accept();
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (!event->isAccepted()) QSGItemKeyFilter::keyReleased(event, post);
+}
+
+void QSGKeyNavigationAttached::setFocusNavigation(QSGItem *currentItem, const char *dir)
+{
+ QSGItem *initialItem = currentItem;
+ bool isNextItem = false;
+ do {
+ isNextItem = false;
+ if (currentItem->isVisible() && currentItem->isEnabled()) {
+ currentItem->setFocus(true);
+ } else {
+ QObject *attached =
+ qmlAttachedPropertiesObject<QSGKeyNavigationAttached>(currentItem, false);
+ if (attached) {
+ QSGItem *tempItem = qvariant_cast<QSGItem*>(attached->property(dir));
+ if (tempItem) {
+ currentItem = tempItem;
+ isNextItem = true;
+ }
+ }
+ }
+ }
+ while (currentItem != initialItem && isNextItem);
+}
+
+const QSGKeysAttached::SigMap QSGKeysAttached::sigMap[] = {
+ { Qt::Key_Left, "leftPressed" },
+ { Qt::Key_Right, "rightPressed" },
+ { Qt::Key_Up, "upPressed" },
+ { Qt::Key_Down, "downPressed" },
+ { Qt::Key_Tab, "tabPressed" },
+ { Qt::Key_Backtab, "backtabPressed" },
+ { Qt::Key_Asterisk, "asteriskPressed" },
+ { Qt::Key_NumberSign, "numberSignPressed" },
+ { Qt::Key_Escape, "escapePressed" },
+ { Qt::Key_Return, "returnPressed" },
+ { Qt::Key_Enter, "enterPressed" },
+ { Qt::Key_Delete, "deletePressed" },
+ { Qt::Key_Space, "spacePressed" },
+ { Qt::Key_Back, "backPressed" },
+ { Qt::Key_Cancel, "cancelPressed" },
+ { Qt::Key_Select, "selectPressed" },
+ { Qt::Key_Yes, "yesPressed" },
+ { Qt::Key_No, "noPressed" },
+ { Qt::Key_Context1, "context1Pressed" },
+ { Qt::Key_Context2, "context2Pressed" },
+ { Qt::Key_Context3, "context3Pressed" },
+ { Qt::Key_Context4, "context4Pressed" },
+ { Qt::Key_Call, "callPressed" },
+ { Qt::Key_Hangup, "hangupPressed" },
+ { Qt::Key_Flip, "flipPressed" },
+ { Qt::Key_Menu, "menuPressed" },
+ { Qt::Key_VolumeUp, "volumeUpPressed" },
+ { Qt::Key_VolumeDown, "volumeDownPressed" },
+ { 0, 0 }
+};
+
+bool QSGKeysAttachedPrivate::isConnected(const char *signalName)
+{
+ return isSignalConnected(signalIndex(signalName));
+}
+
+QSGKeysAttached::QSGKeysAttached(QObject *parent)
+: QObject(*(new QSGKeysAttachedPrivate), parent),
+ QSGItemKeyFilter(qobject_cast<QSGItem*>(parent))
+{
+ Q_D(QSGKeysAttached);
+ m_processPost = false;
+ d->item = qobject_cast<QSGItem*>(parent);
+}
+
+QSGKeysAttached::~QSGKeysAttached()
+{
+}
+
+QSGKeysAttached::Priority QSGKeysAttached::priority() const
+{
+ return m_processPost ? AfterItem : BeforeItem;
+}
+
+void QSGKeysAttached::setPriority(Priority order)
+{
+ bool processPost = order == AfterItem;
+ if (processPost != m_processPost) {
+ m_processPost = processPost;
+ emit priorityChanged();
+ }
+}
+
+void QSGKeysAttached::componentComplete()
+{
+ Q_D(QSGKeysAttached);
+ if (d->item) {
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QSGItem *targetItem = d->targets.at(ii);
+ if (targetItem && (targetItem->flags() & QSGItem::ItemAcceptsInputMethod)) {
+ d->item->setFlag(QSGItem::ItemAcceptsInputMethod);
+ break;
+ }
+ }
+ }
+}
+
+void QSGKeysAttached::keyPressed(QKeyEvent *event, bool post)
+{
+ Q_D(QSGKeysAttached);
+ if (post != m_processPost || !d->enabled || d->inPress) {
+ event->ignore();
+ QSGItemKeyFilter::keyPressed(event, post);
+ return;
+ }
+
+ // first process forwards
+ if (d->item && d->item->canvas()) {
+ d->inPress = true;
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QSGItem *i = d->targets.at(ii);
+ if (i && i->isVisible()) {
+ d->item->canvas()->sendEvent(i, event);
+ if (event->isAccepted()) {
+ d->inPress = false;
+ return;
+ }
+ }
+ }
+ d->inPress = false;
+ }
+
+ QSGKeyEvent ke(*event);
+ QByteArray keySignal = keyToSignal(event->key());
+ if (!keySignal.isEmpty()) {
+ keySignal += "(QSGKeyEvent*)";
+ if (d->isConnected(keySignal)) {
+ // If we specifically handle a key then default to accepted
+ ke.setAccepted(true);
+ int idx = QSGKeysAttached::staticMetaObject.indexOfSignal(keySignal);
+ metaObject()->method(idx).invoke(this, Qt::DirectConnection, Q_ARG(QSGKeyEvent*, &ke));
+ }
+ }
+ if (!ke.isAccepted())
+ emit pressed(&ke);
+ event->setAccepted(ke.isAccepted());
+
+ if (!event->isAccepted()) QSGItemKeyFilter::keyPressed(event, post);
+}
+
+void QSGKeysAttached::keyReleased(QKeyEvent *event, bool post)
+{
+ Q_D(QSGKeysAttached);
+ if (post != m_processPost || !d->enabled || d->inRelease) {
+ event->ignore();
+ QSGItemKeyFilter::keyReleased(event, post);
+ return;
+ }
+
+ if (d->item && d->item->canvas()) {
+ d->inRelease = true;
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QSGItem *i = d->targets.at(ii);
+ if (i && i->isVisible()) {
+ d->item->canvas()->sendEvent(i, event);
+ if (event->isAccepted()) {
+ d->inRelease = false;
+ return;
+ }
+ }
+ }
+ d->inRelease = false;
+ }
+
+ QSGKeyEvent ke(*event);
+ emit released(&ke);
+ event->setAccepted(ke.isAccepted());
+
+ if (!event->isAccepted()) QSGItemKeyFilter::keyReleased(event, post);
+}
+
+void QSGKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post)
+{
+ Q_D(QSGKeysAttached);
+ if (post == m_processPost && d->item && !d->inIM && d->item->canvas()) {
+ d->inIM = true;
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QSGItem *i = d->targets.at(ii);
+ if (i && i->isVisible() && (i->flags() & QSGItem::ItemAcceptsInputMethod)) {
+ d->item->canvas()->sendEvent(i, event);
+ if (event->isAccepted()) {
+ d->imeItem = i;
+ d->inIM = false;
+ return;
+ }
+ }
+ }
+ d->inIM = false;
+ }
+ QSGItemKeyFilter::inputMethodEvent(event, post);
+}
+
+QVariant QSGKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QSGKeysAttached);
+ if (d->item) {
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QSGItem *i = d->targets.at(ii);
+ if (i && i->isVisible() && (i->flags() & QSGItem::ItemAcceptsInputMethod) && i == d->imeItem) {
+ //### how robust is i == d->imeItem check?
+ QVariant v = i->inputMethodQuery(query);
+ if (v.userType() == QVariant::RectF)
+ v = d->item->mapRectFromItem(i, v.toRectF()); //### cost?
+ return v;
+ }
+ }
+ }
+ return QSGItemKeyFilter::inputMethodQuery(query);
+}
+
+QSGKeysAttached *QSGKeysAttached::qmlAttachedProperties(QObject *obj)
+{
+ return new QSGKeysAttached(obj);
+}
+
+
+QSGLayoutMirroringAttached::QSGLayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0)
+{
+ if (QSGItem *item = qobject_cast<QSGItem*>(parent)) {
+ itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->attachedLayoutDirection = this;
+ } else
+ qmlInfo(parent) << tr("LayoutDirection attached property only works with Items");
+}
+
+QSGLayoutMirroringAttached * QSGLayoutMirroringAttached::qmlAttachedProperties(QObject *object)
+{
+ return new QSGLayoutMirroringAttached(object);
+}
+
+bool QSGLayoutMirroringAttached::enabled() const
+{
+ return itemPrivate ? itemPrivate->effectiveLayoutMirror : false;
+}
+
+void QSGLayoutMirroringAttached::setEnabled(bool enabled)
+{
+ if (!itemPrivate)
+ return;
+
+ itemPrivate->isMirrorImplicit = false;
+ if (enabled != itemPrivate->effectiveLayoutMirror) {
+ itemPrivate->setLayoutMirror(enabled);
+ if (itemPrivate->inheritMirrorFromItem)
+ itemPrivate->resolveLayoutMirror();
+ }
+}
+
+void QSGLayoutMirroringAttached::resetEnabled()
+{
+ if (itemPrivate && !itemPrivate->isMirrorImplicit) {
+ itemPrivate->isMirrorImplicit = true;
+ itemPrivate->resolveLayoutMirror();
+ }
+}
+
+bool QSGLayoutMirroringAttached::childrenInherit() const
+{
+ return itemPrivate ? itemPrivate->inheritMirrorFromItem : false;
+}
+
+void QSGLayoutMirroringAttached::setChildrenInherit(bool childrenInherit) {
+ if (itemPrivate && childrenInherit != itemPrivate->inheritMirrorFromItem) {
+ itemPrivate->inheritMirrorFromItem = childrenInherit;
+ itemPrivate->resolveLayoutMirror();
+ childrenInheritChanged();
+ }
+}
+
+void QSGItemPrivate::resolveLayoutMirror()
+{
+ Q_Q(QSGItem);
+ if (QSGItem *parentItem = q->parentItem()) {
+ QSGItemPrivate *parentPrivate = QSGItemPrivate::get(parentItem);
+ setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent);
+ } else {
+ setImplicitLayoutMirror(isMirrorImplicit ? false : effectiveLayoutMirror, inheritMirrorFromItem);
+ }
+}
+
+void QSGItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit)
+{
+ inherit = inherit || inheritMirrorFromItem;
+ if (!isMirrorImplicit && inheritMirrorFromItem)
+ mirror = effectiveLayoutMirror;
+ if (mirror == inheritedLayoutMirror && inherit == inheritMirrorFromParent)
+ return;
+
+ inheritMirrorFromParent = inherit;
+ inheritedLayoutMirror = inheritMirrorFromParent ? mirror : false;
+
+ if (isMirrorImplicit)
+ setLayoutMirror(inherit ? inheritedLayoutMirror : false);
+ for (int i = 0; i < childItems.count(); ++i) {
+ if (QSGItem *child = qobject_cast<QSGItem *>(childItems.at(i))) {
+ QSGItemPrivate *childPrivate = QSGItemPrivate::get(child);
+ childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
+ }
+ }
+}
+
+void QSGItemPrivate::setLayoutMirror(bool mirror)
+{
+ if (mirror != effectiveLayoutMirror) {
+ effectiveLayoutMirror = mirror;
+ if (_anchors) {
+ QSGAnchorsPrivate *anchor_d = QSGAnchorsPrivate::get(_anchors);
+ anchor_d->fillChanged();
+ anchor_d->centerInChanged();
+ anchor_d->updateHorizontalAnchors();
+ emit _anchors->mirroredChanged();
+ }
+ mirrorChange();
+ if (attachedLayoutDirection) {
+ emit attachedLayoutDirection->enabledChanged();
+ }
+ }
+}
+
+QSGItem::QSGItem(QSGItem* parent)
+: QObject(*(new QSGItemPrivate), parent)
+{
+ Q_D(QSGItem);
+ d->init(parent);
+}
+
+QSGItem::QSGItem(QSGItemPrivate &dd, QSGItem *parent)
+: QObject(dd, parent)
+{
+ Q_D(QSGItem);
+ d->init(parent);
+}
+
+QSGItem::~QSGItem()
+{
+ Q_D(QSGItem);
+
+ // XXX todo - optimize
+ setParentItem(0);
+ while (!d->childItems.isEmpty())
+ d->childItems.first()->setParentItem(0);
+
+ for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
+ QSGAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
+ if (anchor)
+ anchor->clearItem(this);
+ }
+
+ // XXX todo - the original checks if the parent is being destroyed
+ for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
+ QSGAnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate();
+ if (anchor && anchor->item && anchor->item->parent() != this) //child will be deleted anyway
+ anchor->updateOnComplete();
+ }
+
+ for (int ii = 0; ii < d->changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Destroyed)
+ change.listener->itemDestroyed(this);
+ }
+ d->changeListeners.clear();
+ delete d->_anchorLines; d->_anchorLines = 0;
+ delete d->_anchors; d->_anchors = 0;
+ delete d->_stateGroup; d->_stateGroup = 0;
+ delete d->_contents; d->_contents = 0;
+}
+
+void QSGItem::setParentItem(QSGItem *parentItem)
+{
+ Q_D(QSGItem);
+ if (parentItem == d->parentItem)
+ return;
+
+ d->removeFromDirtyList();
+
+ QSGItem *oldParentItem = d->parentItem;
+ QSGItem *scopeFocusedItem = 0;
+
+ if (oldParentItem) {
+ QSGItemPrivate *op = QSGItemPrivate::get(oldParentItem);
+
+ QSGItem *scopeItem = 0;
+
+ if (d->canvas && hasFocus()) {
+ scopeItem = oldParentItem;
+ while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem();
+ scopeFocusedItem = this;
+ } else if (d->canvas && !isFocusScope() && d->subFocusItem) {
+ scopeItem = oldParentItem;
+ while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem();
+ scopeFocusedItem = d->subFocusItem;
+ }
+
+ if (scopeFocusedItem)
+ QSGCanvasPrivate::get(d->canvas)->clearFocusInScope(scopeItem, scopeFocusedItem,
+ QSGCanvasPrivate::DontChangeFocusProperty);
+
+ op->removeChild(this);
+ }
+
+ d->parentItem = parentItem;
+
+ QSGCanvas *parentCanvas = parentItem?QSGItemPrivate::get(parentItem)->canvas:0;
+ if (d->canvas != parentCanvas) {
+ if (d->canvas && d->itemNodeInstance)
+ QSGCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance);
+
+ QSGItemPrivate::InitializationState initState;
+ initState.clear();
+ d->initCanvas(&initState, parentCanvas);
+ }
+
+ d->dirty(QSGItemPrivate::ParentChanged);
+
+ if (d->parentItem)
+ QSGItemPrivate::get(d->parentItem)->addChild(this);
+
+ d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
+ d->setEffectiveEnableRecur(d->calcEffectiveEnable());
+
+ if (scopeFocusedItem && d->parentItem && d->canvas) {
+ // We need to test whether this item becomes scope focused
+ QSGItem *scopeItem = 0;
+ scopeItem = d->parentItem;
+ while (!scopeItem->isFocusScope()) scopeItem = scopeItem->parentItem();
+
+ if (scopeItem->scopedFocusItem()) {
+ QSGItemPrivate::get(scopeFocusedItem)->focus = false;
+ emit scopeFocusedItem->focusChanged(false);
+ } else {
+ QSGCanvasPrivate::get(d->canvas)->setFocusInScope(scopeItem, scopeFocusedItem,
+ QSGCanvasPrivate::DontChangeFocusProperty);
+ }
+ }
+
+ d->resolveLayoutMirror();
+
+ d->itemChange(ItemParentHasChanged, d->parentItem);
+
+ emit parentChanged(d->parentItem);
+}
+
+void QSGItem::stackBefore(const QSGItem *sibling)
+{
+ Q_D(QSGItem);
+ if (!sibling || sibling == this || !d->parentItem || d->parentItem != QSGItemPrivate::get(sibling)->parentItem) {
+ qWarning("QSGItem::stackBefore: Cannot stack before %p, which must be a sibling", sibling);
+ return;
+ }
+
+ QSGItemPrivate *parentPrivate = QSGItemPrivate::get(d->parentItem);
+
+ int myIndex = parentPrivate->childItems.indexOf(this);
+ int siblingIndex = parentPrivate->childItems.indexOf(const_cast<QSGItem *>(sibling));
+
+ Q_ASSERT(myIndex != -1 && siblingIndex != -1);
+
+ if (myIndex == siblingIndex - 1)
+ return;
+
+ parentPrivate->childItems.removeAt(myIndex);
+
+ if (myIndex < siblingIndex) --siblingIndex;
+
+ parentPrivate->childItems.insert(siblingIndex, this);
+
+ parentPrivate->dirty(QSGItemPrivate::ChildrenStackingChanged);
+
+ for (int ii = qMin(siblingIndex, myIndex); ii < parentPrivate->childItems.count(); ++ii)
+ QSGItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
+}
+
+void QSGItem::stackAfter(const QSGItem *sibling)
+{
+ Q_D(QSGItem);
+ if (!sibling || sibling == this || !d->parentItem || d->parentItem != QSGItemPrivate::get(sibling)->parentItem) {
+ qWarning("QSGItem::stackAfter: Cannot stack after %p, which must be a sibling", sibling);
+ return;
+ }
+
+ QSGItemPrivate *parentPrivate = QSGItemPrivate::get(d->parentItem);
+
+ int myIndex = parentPrivate->childItems.indexOf(this);
+ int siblingIndex = parentPrivate->childItems.indexOf(const_cast<QSGItem *>(sibling));
+
+ Q_ASSERT(myIndex != -1 && siblingIndex != -1);
+
+ if (myIndex == siblingIndex + 1)
+ return;
+
+ parentPrivate->childItems.removeAt(myIndex);
+
+ if (myIndex < siblingIndex) --siblingIndex;
+
+ parentPrivate->childItems.insert(siblingIndex + 1, this);
+
+ parentPrivate->dirty(QSGItemPrivate::ChildrenStackingChanged);
+
+ for (int ii = qMin(myIndex, siblingIndex + 1); ii < parentPrivate->childItems.count(); ++ii)
+ QSGItemPrivate::get(parentPrivate->childItems.at(ii))->siblingOrderChanged();
+}
+
+/*!
+ Returns the QSGItem parent of this item.
+*/
+QSGItem *QSGItem::parentItem() const
+{
+ Q_D(const QSGItem);
+ return d->parentItem;
+}
+
+QSGEngine *QSGItem::sceneGraphEngine() const
+{
+ return canvas()->sceneGraphEngine();
+}
+
+QSGCanvas *QSGItem::canvas() const
+{
+ Q_D(const QSGItem);
+ return d->canvas;
+}
+
+static bool itemZOrder_sort(QSGItem *lhs, QSGItem *rhs)
+{
+ return lhs->z() < rhs->z();
+}
+
+QList<QSGItem *> QSGItemPrivate::paintOrderChildItems() const
+{
+ // XXX todo - optimize, don't sort and return items that are
+ // ignored anyway, like invisible or disabled items.
+ QList<QSGItem *> items = childItems;
+ qStableSort(items.begin(), items.end(), itemZOrder_sort);
+ return items;
+}
+
+void QSGItemPrivate::addChild(QSGItem *child)
+{
+ Q_Q(QSGItem);
+
+ Q_ASSERT(!childItems.contains(child));
+
+ childItems.append(child);
+
+ dirty(QSGItemPrivate::ChildrenChanged);
+
+ itemChange(QSGItem::ItemChildAddedChange, child);
+
+ emit q->childrenChanged();
+}
+
+void QSGItemPrivate::removeChild(QSGItem *child)
+{
+ Q_Q(QSGItem);
+
+ Q_ASSERT(child);
+ Q_ASSERT(childItems.contains(child));
+ childItems.removeOne(child);
+ Q_ASSERT(!childItems.contains(child));
+
+ dirty(QSGItemPrivate::ChildrenChanged);
+
+ itemChange(QSGItem::ItemChildRemovedChange, child);
+
+ emit q->childrenChanged();
+}
+
+void QSGItemPrivate::InitializationState::clear()
+{
+ focusScope = 0;
+}
+
+void QSGItemPrivate::InitializationState::clear(QSGItem *fs)
+{
+ focusScope = fs;
+}
+
+QSGItem *QSGItemPrivate::InitializationState::getFocusScope(QSGItem *item)
+{
+ if (!focusScope) {
+ QSGItem *fs = item->parentItem();
+ while (!fs->isFocusScope())
+ fs = fs->parentItem();
+ focusScope = fs;
+ }
+ return focusScope;
+}
+
+void QSGItemPrivate::initCanvas(InitializationState *state, QSGCanvas *c)
+{
+ Q_Q(QSGItem);
+
+ if (canvas) {
+ removeFromDirtyList();
+ QSGCanvasPrivate *c = QSGCanvasPrivate::get(canvas);
+ if (polishScheduled)
+ c->itemsToPolish.remove(q);
+ if (c->mouseGrabberItem == q)
+ c->mouseGrabberItem = 0;
+ }
+
+ canvas = c;
+
+ if (canvas && polishScheduled)
+ QSGCanvasPrivate::get(canvas)->itemsToPolish.insert(q);
+
+ if (canvas && hoverEnabled && !canvas->hasMouseTracking())
+ canvas->setMouseTracking(true);
+
+ // XXX todo - why aren't these added to the destroy list?
+ itemNodeInstance = 0;
+ opacityNode = 0;
+ clipNode = 0;
+ rootNode = 0;
+ groupNode = 0;
+ paintNode = 0;
+ paintNodeIndex = 0;
+
+ InitializationState _dummy;
+ InitializationState *childState = state;
+
+ if (c && q->isFocusScope()) {
+ _dummy.clear(q);
+ childState = &_dummy;
+ }
+
+ for (int ii = 0; ii < childItems.count(); ++ii) {
+ QSGItem *child = childItems.at(ii);
+ QSGItemPrivate::get(child)->initCanvas(childState, c);
+ }
+
+ if (c && focus) {
+ // Fixup
+ if (state->getFocusScope(q)->scopedFocusItem()) {
+ focus = false;
+ emit q->focusChanged(false);
+ } else {
+ QSGCanvasPrivate::get(canvas)->setFocusInScope(state->getFocusScope(q), q);
+ }
+ }
+
+ dirty(Canvas);
+
+ itemChange(QSGItem::ItemSceneChange, c);
+}
+
+/*!
+Returns a transform that maps points from canvas space into item space.
+*/
+QTransform QSGItemPrivate::canvasToItemTransform() const
+{
+ // XXX todo - optimize
+ return itemToCanvasTransform().inverted();
+}
+
+/*!
+Returns a transform that maps points from item space into canvas space.
+*/
+QTransform QSGItemPrivate::itemToCanvasTransform() const
+{
+ // XXX todo
+ QTransform rv = parentItem?QSGItemPrivate::get(parentItem)->itemToCanvasTransform():QTransform();
+ itemToParentTransform(rv);
+ return rv;
+}
+
+/*!
+Motifies \a t with this items local transform relative to its parent.
+*/
+void QSGItemPrivate::itemToParentTransform(QTransform &t) const
+{
+ if (x || y)
+ t.translate(x, y);
+
+ if (!transforms.isEmpty()) {
+ QMatrix4x4 m(t);
+ for (int ii = transforms.count() - 1; ii >= 0; --ii)
+ transforms.at(ii)->applyTo(&m);
+ t = m.toTransform();
+ }
+
+ if (scale != 1. || rotation != 0.) {
+ QPointF tp = computeTransformOrigin();
+ t.translate(tp.x(), tp.y());
+ t.scale(scale, scale);
+ t.rotate(rotation);
+ t.translate(-tp.x(), -tp.y());
+ }
+}
+
+bool QSGItem::isComponentComplete() const
+{
+ Q_D(const QSGItem);
+ return d->componentComplete;
+}
+
+QSGItemPrivate::QSGItemPrivate()
+: _anchors(0), _contents(0), baselineOffset(0), _anchorLines(0), _stateGroup(0), origin(QSGItem::Center),
+
+ flags(0), widthValid(false), heightValid(false), componentComplete(true),
+ keepMouse(false), hoverEnabled(false), smooth(false), focus(false), activeFocus(false), notifiedFocus(false),
+ notifiedActiveFocus(false), filtersChildMouseEvents(false), explicitVisible(true),
+ effectiveVisible(true), explicitEnable(true), effectiveEnable(true), polishScheduled(false),
+ inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true),
+ inheritMirrorFromParent(false), inheritMirrorFromItem(false), childrenDoNotOverlap(false),
+
+ canvas(0), parentItem(0),
+
+ subFocusItem(0),
+
+ x(0), y(0), width(0), height(0), implicitWidth(0), implicitHeight(0),
+ z(0), scale(1), rotation(0), opacity(1),
+
+ attachedLayoutDirection(0), acceptedMouseButtons(0),
+ imHints(Qt::ImhNone),
+
+ keyHandler(0),
+
+ dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0),
+
+ itemNodeInstance(0), opacityNode(0), clipNode(0), rootNode(0), groupNode(0), paintNode(0)
+ , paintNodeIndex(0), effectRefCount(0), hideRefCount(0)
+{
+}
+
+void QSGItemPrivate::init(QSGItem *parent)
+{
+ Q_Q(QSGItem);
+ baselineOffset.invalidate();
+
+ if (parent) {
+ q->setParentItem(parent);
+ QSGItemPrivate *parentPrivate = QSGItemPrivate::get(parent);
+ setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent);
+ }
+}
+
+void QSGItemPrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
+{
+ if (!o)
+ return;
+
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+
+ // This test is measurably (albeit only slightly) faster than qobject_cast<>()
+ const QMetaObject *mo = o->metaObject();
+ while (mo && mo != &QSGItem::staticMetaObject) {
+ if (mo == &QDeclarativeItem::staticMetaObject)
+ qWarning("Cannot add a QtQuick 1.0 item (%s) into a QtQuick 2.0 scene!", o->metaObject()->className());
+ mo = mo->d.superdata;
+ }
+
+ if (mo) {
+ QSGItem *item = static_cast<QSGItem *>(o);
+ item->setParentItem(that);
+ } else {
+ // XXX todo - do we really want this behavior?
+ o->setParent(that);
+ }
+}
+
+int QSGItemPrivate::data_count(QDeclarativeListProperty<QObject> *prop)
+{
+ Q_UNUSED(prop);
+ // XXX todo
+ return 0;
+}
+
+QObject *QSGItemPrivate::data_at(QDeclarativeListProperty<QObject> *prop, int i)
+{
+ Q_UNUSED(prop);
+ Q_UNUSED(i);
+ // XXX todo
+ return 0;
+}
+
+void QSGItemPrivate::data_clear(QDeclarativeListProperty<QObject> *prop)
+{
+ Q_UNUSED(prop);
+ // XXX todo
+}
+
+QObject *QSGItemPrivate::resources_at(QDeclarativeListProperty<QObject> *prop, int index)
+{
+ const QObjectList children = prop->object->children();
+ if (index < children.count())
+ return children.at(index);
+ else
+ return 0;
+}
+
+void QSGItemPrivate::resources_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
+{
+ // XXX todo - do we really want this behavior?
+ o->setParent(prop->object);
+}
+
+int QSGItemPrivate::resources_count(QDeclarativeListProperty<QObject> *prop)
+{
+ return prop->object->children().count();
+}
+
+void QSGItemPrivate::resources_clear(QDeclarativeListProperty<QObject> *prop)
+{
+ // XXX todo - do we really want this behavior?
+ const QObjectList children = prop->object->children();
+ for (int index = 0; index < children.count(); index++)
+ children.at(index)->setParent(0);
+}
+
+QSGItem *QSGItemPrivate::children_at(QDeclarativeListProperty<QSGItem> *prop, int index)
+{
+ QSGItemPrivate *p = QSGItemPrivate::get(static_cast<QSGItem *>(prop->object));
+ if (index >= p->childItems.count() || index < 0)
+ return 0;
+ else
+ return p->childItems.at(index);
+}
+
+void QSGItemPrivate::children_append(QDeclarativeListProperty<QSGItem> *prop, QSGItem *o)
+{
+ if (!o)
+ return;
+
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+ if (o->parentItem() == that)
+ o->setParentItem(0);
+
+ o->setParentItem(that);
+}
+
+int QSGItemPrivate::children_count(QDeclarativeListProperty<QSGItem> *prop)
+{
+ QSGItemPrivate *p = QSGItemPrivate::get(static_cast<QSGItem *>(prop->object));
+ return p->childItems.count();
+}
+
+void QSGItemPrivate::children_clear(QDeclarativeListProperty<QSGItem> *prop)
+{
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+ QSGItemPrivate *p = QSGItemPrivate::get(that);
+ while (!p->childItems.isEmpty())
+ p->childItems.at(0)->setParentItem(0);
+}
+
+int QSGItemPrivate::transform_count(QDeclarativeListProperty<QSGTransform> *prop)
+{
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+ return QSGItemPrivate::get(that)->transforms.count();
+}
+
+void QSGTransform::appendToItem(QSGItem *item)
+{
+ Q_D(QSGTransform);
+ if (!item)
+ return;
+
+ QSGItemPrivate *p = QSGItemPrivate::get(item);
+
+ if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) {
+ p->transforms.removeOne(this);
+ p->transforms.append(this);
+ } else {
+ p->transforms.append(this);
+ d->items.append(item);
+ }
+
+ p->dirty(QSGItemPrivate::Transform);
+}
+
+void QSGTransform::prependToItem(QSGItem *item)
+{
+ Q_D(QSGTransform);
+ if (!item)
+ return;
+
+ QSGItemPrivate *p = QSGItemPrivate::get(item);
+
+ if (!d->items.isEmpty() && !p->transforms.isEmpty() && p->transforms.contains(this)) {
+ p->transforms.removeOne(this);
+ p->transforms.prepend(this);
+ } else {
+ p->transforms.prepend(this);
+ d->items.append(item);
+ }
+
+ p->dirty(QSGItemPrivate::Transform);
+}
+
+void QSGItemPrivate::transform_append(QDeclarativeListProperty<QSGTransform> *prop, QSGTransform *transform)
+{
+ if (!transform)
+ return;
+
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+ transform->appendToItem(that);
+}
+
+QSGTransform *QSGItemPrivate::transform_at(QDeclarativeListProperty<QSGTransform> *prop, int idx)
+{
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+ QSGItemPrivate *p = QSGItemPrivate::get(that);
+
+ if (idx < 0 || idx >= p->transforms.count())
+ return 0;
+ else
+ return p->transforms.at(idx);
+}
+
+void QSGItemPrivate::transform_clear(QDeclarativeListProperty<QSGTransform> *prop)
+{
+ QSGItem *that = static_cast<QSGItem *>(prop->object);
+ QSGItemPrivate *p = QSGItemPrivate::get(that);
+
+ for (int ii = 0; ii < p->transforms.count(); ++ii) {
+ QSGTransform *t = p->transforms.at(ii);
+ QSGTransformPrivate *tp = QSGTransformPrivate::get(t);
+ tp->items.removeOne(that);
+ }
+
+ p->transforms.clear();
+
+ p->dirty(QSGItemPrivate::Transform);
+}
+
+QSGAnchors *QSGItemPrivate::anchors() const
+{
+ if (!_anchors) {
+ Q_Q(const QSGItem);
+ _anchors = new QSGAnchors(const_cast<QSGItem *>(q));
+ if (!componentComplete)
+ _anchors->classBegin();
+ }
+ return _anchors;
+}
+
+QSGItemPrivate::AnchorLines *QSGItemPrivate::anchorLines() const
+{
+ Q_Q(const QSGItem);
+ if (!_anchorLines) _anchorLines =
+ new AnchorLines(const_cast<QSGItem *>(q));
+ return _anchorLines;
+}
+
+void QSGItemPrivate::siblingOrderChanged()
+{
+ Q_Q(QSGItem);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::SiblingOrder) {
+ change.listener->itemSiblingOrderChanged(q);
+ }
+ }
+}
+
+QDeclarativeListProperty<QObject> QSGItemPrivate::data()
+{
+ return QDeclarativeListProperty<QObject>(q_func(), 0, QSGItemPrivate::data_append,
+ QSGItemPrivate::data_count,
+ QSGItemPrivate::data_at,
+ QSGItemPrivate::data_clear);
+}
+
+QRectF QSGItem::childrenRect()
+{
+ Q_D(QSGItem);
+ if (!d->_contents) {
+ d->_contents = new QSGContents(this);
+ if (d->componentComplete)
+ d->_contents->complete();
+ }
+ return d->_contents->rectF();
+}
+
+QList<QSGItem *> QSGItem::childItems() const
+{
+ Q_D(const QSGItem);
+ return d->childItems;
+}
+
+bool QSGItem::clip() const
+{
+ return flags() & ItemClipsChildrenToShape;
+}
+
+void QSGItem::setClip(bool c)
+{
+ if (clip() == c)
+ return;
+
+ setFlag(ItemClipsChildrenToShape, c);
+
+ emit clipChanged(c);
+}
+
+void QSGItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QSGItem);
+
+ if (d->_anchors)
+ QSGAnchorsPrivate::get(d->_anchors)->updateMe();
+
+ for(int ii = 0; ii < d->changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Geometry)
+ change.listener->itemGeometryChanged(this, newGeometry, oldGeometry);
+ }
+
+ if (newGeometry.x() != oldGeometry.x())
+ emit xChanged();
+ if (newGeometry.y() != oldGeometry.y())
+ emit yChanged();
+ if (newGeometry.width() != oldGeometry.width())
+ emit widthChanged();
+ if (newGeometry.height() != oldGeometry.height())
+ emit heightChanged();
+}
+
+/*!
+ Called by the rendering thread when it is time to sync the state of the QML objects with the
+ scene graph objects. The function should return the root of the scene graph subtree for
+ this item. \a oldNode is the node that was returned the last time the function was called.
+
+ The main thread is blocked while this function is executed so it is safe to read
+ values from the QSGItem instance and other objects in the main thread.
+
+ \warning This is the only function in which it is allowed to make use of scene graph
+ objects from the main thread. Use of scene graph objects outside this function will
+ result in race conditions and potential crashes.
+ */
+
+QSGNode *QSGItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ delete oldNode;
+ return 0;
+}
+
+QSGTransformNode *QSGItemPrivate::createTransformNode()
+{
+ return new QSGTransformNode;
+}
+
+void QSGItem::updatePolish()
+{
+}
+
+void QSGItemPrivate::removeItemChangeListener(QSGItemChangeListener *listener, ChangeTypes types)
+{
+ ChangeListener change(listener, types);
+ changeListeners.removeOne(change);
+}
+
+void QSGItem::keyPressEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::keyReleaseEvent(QKeyEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::inputMethodEvent(QInputMethodEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::focusInEvent(QFocusEvent *)
+{
+}
+
+void QSGItem::focusOutEvent(QFocusEvent *)
+{
+}
+
+void QSGItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ mousePressEvent(event);
+}
+
+void QSGItem::mouseUngrabEvent()
+{
+ // XXX todo
+}
+
+void QSGItem::wheelEvent(QGraphicsSceneWheelEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::touchEvent(QTouchEvent *event)
+{
+ event->ignore();
+}
+
+void QSGItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+void QSGItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+void QSGItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+bool QSGItem::childMouseEventFilter(QSGItem *, QEvent *)
+{
+ return false;
+}
+
+Qt::InputMethodHints QSGItem::inputMethodHints() const
+{
+ Q_D(const QSGItem);
+ return d->imHints;
+}
+
+void QSGItem::setInputMethodHints(Qt::InputMethodHints hints)
+{
+ Q_D(QSGItem);
+ d->imHints = hints;
+
+ if (!d->canvas || d->canvas->activeFocusItem() != this)
+ return;
+
+ QSGCanvasPrivate::get(d->canvas)->updateInputMethodData();
+#ifndef QT_NO_IM
+ if (d->canvas->hasFocus())
+ if (QInputContext *inputContext = d->canvas->inputContext())
+ inputContext->update();
+#endif
+}
+
+void QSGItem::updateMicroFocus()
+{
+#ifndef QT_NO_IM
+ Q_D(QSGItem);
+ if (d->canvas && d->canvas->hasFocus())
+ if (QInputContext *inputContext = d->canvas->inputContext())
+ inputContext->update();
+#endif
+}
+
+QVariant QSGItem::inputMethodQuery(Qt::InputMethodQuery query) const
+{
+ Q_D(const QSGItem);
+ QVariant v;
+
+ if (d->keyHandler)
+ v = d->keyHandler->inputMethodQuery(query);
+
+ return v;
+}
+
+QSGAnchorLine QSGItemPrivate::left() const
+{
+ return anchorLines()->left;
+}
+
+QSGAnchorLine QSGItemPrivate::right() const
+{
+ return anchorLines()->right;
+}
+
+QSGAnchorLine QSGItemPrivate::horizontalCenter() const
+{
+ return anchorLines()->hCenter;
+}
+
+QSGAnchorLine QSGItemPrivate::top() const
+{
+ return anchorLines()->top;
+}
+
+QSGAnchorLine QSGItemPrivate::bottom() const
+{
+ return anchorLines()->bottom;
+}
+
+QSGAnchorLine QSGItemPrivate::verticalCenter() const
+{
+ return anchorLines()->vCenter;
+}
+
+QSGAnchorLine QSGItemPrivate::baseline() const
+{
+ return anchorLines()->baseline;
+}
+
+qreal QSGItem::baselineOffset() const
+{
+ Q_D(const QSGItem);
+ if (!d->baselineOffset.isValid()) {
+ return 0.0;
+ } else
+ return d->baselineOffset;
+}
+
+void QSGItem::setBaselineOffset(qreal offset)
+{
+ Q_D(QSGItem);
+ if (offset == d->baselineOffset)
+ return;
+
+ d->baselineOffset = offset;
+
+ for(int ii = 0; ii < d->changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = d->changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Geometry) {
+ QSGAnchorsPrivate *anchor = change.listener->anchorPrivate();
+ if (anchor)
+ anchor->updateVerticalAnchors();
+ }
+ }
+ emit baselineOffsetChanged(offset);
+}
+
+void QSGItem::update()
+{
+ Q_D(QSGItem);
+ Q_ASSERT(flags() & ItemHasContents);
+ d->dirty(QSGItemPrivate::Content);
+}
+
+void QSGItem::polish()
+{
+ Q_D(QSGItem);
+ if (!d->polishScheduled) {
+ d->polishScheduled = true;
+ if (d->canvas)
+ QSGCanvasPrivate::get(d->canvas)->itemsToPolish.insert(this);
+ }
+}
+
+QScriptValue QSGItem::mapFromItem(const QScriptValue &item, qreal x, qreal y) const
+{
+ QScriptValue sv = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this))->newObject();
+ QSGItem *itemObj = qobject_cast<QSGItem*>(item.toQObject());
+ if (!itemObj && !item.isNull()) {
+ qmlInfo(this) << "mapFromItem() given argument \"" << item.toString() << "\" which is neither null nor an Item";
+ return 0;
+ }
+
+ // If QSGItem::mapFromItem() is called with 0, behaves the same as mapFromScene()
+ QPointF p = mapFromItem(itemObj, QPointF(x, y));
+ sv.setProperty(QLatin1String("x"), p.x());
+ sv.setProperty(QLatin1String("y"), p.y());
+ return sv;
+}
+
+QTransform QSGItem::itemTransform(QSGItem *other, bool *ok) const
+{
+ Q_D(const QSGItem);
+
+ // XXX todo - we need to be able to handle common parents better and detect
+ // invalid cases
+ if (ok) *ok = true;
+
+ QTransform t = d->itemToCanvasTransform();
+ if (other) t *= QSGItemPrivate::get(other)->canvasToItemTransform();
+
+ return t;
+}
+
+QScriptValue QSGItem::mapToItem(const QScriptValue &item, qreal x, qreal y) const
+{
+ QScriptValue sv = QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(this))->newObject();
+ QSGItem *itemObj = qobject_cast<QSGItem*>(item.toQObject());
+ if (!itemObj && !item.isNull()) {
+ qmlInfo(this) << "mapToItem() given argument \"" << item.toString() << "\" which is neither null nor an Item";
+ return 0;
+ }
+
+ // If QSGItem::mapToItem() is called with 0, behaves the same as mapToScene()
+ QPointF p = mapToItem(itemObj, QPointF(x, y));
+ sv.setProperty(QLatin1String("x"), p.x());
+ sv.setProperty(QLatin1String("y"), p.y());
+ return sv;
+}
+
+void QSGItem::forceActiveFocus()
+{
+ setFocus(true);
+ QSGItem *parent = parentItem();
+ while (parent) {
+ if (parent->flags() & QSGItem::ItemIsFocusScope) {
+ parent->setFocus(true);
+ }
+ parent = parent->parentItem();
+ }
+}
+
+QSGItem *QSGItem::childAt(qreal x, qreal y) const
+{
+ // XXX todo - should this include transform etc.?
+ const QList<QSGItem *> children = childItems();
+ for (int i = children.count()-1; i >= 0; --i) {
+ if (QSGItem *child = qobject_cast<QSGItem *>(children.at(i))) {
+ if (child->isVisible() && child->x() <= x
+ && child->x() + child->width() >= x
+ && child->y() <= y
+ && child->y() + child->height() >= y)
+ return child;
+ }
+ }
+ return 0;
+}
+
+QDeclarativeListProperty<QObject> QSGItemPrivate::resources()
+{
+ return QDeclarativeListProperty<QObject>(q_func(), 0, QSGItemPrivate::resources_append,
+ QSGItemPrivate::resources_count,
+ QSGItemPrivate::resources_at,
+ QSGItemPrivate::resources_clear);
+}
+
+QDeclarativeListProperty<QSGItem> QSGItemPrivate::children()
+{
+ return QDeclarativeListProperty<QSGItem>(q_func(), 0, QSGItemPrivate::children_append,
+ QSGItemPrivate::children_count,
+ QSGItemPrivate::children_at,
+ QSGItemPrivate::children_clear);
+
+}
+
+QDeclarativeListProperty<QDeclarativeState> QSGItemPrivate::states()
+{
+ return _states()->statesProperty();
+}
+
+QDeclarativeListProperty<QDeclarativeTransition> QSGItemPrivate::transitions()
+{
+ return _states()->transitionsProperty();
+}
+
+QString QSGItemPrivate::state() const
+{
+ if (!_stateGroup)
+ return QString();
+ else
+ return _stateGroup->state();
+}
+
+void QSGItemPrivate::setState(const QString &state)
+{
+ _states()->setState(state);
+}
+
+QDeclarativeListProperty<QSGTransform> QSGItem::transform()
+{
+ Q_D(QSGItem);
+ return QDeclarativeListProperty<QSGTransform>(this, 0, d->transform_append, d->transform_count,
+ d->transform_at, d->transform_clear);
+}
+
+void QSGItem::classBegin()
+{
+ Q_D(QSGItem);
+ d->componentComplete = false;
+ if (d->_stateGroup)
+ d->_stateGroup->classBegin();
+ if (d->_anchors)
+ d->_anchors->classBegin();
+}
+
+void QSGItem::componentComplete()
+{
+ Q_D(QSGItem);
+ d->componentComplete = true;
+ if (d->_stateGroup)
+ d->_stateGroup->componentComplete();
+ if (d->_anchors) {
+ d->_anchors->componentComplete();
+ QSGAnchorsPrivate::get(d->_anchors)->updateOnComplete();
+ }
+ if (d->keyHandler)
+ d->keyHandler->componentComplete();
+ if (d->_contents)
+ d->_contents->complete();
+}
+
+QDeclarativeStateGroup *QSGItemPrivate::_states()
+{
+ Q_Q(QSGItem);
+ if (!_stateGroup) {
+ _stateGroup = new QDeclarativeStateGroup;
+ if (!componentComplete)
+ _stateGroup->classBegin();
+ QObject::connect(_stateGroup, SIGNAL(stateChanged(QString)),
+ q, SIGNAL(stateChanged(QString)));
+ }
+
+ return _stateGroup;
+}
+
+QSGItemPrivate::AnchorLines::AnchorLines(QSGItem *q)
+{
+ left.item = q;
+ left.anchorLine = QSGAnchorLine::Left;
+ right.item = q;
+ right.anchorLine = QSGAnchorLine::Right;
+ hCenter.item = q;
+ hCenter.anchorLine = QSGAnchorLine::HCenter;
+ top.item = q;
+ top.anchorLine = QSGAnchorLine::Top;
+ bottom.item = q;
+ bottom.anchorLine = QSGAnchorLine::Bottom;
+ vCenter.item = q;
+ vCenter.anchorLine = QSGAnchorLine::VCenter;
+ baseline.item = q;
+ baseline.anchorLine = QSGAnchorLine::Baseline;
+}
+
+QPointF QSGItemPrivate::computeTransformOrigin() const
+{
+ switch(origin) {
+ default:
+ case QSGItem::TopLeft:
+ return QPointF(0, 0);
+ case QSGItem::Top:
+ return QPointF(width / 2., 0);
+ case QSGItem::TopRight:
+ return QPointF(width, 0);
+ case QSGItem::Left:
+ return QPointF(0, height / 2.);
+ case QSGItem::Center:
+ return QPointF(width / 2., height / 2.);
+ case QSGItem::Right:
+ return QPointF(width, height / 2.);
+ case QSGItem::BottomLeft:
+ return QPointF(0, height);
+ case QSGItem::Bottom:
+ return QPointF(width / 2., height);
+ case QSGItem::BottomRight:
+ return QPointF(width, height);
+ }
+}
+
+void QSGItemPrivate::transformChanged()
+{
+}
+
+void QSGItemPrivate::deliverKeyEvent(QKeyEvent *e)
+{
+ Q_Q(QSGItem);
+
+ Q_ASSERT(e->isAccepted());
+ if (keyHandler) {
+ if (e->type() == QEvent::KeyPress)
+ keyHandler->keyPressed(e, false);
+ else
+ keyHandler->keyReleased(e, false);
+
+ if (e->isAccepted())
+ return;
+ else
+ e->accept();
+ }
+
+ if (e->type() == QEvent::KeyPress)
+ q->keyPressEvent(e);
+ else
+ q->keyReleaseEvent(e);
+
+ if (e->isAccepted())
+ return;
+
+ if (keyHandler) {
+ e->accept();
+
+ if (e->type() == QEvent::KeyPress)
+ keyHandler->keyPressed(e, true);
+ else
+ keyHandler->keyReleased(e, true);
+ }
+}
+
+void QSGItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
+{
+ Q_Q(QSGItem);
+
+ Q_ASSERT(e->isAccepted());
+ if (keyHandler) {
+ keyHandler->inputMethodEvent(e, false);
+
+ if (e->isAccepted())
+ return;
+ else
+ e->accept();
+ }
+
+ q->inputMethodEvent(e);
+
+ if (e->isAccepted())
+ return;
+
+ if (keyHandler) {
+ e->accept();
+
+ keyHandler->inputMethodEvent(e, true);
+ }
+}
+
+void QSGItemPrivate::deliverFocusEvent(QFocusEvent *e)
+{
+ Q_Q(QSGItem);
+
+ if (e->type() == QEvent::FocusIn) {
+ q->focusInEvent(e);
+ } else {
+ q->focusOutEvent(e);
+ }
+}
+
+void QSGItemPrivate::deliverMouseEvent(QGraphicsSceneMouseEvent *e)
+{
+ Q_Q(QSGItem);
+
+ Q_ASSERT(e->isAccepted());
+
+ switch(e->type()) {
+ default:
+ Q_ASSERT(!"Unknown event type");
+ case QEvent::GraphicsSceneMouseMove:
+ q->mouseMoveEvent(e);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ q->mousePressEvent(e);
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ q->mouseReleaseEvent(e);
+ break;
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ q->mouseDoubleClickEvent(e);
+ break;
+ }
+}
+
+void QSGItemPrivate::deliverWheelEvent(QGraphicsSceneWheelEvent *e)
+{
+ Q_Q(QSGItem);
+ q->wheelEvent(e);
+}
+
+void QSGItemPrivate::deliverTouchEvent(QTouchEvent *e)
+{
+ Q_Q(QSGItem);
+ q->touchEvent(e);
+}
+
+void QSGItemPrivate::deliverHoverEvent(QGraphicsSceneHoverEvent *e)
+{
+ Q_Q(QSGItem);
+ switch(e->type()) {
+ default:
+ Q_ASSERT(!"Unknown event type");
+ case QEvent::GraphicsSceneHoverEnter:
+ q->hoverEnterEvent(e);
+ break;
+ case QEvent::GraphicsSceneHoverLeave:
+ q->hoverLeaveEvent(e);
+ break;
+ case QEvent::GraphicsSceneHoverMove:
+ q->hoverMoveEvent(e);
+ break;
+ }
+}
+
+void QSGItem::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_UNUSED(change);
+ Q_UNUSED(value);
+}
+
+/*! \internal */
+// XXX todo - do we want/need this anymore?
+QRectF QSGItem::boundingRect() const
+{
+ Q_D(const QSGItem);
+ return QRectF(0, 0, d->width, d->height);
+}
+
+QSGItem::TransformOrigin QSGItem::transformOrigin() const
+{
+ Q_D(const QSGItem);
+ return d->origin;
+}
+
+void QSGItem::setTransformOrigin(TransformOrigin origin)
+{
+ Q_D(QSGItem);
+ if (origin == d->origin)
+ return;
+
+ d->origin = origin;
+ d->dirty(QSGItemPrivate::TransformOrigin);
+
+ emit transformOriginChanged(d->origin);
+}
+
+QPointF QSGItem::transformOriginPoint() const
+{
+ Q_D(const QSGItem);
+ return d->computeTransformOrigin();
+}
+
+qreal QSGItem::z() const
+{
+ Q_D(const QSGItem);
+ return d->z;
+}
+
+void QSGItem::setZ(qreal v)
+{
+ Q_D(QSGItem);
+ if (d->z == v)
+ return;
+
+ d->z = v;
+
+ d->dirty(QSGItemPrivate::ZValue);
+ if (d->parentItem)
+ QSGItemPrivate::get(d->parentItem)->dirty(QSGItemPrivate::ChildrenStackingChanged);
+
+ emit zChanged();
+}
+
+qreal QSGItem::rotation() const
+{
+ Q_D(const QSGItem);
+ return d->rotation;
+}
+
+void QSGItem::setRotation(qreal r)
+{
+ Q_D(QSGItem);
+ if (d->rotation == r)
+ return;
+
+ d->rotation = r;
+
+ d->dirty(QSGItemPrivate::BasicTransform);
+
+ d->itemChange(ItemRotationHasChanged, r);
+
+ emit rotationChanged();
+}
+
+qreal QSGItem::scale() const
+{
+ Q_D(const QSGItem);
+ return d->scale;
+}
+
+void QSGItem::setScale(qreal s)
+{
+ Q_D(QSGItem);
+ if (d->scale == s)
+ return;
+
+ d->scale = s;
+
+ d->dirty(QSGItemPrivate::BasicTransform);
+
+ emit scaleChanged();
+}
+
+qreal QSGItem::opacity() const
+{
+ Q_D(const QSGItem);
+ return d->opacity;
+}
+
+void QSGItem::setOpacity(qreal o)
+{
+ Q_D(QSGItem);
+ if (d->opacity == o)
+ return;
+
+ d->opacity = o;
+
+ d->dirty(QSGItemPrivate::OpacityValue);
+
+ d->itemChange(ItemOpacityHasChanged, o);
+
+ emit opacityChanged();
+}
+
+bool QSGItem::isVisible() const
+{
+ Q_D(const QSGItem);
+ return d->effectiveVisible;
+}
+
+void QSGItem::setVisible(bool v)
+{
+ Q_D(QSGItem);
+ if (v == d->explicitVisible)
+ return;
+
+ d->explicitVisible = v;
+
+ d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
+}
+
+bool QSGItem::isEnabled() const
+{
+ Q_D(const QSGItem);
+ return d->effectiveEnable;
+}
+
+void QSGItem::setEnabled(bool e)
+{
+ Q_D(QSGItem);
+ if (e == d->explicitEnable)
+ return;
+
+ d->explicitEnable = e;
+
+ d->setEffectiveEnableRecur(d->calcEffectiveEnable());
+}
+
+bool QSGItemPrivate::calcEffectiveVisible() const
+{
+ // XXX todo - Should the effective visible of an element with no parent just be the current
+ // effective visible? This would prevent pointless re-processing in the case of an element
+ // moving to/from a no-parent situation, but it is different from what graphics view does.
+ return explicitVisible && (!parentItem || QSGItemPrivate::get(parentItem)->effectiveVisible);
+}
+
+void QSGItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
+{
+ Q_Q(QSGItem);
+
+ if (newEffectiveVisible && !explicitVisible) {
+ // This item locally overrides visibility
+ return;
+ }
+
+ if (newEffectiveVisible == effectiveVisible) {
+ // No change necessary
+ return;
+ }
+
+ effectiveVisible = newEffectiveVisible;
+ dirty(Visible);
+ if (parentItem) QSGItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
+
+ if (canvas) {
+ QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(canvas);
+ if (canvasPriv->mouseGrabberItem == q)
+ q->ungrabMouse();
+ }
+
+ for (int ii = 0; ii < childItems.count(); ++ii)
+ QSGItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
+
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Visibility)
+ change.listener->itemVisibilityChanged(q);
+ }
+
+ emit q->visibleChanged();
+}
+
+bool QSGItemPrivate::calcEffectiveEnable() const
+{
+ // XXX todo - Should the effective enable of an element with no parent just be the current
+ // effective enable? This would prevent pointless re-processing in the case of an element
+ // moving to/from a no-parent situation, but it is different from what graphics view does.
+ return explicitEnable && (!parentItem || QSGItemPrivate::get(parentItem)->effectiveEnable);
+}
+
+void QSGItemPrivate::setEffectiveEnableRecur(bool newEffectiveEnable)
+{
+ Q_Q(QSGItem);
+
+ // XXX todo - need to fixup focus
+
+ if (newEffectiveEnable && !explicitEnable) {
+ // This item locally overrides enable
+ return;
+ }
+
+ if (newEffectiveEnable == effectiveEnable) {
+ // No change necessary
+ return;
+ }
+
+ effectiveEnable = newEffectiveEnable;
+
+ if (canvas) {
+ QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(canvas);
+ if (canvasPriv->mouseGrabberItem == q)
+ q->ungrabMouse();
+ }
+
+ for (int ii = 0; ii < childItems.count(); ++ii)
+ QSGItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(newEffectiveEnable);
+
+ emit q->enabledChanged();
+}
+
+QString QSGItemPrivate::dirtyToString() const
+{
+#define DIRTY_TO_STRING(value) if (dirtyAttributes & value) { \
+ if (!rv.isEmpty()) \
+ rv.append(QLatin1String("|")); \
+ rv.append(QLatin1String(#value)); \
+}
+
+// QString rv = QLatin1String("0x") + QString::number(dirtyAttributes, 16);
+ QString rv;
+
+ DIRTY_TO_STRING(TransformOrigin);
+ DIRTY_TO_STRING(Transform);
+ DIRTY_TO_STRING(BasicTransform);
+ DIRTY_TO_STRING(Position);
+ DIRTY_TO_STRING(Size);
+ DIRTY_TO_STRING(ZValue);
+ DIRTY_TO_STRING(Content);
+ DIRTY_TO_STRING(Smooth);
+ DIRTY_TO_STRING(OpacityValue);
+ DIRTY_TO_STRING(ChildrenChanged);
+ DIRTY_TO_STRING(ChildrenStackingChanged);
+ DIRTY_TO_STRING(ParentChanged);
+ DIRTY_TO_STRING(Clip);
+ DIRTY_TO_STRING(Canvas);
+ DIRTY_TO_STRING(EffectReference);
+ DIRTY_TO_STRING(Visible);
+ DIRTY_TO_STRING(HideReference);
+
+ return rv;
+}
+
+void QSGItemPrivate::dirty(DirtyType type)
+{
+ Q_Q(QSGItem);
+ if (type & (TransformOrigin | Transform | BasicTransform | Position | Size))
+ transformChanged();
+
+ if (!(dirtyAttributes & type) || (canvas && !prevDirtyItem)) {
+ dirtyAttributes |= type;
+ if (canvas) {
+ addToDirtyList();
+ QSGCanvasPrivate::get(canvas)->dirtyItem(q);
+ }
+ }
+}
+
+void QSGItemPrivate::addToDirtyList()
+{
+ Q_Q(QSGItem);
+
+ Q_ASSERT(canvas);
+ if (!prevDirtyItem) {
+ Q_ASSERT(!nextDirtyItem);
+
+ QSGCanvasPrivate *p = QSGCanvasPrivate::get(canvas);
+ nextDirtyItem = p->dirtyItemList;
+ if (nextDirtyItem) QSGItemPrivate::get(nextDirtyItem)->prevDirtyItem = &nextDirtyItem;
+ prevDirtyItem = &p->dirtyItemList;
+ p->dirtyItemList = q;
+ p->dirtyItem(q);
+ }
+ Q_ASSERT(prevDirtyItem);
+}
+
+void QSGItemPrivate::removeFromDirtyList()
+{
+ if (prevDirtyItem) {
+ if (nextDirtyItem) QSGItemPrivate::get(nextDirtyItem)->prevDirtyItem = prevDirtyItem;
+ *prevDirtyItem = nextDirtyItem;
+ prevDirtyItem = 0;
+ nextDirtyItem = 0;
+ }
+ Q_ASSERT(!prevDirtyItem);
+ Q_ASSERT(!nextDirtyItem);
+}
+
+void QSGItemPrivate::refFromEffectItem(bool hide)
+{
+ ++effectRefCount;
+ if (1 == effectRefCount) {
+ dirty(EffectReference);
+ if (parentItem) QSGItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
+ }
+ if (hide) {
+ if (++hideRefCount == 1)
+ dirty(HideReference);
+ }
+}
+
+void QSGItemPrivate::derefFromEffectItem(bool unhide)
+{
+ Q_ASSERT(effectRefCount);
+ --effectRefCount;
+ if (0 == effectRefCount) {
+ dirty(EffectReference);
+ if (parentItem) QSGItemPrivate::get(parentItem)->dirty(ChildrenStackingChanged);
+ }
+ if (unhide) {
+ if (--hideRefCount == 0)
+ dirty(HideReference);
+ }
+}
+
+void QSGItemPrivate::itemChange(QSGItem::ItemChange change, const QSGItem::ItemChangeData &data)
+{
+ Q_Q(QSGItem);
+ switch(change) {
+ case QSGItem::ItemChildAddedChange:
+ q->itemChange(change, data);
+ if (_contents && componentComplete)
+ _contents->childAdded(data.item);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Children) {
+ change.listener->itemChildAdded(q, data.item);
+ }
+ }
+ break;
+ case QSGItem::ItemChildRemovedChange:
+ q->itemChange(change, data);
+ if (_contents && componentComplete)
+ _contents->childRemoved(data.item);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Children) {
+ change.listener->itemChildRemoved(q, data.item);
+ }
+ }
+ break;
+ case QSGItem::ItemSceneChange:
+ q->itemChange(change, data);
+ break;
+ case QSGItem::ItemVisibleHasChanged:
+ q->itemChange(change, data);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Visibility) {
+ change.listener->itemVisibilityChanged(q);
+ }
+ }
+ break;
+ case QSGItem::ItemParentHasChanged:
+ q->itemChange(change, data);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Parent) {
+ change.listener->itemParentChanged(q, data.item);
+ }
+ }
+ break;
+ case QSGItem::ItemOpacityHasChanged:
+ q->itemChange(change, data);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Opacity) {
+ change.listener->itemOpacityChanged(q);
+ }
+ }
+ break;
+ case QSGItem::ItemActiveFocusHasChanged:
+ q->itemChange(change, data);
+ break;
+ case QSGItem::ItemRotationHasChanged:
+ q->itemChange(change, data);
+ for(int ii = 0; ii < changeListeners.count(); ++ii) {
+ const QSGItemPrivate::ChangeListener &change = changeListeners.at(ii);
+ if (change.types & QSGItemPrivate::Rotation) {
+ change.listener->itemRotationChanged(q);
+ }
+ }
+ break;
+ }
+}
+
+bool QSGItem::smooth() const
+{
+ Q_D(const QSGItem);
+ return d->smooth;
+}
+
+void QSGItem::setSmooth(bool smooth)
+{
+ Q_D(QSGItem);
+ if (d->smooth == smooth)
+ return;
+
+ d->smooth = smooth;
+ d->dirty(QSGItemPrivate::Smooth);
+
+ emit smoothChanged(smooth);
+}
+
+QSGItem::Flags QSGItem::flags() const
+{
+ Q_D(const QSGItem);
+ return (QSGItem::Flags)d->flags;
+}
+
+void QSGItem::setFlag(Flag flag, bool enabled)
+{
+ Q_D(QSGItem);
+ if (enabled)
+ setFlags((Flags)(d->flags | (quint32)flag));
+ else
+ setFlags((Flags)(d->flags & ~(quint32)flag));
+}
+
+void QSGItem::setFlags(Flags flags)
+{
+ Q_D(QSGItem);
+
+ if ((flags & ItemIsFocusScope) != (d->flags & ItemIsFocusScope)) {
+ if (flags & ItemIsFocusScope && !d->childItems.isEmpty() && d->canvas) {
+ qWarning("QSGItem: Cannot set FocusScope once item has children and is in a canvas.");
+ flags &= ~ItemIsFocusScope;
+ } else if (d->flags & ItemIsFocusScope) {
+ qWarning("QSGItem: Cannot unset FocusScope flag.");
+ flags |= ItemIsFocusScope;
+ }
+ }
+
+ if ((flags & ItemClipsChildrenToShape ) != (d->flags & ItemClipsChildrenToShape))
+ d->dirty(QSGItemPrivate::Clip);
+
+ d->flags = flags;
+}
+
+qreal QSGItem::x() const
+{
+ Q_D(const QSGItem);
+ return d->x;
+}
+
+qreal QSGItem::y() const
+{
+ Q_D(const QSGItem);
+ return d->y;
+}
+
+QPointF QSGItem::pos() const
+{
+ Q_D(const QSGItem);
+ return QPointF(d->x, d->y);
+}
+
+void QSGItem::setX(qreal v)
+{
+ Q_D(QSGItem);
+ if (d->x == v)
+ return;
+
+ qreal oldx = d->x;
+ d->x = v;
+
+ d->dirty(QSGItemPrivate::Position);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(oldx, y(), width(), height()));
+}
+
+void QSGItem::setY(qreal v)
+{
+ Q_D(QSGItem);
+ if (d->y == v)
+ return;
+
+ qreal oldy = d->y;
+ d->y = v;
+
+ d->dirty(QSGItemPrivate::Position);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(x(), oldy, width(), height()));
+}
+
+void QSGItem::setPos(const QPointF &pos)
+{
+ Q_D(QSGItem);
+ if (QPointF(d->x, d->y) == pos)
+ return;
+
+ qreal oldx = d->x;
+ qreal oldy = d->y;
+
+ d->x = pos.x();
+ d->y = pos.y();
+
+ d->dirty(QSGItemPrivate::Position);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(oldx, oldy, width(), height()));
+}
+
+qreal QSGItem::width() const
+{
+ Q_D(const QSGItem);
+ return d->width;
+}
+
+void QSGItem::setWidth(qreal w)
+{
+ Q_D(QSGItem);
+ if (qIsNaN(w))
+ return;
+
+ d->widthValid = true;
+ if (d->width == w)
+ return;
+
+ qreal oldWidth = d->width;
+ d->width = w;
+
+ d->dirty(QSGItemPrivate::Size);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(x(), y(), oldWidth, height()));
+}
+
+void QSGItem::resetWidth()
+{
+ Q_D(QSGItem);
+ d->widthValid = false;
+ setImplicitWidth(implicitWidth());
+}
+
+void QSGItemPrivate::implicitWidthChanged()
+{
+ Q_Q(QSGItem);
+ emit q->implicitWidthChanged();
+}
+
+qreal QSGItemPrivate::getImplicitWidth() const
+{
+ return implicitWidth;
+}
+
+qreal QSGItem::implicitWidth() const
+{
+ Q_D(const QSGItem);
+ return d->getImplicitWidth();
+}
+
+void QSGItem::setImplicitWidth(qreal w)
+{
+ Q_D(QSGItem);
+ bool changed = w != d->implicitWidth;
+ d->implicitWidth = w;
+ if (d->width == w || widthValid()) {
+ if (changed)
+ d->implicitWidthChanged();
+ return;
+ }
+
+ qreal oldWidth = d->width;
+ d->width = w;
+
+ d->dirty(QSGItemPrivate::Size);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(x(), y(), oldWidth, height()));
+
+ if (changed)
+ d->implicitWidthChanged();
+}
+
+bool QSGItem::widthValid() const
+{
+ Q_D(const QSGItem);
+ return d->widthValid;
+}
+
+qreal QSGItem::height() const
+{
+ Q_D(const QSGItem);
+ return d->height;
+}
+
+void QSGItem::setHeight(qreal h)
+{
+ Q_D(QSGItem);
+ if (qIsNaN(h))
+ return;
+
+ d->heightValid = true;
+ if (d->height == h)
+ return;
+
+ qreal oldHeight = d->height;
+ d->height = h;
+
+ d->dirty(QSGItemPrivate::Size);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(x(), y(), width(), oldHeight));
+}
+
+void QSGItem::resetHeight()
+{
+ Q_D(QSGItem);
+ d->heightValid = false;
+ setImplicitHeight(implicitHeight());
+}
+
+void QSGItemPrivate::implicitHeightChanged()
+{
+ Q_Q(QSGItem);
+ emit q->implicitHeightChanged();
+}
+
+qreal QSGItemPrivate::getImplicitHeight() const
+{
+ return implicitHeight;
+}
+
+qreal QSGItem::implicitHeight() const
+{
+ Q_D(const QSGItem);
+ return d->getImplicitHeight();
+}
+
+void QSGItem::setImplicitHeight(qreal h)
+{
+ Q_D(QSGItem);
+ bool changed = h != d->implicitHeight;
+ d->implicitHeight = h;
+ if (d->height == h || heightValid()) {
+ if (changed)
+ d->implicitHeightChanged();
+ return;
+ }
+
+ qreal oldHeight = d->height;
+ d->height = h;
+
+ d->dirty(QSGItemPrivate::Size);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(x(), y(), width(), oldHeight));
+
+ if (changed)
+ d->implicitHeightChanged();
+}
+
+bool QSGItem::heightValid() const
+{
+ Q_D(const QSGItem);
+ return d->heightValid;
+}
+
+void QSGItem::setSize(const QSizeF &size)
+{
+ Q_D(QSGItem);
+ d->heightValid = true;
+ d->widthValid = true;
+
+ if (QSizeF(d->width, d->height) == size)
+ return;
+
+ qreal oldHeight = d->height;
+ qreal oldWidth = d->width;
+ d->height = size.height();
+ d->width = size.width();
+
+ d->dirty(QSGItemPrivate::Size);
+
+ geometryChanged(QRectF(x(), y(), width(), height()),
+ QRectF(x(), y(), oldWidth, oldHeight));
+}
+
+bool QSGItem::hasActiveFocus() const
+{
+ Q_D(const QSGItem);
+ return d->activeFocus;
+}
+
+bool QSGItem::hasFocus() const
+{
+ Q_D(const QSGItem);
+ return d->focus;
+}
+
+void QSGItem::setFocus(bool focus)
+{
+ Q_D(QSGItem);
+ if (d->focus == focus)
+ return;
+
+ if (d->canvas) {
+ // Need to find our nearest focus scope
+ QSGItem *scope = parentItem();
+ while (scope && !scope->isFocusScope())
+ scope = scope->parentItem();
+ if (focus)
+ QSGCanvasPrivate::get(d->canvas)->setFocusInScope(scope, this);
+ else
+ QSGCanvasPrivate::get(d->canvas)->clearFocusInScope(scope, this);
+ } else {
+ d->focus = focus;
+ emit focusChanged(focus);
+ }
+}
+
+bool QSGItem::isFocusScope() const
+{
+ return flags() & ItemIsFocusScope;
+}
+
+QSGItem *QSGItem::scopedFocusItem() const
+{
+ Q_D(const QSGItem);
+ if (!isFocusScope())
+ return 0;
+ else
+ return d->subFocusItem;
+}
+
+
+Qt::MouseButtons QSGItem::acceptedMouseButtons() const
+{
+ Q_D(const QSGItem);
+ return d->acceptedMouseButtons;
+}
+
+void QSGItem::setAcceptedMouseButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QSGItem);
+ d->acceptedMouseButtons = buttons;
+}
+
+bool QSGItem::filtersChildMouseEvents() const
+{
+ Q_D(const QSGItem);
+ return d->filtersChildMouseEvents;
+}
+
+void QSGItem::setFiltersChildMouseEvents(bool filter)
+{
+ Q_D(QSGItem);
+ d->filtersChildMouseEvents = filter;
+}
+
+bool QSGItem::isUnderMouse() const
+{
+ Q_D(const QSGItem);
+ if (!d->canvas)
+ return false;
+
+ QPoint cursorPos = QCursor::pos();
+ if (QRectF(0, 0, width(), height()).contains(mapFromScene(d->canvas->mapFromGlobal(cursorPos))))
+ return true;
+ return false;
+}
+
+bool QSGItem::acceptHoverEvents() const
+{
+ Q_D(const QSGItem);
+ return d->hoverEnabled;
+}
+
+void QSGItem::setAcceptHoverEvents(bool enabled)
+{
+ Q_D(QSGItem);
+ d->hoverEnabled = enabled;
+
+ if (d->canvas && d->hoverEnabled && !d->canvas->hasMouseTracking())
+ d->canvas->setMouseTracking(true);
+}
+
+void QSGItem::grabMouse()
+{
+ Q_D(QSGItem);
+ if (!d->canvas)
+ return;
+ QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(d->canvas);
+ if (canvasPriv->mouseGrabberItem == this)
+ return;
+
+ QSGItem *oldGrabber = canvasPriv->mouseGrabberItem;
+ canvasPriv->mouseGrabberItem = this;
+ if (oldGrabber)
+ oldGrabber->mouseUngrabEvent();
+}
+
+void QSGItem::ungrabMouse()
+{
+ Q_D(QSGItem);
+ if (!d->canvas)
+ return;
+ QSGCanvasPrivate *canvasPriv = QSGCanvasPrivate::get(d->canvas);
+ if (canvasPriv->mouseGrabberItem != this) {
+ qWarning("QSGItem::ungrabMouse(): Item is not the mouse grabber.");
+ return;
+ }
+
+ canvasPriv->mouseGrabberItem = 0;
+ mouseUngrabEvent();
+}
+
+bool QSGItem::keepMouseGrab() const
+{
+ Q_D(const QSGItem);
+ return d->keepMouse;
+}
+
+void QSGItem::setKeepMouseGrab(bool keep)
+{
+ Q_D(QSGItem);
+ d->keepMouse = keep;
+}
+
+QPointF QSGItem::mapToItem(const QSGItem *item, const QPointF &point) const
+{
+ QPointF p = mapToScene(point);
+ if (item)
+ p = item->mapFromScene(p);
+ return p;
+}
+
+QPointF QSGItem::mapToScene(const QPointF &point) const
+{
+ Q_D(const QSGItem);
+ return d->itemToCanvasTransform().map(point);
+}
+
+QRectF QSGItem::mapRectToItem(const QSGItem *item, const QRectF &rect) const
+{
+ Q_D(const QSGItem);
+ QTransform t = d->itemToCanvasTransform();
+ if (item)
+ t *= QSGItemPrivate::get(item)->canvasToItemTransform();
+ return t.mapRect(rect);
+}
+
+QRectF QSGItem::mapRectToScene(const QRectF &rect) const
+{
+ Q_D(const QSGItem);
+ return d->itemToCanvasTransform().mapRect(rect);
+}
+
+QPointF QSGItem::mapFromItem(const QSGItem *item, const QPointF &point) const
+{
+ QPointF p = item?item->mapToScene(point):point;
+ return mapFromScene(p);
+}
+
+QPointF QSGItem::mapFromScene(const QPointF &point) const
+{
+ Q_D(const QSGItem);
+ return d->canvasToItemTransform().map(point);
+}
+
+QRectF QSGItem::mapRectFromItem(const QSGItem *item, const QRectF &rect) const
+{
+ Q_D(const QSGItem);
+ QTransform t = item?QSGItemPrivate::get(item)->itemToCanvasTransform():QTransform();
+ t *= d->canvasToItemTransform();
+ return t.mapRect(rect);
+}
+
+QRectF QSGItem::mapRectFromScene(const QRectF &rect) const
+{
+ Q_D(const QSGItem);
+ return d->canvasToItemTransform().mapRect(rect);
+}
+
+bool QSGItem::event(QEvent *ev)
+{
+ return QObject::event(ev);
+
+#if 0
+ if (ev->type() == QEvent::PolishRequest) {
+ Q_D(QSGItem);
+ d->polishScheduled = false;
+ updatePolish();
+ return true;
+ } else {
+ return QObject::event(ev);
+ }
+#endif
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, QSGItem *item)
+{
+ if (!item) {
+ debug << "QSGItem(0)";
+ return debug;
+ }
+
+ debug << item->metaObject()->className() << "(this =" << ((void*)item)
+ << ", name=" << item->objectName()
+ << ", parent =" << ((void*)item->parentItem())
+ << ", geometry =" << QRectF(item->pos(), QSizeF(item->width(), item->height()))
+ << ", z =" << item->z() << ')';
+ return debug;
+}
+#endif
+
+qint64 QSGItemPrivate::consistentTime = -1;
+void QSGItemPrivate::setConsistentTime(qint64 t)
+{
+ consistentTime = t;
+}
+
+class QElapsedTimerConsistentTimeHack
+{
+public:
+ void start() {
+ t1 = QSGItemPrivate::consistentTime;
+ t2 = 0;
+ }
+ qint64 elapsed() {
+ return QSGItemPrivate::consistentTime - t1;
+ }
+ qint64 restart() {
+ qint64 val = QSGItemPrivate::consistentTime - t1;
+ t1 = QSGItemPrivate::consistentTime;
+ t2 = 0;
+ return val;
+ }
+
+private:
+ qint64 t1;
+ qint64 t2;
+};
+
+void QSGItemPrivate::start(QElapsedTimer &t)
+{
+ if (QSGItemPrivate::consistentTime == -1)
+ t.start();
+ else
+ ((QElapsedTimerConsistentTimeHack*)&t)->start();
+}
+
+qint64 QSGItemPrivate::elapsed(QElapsedTimer &t)
+{
+ if (QSGItemPrivate::consistentTime == -1)
+ return t.elapsed();
+ else
+ return ((QElapsedTimerConsistentTimeHack*)&t)->elapsed();
+}
+
+qint64 QSGItemPrivate::restart(QElapsedTimer &t)
+{
+ if (QSGItemPrivate::consistentTime == -1)
+ return t.restart();
+ else
+ return ((QElapsedTimerConsistentTimeHack*)&t)->restart();
+}
+
+QT_END_NAMESPACE
+
+#include <moc_qsgitem.cpp>
diff --git a/src/declarative/items/qsgitem.h b/src/declarative/items/qsgitem.h
new file mode 100644
index 0000000000..564d819000
--- /dev/null
+++ b/src/declarative/items/qsgitem.h
@@ -0,0 +1,399 @@
+// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGITEM_H
+#define QSGITEM_H
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QList>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qfont.h>
+#include <QtGui/qaction.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGItem;
+class QSGTransformPrivate;
+class QSGTransform : public QObject
+{
+ Q_OBJECT
+public:
+ QSGTransform(QObject *parent = 0);
+ ~QSGTransform();
+
+ void appendToItem(QSGItem *);
+ void prependToItem(QSGItem *);
+
+ virtual void applyTo(QMatrix4x4 *matrix) const = 0;
+
+protected Q_SLOTS:
+ void update();
+
+protected:
+ QSGTransform(QSGTransformPrivate &dd, QObject *parent);
+
+private:
+ Q_DECLARE_PRIVATE(QSGTransform);
+};
+
+class QDeclarativeState;
+class QSGAnchorLine;
+class QDeclarativeTransition;
+class QSGKeyEvent;
+class QSGAnchors;
+class QSGItemPrivate;
+class QSGCanvas;
+class QSGEngine;
+class QTouchEvent;
+class QSGNode;
+class QSGTransformNode;
+class Q_DECLARATIVE_EXPORT QSGItem : public QObject, public QDeclarativeParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QDeclarativeParserStatus)
+
+ Q_PROPERTY(QSGItem *parent READ parentItem WRITE setParentItem NOTIFY parentChanged DESIGNABLE false FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty<QObject> data READ data DESIGNABLE false)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty<QObject> resources READ resources DESIGNABLE false)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty<QSGItem> children READ children NOTIFY childrenChanged DESIGNABLE false)
+
+ Q_PROPERTY(QPointF pos READ pos FINAL)
+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged FINAL)
+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged FINAL)
+ Q_PROPERTY(qreal z READ z WRITE setZ NOTIFY zChanged FINAL)
+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged RESET resetWidth FINAL)
+ Q_PROPERTY(qreal height READ height WRITE setHeight NOTIFY heightChanged RESET resetHeight FINAL)
+
+ Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity NOTIFY opacityChanged FINAL)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged FINAL)
+
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty<QDeclarativeState> states READ states DESIGNABLE false)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QDeclarativeListProperty<QDeclarativeTransition> transitions READ transitions DESIGNABLE false)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QString state READ state WRITE setState NOTIFY stateChanged)
+ Q_PROPERTY(QRectF childrenRect READ childrenRect NOTIFY childrenRectChanged DESIGNABLE false FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine left READ left CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine right READ right CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine top READ top CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine bottom READ bottom CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine verticalCenter READ verticalCenter CONSTANT FINAL)
+ Q_PRIVATE_PROPERTY(QSGItem::d_func(), QSGAnchorLine baseline READ baseline CONSTANT FINAL)
+ Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged)
+
+ Q_PROPERTY(bool clip READ clip WRITE setClip NOTIFY clipChanged)
+
+ Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL)
+ Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL)
+
+ Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged)
+ Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged)
+ Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged)
+ Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint) // XXX todo - notify?
+ Q_PROPERTY(QDeclarativeListProperty<QSGTransform> transform READ transform DESIGNABLE false FINAL)
+
+ Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged)
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged)
+ Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged)
+
+ Q_ENUMS(TransformOrigin)
+ Q_CLASSINFO("DefaultProperty", "data")
+
+public:
+ enum Flag {
+ ItemClipsChildrenToShape = 0x01,
+ ItemAcceptsInputMethod = 0x02,
+ ItemIsFocusScope = 0x04,
+ ItemHasContents = 0x08,
+ // Remember to increment the size of QSGItemPrivate::flags
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ enum ItemChange {
+ ItemChildAddedChange, // value.item
+ ItemChildRemovedChange, // value.item
+ ItemSceneChange, // value.canvas
+ ItemVisibleHasChanged, // value.realValue
+ ItemParentHasChanged, // value.item
+ ItemOpacityHasChanged, // value.realValue
+ ItemActiveFocusHasChanged, // value.boolValue
+ ItemRotationHasChanged, // value.realValue
+ };
+
+ union ItemChangeData {
+ ItemChangeData(QSGItem *v) : item(v) {}
+ ItemChangeData(QSGCanvas *v) : canvas(v) {}
+ ItemChangeData(qreal v) : realValue(v) {}
+ ItemChangeData(bool v) : boolValue(v) {}
+
+ QSGItem *item;
+ QSGCanvas *canvas;
+ qreal realValue;
+ bool boolValue;
+ };
+
+ enum TransformOrigin {
+ TopLeft, Top, TopRight,
+ Left, Center, Right,
+ BottomLeft, Bottom, BottomRight
+ };
+
+ QSGItem(QSGItem *parent = 0);
+ virtual ~QSGItem();
+
+ QSGEngine *sceneGraphEngine() const;
+
+ QSGCanvas *canvas() const;
+ QSGItem *parentItem() const;
+ void setParentItem(QSGItem *parent);
+ void stackBefore(const QSGItem *);
+ void stackAfter(const QSGItem *);
+
+ QRectF childrenRect();
+ QList<QSGItem *> childItems() const;
+
+ bool clip() const;
+ void setClip(bool);
+
+ qreal baselineOffset() const;
+ void setBaselineOffset(qreal);
+
+ QDeclarativeListProperty<QSGTransform> transform();
+
+ qreal x() const;
+ qreal y() const;
+ QPointF pos() const;
+ void setX(qreal);
+ void setY(qreal);
+ void setPos(const QPointF &);
+
+ qreal width() const;
+ void setWidth(qreal);
+ void resetWidth();
+ qreal implicitWidth() const;
+
+ qreal height() const;
+ void setHeight(qreal);
+ void resetHeight();
+ qreal implicitHeight() const;
+
+ void setSize(const QSizeF &size);
+
+ TransformOrigin transformOrigin() const;
+ void setTransformOrigin(TransformOrigin);
+ QPointF transformOriginPoint() const;
+
+ qreal z() const;
+ void setZ(qreal);
+
+ qreal rotation() const;
+ void setRotation(qreal);
+ qreal scale() const;
+ void setScale(qreal);
+
+ qreal opacity() const;
+ void setOpacity(qreal);
+
+ bool isVisible() const;
+ void setVisible(bool);
+
+ bool isEnabled() const;
+ void setEnabled(bool);
+
+ bool smooth() const;
+ void setSmooth(bool);
+
+ Flags flags() const;
+ void setFlag(Flag flag, bool enabled = true);
+ void setFlags(Flags flags);
+
+ QRectF boundingRect() const;
+
+ bool hasActiveFocus() const;
+ bool hasFocus() const;
+ void setFocus(bool);
+ bool isFocusScope() const;
+ QSGItem *scopedFocusItem() const;
+
+ Qt::MouseButtons acceptedMouseButtons() const;
+ void setAcceptedMouseButtons(Qt::MouseButtons buttons);
+ bool acceptHoverEvents() const;
+ void setAcceptHoverEvents(bool enabled);
+
+ bool isUnderMouse() const;
+ void grabMouse();
+ void ungrabMouse();
+ bool keepMouseGrab() const;
+ void setKeepMouseGrab(bool);
+ bool filtersChildMouseEvents() const;
+ void setFiltersChildMouseEvents(bool filter);
+
+ QTransform itemTransform(QSGItem *, bool *) const;
+ QPointF mapToItem(const QSGItem *item, const QPointF &point) const;
+ QPointF mapToScene(const QPointF &point) const;
+ QRectF mapRectToItem(const QSGItem *item, const QRectF &rect) const;
+ QRectF mapRectToScene(const QRectF &rect) const;
+ QPointF mapFromItem(const QSGItem *item, const QPointF &point) const;
+ QPointF mapFromScene(const QPointF &point) const;
+ QRectF mapRectFromItem(const QSGItem *item, const QRectF &rect) const;
+ QRectF mapRectFromScene(const QRectF &rect) const;
+
+ void polish();
+
+ Q_INVOKABLE QScriptValue mapFromItem(const QScriptValue &item, qreal x, qreal y) const;
+ Q_INVOKABLE QScriptValue mapToItem(const QScriptValue &item, qreal x, qreal y) const;
+ Q_INVOKABLE void forceActiveFocus();
+ Q_INVOKABLE QSGItem *childAt(qreal x, qreal y) const;
+
+ Qt::InputMethodHints inputMethodHints() const;
+ void setInputMethodHints(Qt::InputMethodHints hints);
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ struct UpdatePaintNodeData {
+ QSGTransformNode *transformNode;
+ private:
+ friend class QSGCanvasPrivate;
+ UpdatePaintNodeData();
+ };
+
+public Q_SLOTS:
+ void update();
+ void updateMicroFocus();
+
+Q_SIGNALS:
+ void childrenRectChanged(const QRectF &);
+ void baselineOffsetChanged(qreal);
+ void stateChanged(const QString &);
+ void focusChanged(bool);
+ void activeFocusChanged(bool);
+ void parentChanged(QSGItem *);
+ void transformOriginChanged(TransformOrigin);
+ void smoothChanged(bool);
+ void clipChanged(bool);
+
+ // XXX todo
+ void childrenChanged();
+ void opacityChanged();
+ void enabledChanged();
+ void visibleChanged();
+ void rotationChanged();
+ void scaleChanged();
+
+ void xChanged();
+ void yChanged();
+ void widthChanged();
+ void heightChanged();
+ void zChanged();
+ void implicitWidthChanged();
+ void implicitHeightChanged();
+
+protected:
+ virtual bool event(QEvent *);
+
+ bool isComponentComplete() const;
+ virtual void itemChange(ItemChange, const ItemChangeData &);
+
+ void setImplicitWidth(qreal);
+ bool widthValid() const; // ### better name?
+ void setImplicitHeight(qreal);
+ bool heightValid() const; // ### better name?
+
+ virtual void classBegin();
+ virtual void componentComplete();
+
+ virtual void keyPressEvent(QKeyEvent *event);
+ virtual void keyReleaseEvent(QKeyEvent *event);
+ virtual void inputMethodEvent(QInputMethodEvent *);
+ virtual void focusInEvent(QFocusEvent *);
+ virtual void focusOutEvent(QFocusEvent *);
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseUngrabEvent(); // XXX todo - params?
+ virtual void wheelEvent(QGraphicsSceneWheelEvent *event);
+ virtual void touchEvent(QTouchEvent *event);
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual bool childMouseEventFilter(QSGItem *, QEvent *);
+
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ virtual void updatePolish();
+
+protected:
+ QSGItem(QSGItemPrivate &dd, QSGItem *parent = 0);
+
+private:
+ friend class QSGCanvas;
+ friend class QSGCanvasPrivate;
+ friend class QSGRenderer;
+ Q_DISABLE_COPY(QSGItem)
+ Q_DECLARE_PRIVATE(QSGItem)
+};
+
+// XXX todo
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGItem::Flags)
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, QSGItem *item);
+#endif
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGItem)
+QML_DECLARE_TYPE(QSGTransform)
+
+QT_END_HEADER
+
+#endif // QSGITEM_H
diff --git a/src/declarative/items/qsgitem_p.h b/src/declarative/items/qsgitem_p.h
new file mode 100644
index 0000000000..c76eceb674
--- /dev/null
+++ b/src/declarative/items/qsgitem_p.h
@@ -0,0 +1,712 @@
+// Commit: 5c783d0a9a912816813945387903857a314040b5
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGITEM_P_H
+#define QSGITEM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgitem.h"
+
+#include "qsganchors_p.h"
+#include "qsganchors_p_p.h"
+#include "qsgitemchangelistener_p.h"
+
+#include "qsgcanvas_p.h"
+
+#include "qsgnode.h"
+#include "qsgclipnode_p.h"
+
+#include <private/qpodvector_p.h>
+#include <private/qdeclarativestate_p.h>
+#include <private/qdeclarativenullablevalue_p_p.h>
+#include <private/qdeclarativenotifier_p.h>
+#include <private/qdeclarativeglobal_p.h>
+
+#include <qdeclarative.h>
+#include <qdeclarativecontext.h>
+
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qelapsedtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkReply;
+class QSGItemKeyFilter;
+class QSGLayoutMirroringAttached;
+
+//### merge into private?
+class QSGContents : public QObject, public QSGItemChangeListener
+{
+ Q_OBJECT
+public:
+ QSGContents(QSGItem *item);
+ ~QSGContents();
+
+ QRectF rectF() const;
+
+ void childRemoved(QSGItem *item);
+ void childAdded(QSGItem *item);
+
+ void calcGeometry() { calcWidth(); calcHeight(); }
+ void complete();
+
+Q_SIGNALS:
+ void rectChanged(QRectF);
+
+protected:
+ void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
+ void itemDestroyed(QSGItem *item);
+ //void itemVisibilityChanged(QSGItem *item)
+
+private:
+ void calcHeight(QSGItem *changed = 0);
+ void calcWidth(QSGItem *changed = 0);
+
+ QSGItem *m_item;
+ qreal m_x;
+ qreal m_y;
+ qreal m_width;
+ qreal m_height;
+};
+
+class QSGTransformPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGTransform);
+public:
+ static QSGTransformPrivate* get(QSGTransform *transform) { return transform->d_func(); }
+
+ QSGTransformPrivate();
+
+ QList<QSGItem *> items;
+};
+
+class Q_DECLARATIVE_EXPORT QSGItemPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGItem)
+
+public:
+ static QSGItemPrivate* get(QSGItem *item) { return item->d_func(); }
+ static const QSGItemPrivate* get(const QSGItem *item) { return item->d_func(); }
+
+ QSGItemPrivate();
+ void init(QSGItem *parent);
+
+ QDeclarativeListProperty<QObject> data();
+ QDeclarativeListProperty<QObject> resources();
+ QDeclarativeListProperty<QSGItem> children();
+
+ QDeclarativeListProperty<QDeclarativeState> states();
+ QDeclarativeListProperty<QDeclarativeTransition> transitions();
+
+ QString state() const;
+ void setState(const QString &);
+
+ QSGAnchorLine left() const;
+ QSGAnchorLine right() const;
+ QSGAnchorLine horizontalCenter() const;
+ QSGAnchorLine top() const;
+ QSGAnchorLine bottom() const;
+ QSGAnchorLine verticalCenter() const;
+ QSGAnchorLine baseline() const;
+
+ // data property
+ static void data_append(QDeclarativeListProperty<QObject> *, QObject *);
+ static int data_count(QDeclarativeListProperty<QObject> *);
+ static QObject *data_at(QDeclarativeListProperty<QObject> *, int);
+ static void data_clear(QDeclarativeListProperty<QObject> *);
+
+ // resources property
+ static QObject *resources_at(QDeclarativeListProperty<QObject> *, int);
+ static void resources_append(QDeclarativeListProperty<QObject> *, QObject *);
+ static int resources_count(QDeclarativeListProperty<QObject> *);
+ static void resources_clear(QDeclarativeListProperty<QObject> *);
+
+ // children property
+ static void children_append(QDeclarativeListProperty<QSGItem> *, QSGItem *);
+ static int children_count(QDeclarativeListProperty<QSGItem> *);
+ static QSGItem *children_at(QDeclarativeListProperty<QSGItem> *, int);
+ static void children_clear(QDeclarativeListProperty<QSGItem> *);
+
+ // transform property
+ static int transform_count(QDeclarativeListProperty<QSGTransform> *list);
+ static void transform_append(QDeclarativeListProperty<QSGTransform> *list, QSGTransform *);
+ static QSGTransform *transform_at(QDeclarativeListProperty<QSGTransform> *list, int);
+ static void transform_clear(QDeclarativeListProperty<QSGTransform> *list);
+
+ QSGAnchors *anchors() const;
+ mutable QSGAnchors *_anchors;
+ QSGContents *_contents;
+
+ QDeclarativeNullableValue<qreal> baselineOffset;
+
+ struct AnchorLines {
+ AnchorLines(QSGItem *);
+ QSGAnchorLine left;
+ QSGAnchorLine right;
+ QSGAnchorLine hCenter;
+ QSGAnchorLine top;
+ QSGAnchorLine bottom;
+ QSGAnchorLine vCenter;
+ QSGAnchorLine baseline;
+ };
+ mutable AnchorLines *_anchorLines;
+ AnchorLines *anchorLines() const;
+
+ enum ChangeType {
+ Geometry = 0x01,
+ SiblingOrder = 0x02,
+ Visibility = 0x04,
+ Opacity = 0x08,
+ Destroyed = 0x10,
+ Parent = 0x20,
+ Children = 0x40,
+ Rotation = 0x80,
+ };
+
+ Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
+
+ struct ChangeListener {
+ ChangeListener(QSGItemChangeListener *l, QSGItemPrivate::ChangeTypes t) : listener(l), types(t) {}
+ QSGItemChangeListener *listener;
+ QSGItemPrivate::ChangeTypes types;
+ bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
+ };
+
+ void addItemChangeListener(QSGItemChangeListener *listener, ChangeTypes types) {
+ changeListeners.append(ChangeListener(listener, types));
+ }
+ void removeItemChangeListener(QSGItemChangeListener *, ChangeTypes types);
+ QPODVector<ChangeListener,4> changeListeners;
+
+ QDeclarativeStateGroup *_states();
+ QDeclarativeStateGroup *_stateGroup;
+
+ QSGItem::TransformOrigin origin:5;
+ quint32 flags:4;
+ bool widthValid:1;
+ bool heightValid:1;
+ bool componentComplete:1;
+ bool keepMouse:1;
+ bool hoverEnabled:1;
+ bool smooth:1;
+ bool focus:1;
+ bool activeFocus:1;
+ bool notifiedFocus:1;
+ bool notifiedActiveFocus:1;
+ bool filtersChildMouseEvents:1;
+ bool explicitVisible:1;
+ bool effectiveVisible:1;
+ bool explicitEnable:1;
+ bool effectiveEnable:1;
+ bool polishScheduled:1;
+ bool inheritedLayoutMirror:1;
+ bool effectiveLayoutMirror:1;
+ bool isMirrorImplicit:1;
+ bool inheritMirrorFromParent:1;
+ bool inheritMirrorFromItem:1;
+ bool childrenDoNotOverlap:1;
+ quint32 dummy:1;
+
+ QSGCanvas *canvas;
+ QSGContext *sceneGraphContext() const { return static_cast<QSGCanvasPrivate *>(QObjectPrivate::get(canvas))->context; }
+
+ QSGItem *parentItem;
+ QList<QSGItem *> childItems;
+ QList<QSGItem *> paintOrderChildItems() const;
+ void addChild(QSGItem *);
+ void removeChild(QSGItem *);
+ void siblingOrderChanged();
+
+ class InitializationState {
+ public:
+ QSGItem *getFocusScope(QSGItem *item);
+ void clear();
+ void clear(QSGItem *focusScope);
+ private:
+ QSGItem *focusScope;
+ };
+ void initCanvas(InitializationState *, QSGCanvas *);
+
+ QSGItem *subFocusItem;
+
+ QTransform canvasToItemTransform() const;
+ QTransform itemToCanvasTransform() const;
+ void itemToParentTransform(QTransform &) const;
+
+ qreal x;
+ qreal y;
+ qreal width;
+ qreal height;
+ qreal implicitWidth;
+ qreal implicitHeight;
+
+ qreal z;
+ qreal scale;
+ qreal rotation;
+ qreal opacity;
+
+ QSGLayoutMirroringAttached* attachedLayoutDirection;
+
+ Qt::MouseButtons acceptedMouseButtons;
+ Qt::InputMethodHints imHints;
+
+ virtual qreal getImplicitWidth() const;
+ virtual qreal getImplicitHeight() const;
+ virtual void implicitWidthChanged();
+ virtual void implicitHeightChanged();
+
+ void resolveLayoutMirror();
+ void setImplicitLayoutMirror(bool mirror, bool inherit);
+ void setLayoutMirror(bool mirror);
+ bool isMirrored() const {
+ return effectiveLayoutMirror;
+ }
+
+ QPointF computeTransformOrigin() const;
+ QList<QSGTransform *> transforms;
+ virtual void transformChanged();
+
+ QSGItemKeyFilter *keyHandler;
+ void deliverKeyEvent(QKeyEvent *);
+ void deliverInputMethodEvent(QInputMethodEvent *);
+ void deliverFocusEvent(QFocusEvent *);
+ void deliverMouseEvent(QGraphicsSceneMouseEvent *);
+ void deliverWheelEvent(QGraphicsSceneWheelEvent *);
+ void deliverTouchEvent(QTouchEvent *);
+ void deliverHoverEvent(QGraphicsSceneHoverEvent *);
+
+ bool calcEffectiveVisible() const;
+ void setEffectiveVisibleRecur(bool);
+ bool calcEffectiveEnable() const;
+ void setEffectiveEnableRecur(bool);
+
+ // XXX todo
+ enum DirtyType {
+ TransformOrigin = 0x00000001,
+ Transform = 0x00000002,
+ BasicTransform = 0x00000004,
+ Position = 0x00000008,
+ Size = 0x00000010,
+
+ ZValue = 0x00000020,
+ Content = 0x00000040,
+ Smooth = 0x00000080,
+ OpacityValue = 0x00000100,
+ ChildrenChanged = 0x00000200,
+ ChildrenStackingChanged = 0x00000400,
+ ParentChanged = 0x00000800,
+
+ Clip = 0x00001000,
+ Canvas = 0x00002000,
+
+ EffectReference = 0x00008000,
+ Visible = 0x00010000,
+ HideReference = 0x00020000,
+ // When you add an attribute here, don't forget to update
+ // dirtyToString()
+
+ TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | Size | Canvas,
+ ComplexTransformUpdateMask = Transform | Canvas,
+ ContentUpdateMask = Size | Content | Smooth | Canvas,
+ ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Canvas,
+
+ };
+ quint32 dirtyAttributes;
+ QString dirtyToString() const;
+ void dirty(DirtyType);
+ void addToDirtyList();
+ void removeFromDirtyList();
+ QSGItem *nextDirtyItem;
+ QSGItem**prevDirtyItem;
+
+ inline QSGTransformNode *itemNode();
+ inline QSGNode *childContainerNode();
+
+ /*
+ QSGNode order is:
+ - itemNode
+ - (opacityNode)
+ - (clipNode)
+ - (effectNode)
+ - groupNode
+ */
+
+ QSGTransformNode *itemNodeInstance;
+ QSGOpacityNode *opacityNode;
+ QSGDefaultClipNode *clipNode;
+ QSGRootNode *rootNode;
+ QSGNode *groupNode;
+ QSGNode *paintNode;
+ int paintNodeIndex;
+
+ virtual QSGTransformNode *createTransformNode();
+
+ // A reference from an effect item means that this item is used by the effect, so
+ // it should insert a root node.
+ void refFromEffectItem(bool hide);
+ void derefFromEffectItem(bool unhide);
+ int effectRefCount;
+ int hideRefCount;
+
+ void itemChange(QSGItem::ItemChange, const QSGItem::ItemChangeData &);
+
+ virtual void mirrorChange() {}
+
+ static qint64 consistentTime;
+ static void setConsistentTime(qint64 t);
+ static void start(QElapsedTimer &);
+ static qint64 elapsed(QElapsedTimer &);
+ static qint64 restart(QElapsedTimer &);
+};
+
+/*
+ Key filters can be installed on a QSGItem, but not removed. Currently they
+ are only used by attached objects (which are only destroyed on Item
+ destruction), so this isn't a problem. If in future this becomes any form
+ of public API, they will have to support removal too.
+*/
+class QSGItemKeyFilter
+{
+public:
+ QSGItemKeyFilter(QSGItem * = 0);
+ virtual ~QSGItemKeyFilter();
+
+ virtual void keyPressed(QKeyEvent *event, bool post);
+ virtual void keyReleased(QKeyEvent *event, bool post);
+ virtual void inputMethodEvent(QInputMethodEvent *event, bool post);
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+ virtual void componentComplete();
+
+ bool m_processPost;
+
+private:
+ QSGItemKeyFilter *m_next;
+};
+
+class QSGKeyNavigationAttachedPrivate : public QObjectPrivate
+{
+public:
+ QSGKeyNavigationAttachedPrivate()
+ : QObjectPrivate(),
+ left(0), right(0), up(0), down(0), tab(0), backtab(0),
+ leftSet(false), rightSet(false), upSet(false), downSet(false),
+ tabSet(false), backtabSet(false) {}
+
+ QSGItem *left;
+ QSGItem *right;
+ QSGItem *up;
+ QSGItem *down;
+ QSGItem *tab;
+ QSGItem *backtab;
+ bool leftSet : 1;
+ bool rightSet : 1;
+ bool upSet : 1;
+ bool downSet : 1;
+ bool tabSet : 1;
+ bool backtabSet : 1;
+};
+
+class QSGKeyNavigationAttached : public QObject, public QSGItemKeyFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGKeyNavigationAttached)
+
+ Q_PROPERTY(QSGItem *left READ left WRITE setLeft NOTIFY leftChanged)
+ Q_PROPERTY(QSGItem *right READ right WRITE setRight NOTIFY rightChanged)
+ Q_PROPERTY(QSGItem *up READ up WRITE setUp NOTIFY upChanged)
+ Q_PROPERTY(QSGItem *down READ down WRITE setDown NOTIFY downChanged)
+ Q_PROPERTY(QSGItem *tab READ tab WRITE setTab NOTIFY tabChanged)
+ Q_PROPERTY(QSGItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
+
+ Q_ENUMS(Priority)
+
+public:
+ QSGKeyNavigationAttached(QObject * = 0);
+
+ QSGItem *left() const;
+ void setLeft(QSGItem *);
+ QSGItem *right() const;
+ void setRight(QSGItem *);
+ QSGItem *up() const;
+ void setUp(QSGItem *);
+ QSGItem *down() const;
+ void setDown(QSGItem *);
+ QSGItem *tab() const;
+ void setTab(QSGItem *);
+ QSGItem *backtab() const;
+ void setBacktab(QSGItem *);
+
+ enum Priority { BeforeItem, AfterItem };
+ Priority priority() const;
+ void setPriority(Priority);
+
+ static QSGKeyNavigationAttached *qmlAttachedProperties(QObject *);
+
+Q_SIGNALS:
+ void leftChanged();
+ void rightChanged();
+ void upChanged();
+ void downChanged();
+ void tabChanged();
+ void backtabChanged();
+ void priorityChanged();
+
+private:
+ virtual void keyPressed(QKeyEvent *event, bool post);
+ virtual void keyReleased(QKeyEvent *event, bool post);
+ void setFocusNavigation(QSGItem *currentItem, const char *dir);
+};
+
+class QSGLayoutMirroringAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled RESET resetEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool childrenInherit READ childrenInherit WRITE setChildrenInherit NOTIFY childrenInheritChanged)
+
+public:
+ explicit QSGLayoutMirroringAttached(QObject *parent = 0);
+
+ bool enabled() const;
+ void setEnabled(bool);
+ void resetEnabled();
+
+ bool childrenInherit() const;
+ void setChildrenInherit(bool);
+
+ static QSGLayoutMirroringAttached *qmlAttachedProperties(QObject *);
+Q_SIGNALS:
+ void enabledChanged();
+ void childrenInheritChanged();
+private:
+ friend class QSGItemPrivate;
+ QSGItemPrivate *itemPrivate;
+};
+
+class QSGKeysAttachedPrivate : public QObjectPrivate
+{
+public:
+ QSGKeysAttachedPrivate()
+ : QObjectPrivate(), inPress(false), inRelease(false)
+ , inIM(false), enabled(true), imeItem(0), item(0)
+ {}
+
+ bool isConnected(const char *signalName);
+
+ //loop detection
+ bool inPress:1;
+ bool inRelease:1;
+ bool inIM:1;
+
+ bool enabled : 1;
+
+ QSGItem *imeItem;
+ QList<QSGItem *> targets;
+ QSGItem *item;
+};
+
+class QSGKeysAttached : public QObject, public QSGItemKeyFilter
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGKeysAttached)
+
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(QDeclarativeListProperty<QSGItem> forwardTo READ forwardTo)
+ Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
+
+ Q_ENUMS(Priority)
+
+public:
+ QSGKeysAttached(QObject *parent=0);
+ ~QSGKeysAttached();
+
+ bool enabled() const { Q_D(const QSGKeysAttached); return d->enabled; }
+ void setEnabled(bool enabled) {
+ Q_D(QSGKeysAttached);
+ if (enabled != d->enabled) {
+ d->enabled = enabled;
+ emit enabledChanged();
+ }
+ }
+
+ enum Priority { BeforeItem, AfterItem};
+ Priority priority() const;
+ void setPriority(Priority);
+
+ QDeclarativeListProperty<QSGItem> forwardTo() {
+ Q_D(QSGKeysAttached);
+ return QDeclarativeListProperty<QSGItem>(this, d->targets);
+ }
+
+ virtual void componentComplete();
+
+ static QSGKeysAttached *qmlAttachedProperties(QObject *);
+
+Q_SIGNALS:
+ void enabledChanged();
+ void priorityChanged();
+ void pressed(QSGKeyEvent *event);
+ void released(QSGKeyEvent *event);
+ void digit0Pressed(QSGKeyEvent *event);
+ void digit1Pressed(QSGKeyEvent *event);
+ void digit2Pressed(QSGKeyEvent *event);
+ void digit3Pressed(QSGKeyEvent *event);
+ void digit4Pressed(QSGKeyEvent *event);
+ void digit5Pressed(QSGKeyEvent *event);
+ void digit6Pressed(QSGKeyEvent *event);
+ void digit7Pressed(QSGKeyEvent *event);
+ void digit8Pressed(QSGKeyEvent *event);
+ void digit9Pressed(QSGKeyEvent *event);
+
+ void leftPressed(QSGKeyEvent *event);
+ void rightPressed(QSGKeyEvent *event);
+ void upPressed(QSGKeyEvent *event);
+ void downPressed(QSGKeyEvent *event);
+ void tabPressed(QSGKeyEvent *event);
+ void backtabPressed(QSGKeyEvent *event);
+
+ void asteriskPressed(QSGKeyEvent *event);
+ void numberSignPressed(QSGKeyEvent *event);
+ void escapePressed(QSGKeyEvent *event);
+ void returnPressed(QSGKeyEvent *event);
+ void enterPressed(QSGKeyEvent *event);
+ void deletePressed(QSGKeyEvent *event);
+ void spacePressed(QSGKeyEvent *event);
+ void backPressed(QSGKeyEvent *event);
+ void cancelPressed(QSGKeyEvent *event);
+ void selectPressed(QSGKeyEvent *event);
+ void yesPressed(QSGKeyEvent *event);
+ void noPressed(QSGKeyEvent *event);
+ void context1Pressed(QSGKeyEvent *event);
+ void context2Pressed(QSGKeyEvent *event);
+ void context3Pressed(QSGKeyEvent *event);
+ void context4Pressed(QSGKeyEvent *event);
+ void callPressed(QSGKeyEvent *event);
+ void hangupPressed(QSGKeyEvent *event);
+ void flipPressed(QSGKeyEvent *event);
+ void menuPressed(QSGKeyEvent *event);
+ void volumeUpPressed(QSGKeyEvent *event);
+ void volumeDownPressed(QSGKeyEvent *event);
+
+private:
+ virtual void keyPressed(QKeyEvent *event, bool post);
+ virtual void keyReleased(QKeyEvent *event, bool post);
+ virtual void inputMethodEvent(QInputMethodEvent *, bool post);
+ virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const;
+
+ const QByteArray keyToSignal(int key) {
+ QByteArray keySignal;
+ if (key >= Qt::Key_0 && key <= Qt::Key_9) {
+ keySignal = "digit0Pressed";
+ keySignal[5] = '0' + (key - Qt::Key_0);
+ } else {
+ int i = 0;
+ while (sigMap[i].key && sigMap[i].key != key)
+ ++i;
+ keySignal = sigMap[i].sig;
+ }
+ return keySignal;
+ }
+
+ struct SigMap {
+ int key;
+ const char *sig;
+ };
+
+ static const SigMap sigMap[];
+};
+
+QSGTransformNode *QSGItemPrivate::itemNode()
+{
+ if (!itemNodeInstance) {
+ itemNodeInstance = createTransformNode();
+#ifdef QML_RUNTIME_TESTING
+ Q_Q(QSGItem);
+ itemNodeInstance->description = QString::fromLatin1("QSGItem(%1)").arg(QString::fromLatin1(q->metaObject()->className()));
+#endif
+ }
+ return itemNodeInstance;
+}
+
+QSGNode *QSGItemPrivate::childContainerNode()
+{
+ if (!groupNode) {
+ groupNode = new QSGNode();
+ if (rootNode)
+ rootNode->appendChildNode(groupNode);
+ else if (clipNode)
+ clipNode->appendChildNode(groupNode);
+ else if (opacityNode)
+ opacityNode->appendChildNode(groupNode);
+ else
+ itemNode()->appendChildNode(groupNode);
+ groupNode->setFlag(QSGNode::ChildrenDoNotOverlap, childrenDoNotOverlap);
+#ifdef QML_RUNTIME_TESTING
+ groupNode->description = QLatin1String("group");
+#endif
+ }
+ return groupNode;
+}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QSGItemPrivate::ChangeTypes);
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGKeysAttached)
+QML_DECLARE_TYPEINFO(QSGKeysAttached, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QSGKeyNavigationAttached)
+QML_DECLARE_TYPEINFO(QSGKeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QSGLayoutMirroringAttached)
+QML_DECLARE_TYPEINFO(QSGLayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QSGITEM_P_H
diff --git a/src/declarative/items/qsgitemchangelistener_p.h b/src/declarative/items/qsgitemchangelistener_p.h
new file mode 100644
index 0000000000..3b4018a772
--- /dev/null
+++ b/src/declarative/items/qsgitemchangelistener_p.h
@@ -0,0 +1,82 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGITEMCHANGELISTENER_P_H
+#define QSGITEMCHANGELISTENER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRectF;
+class QSGItem;
+class QSGAnchorsPrivate;
+class QSGItemChangeListener
+{
+public:
+ virtual void itemGeometryChanged(QSGItem *, const QRectF &, const QRectF &) {}
+ virtual void itemSiblingOrderChanged(QSGItem *) {}
+ virtual void itemVisibilityChanged(QSGItem *) {}
+ virtual void itemOpacityChanged(QSGItem *) {}
+ virtual void itemDestroyed(QSGItem *) {}
+ virtual void itemChildAdded(QSGItem *, QSGItem *) {}
+ virtual void itemChildRemoved(QSGItem *, QSGItem *) {}
+ virtual void itemParentChanged(QSGItem *, QSGItem *) {}
+ virtual void itemRotationChanged(QSGItem *) {}
+
+ virtual QSGAnchorsPrivate *anchorPrivate() { return 0; }
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGITEMCHANGELISTENER_P_H
diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp
new file mode 100644
index 0000000000..6ea20bb38b
--- /dev/null
+++ b/src/declarative/items/qsgitemsmodule.cpp
@@ -0,0 +1,212 @@
+// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgitemsmodule_p.h"
+
+#include "qsgitem.h"
+#include "qsgitem_p.h"
+#include "qsgevents_p_p.h"
+#include "qsgrectangle_p.h"
+#include "qsgfocusscope_p.h"
+#include "qsgtext_p.h"
+#include "qsgtextinput_p.h"
+#include "qsgtextedit_p.h"
+#include "qsgimage_p.h"
+#include "qsgborderimage_p.h"
+#include "qsgscalegrid_p_p.h"
+#include "qsgmousearea_p.h"
+#include "qsgpincharea_p.h"
+#include "qsgflickable_p.h"
+#include "qsgflickable_p_p.h"
+#include "qsglistview_p.h"
+#include "qsgvisualitemmodel_p.h"
+#include "qsggridview_p.h"
+#include "qsgpathview_p.h"
+#include <private/qdeclarativepath_p.h>
+#include "qsgpositioners_p.h"
+#include "qsgrepeater_p.h"
+#include "qsgloader_p.h"
+#include "qsganimatedimage_p.h"
+#include "qsgflipable_p.h"
+#include "qsgtranslate_p.h"
+#include "qsgstateoperations_p.h"
+#include "qsganimation_p.h"
+#include <private/qsgshadereffectitem_p.h>
+#include <private/qsgshadereffectsource_p.h>
+//#include "private/qsgpincharea_p.h"
+#include "qsgcanvasitem_p.h"
+#include "qsgcontext2d_p.h"
+
+static QDeclarativePrivate::AutoParentResult qsgitem_autoParent(QObject *obj, QObject *parent)
+{
+ QSGItem *item = qobject_cast<QSGItem *>(obj);
+ if (!item)
+ return QDeclarativePrivate::IncompatibleObject;
+
+ QSGItem *parentItem = qobject_cast<QSGItem *>(parent);
+ if (!parentItem)
+ return QDeclarativePrivate::IncompatibleParent;
+
+ item->setParentItem(parentItem);
+ return QDeclarativePrivate::Parented;
+}
+
+static void qt_sgitems_defineModule(const char *uri, int major, int minor)
+{
+ QDeclarativePrivate::RegisterAutoParent autoparent = { 0, &qsgitem_autoParent };
+ QDeclarativePrivate::qmlregister(QDeclarativePrivate::AutoParentRegistration, &autoparent);
+
+#ifdef QT_NO_MOVIE
+ qmlRegisterTypeNotAvailable(uri,major,minor,"AnimatedImage", qApp->translate("QSGAnimatedImage","Qt was built without support for QMovie"));
+#else
+ qmlRegisterType<QSGAnimatedImage>(uri,major,minor,"AnimatedImage");
+#endif
+ qmlRegisterType<QSGBorderImage>(uri,major,minor,"BorderImage");
+ qmlRegisterType<QSGColumn>(uri,major,minor,"Column");
+ qmlRegisterType<QSGDrag>(uri,major,minor,"Drag");
+ qmlRegisterType<QSGFlickable>(uri,major,minor,"Flickable");
+ qmlRegisterType<QSGFlipable>(uri,major,minor,"Flipable");
+ qmlRegisterType<QSGFlow>(uri,major,minor,"Flow");
+// qmlRegisterType<QDeclarativeFocusPanel>(uri,major,minor,"FocusPanel");
+ qmlRegisterType<QSGFocusScope>(uri,major,minor,"FocusScope");
+ qmlRegisterType<QSGGradient>(uri,major,minor,"Gradient");
+ qmlRegisterType<QSGGradientStop>(uri,major,minor,"GradientStop");
+ qmlRegisterType<QSGGrid>(uri,major,minor,"Grid");
+ qmlRegisterType<QSGGridView>(uri,major,minor,"GridView");
+ qmlRegisterType<QSGImage>(uri,major,minor,"Image");
+ qmlRegisterType<QSGItem>(uri,major,minor,"Item");
+ qmlRegisterType<QSGListView>(uri,major,minor,"ListView");
+ qmlRegisterType<QSGLoader>(uri,major,minor,"Loader");
+ qmlRegisterType<QSGMouseArea>(uri,major,minor,"MouseArea");
+ qmlRegisterType<QDeclarativePath>(uri,major,minor,"Path");
+ qmlRegisterType<QDeclarativePathAttribute>(uri,major,minor,"PathAttribute");
+ qmlRegisterType<QDeclarativePathCubic>(uri,major,minor,"PathCubic");
+ qmlRegisterType<QDeclarativePathLine>(uri,major,minor,"PathLine");
+ qmlRegisterType<QDeclarativePathPercent>(uri,major,minor,"PathPercent");
+ qmlRegisterType<QDeclarativePathQuad>(uri,major,minor,"PathQuad");
+ qmlRegisterType<QSGPathView>(uri,major,minor,"PathView");
+#ifndef QT_NO_VALIDATOR
+ qmlRegisterType<QIntValidator>(uri,major,minor,"IntValidator");
+ qmlRegisterType<QDoubleValidator>(uri,major,minor,"DoubleValidator");
+ qmlRegisterType<QRegExpValidator>(uri,major,minor,"RegExpValidator");
+#endif
+ qmlRegisterType<QSGRectangle>(uri,major,minor,"Rectangle");
+ qmlRegisterType<QSGRepeater>(uri,major,minor,"Repeater");
+ qmlRegisterType<QSGRow>(uri,major,minor,"Row");
+ qmlRegisterType<QSGTranslate>(uri,major,minor,"Translate");
+ qmlRegisterType<QSGRotation>(uri,major,minor,"Rotation");
+ qmlRegisterType<QSGScale>(uri,major,minor,"Scale");
+ qmlRegisterType<QSGText>(uri,major,minor,"Text");
+ qmlRegisterType<QSGTextEdit>(uri,major,minor,"TextEdit");
+ qmlRegisterType<QSGTextInput>(uri,major,minor,"TextInput");
+ qmlRegisterType<QSGViewSection>(uri,major,minor,"ViewSection");
+ qmlRegisterType<QSGVisualDataModel>(uri,major,minor,"VisualDataModel");
+ qmlRegisterType<QSGVisualItemModel>(uri,major,minor,"VisualItemModel");
+
+ qmlRegisterType<QSGAnchors>();
+ qmlRegisterType<QSGKeyEvent>();
+ qmlRegisterType<QSGMouseEvent>();
+ qmlRegisterType<QSGTransform>();
+ qmlRegisterType<QDeclarativePathElement>();
+ qmlRegisterType<QDeclarativeCurve>();
+ qmlRegisterType<QSGScaleGrid>();
+#ifndef QT_NO_VALIDATOR
+ qmlRegisterType<QValidator>();
+#endif
+ qmlRegisterType<QSGVisualModel>();
+#ifndef QT_NO_ACTION
+ qmlRegisterType<QAction>();
+#endif
+ qmlRegisterType<QSGPen>();
+ qmlRegisterType<QSGFlickableVisibleArea>();
+ qRegisterMetaType<QSGAnchorLine>("QSGAnchorLine");
+
+ qmlRegisterUncreatableType<QSGKeyNavigationAttached>(uri,major,minor,"KeyNavigation",QSGKeyNavigationAttached::tr("KeyNavigation is only available via attached properties"));
+ qmlRegisterUncreatableType<QSGKeysAttached>(uri,major,minor,"Keys",QSGKeysAttached::tr("Keys is only available via attached properties"));
+ qmlRegisterUncreatableType<QSGLayoutMirroringAttached>(uri,major,minor,"LayoutMirroring", QSGLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties"));
+
+ qmlRegisterType<QSGPinchArea>(uri,major,minor,"PinchArea");
+ qmlRegisterType<QSGPinch>(uri,major,minor,"Pinch");
+ qmlRegisterType<QSGPinchEvent>();
+
+ qmlRegisterType<QSGShaderEffectItem>("QtQuick", 2, 0, "ShaderEffectItem");
+ qmlRegisterType<QSGShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource");
+ qmlRegisterUncreatableType<QSGShaderEffectMesh>("QtQuick", 2, 0, "ShaderEffectMesh", QSGShaderEffectMesh::tr("Cannot create instance of abstract class ShaderEffectMesh."));
+ qmlRegisterType<QSGGridMesh>("QtQuick", 2, 0, "GridMesh");
+
+ qmlRegisterUncreatableType<QSGPaintedItem>("QtQuick", 2, 0, "PaintedItem", QSGPaintedItem::tr("Cannot create instance of abstract class PaintedItem"));
+
+ qmlRegisterType<QSGCanvasItem>("QtQuick", 2, 0, "Canvas");
+ qmlRegisterType<QSGContext2D>();
+ qmlRegisterType<QSGCanvasGradient>();
+
+
+ qmlRegisterType<QSGParentChange>(uri, major, minor,"ParentChange");
+ qmlRegisterType<QSGAnchorChanges>(uri, major, minor,"AnchorChanges");
+ qmlRegisterType<QSGAnchorSet>();
+ qmlRegisterType<QSGAnchorAnimation>(uri, major, minor,"AnchorAnimation");
+ qmlRegisterType<QSGParentAnimation>(uri, major, minor,"ParentAnimation");
+}
+
+void QSGItemsModule::defineModule()
+{
+ static bool initialized = false;
+ if (initialized)
+ return;
+ initialized = true;
+
+ // XXX todo - Remove before final integration...
+ QByteArray mode = qgetenv("QMLSCENE_IMPORT_NAME");
+ QByteArray name = "QtQuick";
+ int majorVersion = 2;
+ int minorVersion = 0;
+ if (mode == "quick1") {
+ majorVersion = 1;
+ } else if (mode == "qt") {
+ name = "Qt";
+ majorVersion = 4;
+ minorVersion = 7;
+ }
+
+ qt_sgitems_defineModule(name, majorVersion, minorVersion);
+}
+
diff --git a/src/declarative/items/qsgitemsmodule_p.h b/src/declarative/items/qsgitemsmodule_p.h
new file mode 100644
index 0000000000..2d8a971c22
--- /dev/null
+++ b/src/declarative/items/qsgitemsmodule_p.h
@@ -0,0 +1,65 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGITEMSMODULE_P_H
+#define QSGITEMSMODULE_P_H
+
+#include <qdeclarative.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGItemsModule
+{
+public:
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSGITEMSMODULE_P_H
+
diff --git a/src/declarative/items/qsglistview.cpp b/src/declarative/items/qsglistview.cpp
new file mode 100644
index 0000000000..a61ca97256
--- /dev/null
+++ b/src/declarative/items/qsglistview.cpp
@@ -0,0 +1,3064 @@
+// Commit: 806f031efeda71d3f4d7d2f949b437493e79cf52
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsglistview_p.h"
+#include "qsgflickable_p_p.h"
+#include "qsgvisualitemmodel_p.h"
+
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qevent.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <private/qdeclarativesmoothedanimation_p_p.h>
+#include <private/qlistmodelinterface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QSGViewSection::setProperty(const QString &property)
+{
+ if (property != m_property) {
+ m_property = property;
+ emit propertyChanged();
+ }
+}
+
+void QSGViewSection::setCriteria(QSGViewSection::SectionCriteria criteria)
+{
+ if (criteria != m_criteria) {
+ m_criteria = criteria;
+ emit criteriaChanged();
+ }
+}
+
+void QSGViewSection::setDelegate(QDeclarativeComponent *delegate)
+{
+ if (delegate != m_delegate) {
+ m_delegate = delegate;
+ emit delegateChanged();
+ }
+}
+
+QString QSGViewSection::sectionString(const QString &value)
+{
+ if (m_criteria == FirstCharacter)
+ return value.isEmpty() ? QString() : value.at(0);
+ else
+ return value;
+}
+
+//----------------------------------------------------------------------------
+
+class FxListItemSG
+{
+public:
+ FxListItemSG(QSGItem *i, QSGListView *v) : item(i), section(0), view(v) {
+ attached = static_cast<QSGListViewAttached*>(qmlAttachedPropertiesObject<QSGListView>(item));
+ if (attached)
+ attached->setView(view);
+ }
+ ~FxListItemSG() {}
+ qreal position() const {
+ if (section) {
+ if (view->orientation() == QSGListView::Vertical)
+ return section->y();
+ else
+ return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
+ } else {
+ return itemPosition();
+ }
+ }
+ qreal itemPosition() const {
+ if (view->orientation() == QSGListView::Vertical)
+ return item->y();
+ else
+ return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
+ }
+ qreal size() const {
+ if (section)
+ return (view->orientation() == QSGListView::Vertical ? item->height()+section->height() : item->width()+section->width());
+ else
+ return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
+ }
+ qreal itemSize() const {
+ return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
+ }
+ qreal sectionSize() const {
+ if (section)
+ return (view->orientation() == QSGListView::Vertical ? section->height() : section->width());
+ return 0.0;
+ }
+ qreal endPosition() const {
+ if (view->orientation() == QSGListView::Vertical) {
+ return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
+ } else {
+ return (view->effectiveLayoutDirection() == Qt::RightToLeft
+ ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
+ : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
+ }
+ }
+ void setPosition(qreal pos) {
+ if (view->orientation() == QSGListView::Vertical) {
+ if (section) {
+ section->setY(pos);
+ pos += section->height();
+ }
+ item->setY(pos);
+ } else {
+ if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
+ if (section) {
+ section->setX(-section->width()-pos);
+ pos += section->width();
+ }
+ item->setX(-item->width()-pos);
+ } else {
+ if (section) {
+ section->setX(pos);
+ pos += section->width();
+ }
+ item->setX(pos);
+ }
+ }
+ }
+ void setSize(qreal size) {
+ if (view->orientation() == QSGListView::Vertical)
+ item->setHeight(size);
+ else
+ item->setWidth(size);
+ }
+ bool contains(qreal x, qreal y) const {
+ return (x >= item->x() && x < item->x() + item->width() &&
+ y >= item->y() && y < item->y() + item->height());
+ }
+
+ QSGItem *item;
+ QSGItem *section;
+ QSGListView *view;
+ QSGListViewAttached *attached;
+ int index;
+};
+
+//----------------------------------------------------------------------------
+
+class QSGListViewPrivate : public QSGFlickablePrivate
+{
+ Q_DECLARE_PUBLIC(QSGListView)
+
+public:
+ QSGListViewPrivate()
+ : currentItem(0), orient(QSGListView::Vertical), layoutDirection(Qt::LeftToRight)
+ , visiblePos(0), visibleIndex(0)
+ , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
+ , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
+ , highlightComponent(0), highlight(0), trackedItem(0)
+ , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
+ , sectionCriteria(0), spacing(0.0)
+ , highlightMoveSpeed(400), highlightMoveDuration(-1)
+ , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QSGListView::NoHighlightRange)
+ , snapMode(QSGListView::NoSnap), overshootDist(0.0)
+ , footerComponent(0), footer(0), headerComponent(0), header(0)
+ , bufferMode(BufferBefore | BufferAfter)
+ , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
+ , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
+ , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
+ , inViewportMoved(false)
+ , highlightRangeStartValid(false), highlightRangeEndValid(false)
+ , minExtentDirty(true), maxExtentDirty(true)
+ {}
+
+ void init();
+ void clear();
+ FxListItemSG *createItem(int modelIndex);
+ void releaseItem(FxListItemSG *item);
+
+ FxListItemSG *visibleItem(int modelIndex) const {
+ if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
+ for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
+ FxListItemSG *item = visibleItems.at(i);
+ if (item->index == modelIndex)
+ return item;
+ }
+ }
+ return 0;
+ }
+
+ FxListItemSG *firstVisibleItem() const {
+ const qreal pos = isRightToLeft() ? -position()-size() : position();
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItemSG *item = visibleItems.at(i);
+ if (item->index != -1 && item->endPosition() > pos)
+ return item;
+ }
+ return visibleItems.count() ? visibleItems.first() : 0;
+ }
+
+ FxListItemSG *nextVisibleItem() const {
+ const qreal pos = isRightToLeft() ? -position()-size() : position();
+ bool foundFirst = false;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItemSG *item = visibleItems.at(i);
+ if (item->index != -1) {
+ if (foundFirst)
+ return item;
+ else if (item->position() < pos && item->endPosition() > pos)
+ foundFirst = true;
+ }
+ }
+ return 0;
+ }
+
+ // Returns the item before modelIndex, if created.
+ // May return an item marked for removal.
+ FxListItemSG *itemBefore(int modelIndex) const {
+ if (modelIndex < visibleIndex)
+ return 0;
+ int idx = 1;
+ int lastIndex = -1;
+ while (idx < visibleItems.count()) {
+ FxListItemSG *item = visibleItems.at(idx);
+ if (item->index != -1)
+ lastIndex = item->index;
+ if (item->index == modelIndex)
+ return visibleItems.at(idx-1);
+ ++idx;
+ }
+ if (lastIndex == modelIndex-1)
+ return visibleItems.last();
+ return 0;
+ }
+
+ void regenerate() {
+ Q_Q(QSGListView);
+ if (q->isComponentComplete()) {
+ if (header) {
+ // XXX todo - the original did scene()->removeItem(). Why?
+ header->item->setParentItem(0);
+ header->item->deleteLater();
+ delete header;
+ header = 0;
+ }
+ if (footer) {
+ // XXX todo - the original did scene()->removeItem(). Why?
+ footer->item->setParentItem(0);
+ footer->item->deleteLater();
+ delete footer;
+ footer = 0;
+ }
+ updateHeader();
+ updateFooter();
+ clear();
+ setPosition(0);
+ q->refill();
+ updateCurrent(currentIndex);
+ }
+ }
+
+ void mirrorChange() {
+ Q_Q(QSGListView);
+ regenerate();
+ emit q->effectiveLayoutDirectionChanged();
+ }
+
+ bool isRightToLeft() const {
+ Q_Q(const QSGListView);
+ return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
+ }
+
+ qreal position() const {
+ Q_Q(const QSGListView);
+ return orient == QSGListView::Vertical ? q->contentY() : q->contentX();
+ }
+ void setPosition(qreal pos) {
+ Q_Q(QSGListView);
+ if (orient == QSGListView::Vertical) {
+ q->QSGFlickable::setContentY(pos);
+ } else {
+ if (isRightToLeft())
+ q->QSGFlickable::setContentX(-pos-size());
+ else
+ q->QSGFlickable::setContentX(pos);
+ }
+ }
+ qreal size() const {
+ Q_Q(const QSGListView);
+ return orient == QSGListView::Vertical ? q->height() : q->width();
+ }
+
+ qreal originPosition() const {
+ qreal pos = 0;
+ if (!visibleItems.isEmpty()) {
+ pos = (*visibleItems.constBegin())->position();
+ if (visibleIndex > 0)
+ pos -= visibleIndex * (averageSize + spacing);
+ }
+ return pos;
+ }
+
+ qreal lastPosition() const {
+ qreal pos = 0;
+ if (!visibleItems.isEmpty()) {
+ int invisibleCount = visibleItems.count() - visibleIndex;
+ for (int i = visibleItems.count()-1; i >= 0; --i) {
+ if (visibleItems.at(i)->index != -1) {
+ invisibleCount = model->count() - visibleItems.at(i)->index - 1;
+ break;
+ }
+ }
+ pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
+ } else if (model && model->count()) {
+ pos = model->count() * averageSize + (model->count()-1) * spacing;
+ }
+ return pos;
+ }
+
+ qreal startPosition() const {
+ return isRightToLeft() ? -lastPosition()-1 : originPosition();
+ }
+
+ qreal endPosition() const {
+ return isRightToLeft() ? -originPosition()-1 : lastPosition();
+ }
+
+ qreal positionAt(int modelIndex) const {
+ if (FxListItemSG *item = visibleItem(modelIndex))
+ return item->position();
+ if (!visibleItems.isEmpty()) {
+ if (modelIndex < visibleIndex) {
+ int count = visibleIndex - modelIndex;
+ qreal cs = 0;
+ if (modelIndex == currentIndex && currentItem) {
+ cs = currentItem->size() + spacing;
+ --count;
+ }
+ return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
+ } else {
+ int idx = visibleItems.count() - 1;
+ while (idx >= 0 && visibleItems.at(idx)->index == -1)
+ --idx;
+ if (idx < 0)
+ idx = visibleIndex;
+ else
+ idx = visibleItems.at(idx)->index;
+ int count = modelIndex - idx - 1;
+ return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
+ }
+ }
+ return 0;
+ }
+
+ qreal endPositionAt(int modelIndex) const {
+ if (FxListItemSG *item = visibleItem(modelIndex))
+ return item->endPosition();
+ if (!visibleItems.isEmpty()) {
+ if (modelIndex < visibleIndex) {
+ int count = visibleIndex - modelIndex;
+ return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
+ } else {
+ int idx = visibleItems.count() - 1;
+ while (idx >= 0 && visibleItems.at(idx)->index == -1)
+ --idx;
+ if (idx < 0)
+ idx = visibleIndex;
+ else
+ idx = visibleItems.at(idx)->index;
+ int count = modelIndex - idx - 1;
+ return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
+ }
+ }
+ return 0;
+ }
+
+ QString sectionAt(int modelIndex) {
+ if (FxListItemSG *item = visibleItem(modelIndex))
+ return item->attached->section();
+
+ QString section;
+ if (sectionCriteria) {
+ QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
+ section = sectionCriteria->sectionString(propValue);
+ }
+
+ return section;
+ }
+
+ bool isValid() const {
+ return model && model->count() && model->isValid();
+ }
+
+ qreal snapPosAt(qreal pos) {
+ if (FxListItemSG *snapItem = snapItemAt(pos))
+ return snapItem->position();
+ if (visibleItems.count()) {
+ qreal firstPos = visibleItems.first()->position();
+ qreal endPos = visibleItems.last()->position();
+ if (pos < firstPos) {
+ return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
+ } else if (pos > endPos)
+ return endPos + qRound((pos - endPos) / averageSize) * averageSize;
+ }
+ return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
+ }
+
+ FxListItemSG *snapItemAt(qreal pos) {
+ FxListItemSG *snapItem = 0;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItemSG *item = visibleItems[i];
+ if (item->index == -1)
+ continue;
+ qreal itemTop = item->position();
+ if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
+ return item;
+ if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
+ snapItem = item;
+ }
+ return snapItem;
+ }
+
+ int lastVisibleIndex() const {
+ int lastIndex = -1;
+ for (int i = visibleItems.count()-1; i >= 0; --i) {
+ FxListItemSG *listItem = visibleItems.at(i);
+ if (listItem->index != -1) {
+ lastIndex = listItem->index;
+ break;
+ }
+ }
+ return lastIndex;
+ }
+
+ // map a model index to visibleItems index.
+ int mapFromModel(int modelIndex) const {
+ if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
+ return -1;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItemSG *listItem = visibleItems.at(i);
+ if (listItem->index == modelIndex)
+ return i;
+ if (listItem->index > modelIndex)
+ return -1;
+ }
+ return -1; // Not in visibleList
+ }
+
+ void updateViewport() {
+ Q_Q(QSGListView);
+ if (orient == QSGListView::Vertical) {
+ q->setContentHeight(endPosition() - startPosition() + 1);
+ } else {
+ q->setContentWidth(endPosition() - startPosition() + 1);
+ }
+ }
+
+ void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
+ Q_Q(QSGListView);
+ QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
+ if (!q->isComponentComplete())
+ return;
+ if (item != contentItem && (!highlight || item != highlight->item)) {
+ if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height())
+ || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
+ scheduleLayout();
+ }
+ }
+ if ((header && header->item == item) || (footer && footer->item == item)) {
+ if (header)
+ updateHeader();
+ if (footer)
+ updateFooter();
+ }
+ if (currentItem && currentItem->item == item)
+ updateHighlight();
+ if (trackedItem && trackedItem->item == item)
+ q->trackedPositionChanged();
+ }
+
+ // for debugging only
+ void checkVisible() const {
+ int skip = 0;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ FxListItemSG *listItem = visibleItems.at(i);
+ if (listItem->index == -1) {
+ ++skip;
+ } else if (listItem->index != visibleIndex + i - skip) {
+ qFatal("index %d %d %d", visibleIndex, i, listItem->index);
+ }
+ }
+ }
+
+ void refill(qreal from, qreal to, bool doBuffer = false);
+ void scheduleLayout();
+ void layout();
+ void updateUnrequestedIndexes();
+ void updateUnrequestedPositions();
+ void updateTrackedItem();
+ void createHighlight();
+ void updateHighlight();
+ void createSection(FxListItemSG *);
+ void updateSections();
+ void updateCurrentSection();
+ void updateCurrent(int);
+ void updateAverage();
+ void updateHeader();
+ void updateFooter();
+ void fixupPosition();
+ void positionViewAtIndex(int index, int mode);
+ virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
+ virtual void flick(QSGFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+ QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
+
+ QDeclarativeGuard<QSGVisualModel> model;
+ QVariant modelVariant;
+ QList<FxListItemSG*> visibleItems;
+ QHash<QSGItem*,int> unrequestedItems;
+ FxListItemSG *currentItem;
+ QSGListView::Orientation orient;
+ Qt::LayoutDirection layoutDirection;
+ qreal visiblePos;
+ int visibleIndex;
+ qreal averageSize;
+ int currentIndex;
+ int requestedIndex;
+ int itemCount;
+ qreal highlightRangeStart;
+ qreal highlightRangeEnd;
+ QDeclarativeComponent *highlightComponent;
+ FxListItemSG *highlight;
+ FxListItemSG *trackedItem;
+ enum MovementReason { Other, SetIndex, Mouse };
+ MovementReason moveReason;
+ int buffer;
+ QSmoothedAnimation *highlightPosAnimator;
+ QSmoothedAnimation *highlightSizeAnimator;
+ QSGViewSection *sectionCriteria;
+ QString currentSection;
+ static const int sectionCacheSize = 4;
+ QSGItem *sectionCache[sectionCacheSize];
+ qreal spacing;
+ qreal highlightMoveSpeed;
+ int highlightMoveDuration;
+ qreal highlightResizeSpeed;
+ int highlightResizeDuration;
+ QSGListView::HighlightRangeMode highlightRange;
+ QSGListView::SnapMode snapMode;
+ qreal overshootDist;
+ QDeclarativeComponent *footerComponent;
+ FxListItemSG *footer;
+ QDeclarativeComponent *headerComponent;
+ FxListItemSG *header;
+ enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
+ int bufferMode;
+ mutable qreal minExtent;
+ mutable qreal maxExtent;
+
+ bool ownModel : 1;
+ bool wrap : 1;
+ bool autoHighlight : 1;
+ bool haveHighlightRange : 1;
+ bool correctFlick : 1;
+ bool inFlickCorrection : 1;
+ bool lazyRelease : 1;
+ bool deferredRelease : 1;
+ bool layoutScheduled : 1;
+ bool currentIndexCleared : 1;
+ bool inViewportMoved : 1;
+ bool highlightRangeStartValid : 1;
+ bool highlightRangeEndValid : 1;
+ mutable bool minExtentDirty : 1;
+ mutable bool maxExtentDirty : 1;
+};
+
+void QSGListViewPrivate::init()
+{
+ Q_Q(QSGListView);
+ QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
+ q->setFlag(QSGItem::ItemIsFocusScope);
+ addItemChangeListener(this, Geometry);
+ QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
+ q->setFlickableDirection(QSGFlickable::VerticalFlick);
+ ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
+}
+
+void QSGListViewPrivate::clear()
+{
+ timeline.clear();
+ for (int i = 0; i < visibleItems.count(); ++i)
+ releaseItem(visibleItems.at(i));
+ visibleItems.clear();
+ for (int i = 0; i < sectionCacheSize; ++i) {
+ delete sectionCache[i];
+ sectionCache[i] = 0;
+ }
+ visiblePos = header ? header->size() : 0;
+ visibleIndex = 0;
+ releaseItem(currentItem);
+ currentItem = 0;
+ createHighlight();
+ trackedItem = 0;
+ minExtentDirty = true;
+ maxExtentDirty = true;
+ itemCount = 0;
+}
+
+FxListItemSG *QSGListViewPrivate::createItem(int modelIndex)
+{
+ Q_Q(QSGListView);
+ // create object
+ requestedIndex = modelIndex;
+ FxListItemSG *listItem = 0;
+ if (QSGItem *item = model->item(modelIndex, false)) {
+ listItem = new FxListItemSG(item, q);
+ listItem->index = modelIndex;
+ // initialise attached properties
+ if (sectionCriteria) {
+ QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
+ listItem->attached->m_section = sectionCriteria->sectionString(propValue);
+ if (modelIndex > 0) {
+ if (FxListItemSG *item = itemBefore(modelIndex))
+ listItem->attached->m_prevSection = item->attached->section();
+ else
+ listItem->attached->m_prevSection = sectionAt(modelIndex-1);
+ }
+ if (modelIndex < model->count()-1) {
+ if (FxListItemSG *item = visibleItem(modelIndex+1))
+ listItem->attached->m_nextSection = item->attached->section();
+ else
+ listItem->attached->m_nextSection = sectionAt(modelIndex+1);
+ }
+ }
+ if (model->completePending()) {
+ // complete
+ listItem->item->setZ(1);
+ listItem->item->setParentItem(q->contentItem());
+ model->completeItem();
+ } else {
+ listItem->item->setParentItem(q->contentItem());
+ }
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ if (sectionCriteria && sectionCriteria->delegate()) {
+ if (listItem->attached->m_prevSection != listItem->attached->m_section)
+ createSection(listItem);
+ }
+ unrequestedItems.remove(listItem->item);
+ }
+ requestedIndex = -1;
+
+ return listItem;
+}
+
+void QSGListViewPrivate::releaseItem(FxListItemSG *item)
+{
+ Q_Q(QSGListView);
+ if (!item || !model)
+ return;
+ if (trackedItem == item)
+ trackedItem = 0;
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
+ itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
+ if (model->release(item->item) == 0) {
+ // item was not destroyed, and we no longer reference it.
+ unrequestedItems.insert(item->item, model->indexOf(item->item, q));
+ }
+ if (item->section) {
+ int i = 0;
+ do {
+ if (!sectionCache[i]) {
+ sectionCache[i] = item->section;
+ sectionCache[i]->setVisible(false);
+ item->section = 0;
+ break;
+ }
+ ++i;
+ } while (i < sectionCacheSize);
+ delete item->section;
+ }
+ delete item;
+}
+
+void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
+{
+ Q_Q(QSGListView);
+ if (!isValid() || !q->isComponentComplete())
+ return;
+ itemCount = model->count();
+ qreal bufferFrom = from - buffer;
+ qreal bufferTo = to + buffer;
+ qreal fillFrom = from;
+ qreal fillTo = to;
+ if (doBuffer && (bufferMode & BufferAfter))
+ fillTo = bufferTo;
+ if (doBuffer && (bufferMode & BufferBefore))
+ fillFrom = bufferFrom;
+
+ bool haveValidItems = false;
+ int modelIndex = visibleIndex;
+ qreal itemEnd = visiblePos-1;
+ if (!visibleItems.isEmpty()) {
+ visiblePos = (*visibleItems.constBegin())->position();
+ itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
+ int i = visibleItems.count() - 1;
+ while (i > 0 && visibleItems.at(i)->index == -1)
+ --i;
+ if (visibleItems.at(i)->index != -1) {
+ haveValidItems = true;
+ modelIndex = visibleItems.at(i)->index + 1;
+ }
+ }
+
+ if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
+ || fillTo < visiblePos - averageSize - spacing)) {
+ // We've jumped more than a page. Estimate which items are now
+ // visible and fill from there.
+ int count = (fillFrom - itemEnd) / (averageSize + spacing);
+ for (int i = 0; i < visibleItems.count(); ++i)
+ releaseItem(visibleItems.at(i));
+ visibleItems.clear();
+ modelIndex += count;
+ if (modelIndex >= model->count()) {
+ count -= modelIndex - model->count() + 1;
+ modelIndex = model->count() - 1;
+ } else if (modelIndex < 0) {
+ count -= modelIndex;
+ modelIndex = 0;
+ }
+ visibleIndex = modelIndex;
+ visiblePos = itemEnd + count * (averageSize + spacing) + 1;
+ itemEnd = visiblePos-1;
+ }
+
+ bool changed = false;
+ FxListItemSG *item = 0;
+ qreal pos = itemEnd + 1;
+ while (modelIndex < model->count() && pos <= fillTo) {
+// qDebug() << "refill: append item" << modelIndex << "pos" << pos;
+ if (!(item = createItem(modelIndex)))
+ break;
+ item->setPosition(pos);
+ pos += item->size() + spacing;
+ visibleItems.append(item);
+ ++modelIndex;
+ changed = true;
+ if (doBuffer) // never buffer more than one item per frame
+ break;
+ }
+ while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
+// qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
+ if (!(item = createItem(visibleIndex-1)))
+ break;
+ --visibleIndex;
+ visiblePos -= item->size() + spacing;
+ item->setPosition(visiblePos);
+ visibleItems.prepend(item);
+ changed = true;
+ if (doBuffer) // never buffer more than one item per frame
+ break;
+ }
+
+ if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
+ while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
+ if (item->attached->delayRemove())
+ break;
+// qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
+ if (item->index != -1)
+ visibleIndex++;
+ visibleItems.removeFirst();
+ releaseItem(item);
+ changed = true;
+ }
+ while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
+ if (item->attached->delayRemove())
+ break;
+// qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
+ visibleItems.removeLast();
+ releaseItem(item);
+ changed = true;
+ }
+ deferredRelease = false;
+ } else {
+ deferredRelease = true;
+ }
+ if (changed) {
+ minExtentDirty = true;
+ maxExtentDirty = true;
+ if (visibleItems.count())
+ visiblePos = (*visibleItems.constBegin())->position();
+ updateAverage();
+ if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
+ currentItem->setPosition(positionAt(currentIndex));
+ updateHighlight();
+ }
+
+ if (sectionCriteria)
+ updateCurrentSection();
+ if (header)
+ updateHeader();
+ if (footer)
+ updateFooter();
+ updateViewport();
+ updateUnrequestedPositions();
+ } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
+ refill(from, to, true);
+ }
+ lazyRelease = false;
+}
+
+void QSGListViewPrivate::scheduleLayout()
+{
+ Q_Q(QSGListView);
+ if (!layoutScheduled) {
+ layoutScheduled = true;
+ q->polish();
+ }
+}
+
+void QSGListViewPrivate::layout()
+{
+ Q_Q(QSGListView);
+ layoutScheduled = false;
+ if (!isValid() && !visibleItems.count()) {
+ clear();
+ setPosition(0);
+ return;
+ }
+ if (!visibleItems.isEmpty()) {
+ bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
+ qreal sum = visibleItems.first()->size();
+ qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
+ for (int i=1; i < visibleItems.count(); ++i) {
+ FxListItemSG *item = visibleItems.at(i);
+ item->setPosition(pos);
+ pos += item->size() + spacing;
+ sum += item->size();
+ fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
+ }
+ averageSize = qRound(sum / visibleItems.count());
+ // move current item if it is not a visible item.
+ if (currentIndex >= 0 && currentItem && !fixedCurrent)
+ currentItem->setPosition(positionAt(currentIndex));
+ }
+ q->refill();
+ minExtentDirty = true;
+ maxExtentDirty = true;
+ updateHighlight();
+ if (!q->isMoving() && !q->isFlicking()) {
+ fixupPosition();
+ q->refill();
+ }
+ if (header)
+ updateHeader();
+ if (footer)
+ updateFooter();
+ updateViewport();
+}
+
+void QSGListViewPrivate::updateUnrequestedIndexes()
+{
+ Q_Q(QSGListView);
+ QHash<QSGItem*,int>::iterator it;
+ for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
+ *it = model->indexOf(it.key(), q);
+}
+
+void QSGListViewPrivate::updateUnrequestedPositions()
+{
+ Q_Q(QSGListView);
+ if (unrequestedItems.count()) {
+ qreal pos = position();
+ QHash<QSGItem*,int>::const_iterator it;
+ for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
+ QSGItem *item = it.key();
+ if (orient == QSGListView::Vertical) {
+ if (item->y() + item->height() > pos && item->y() < pos + q->height())
+ item->setY(positionAt(*it));
+ } else {
+ if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
+ if (isRightToLeft())
+ item->setX(-positionAt(*it)-item->width());
+ else
+ item->setX(positionAt(*it));
+ }
+ }
+ }
+ }
+}
+
+void QSGListViewPrivate::updateTrackedItem()
+{
+ Q_Q(QSGListView);
+ FxListItemSG *item = currentItem;
+ if (highlight)
+ item = highlight;
+ trackedItem = item;
+ if (trackedItem)
+ q->trackedPositionChanged();
+}
+
+void QSGListViewPrivate::createHighlight()
+{
+ Q_Q(QSGListView);
+ bool changed = false;
+ if (highlight) {
+ if (trackedItem == highlight)
+ trackedItem = 0;
+ highlight->item->setParentItem(0);
+ highlight->item->deleteLater();
+ delete highlight;
+ highlight = 0;
+ delete highlightPosAnimator;
+ delete highlightSizeAnimator;
+ highlightPosAnimator = 0;
+ highlightSizeAnimator = 0;
+ changed = true;
+ }
+
+ if (currentItem) {
+ QSGItem *item = 0;
+ if (highlightComponent) {
+ QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = highlightComponent->create(highlightContext);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(highlightContext, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete highlightContext;
+ }
+ } else {
+ item = new QSGItem;
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ highlight = new FxListItemSG(item, q);
+ if (currentItem && autoHighlight) {
+ if (orient == QSGListView::Vertical) {
+ highlight->item->setHeight(currentItem->item->height());
+ } else {
+ highlight->item->setWidth(currentItem->item->width());
+ }
+ highlight->setPosition(currentItem->itemPosition());
+ }
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x");
+ highlightPosAnimator = new QSmoothedAnimation(q);
+ highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
+ highlightPosAnimator->velocity = highlightMoveSpeed;
+ highlightPosAnimator->userDuration = highlightMoveDuration;
+ const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width");
+ highlightSizeAnimator = new QSmoothedAnimation(q);
+ highlightSizeAnimator->velocity = highlightResizeSpeed;
+ highlightSizeAnimator->userDuration = highlightResizeDuration;
+ highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
+ if (autoHighlight) {
+ highlightPosAnimator->restart();
+ highlightSizeAnimator->restart();
+ }
+ changed = true;
+ }
+ }
+ if (changed)
+ emit q->highlightItemChanged();
+}
+
+void QSGListViewPrivate::updateHighlight()
+{
+ if ((!currentItem && highlight) || (currentItem && !highlight))
+ createHighlight();
+ if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
+ // auto-update highlight
+ highlightPosAnimator->to = isRightToLeft()
+ ? -currentItem->itemPosition()-currentItem->itemSize()
+ : currentItem->itemPosition();
+ highlightSizeAnimator->to = currentItem->itemSize();
+ if (orient == QSGListView::Vertical) {
+ if (highlight->item->width() == 0)
+ highlight->item->setWidth(currentItem->item->width());
+ } else {
+ if (highlight->item->height() == 0)
+ highlight->item->setHeight(currentItem->item->height());
+ }
+ highlightPosAnimator->restart();
+ highlightSizeAnimator->restart();
+ }
+ updateTrackedItem();
+}
+
+void QSGListViewPrivate::createSection(FxListItemSG *listItem)
+{
+ Q_Q(QSGListView);
+ if (!sectionCriteria || !sectionCriteria->delegate())
+ return;
+ if (listItem->attached->m_prevSection != listItem->attached->m_section) {
+ if (!listItem->section) {
+ qreal pos = listItem->position();
+ int i = sectionCacheSize-1;
+ while (i >= 0 && !sectionCache[i])
+ --i;
+ if (i >= 0) {
+ listItem->section = sectionCache[i];
+ sectionCache[i] = 0;
+ listItem->section->setVisible(true);
+ QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
+ context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
+ } else {
+ QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
+ context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
+ QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(context, nobj);
+ listItem->section = qobject_cast<QSGItem *>(nobj);
+ if (!listItem->section) {
+ delete nobj;
+ } else {
+ listItem->section->setZ(1);
+ QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
+ listItem->section->setParentItem(q->contentItem());
+ }
+ } else {
+ delete context;
+ }
+ sectionCriteria->delegate()->completeCreate();
+ }
+ listItem->setPosition(pos);
+ } else {
+ QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
+ context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
+ }
+ } else if (listItem->section) {
+ qreal pos = listItem->position();
+ int i = 0;
+ do {
+ if (!sectionCache[i]) {
+ sectionCache[i] = listItem->section;
+ sectionCache[i]->setVisible(false);
+ listItem->section = 0;
+ return;
+ }
+ ++i;
+ } while (i < sectionCacheSize);
+ delete listItem->section;
+ listItem->section = 0;
+ listItem->setPosition(pos);
+ }
+}
+
+void QSGListViewPrivate::updateSections()
+{
+ if (sectionCriteria && !visibleItems.isEmpty()) {
+ QString prevSection;
+ if (visibleIndex > 0)
+ prevSection = sectionAt(visibleIndex-1);
+ QSGListViewAttached *prevAtt = 0;
+ int idx = -1;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ QSGListViewAttached *attached = visibleItems.at(i)->attached;
+ attached->setPrevSection(prevSection);
+ if (visibleItems.at(i)->index != -1) {
+ QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
+ attached->setSection(sectionCriteria->sectionString(propValue));
+ idx = visibleItems.at(i)->index;
+ }
+ createSection(visibleItems.at(i));
+ if (prevAtt)
+ prevAtt->setNextSection(attached->section());
+ prevSection = attached->section();
+ prevAtt = attached;
+ }
+ if (prevAtt) {
+ if (idx > 0 && idx < model->count()-1)
+ prevAtt->setNextSection(sectionAt(idx+1));
+ else
+ prevAtt->setNextSection(QString());
+ }
+ }
+}
+
+void QSGListViewPrivate::updateCurrentSection()
+{
+ Q_Q(QSGListView);
+ if (!sectionCriteria || visibleItems.isEmpty()) {
+ if (!currentSection.isEmpty()) {
+ currentSection.clear();
+ emit q->currentSectionChanged();
+ }
+ return;
+ }
+ int index = 0;
+ while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
+ ++index;
+
+ QString newSection = currentSection;
+ if (index < visibleItems.count())
+ newSection = visibleItems.at(index)->attached->section();
+ else
+ newSection = visibleItems.first()->attached->section();
+ if (newSection != currentSection) {
+ currentSection = newSection;
+ emit q->currentSectionChanged();
+ }
+}
+
+void QSGListViewPrivate::updateCurrent(int modelIndex)
+{
+ Q_Q(QSGListView);
+ if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
+ if (currentItem) {
+ currentItem->attached->setIsCurrentItem(false);
+ releaseItem(currentItem);
+ currentItem = 0;
+ currentIndex = modelIndex;
+ emit q->currentIndexChanged();
+ updateHighlight();
+ } else if (currentIndex != modelIndex) {
+ currentIndex = modelIndex;
+ emit q->currentIndexChanged();
+ }
+ return;
+ }
+
+ if (currentItem && currentIndex == modelIndex) {
+ updateHighlight();
+ return;
+ }
+ FxListItemSG *oldCurrentItem = currentItem;
+ currentIndex = modelIndex;
+ currentItem = createItem(modelIndex);
+ if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
+ oldCurrentItem->attached->setIsCurrentItem(false);
+ if (currentItem) {
+ if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
+ // We can calculate exact postion in this case
+ currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
+ } else {
+ // Create current item now and position as best we can.
+ // Its position will be corrected when it becomes visible.
+ currentItem->setPosition(positionAt(modelIndex));
+ }
+ currentItem->item->setFocus(true);
+ currentItem->attached->setIsCurrentItem(true);
+ // Avoid showing section delegate twice. We still need the section heading so that
+ // currentItem positioning works correctly.
+ // This is slightly sub-optimal, but section heading caching minimizes the impact.
+ if (currentItem->section)
+ currentItem->section->setVisible(false);
+ if (visibleItems.isEmpty())
+ averageSize = currentItem->size();
+ }
+ updateHighlight();
+ emit q->currentIndexChanged();
+ // Release the old current item
+ releaseItem(oldCurrentItem);
+}
+
+void QSGListViewPrivate::updateAverage()
+{
+ if (!visibleItems.count())
+ return;
+ qreal sum = 0.0;
+ for (int i = 0; i < visibleItems.count(); ++i)
+ sum += visibleItems.at(i)->size();
+ averageSize = qRound(sum / visibleItems.count());
+}
+
+void QSGListViewPrivate::updateFooter()
+{
+ Q_Q(QSGListView);
+ if (!footer && footerComponent) {
+ QSGItem *item = 0;
+ QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = footerComponent->create(context);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(context, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete context;
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ item->setZ(1);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ footer = new FxListItemSG(item, q);
+ }
+ }
+ if (footer) {
+ if (visibleItems.count()) {
+ qreal endPos = lastPosition() + 1;
+ if (lastVisibleIndex() == model->count()-1) {
+ footer->setPosition(endPos);
+ } else {
+ qreal visiblePos = position() + q->height();
+ if (endPos <= visiblePos || footer->position() < endPos)
+ footer->setPosition(endPos);
+ }
+ } else {
+ footer->setPosition(visiblePos);
+ }
+ }
+}
+
+void QSGListViewPrivate::updateHeader()
+{
+ Q_Q(QSGListView);
+ if (!header && headerComponent) {
+ QSGItem *item = 0;
+ QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = headerComponent->create(context);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(context, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete context;
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q->contentItem());
+ item->setParentItem(q->contentItem());
+ item->setZ(1);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ header = new FxListItemSG(item, q);
+ }
+ }
+ if (header) {
+ if (visibleItems.count()) {
+ qreal startPos = originPosition();
+ if (visibleIndex == 0) {
+ header->setPosition(startPos - header->size());
+ } else {
+ if (position() <= startPos || header->position() > startPos - header->size())
+ header->setPosition(startPos - header->size());
+ }
+ } else {
+ if (itemCount == 0)
+ visiblePos = header->size();
+ header->setPosition(0);
+ }
+ }
+}
+
+void QSGListViewPrivate::fixupPosition()
+{
+ if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange)
+ || snapMode != QSGListView::NoSnap)
+ moveReason = Other;
+ if (orient == QSGListView::Vertical)
+ fixupY();
+ else
+ fixupX();
+}
+
+void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
+{
+ if ((orient == QSGListView::Horizontal && &data == &vData)
+ || (orient == QSGListView::Vertical && &data == &hData))
+ return;
+
+ correctFlick = false;
+ fixupMode = moveReason == Mouse ? fixupMode : Immediate;
+
+ qreal highlightStart;
+ qreal highlightEnd;
+ qreal viewPos;
+ if (isRightToLeft()) {
+ // Handle Right-To-Left exceptions
+ viewPos = -position()-size();
+ highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
+ highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
+ } else {
+ viewPos = position();
+ highlightStart = highlightRangeStart;
+ highlightEnd = highlightRangeEnd;
+ }
+
+ if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange
+ && moveReason != QSGListViewPrivate::SetIndex) {
+ updateHighlight();
+ qreal pos = currentItem->itemPosition();
+ if (viewPos < pos + currentItem->itemSize() - highlightEnd)
+ viewPos = pos + currentItem->itemSize() - highlightEnd;
+ if (viewPos > pos - highlightStart)
+ viewPos = pos - highlightStart;
+ if (isRightToLeft())
+ viewPos = -viewPos-size();
+
+ timeline.reset(data.move);
+ if (viewPos != position()) {
+ if (fixupMode != Immediate) {
+ timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
+ data.fixingUp = true;
+ } else {
+ timeline.set(data.move, -viewPos);
+ }
+ }
+ vTime = timeline.time();
+ } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) {
+ qreal tempPosition = isRightToLeft() ? -position()-size() : position();
+ FxListItemSG *topItem = snapItemAt(tempPosition+highlightStart);
+ FxListItemSG *bottomItem = snapItemAt(tempPosition+highlightEnd);
+ qreal pos;
+ bool isInBounds = -position() > maxExtent && -position() < minExtent;
+ if (topItem && isInBounds) {
+ if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2) {
+ pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
+ } else {
+ if (isRightToLeft())
+ pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
+ else
+ pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
+ }
+ } else if (bottomItem && isInBounds) {
+ if (isRightToLeft())
+ pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
+ else
+ pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
+ } else {
+ QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
+ return;
+ }
+
+ qreal dist = qAbs(data.move + pos);
+ if (dist > 0) {
+ timeline.reset(data.move);
+ if (fixupMode != Immediate) {
+ timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
+ data.fixingUp = true;
+ } else {
+ timeline.set(data.move, -pos);
+ }
+ vTime = timeline.time();
+ }
+ } else {
+ QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
+ }
+ data.inOvershoot = false;
+ fixupMode = Normal;
+}
+
+void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
+ QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
+{
+ Q_Q(QSGListView);
+
+ data.fixingUp = false;
+ moveReason = Mouse;
+ if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) {
+ correctFlick = true;
+ QSGFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
+ return;
+ }
+ qreal maxDistance = 0;
+ qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
+ // -ve velocity means list is moving up/left
+ if (velocity > 0) {
+ if (data.move.value() < minExtent) {
+ if (snapMode == QSGListView::SnapOneItem) {
+ if (FxListItemSG *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
+ maxDistance = qAbs(item->position() + dataValue);
+ } else {
+ maxDistance = qAbs(minExtent - data.move.value());
+ }
+ }
+ if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
+ data.flickTarget = minExtent;
+ } else {
+ if (data.move.value() > maxExtent) {
+ if (snapMode == QSGListView::SnapOneItem) {
+ if (FxListItemSG *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
+ maxDistance = qAbs(item->position() + dataValue);
+ } else {
+ maxDistance = qAbs(maxExtent - data.move.value());
+ }
+ }
+ if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
+ data.flickTarget = maxExtent;
+ }
+ bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
+ qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
+ if (maxDistance > 0 || overShoot) {
+ // These modes require the list to stop exactly on an item boundary.
+ // The initial flick will estimate the boundary to stop on.
+ // Since list items can have variable sizes, the boundary will be
+ // reevaluated and adjusted as we approach the boundary.
+ qreal v = velocity;
+ if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
+ if (v < 0)
+ v = -maxVelocity;
+ else
+ v = maxVelocity;
+ }
+ if (!flickingHorizontally && !flickingVertically) {
+ // the initial flick - estimate boundary
+ qreal accel = deceleration;
+ qreal v2 = v * v;
+ overshootDist = 0.0;
+ // + averageSize/4 to encourage moving at least one item in the flick direction
+ qreal dist = v2 / (accel * 2.0) + averageSize/4;
+ if (maxDistance > 0)
+ dist = qMin(dist, maxDistance);
+ if (v > 0)
+ dist = -dist;
+ if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) {
+ qreal distTemp = isRightToLeft() ? -dist : dist;
+ data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
+ data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
+ if (overShoot) {
+ if (data.flickTarget >= minExtent) {
+ overshootDist = overShootDistance(vSize);
+ data.flickTarget += overshootDist;
+ } else if (data.flickTarget <= maxExtent) {
+ overshootDist = overShootDistance(vSize);
+ data.flickTarget -= overshootDist;
+ }
+ }
+ qreal adjDist = -data.flickTarget + data.move.value();
+ if (qAbs(adjDist) > qAbs(dist)) {
+ // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
+ qreal adjv2 = accel * 2.0f * qAbs(adjDist);
+ if (adjv2 > v2) {
+ v2 = adjv2;
+ v = qSqrt(v2);
+ if (dist > 0)
+ v = -v;
+ }
+ }
+ dist = adjDist;
+ accel = v2 / (2.0f * qAbs(dist));
+ } else if (overShoot) {
+ data.flickTarget = data.move.value() - dist;
+ if (data.flickTarget >= minExtent) {
+ overshootDist = overShootDistance(vSize);
+ data.flickTarget += overshootDist;
+ } else if (data.flickTarget <= maxExtent) {
+ overshootDist = overShootDistance(vSize);
+ data.flickTarget -= overshootDist;
+ }
+ }
+ timeline.reset(data.move);
+ timeline.accel(data.move, v, accel, maxDistance + overshootDist);
+ timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
+ if (!flickingHorizontally && q->xflick()) {
+ flickingHorizontally = true;
+ emit q->flickingChanged();
+ emit q->flickingHorizontallyChanged();
+ emit q->flickStarted();
+ }
+ if (!flickingVertically && q->yflick()) {
+ flickingVertically = true;
+ emit q->flickingChanged();
+ emit q->flickingVerticallyChanged();
+ emit q->flickStarted();
+ }
+ correctFlick = true;
+ } else {
+ // reevaluate the target boundary.
+ qreal newtarget = data.flickTarget;
+ if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) {
+ qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
+ newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
+ newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
+ }
+ if (velocity < 0 && newtarget <= maxExtent)
+ newtarget = maxExtent - overshootDist;
+ else if (velocity > 0 && newtarget >= minExtent)
+ newtarget = minExtent + overshootDist;
+ if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
+ if (qAbs(velocity) < MinimumFlickVelocity)
+ correctFlick = false;
+ return;
+ }
+ data.flickTarget = newtarget;
+ qreal dist = -newtarget + data.move.value();
+ if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
+ correctFlick = false;
+ timeline.reset(data.move);
+ fixup(data, minExtent, maxExtent);
+ return;
+ }
+ timeline.reset(data.move);
+ timeline.accelDistance(data.move, v, -dist);
+ timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
+ }
+ } else {
+ correctFlick = false;
+ timeline.reset(data.move);
+ fixup(data, minExtent, maxExtent);
+ }
+}
+
+//----------------------------------------------------------------------------
+
+QSGListView::QSGListView(QSGItem *parent)
+ : QSGFlickable(*(new QSGListViewPrivate), parent)
+{
+ Q_D(QSGListView);
+ d->init();
+}
+
+QSGListView::~QSGListView()
+{
+ Q_D(QSGListView);
+ d->clear();
+ if (d->ownModel)
+ delete d->model;
+ delete d->header;
+ delete d->footer;
+}
+
+QVariant QSGListView::model() const
+{
+ Q_D(const QSGListView);
+ return d->modelVariant;
+}
+
+void QSGListView::setModel(const QVariant &model)
+{
+ Q_D(QSGListView);
+ if (d->modelVariant == model)
+ return;
+ if (d->model) {
+ disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
+ }
+ d->clear();
+ QSGVisualModel *oldModel = d->model;
+ d->model = 0;
+ d->setPosition(0);
+ d->modelVariant = model;
+ QObject *object = qvariant_cast<QObject*>(model);
+ QSGVisualModel *vim = 0;
+ if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
+ if (d->ownModel) {
+ delete oldModel;
+ d->ownModel = false;
+ }
+ d->model = vim;
+ } else {
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this), this);
+ d->ownModel = true;
+ } else {
+ d->model = oldModel;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ dataModel->setModel(model);
+ }
+ if (d->model) {
+ d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter;
+ if (isComponentComplete()) {
+ updateSections();
+ refill();
+ if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
+ setCurrentIndex(0);
+ } else {
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->position());
+ d->updateTrackedItem();
+ }
+ }
+ d->updateViewport();
+ }
+ connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
+ emit countChanged();
+ }
+ emit modelChanged();
+}
+
+QDeclarativeComponent *QSGListView::delegate() const
+{
+ Q_D(const QSGListView);
+ if (d->model) {
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ return dataModel->delegate();
+ }
+
+ return 0;
+}
+
+void QSGListView::setDelegate(QDeclarativeComponent *delegate)
+{
+ Q_D(QSGListView);
+ if (delegate == this->delegate())
+ return;
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this));
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
+ int oldCount = dataModel->count();
+ dataModel->setDelegate(delegate);
+ if (isComponentComplete()) {
+ for (int i = 0; i < d->visibleItems.count(); ++i)
+ d->releaseItem(d->visibleItems.at(i));
+ d->visibleItems.clear();
+ d->releaseItem(d->currentItem);
+ d->currentItem = 0;
+ updateSections();
+ refill();
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->position());
+ d->updateTrackedItem();
+ }
+ d->updateViewport();
+ }
+ if (oldCount != dataModel->count())
+ emit countChanged();
+ }
+ emit delegateChanged();
+}
+
+int QSGListView::currentIndex() const
+{
+ Q_D(const QSGListView);
+ return d->currentIndex;
+}
+
+void QSGListView::setCurrentIndex(int index)
+{
+ Q_D(QSGListView);
+ if (d->requestedIndex >= 0) // currently creating item
+ return;
+ d->currentIndexCleared = (index == -1);
+ if (index == d->currentIndex)
+ return;
+ if (isComponentComplete() && d->isValid()) {
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ d->updateCurrent(index);
+ } else if (d->currentIndex != index) {
+ d->currentIndex = index;
+ emit currentIndexChanged();
+ }
+}
+
+QSGItem *QSGListView::currentItem()
+{
+ Q_D(QSGListView);
+ if (!d->currentItem)
+ return 0;
+ return d->currentItem->item;
+}
+
+QSGItem *QSGListView::highlightItem()
+{
+ Q_D(QSGListView);
+ if (!d->highlight)
+ return 0;
+ return d->highlight->item;
+}
+
+int QSGListView::count() const
+{
+ Q_D(const QSGListView);
+ if (d->model)
+ return d->model->count();
+ return 0;
+}
+
+QDeclarativeComponent *QSGListView::highlight() const
+{
+ Q_D(const QSGListView);
+ return d->highlightComponent;
+}
+
+void QSGListView::setHighlight(QDeclarativeComponent *highlight)
+{
+ Q_D(QSGListView);
+ if (highlight != d->highlightComponent) {
+ d->highlightComponent = highlight;
+ d->createHighlight();
+ if (d->currentItem)
+ d->updateHighlight();
+ emit highlightChanged();
+ }
+}
+
+bool QSGListView::highlightFollowsCurrentItem() const
+{
+ Q_D(const QSGListView);
+ return d->autoHighlight;
+}
+
+void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
+{
+ Q_D(QSGListView);
+ if (d->autoHighlight != autoHighlight) {
+ d->autoHighlight = autoHighlight;
+ if (autoHighlight) {
+ d->updateHighlight();
+ } else {
+ if (d->highlightPosAnimator)
+ d->highlightPosAnimator->stop();
+ if (d->highlightSizeAnimator)
+ d->highlightSizeAnimator->stop();
+ }
+ emit highlightFollowsCurrentItemChanged();
+ }
+}
+
+//###Possibly rename these properties, since they are very useful even without a highlight?
+qreal QSGListView::preferredHighlightBegin() const
+{
+ Q_D(const QSGListView);
+ return d->highlightRangeStart;
+}
+
+void QSGListView::setPreferredHighlightBegin(qreal start)
+{
+ Q_D(QSGListView);
+ d->highlightRangeStartValid = true;
+ if (d->highlightRangeStart == start)
+ return;
+ d->highlightRangeStart = start;
+ d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit preferredHighlightBeginChanged();
+}
+
+void QSGListView::resetPreferredHighlightBegin()
+{
+ Q_D(QSGListView);
+ d->highlightRangeStartValid = false;
+ if (d->highlightRangeStart == 0)
+ return;
+ d->highlightRangeStart = 0;
+ emit preferredHighlightBeginChanged();
+}
+
+qreal QSGListView::preferredHighlightEnd() const
+{
+ Q_D(const QSGListView);
+ return d->highlightRangeEnd;
+}
+
+void QSGListView::setPreferredHighlightEnd(qreal end)
+{
+ Q_D(QSGListView);
+ d->highlightRangeEndValid = true;
+ if (d->highlightRangeEnd == end)
+ return;
+ d->highlightRangeEnd = end;
+ d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit preferredHighlightEndChanged();
+}
+
+void QSGListView::resetPreferredHighlightEnd()
+{
+ Q_D(QSGListView);
+ d->highlightRangeEndValid = false;
+ if (d->highlightRangeEnd == 0)
+ return;
+ d->highlightRangeEnd = 0;
+ emit preferredHighlightEndChanged();
+}
+
+QSGListView::HighlightRangeMode QSGListView::highlightRangeMode() const
+{
+ Q_D(const QSGListView);
+ return d->highlightRange;
+}
+
+void QSGListView::setHighlightRangeMode(HighlightRangeMode mode)
+{
+ Q_D(QSGListView);
+ if (d->highlightRange == mode)
+ return;
+ d->highlightRange = mode;
+ d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit highlightRangeModeChanged();
+}
+
+qreal QSGListView::spacing() const
+{
+ Q_D(const QSGListView);
+ return d->spacing;
+}
+
+void QSGListView::setSpacing(qreal spacing)
+{
+ Q_D(QSGListView);
+ if (spacing != d->spacing) {
+ d->spacing = spacing;
+ d->layout();
+ emit spacingChanged();
+ }
+}
+
+QSGListView::Orientation QSGListView::orientation() const
+{
+ Q_D(const QSGListView);
+ return d->orient;
+}
+
+void QSGListView::setOrientation(QSGListView::Orientation orientation)
+{
+ Q_D(QSGListView);
+ if (d->orient != orientation) {
+ d->orient = orientation;
+ if (d->orient == QSGListView::Vertical) {
+ setContentWidth(-1);
+ setFlickableDirection(VerticalFlick);
+ setContentX(0);
+ } else {
+ setContentHeight(-1);
+ setFlickableDirection(HorizontalFlick);
+ setContentY(0);
+ }
+ d->regenerate();
+ emit orientationChanged();
+ }
+}
+
+Qt::LayoutDirection QSGListView::layoutDirection() const
+{
+ Q_D(const QSGListView);
+ return d->layoutDirection;
+}
+
+void QSGListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
+{
+ Q_D(QSGListView);
+ if (d->layoutDirection != layoutDirection) {
+ d->layoutDirection = layoutDirection;
+ d->regenerate();
+ emit layoutDirectionChanged();
+ emit effectiveLayoutDirectionChanged();
+ }
+}
+
+Qt::LayoutDirection QSGListView::effectiveLayoutDirection() const
+{
+ Q_D(const QSGListView);
+ if (d->effectiveLayoutMirror)
+ return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
+ else
+ return d->layoutDirection;
+}
+
+bool QSGListView::isWrapEnabled() const
+{
+ Q_D(const QSGListView);
+ return d->wrap;
+}
+
+void QSGListView::setWrapEnabled(bool wrap)
+{
+ Q_D(QSGListView);
+ if (d->wrap == wrap)
+ return;
+ d->wrap = wrap;
+ emit keyNavigationWrapsChanged();
+}
+
+int QSGListView::cacheBuffer() const
+{
+ Q_D(const QSGListView);
+ return d->buffer;
+}
+
+void QSGListView::setCacheBuffer(int b)
+{
+ Q_D(QSGListView);
+ if (d->buffer != b) {
+ d->buffer = b;
+ if (isComponentComplete()) {
+ d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter;
+ refill();
+ }
+ emit cacheBufferChanged();
+ }
+}
+
+QSGViewSection *QSGListView::sectionCriteria()
+{
+ Q_D(QSGListView);
+ if (!d->sectionCriteria) {
+ d->sectionCriteria = new QSGViewSection(this);
+ connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
+ }
+ return d->sectionCriteria;
+}
+
+QString QSGListView::currentSection() const
+{
+ Q_D(const QSGListView);
+ return d->currentSection;
+}
+
+qreal QSGListView::highlightMoveSpeed() const
+{
+ Q_D(const QSGListView);\
+ return d->highlightMoveSpeed;
+}
+
+void QSGListView::setHighlightMoveSpeed(qreal speed)
+{
+ Q_D(QSGListView);\
+ if (d->highlightMoveSpeed != speed) {
+ d->highlightMoveSpeed = speed;
+ if (d->highlightPosAnimator)
+ d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
+ emit highlightMoveSpeedChanged();
+ }
+}
+
+int QSGListView::highlightMoveDuration() const
+{
+ Q_D(const QSGListView);
+ return d->highlightMoveDuration;
+}
+
+void QSGListView::setHighlightMoveDuration(int duration)
+{
+ Q_D(QSGListView);\
+ if (d->highlightMoveDuration != duration) {
+ d->highlightMoveDuration = duration;
+ if (d->highlightPosAnimator)
+ d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
+ emit highlightMoveDurationChanged();
+ }
+}
+
+qreal QSGListView::highlightResizeSpeed() const
+{
+ Q_D(const QSGListView);\
+ return d->highlightResizeSpeed;
+}
+
+void QSGListView::setHighlightResizeSpeed(qreal speed)
+{
+ Q_D(QSGListView);\
+ if (d->highlightResizeSpeed != speed) {
+ d->highlightResizeSpeed = speed;
+ if (d->highlightSizeAnimator)
+ d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
+ emit highlightResizeSpeedChanged();
+ }
+}
+
+int QSGListView::highlightResizeDuration() const
+{
+ Q_D(const QSGListView);
+ return d->highlightResizeDuration;
+}
+
+void QSGListView::setHighlightResizeDuration(int duration)
+{
+ Q_D(QSGListView);\
+ if (d->highlightResizeDuration != duration) {
+ d->highlightResizeDuration = duration;
+ if (d->highlightSizeAnimator)
+ d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
+ emit highlightResizeDurationChanged();
+ }
+}
+
+QSGListView::SnapMode QSGListView::snapMode() const
+{
+ Q_D(const QSGListView);
+ return d->snapMode;
+}
+
+void QSGListView::setSnapMode(SnapMode mode)
+{
+ Q_D(QSGListView);
+ if (d->snapMode != mode) {
+ d->snapMode = mode;
+ emit snapModeChanged();
+ }
+}
+
+QDeclarativeComponent *QSGListView::footer() const
+{
+ Q_D(const QSGListView);
+ return d->footerComponent;
+}
+
+void QSGListView::setFooter(QDeclarativeComponent *footer)
+{
+ Q_D(QSGListView);
+ if (d->footerComponent != footer) {
+ if (d->footer) {
+ // XXX todo - the original did scene()->removeItem(). Why?
+ d->footer->item->setParentItem(0);
+ d->footer->item->deleteLater();
+ delete d->footer;
+ d->footer = 0;
+ }
+ d->footerComponent = footer;
+ d->minExtentDirty = true;
+ d->maxExtentDirty = true;
+ if (isComponentComplete()) {
+ d->updateFooter();
+ d->updateViewport();
+ d->fixupPosition();
+ }
+ emit footerChanged();
+ }
+}
+
+QDeclarativeComponent *QSGListView::header() const
+{
+ Q_D(const QSGListView);
+ return d->headerComponent;
+}
+
+void QSGListView::setHeader(QDeclarativeComponent *header)
+{
+ Q_D(QSGListView);
+ if (d->headerComponent != header) {
+ if (d->header) {
+ // XXX todo - the original did scene()->removeItem(). Why?
+ d->header->item->setParentItem(0);
+ d->header->item->deleteLater();
+ delete d->header;
+ d->header = 0;
+ }
+ d->headerComponent = header;
+ d->minExtentDirty = true;
+ d->maxExtentDirty = true;
+ if (isComponentComplete()) {
+ d->updateHeader();
+ d->updateFooter();
+ d->updateViewport();
+ d->fixupPosition();
+ }
+ emit headerChanged();
+ }
+}
+
+void QSGListView::setContentX(qreal pos)
+{
+ Q_D(QSGListView);
+ // Positioning the view manually should override any current movement state
+ d->moveReason = QSGListViewPrivate::Other;
+ QSGFlickable::setContentX(pos);
+}
+
+void QSGListView::setContentY(qreal pos)
+{
+ Q_D(QSGListView);
+ // Positioning the view manually should override any current movement state
+ d->moveReason = QSGListViewPrivate::Other;
+ QSGFlickable::setContentY(pos);
+}
+
+void QSGListView::updatePolish()
+{
+ Q_D(QSGListView);
+ QSGFlickable::updatePolish();
+ d->layout();
+}
+
+void QSGListView::viewportMoved()
+{
+ Q_D(QSGListView);
+ QSGFlickable::viewportMoved();
+ if (!d->itemCount)
+ return;
+ // Recursion can occur due to refill changing the content size.
+ if (d->inViewportMoved)
+ return;
+ d->inViewportMoved = true;
+ d->lazyRelease = true;
+ refill();
+ if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
+ d->moveReason = QSGListViewPrivate::Mouse;
+ if (d->moveReason != QSGListViewPrivate::SetIndex) {
+ if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
+ // reposition highlight
+ qreal pos = d->highlight->position();
+ qreal viewPos;
+ qreal highlightStart;
+ qreal highlightEnd;
+ if (d->isRightToLeft()) {
+ // Handle Right-To-Left exceptions
+ viewPos = -d->position()-d->size();
+ highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
+ } else {
+ viewPos = d->position();
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ }
+ if (pos > viewPos + highlightEnd - d->highlight->size())
+ pos = viewPos + highlightEnd - d->highlight->size();
+ if (pos < viewPos + highlightStart)
+ pos = viewPos + highlightStart;
+ d->highlightPosAnimator->stop();
+ d->highlight->setPosition(qRound(pos));
+
+ // update current index
+ if (FxListItemSG *snapItem = d->snapItemAt(d->highlight->position())) {
+ if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
+ d->updateCurrent(snapItem->index);
+ }
+ }
+ }
+
+ if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
+ d->inFlickCorrection = true;
+ // Near an end and it seems that the extent has changed?
+ // Recalculate the flick so that we don't end up in an odd position.
+ if (yflick() && !d->vData.inOvershoot) {
+ if (d->vData.velocity > 0) {
+ const qreal minY = minYExtent();
+ if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
+ && minY != d->vData.flickTarget)
+ d->flickY(-d->vData.smoothVelocity.value());
+ d->bufferMode = QSGListViewPrivate::BufferBefore;
+ } else if (d->vData.velocity < 0) {
+ const qreal maxY = maxYExtent();
+ if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
+ && maxY != d->vData.flickTarget)
+ d->flickY(-d->vData.smoothVelocity.value());
+ d->bufferMode = QSGListViewPrivate::BufferAfter;
+ }
+ }
+
+ if (xflick() && !d->hData.inOvershoot) {
+ if (d->hData.velocity > 0) {
+ const qreal minX = minXExtent();
+ if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
+ && minX != d->hData.flickTarget)
+ d->flickX(-d->hData.smoothVelocity.value());
+ d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore;
+ } else if (d->hData.velocity < 0) {
+ const qreal maxX = maxXExtent();
+ if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
+ && maxX != d->hData.flickTarget)
+ d->flickX(-d->hData.smoothVelocity.value());
+ d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter;
+ }
+ }
+ d->inFlickCorrection = false;
+ }
+ d->inViewportMoved = false;
+}
+
+qreal QSGListView::minYExtent() const
+{
+ Q_D(const QSGListView);
+ if (d->orient == QSGListView::Horizontal)
+ return QSGFlickable::minYExtent();
+ if (d->minExtentDirty) {
+ d->minExtent = -d->startPosition();
+ if (d->header && d->visibleItems.count())
+ d->minExtent += d->header->size();
+ if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ d->minExtent += d->highlightRangeStart;
+ if (d->sectionCriteria) {
+ if (d->visibleItem(0))
+ d->minExtent -= d->visibleItem(0)->sectionSize();
+ }
+ d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
+ }
+ d->minExtentDirty = false;
+ }
+
+ return d->minExtent;
+}
+
+qreal QSGListView::maxYExtent() const
+{
+ Q_D(const QSGListView);
+ if (d->orient == QSGListView::Horizontal)
+ return height();
+ if (d->maxExtentDirty) {
+ if (!d->model || !d->model->count()) {
+ d->maxExtent = d->header ? -d->header->size() : 0;
+ d->maxExtent += height();
+ } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
+ if (d->highlightRangeEnd != d->highlightRangeStart)
+ d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
+ } else {
+ d->maxExtent = -(d->endPosition() - height() + 1);
+ }
+ if (d->footer)
+ d->maxExtent -= d->footer->size();
+ qreal minY = minYExtent();
+ if (d->maxExtent > minY)
+ d->maxExtent = minY;
+ d->maxExtentDirty = false;
+ }
+ return d->maxExtent;
+}
+
+qreal QSGListView::minXExtent() const
+{
+ Q_D(const QSGListView);
+ if (d->orient == QSGListView::Vertical)
+ return QSGFlickable::minXExtent();
+ if (d->minExtentDirty) {
+ d->minExtent = -d->startPosition();
+ qreal highlightStart;
+ qreal highlightEnd;
+ qreal endPositionFirstItem = 0;
+ if (d->isRightToLeft()) {
+ if (d->model && d->model->count())
+ endPositionFirstItem = d->positionAt(d->model->count()-1);
+ else if (d->header)
+ d->minExtent += d->header->size();
+ highlightStart = d->highlightRangeStartValid
+ ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
+ : d->size() - (d->lastPosition()-endPositionFirstItem);
+ highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
+ if (d->footer)
+ d->minExtent += d->footer->size();
+ qreal maxX = maxXExtent();
+ if (d->minExtent < maxX)
+ d->minExtent = maxX;
+ } else {
+ endPositionFirstItem = d->endPositionAt(0);
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ if (d->header && d->visibleItems.count())
+ d->minExtent += d->header->size();
+ }
+ if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ d->minExtent += highlightStart;
+ d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
+ }
+ d->minExtentDirty = false;
+ }
+
+ return d->minExtent;
+}
+
+qreal QSGListView::maxXExtent() const
+{
+ Q_D(const QSGListView);
+ if (d->orient == QSGListView::Vertical)
+ return width();
+ if (d->maxExtentDirty) {
+ qreal highlightStart;
+ qreal highlightEnd;
+ qreal lastItemPosition = 0;
+ d->maxExtent = 0;
+ if (d->isRightToLeft()) {
+ highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
+ highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
+ lastItemPosition = d->endPosition();
+ } else {
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ if (d->model && d->model->count())
+ lastItemPosition = d->positionAt(d->model->count()-1);
+ }
+ if (!d->model || !d->model->count()) {
+ if (!d->isRightToLeft())
+ d->maxExtent = d->header ? -d->header->size() : 0;
+ d->maxExtent += width();
+ } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
+ d->maxExtent = -(lastItemPosition - highlightStart);
+ if (highlightEnd != highlightStart) {
+ d->maxExtent = d->isRightToLeft()
+ ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
+ : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
+ }
+ } else {
+ d->maxExtent = -(d->endPosition() - width() + 1);
+ }
+ if (d->isRightToLeft()) {
+ if (d->header && d->visibleItems.count())
+ d->maxExtent -= d->header->size();
+ } else {
+ if (d->footer)
+ d->maxExtent -= d->footer->size();
+ qreal minX = minXExtent();
+ if (d->maxExtent > minX)
+ d->maxExtent = minX;
+ }
+ d->maxExtentDirty = false;
+ }
+
+ return d->maxExtent;
+}
+
+void QSGListView::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QSGListView);
+ if (d->model && d->model->count() && d->interactive) {
+ if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
+ || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
+ || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) {
+ if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
+ decrementCurrentIndex();
+ event->accept();
+ return;
+ } else if (d->wrap) {
+ event->accept();
+ return;
+ }
+ } else if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
+ || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
+ || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) {
+ if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
+ incrementCurrentIndex();
+ event->accept();
+ return;
+ } else if (d->wrap) {
+ event->accept();
+ return;
+ }
+ }
+ }
+ event->ignore();
+ QSGFlickable::keyPressEvent(event);
+}
+
+void QSGListView::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ Q_D(QSGListView);
+ d->maxExtentDirty = true;
+ d->minExtentDirty = true;
+ if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) {
+ // maintain position relative to the right edge
+ int dx = newGeometry.width() - oldGeometry.width();
+ setContentX(contentX() - dx);
+ }
+ QSGFlickable::geometryChanged(newGeometry, oldGeometry);
+}
+
+
+void QSGListView::incrementCurrentIndex()
+{
+ Q_D(QSGListView);
+ int count = d->model ? d->model->count() : 0;
+ if (count && (currentIndex() < count - 1 || d->wrap)) {
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ int index = currentIndex()+1;
+ setCurrentIndex((index >= 0 && index < count) ? index : 0);
+ }
+}
+
+void QSGListView::decrementCurrentIndex()
+{
+ Q_D(QSGListView);
+ int count = d->model ? d->model->count() : 0;
+ if (count && (currentIndex() > 0 || d->wrap)) {
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ int index = currentIndex()-1;
+ setCurrentIndex((index >= 0 && index < count) ? index : count-1);
+ }
+}
+
+void QSGListViewPrivate::positionViewAtIndex(int index, int mode)
+{
+ Q_Q(QSGListView);
+ if (!isValid())
+ return;
+ if (mode < QSGListView::Beginning || mode > QSGListView::Contain)
+ return;
+ int idx = qMax(qMin(index, model->count()-1), 0);
+
+ if (layoutScheduled)
+ layout();
+ qreal pos = isRightToLeft() ? -position() - size() : position();
+ FxListItemSG *item = visibleItem(idx);
+ qreal maxExtent;
+ if (orient == QSGListView::Vertical)
+ maxExtent = -q->maxYExtent();
+ else
+ maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
+ if (!item) {
+ int itemPos = positionAt(idx);
+ // save the currently visible items in case any of them end up visible again
+ QList<FxListItemSG*> oldVisible = visibleItems;
+ visibleItems.clear();
+ visiblePos = itemPos;
+ visibleIndex = idx;
+ setPosition(qMin(qreal(itemPos), maxExtent));
+ // now release the reference to all the old visible items.
+ for (int i = 0; i < oldVisible.count(); ++i)
+ releaseItem(oldVisible.at(i));
+ item = visibleItem(idx);
+ }
+ if (item) {
+ const qreal itemPos = item->position();
+ switch (mode) {
+ case QSGListView::Beginning:
+ pos = itemPos;
+ if (index < 0 && header)
+ pos -= header->size();
+ break;
+ case QSGListView::Center:
+ pos = itemPos - (size() - item->size())/2;
+ break;
+ case QSGListView::End:
+ pos = itemPos - size() + item->size();
+ if (index >= model->count() && footer)
+ pos += footer->size();
+ break;
+ case QSGListView::Visible:
+ if (itemPos > pos + size())
+ pos = itemPos - size() + item->size();
+ else if (item->endPosition() < pos)
+ pos = itemPos;
+ break;
+ case QSGListView::Contain:
+ if (item->endPosition() > pos + size())
+ pos = itemPos - size() + item->size();
+ if (itemPos < pos)
+ pos = itemPos;
+ }
+ pos = qMin(pos, maxExtent);
+ qreal minExtent;
+ if (orient == QSGListView::Vertical) {
+ minExtent = -q->minYExtent();
+ } else {
+ minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
+ }
+ pos = qMax(pos, minExtent);
+ moveReason = QSGListViewPrivate::Other;
+ q->cancelFlick();
+ setPosition(pos);
+ if (highlight) {
+ if (autoHighlight) {
+ highlight->setPosition(currentItem->itemPosition());
+ highlight->setSize(currentItem->itemSize());
+ }
+ updateHighlight();
+ }
+ }
+ fixupPosition();
+}
+
+void QSGListView::positionViewAtIndex(int index, int mode)
+{
+ Q_D(QSGListView);
+ if (!d->isValid() || index < 0 || index >= d->model->count())
+ return;
+ d->positionViewAtIndex(index, mode);
+}
+
+void QSGListView::positionViewAtBeginning()
+{
+ Q_D(QSGListView);
+ if (!d->isValid())
+ return;
+ d->positionViewAtIndex(-1, Beginning);
+}
+
+void QSGListView::positionViewAtEnd()
+{
+ Q_D(QSGListView);
+ if (!d->isValid())
+ return;
+ d->positionViewAtIndex(d->model->count(), End);
+}
+
+int QSGListView::indexAt(qreal x, qreal y) const
+{
+ Q_D(const QSGListView);
+ for (int i = 0; i < d->visibleItems.count(); ++i) {
+ const FxListItemSG *listItem = d->visibleItems.at(i);
+ if(listItem->contains(x, y))
+ return listItem->index;
+ }
+
+ return -1;
+}
+
+void QSGListView::componentComplete()
+{
+ Q_D(QSGListView);
+ QSGFlickable::componentComplete();
+ updateSections();
+ d->updateHeader();
+ d->updateFooter();
+ if (d->isValid()) {
+ refill();
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ if (d->currentIndex < 0 && !d->currentIndexCleared)
+ d->updateCurrent(0);
+ else
+ d->updateCurrent(d->currentIndex);
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->position());
+ d->updateTrackedItem();
+ }
+ d->moveReason = QSGListViewPrivate::Other;
+ d->fixupPosition();
+ }
+}
+
+void QSGListView::updateSections()
+{
+ Q_D(QSGListView);
+ if (isComponentComplete() && d->model) {
+ QList<QByteArray> roles;
+ if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
+ roles << d->sectionCriteria->property().toUtf8();
+ d->model->setWatchedRoles(roles);
+ d->updateSections();
+ if (d->itemCount)
+ d->layout();
+ }
+}
+
+void QSGListView::refill()
+{
+ Q_D(QSGListView);
+ if (d->isRightToLeft())
+ d->refill(-d->position()-d->size()+1, -d->position());
+ else
+ d->refill(d->position(), d->position()+d->size()-1);
+}
+
+void QSGListView::trackedPositionChanged()
+{
+ Q_D(QSGListView);
+ if (!d->trackedItem || !d->currentItem)
+ return;
+ if (d->moveReason == QSGListViewPrivate::SetIndex) {
+ qreal trackedPos = qCeil(d->trackedItem->position());
+ qreal trackedSize = d->trackedItem->size();
+ if (d->trackedItem != d->currentItem) {
+ trackedPos -= d->currentItem->sectionSize();
+ trackedSize += d->currentItem->sectionSize();
+ }
+ qreal viewPos;
+ qreal highlightStart;
+ qreal highlightEnd;
+ if (d->isRightToLeft()) {
+ viewPos = -d->position()-d->size();
+ highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
+ } else {
+ viewPos = d->position();
+ highlightStart = d->highlightRangeStart;
+ highlightEnd = d->highlightRangeEnd;
+ }
+ qreal pos = viewPos;
+ if (d->haveHighlightRange) {
+ if (d->highlightRange == StrictlyEnforceRange) {
+ if (trackedPos > pos + highlightEnd - d->trackedItem->size())
+ pos = trackedPos - highlightEnd + d->trackedItem->size();
+ if (trackedPos < pos + highlightStart)
+ pos = trackedPos - highlightStart;
+ } else {
+ if (trackedPos < d->startPosition() + highlightStart) {
+ pos = d->startPosition();
+ } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
+ pos = d->endPosition() - d->size() + 1;
+ if (pos < d->startPosition())
+ pos = d->startPosition();
+ } else {
+ if (trackedPos < viewPos + highlightStart) {
+ pos = trackedPos - highlightStart;
+ } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
+ pos = trackedPos - highlightEnd + trackedSize;
+ }
+ }
+ }
+ } else {
+ if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
+ pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
+ } else if (d->trackedItem->endPosition() >= viewPos + d->size()
+ && d->currentItem->endPosition() >= viewPos + d->size()) {
+ if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
+ pos = d->trackedItem->endPosition() - d->size() + 1;
+ if (trackedSize > d->size())
+ pos = trackedPos;
+ } else {
+ pos = d->currentItem->endPosition() - d->size() + 1;
+ if (d->currentItem->size() > d->size())
+ pos = d->currentItem->position();
+ }
+ }
+ }
+ if (viewPos != pos) {
+ cancelFlick();
+ d->calcVelocity = true;
+ d->setPosition(pos);
+ d->calcVelocity = false;
+ }
+ }
+}
+
+void QSGListView::itemsInserted(int modelIndex, int count)
+{
+ Q_D(QSGListView);
+ if (!isComponentComplete())
+ return;
+ d->updateUnrequestedIndexes();
+ d->moveReason = QSGListViewPrivate::Other;
+
+ qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
+ int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
+ if (index < 0) {
+ int i = d->visibleItems.count() - 1;
+ while (i > 0 && d->visibleItems.at(i)->index == -1)
+ --i;
+ if (i == 0 && d->visibleItems.first()->index == -1) {
+ // there are no visible items except items marked for removal
+ index = d->visibleItems.count();
+ } else if (d->visibleItems.at(i)->index + 1 == modelIndex
+ && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
+ // Special case of appending an item to the model.
+ index = d->visibleItems.count();
+ } else {
+ if (modelIndex < d->visibleIndex) {
+ // Insert before visible items
+ d->visibleIndex += count;
+ for (int i = 0; i < d->visibleItems.count(); ++i) {
+ FxListItemSG *listItem = d->visibleItems.at(i);
+ if (listItem->index != -1 && listItem->index >= modelIndex)
+ listItem->index += count;
+ }
+ }
+ if (d->currentIndex >= modelIndex) {
+ // adjust current item index
+ d->currentIndex += count;
+ if (d->currentItem)
+ d->currentItem->index = d->currentIndex;
+ emit currentIndexChanged();
+ }
+ d->scheduleLayout();
+ d->itemCount += count;
+ emit countChanged();
+ return;
+ }
+ }
+
+ // index can be the next item past the end of the visible items list (i.e. appended)
+ int pos = 0;
+ if (d->visibleItems.count()) {
+ pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
+ : d->visibleItems.last()->endPosition()+d->spacing+1;
+ } else if (d->itemCount == 0 && d->header) {
+ pos = d->header->size();
+ }
+
+ int initialPos = pos;
+ int diff = 0;
+ QList<FxListItemSG*> added;
+ bool addedVisible = false;
+ FxListItemSG *firstVisible = d->firstVisibleItem();
+ if (firstVisible && pos < firstVisible->position()) {
+ // Insert items before the visible item.
+ int insertionIdx = index;
+ int i = 0;
+ int from = tempPos - d->buffer;
+ for (i = count-1; i >= 0 && pos > from; --i) {
+ if (!addedVisible) {
+ d->scheduleLayout();
+ addedVisible = true;
+ }
+ FxListItemSG *item = d->createItem(modelIndex + i);
+ d->visibleItems.insert(insertionIdx, item);
+ pos -= item->size() + d->spacing;
+ item->setPosition(pos);
+ index++;
+ }
+ if (i >= 0) {
+ // If we didn't insert all our new items - anything
+ // before the current index is not visible - remove it.
+ while (insertionIdx--) {
+ FxListItemSG *item = d->visibleItems.takeFirst();
+ if (item->index != -1)
+ d->visibleIndex++;
+ d->releaseItem(item);
+ }
+ } else {
+ // adjust pos of items before inserted items.
+ for (int i = insertionIdx-1; i >= 0; i--) {
+ FxListItemSG *listItem = d->visibleItems.at(i);
+ listItem->setPosition(listItem->position() - (initialPos - pos));
+ }
+ }
+ } else {
+ int i = 0;
+ int to = d->buffer+tempPos+d->size()-1;
+ for (i = 0; i < count && pos <= to; ++i) {
+ if (!addedVisible) {
+ d->scheduleLayout();
+ addedVisible = true;
+ }
+ FxListItemSG *item = d->createItem(modelIndex + i);
+ d->visibleItems.insert(index, item);
+ item->setPosition(pos);
+ added.append(item);
+ pos += item->size() + d->spacing;
+ ++index;
+ }
+ if (i != count) {
+ // We didn't insert all our new items, which means anything
+ // beyond the current index is not visible - remove it.
+ while (d->visibleItems.count() > index)
+ d->releaseItem(d->visibleItems.takeLast());
+ }
+ diff = pos - initialPos;
+ }
+ if (d->itemCount && d->currentIndex >= modelIndex) {
+ // adjust current item index
+ d->currentIndex += count;
+ if (d->currentItem) {
+ d->currentItem->index = d->currentIndex;
+ d->currentItem->setPosition(d->currentItem->position() + diff);
+ }
+ emit currentIndexChanged();
+ } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
+ d->updateCurrent(0);
+ }
+ // Update the indexes of the following visible items.
+ for (; index < d->visibleItems.count(); ++index) {
+ FxListItemSG *listItem = d->visibleItems.at(index);
+ if (d->currentItem && listItem->item != d->currentItem->item)
+ listItem->setPosition(listItem->position() + diff);
+ if (listItem->index != -1)
+ listItem->index += count;
+ }
+ // everything is in order now - emit add() signal
+ for (int j = 0; j < added.count(); ++j)
+ added.at(j)->attached->emitAdd();
+
+ d->updateSections();
+ d->itemCount += count;
+ emit countChanged();
+}
+
+void QSGListView::itemsRemoved(int modelIndex, int count)
+{
+ Q_D(QSGListView);
+ if (!isComponentComplete())
+ return;
+ d->moveReason = QSGListViewPrivate::Other;
+ d->updateUnrequestedIndexes();
+ d->itemCount -= count;
+
+ FxListItemSG *firstVisible = d->firstVisibleItem();
+ int preRemovedSize = 0;
+ bool removedVisible = false;
+ // Remove the items from the visible list, skipping anything already marked for removal
+ QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
+ while (it != d->visibleItems.end()) {
+ FxListItemSG *item = *it;
+ if (item->index == -1 || item->index < modelIndex) {
+ // already removed, or before removed items
+ ++it;
+ } else if (item->index >= modelIndex + count) {
+ // after removed items
+ item->index -= count;
+ ++it;
+ } else {
+ // removed item
+ if (!removedVisible) {
+ d->scheduleLayout();
+ removedVisible = true;
+ }
+ item->attached->emitRemove();
+ if (item->attached->delayRemove()) {
+ item->index = -1;
+ connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
+ ++it;
+ } else {
+ if (item == firstVisible)
+ firstVisible = 0;
+ if (firstVisible && item->position() < firstVisible->position())
+ preRemovedSize += item->size();
+ it = d->visibleItems.erase(it);
+ d->releaseItem(item);
+ }
+ }
+ }
+
+ if (firstVisible && d->visibleItems.first() != firstVisible)
+ d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
+
+ // fix current
+ if (d->currentIndex >= modelIndex + count) {
+ d->currentIndex -= count;
+ if (d->currentItem)
+ d->currentItem->index -= count;
+ emit currentIndexChanged();
+ } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
+ // current item has been removed.
+ if (d->currentItem) {
+ d->currentItem->attached->setIsCurrentItem(false);
+ d->releaseItem(d->currentItem);
+ d->currentItem = 0;
+ }
+ d->currentIndex = -1;
+ if (d->itemCount)
+ d->updateCurrent(qMin(modelIndex, d->itemCount-1));
+ else
+ emit currentIndexChanged();
+ }
+
+ // update visibleIndex
+ bool haveVisibleIndex = false;
+ for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
+ if ((*it)->index != -1) {
+ d->visibleIndex = (*it)->index;
+ haveVisibleIndex = true;
+ break;
+ }
+ }
+
+ if (!haveVisibleIndex) {
+ d->timeline.clear();
+ if (removedVisible && d->itemCount == 0) {
+ d->visibleIndex = 0;
+ d->visiblePos = d->header ? d->header->size() : 0;
+ d->setPosition(0);
+ d->updateHeader();
+ d->updateFooter();
+ } else {
+ if (modelIndex < d->visibleIndex)
+ d->visibleIndex = modelIndex+1;
+ d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
+ }
+ }
+
+ d->updateSections();
+ emit countChanged();
+}
+
+void QSGListView::destroyRemoved()
+{
+ Q_D(QSGListView);
+ for (QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
+ it != d->visibleItems.end();) {
+ FxListItemSG *listItem = *it;
+ if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
+ d->releaseItem(listItem);
+ it = d->visibleItems.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ // Correct the positioning of the items
+ d->updateSections();
+ d->layout();
+}
+
+void QSGListView::itemsMoved(int from, int to, int count)
+{
+ Q_D(QSGListView);
+ if (!isComponentComplete())
+ return;
+ d->updateUnrequestedIndexes();
+
+ if (d->visibleItems.isEmpty()) {
+ refill();
+ return;
+ }
+
+ d->moveReason = QSGListViewPrivate::Other;
+ FxListItemSG *firstVisible = d->firstVisibleItem();
+ qreal firstItemPos = firstVisible->position();
+ QHash<int,FxListItemSG*> moved;
+ int moveBy = 0;
+
+ QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
+ while (it != d->visibleItems.end()) {
+ FxListItemSG *item = *it;
+ if (item->index >= from && item->index < from + count) {
+ // take the items that are moving
+ item->index += (to-from);
+ moved.insert(item->index, item);
+ if (item->position() < firstItemPos)
+ moveBy += item->size();
+ it = d->visibleItems.erase(it);
+ } else {
+ // move everything after the moved items.
+ if (item->index > from && item->index != -1)
+ item->index -= count;
+ ++it;
+ }
+ }
+
+ int remaining = count;
+ int endIndex = d->visibleIndex;
+ it = d->visibleItems.begin();
+ while (it != d->visibleItems.end()) {
+ FxListItemSG *item = *it;
+ if (remaining && item->index >= to && item->index < to + count) {
+ // place items in the target position, reusing any existing items
+ FxListItemSG *movedItem = moved.take(item->index);
+ if (!movedItem)
+ movedItem = d->createItem(item->index);
+ if (item->index <= firstVisible->index)
+ moveBy -= movedItem->size();
+ it = d->visibleItems.insert(it, movedItem);
+ ++it;
+ --remaining;
+ } else {
+ if (item->index != -1) {
+ if (item->index >= to) {
+ // update everything after the moved items.
+ item->index += count;
+ }
+ endIndex = item->index;
+ }
+ ++it;
+ }
+ }
+
+ // If we have moved items to the end of the visible items
+ // then add any existing moved items that we have
+ while (FxListItemSG *item = moved.take(endIndex+1)) {
+ d->visibleItems.append(item);
+ ++endIndex;
+ }
+
+ // update visibleIndex
+ for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
+ if ((*it)->index != -1) {
+ d->visibleIndex = (*it)->index;
+ break;
+ }
+ }
+
+ // Fix current index
+ if (d->currentIndex >= 0 && d->currentItem) {
+ int oldCurrent = d->currentIndex;
+ d->currentIndex = d->model->indexOf(d->currentItem->item, this);
+ if (oldCurrent != d->currentIndex) {
+ d->currentItem->index = d->currentIndex;
+ emit currentIndexChanged();
+ }
+ }
+
+ // Whatever moved items remain are no longer visible items.
+ while (moved.count()) {
+ int idx = moved.begin().key();
+ FxListItemSG *item = moved.take(idx);
+ if (d->currentItem && item->item == d->currentItem->item)
+ item->setPosition(d->positionAt(idx));
+ d->releaseItem(item);
+ }
+
+ // Ensure we don't cause an ugly list scroll.
+ d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
+
+ d->updateSections();
+ d->layout();
+}
+
+void QSGListView::itemsChanged(int, int)
+{
+ Q_D(QSGListView);
+ d->updateSections();
+ d->layout();
+}
+
+void QSGListView::modelReset()
+{
+ Q_D(QSGListView);
+ d->moveReason = QSGListViewPrivate::SetIndex;
+ d->regenerate();
+ if (d->highlight && d->currentItem) {
+ if (d->autoHighlight)
+ d->highlight->setPosition(d->currentItem->position());
+ d->updateTrackedItem();
+ }
+ d->moveReason = QSGListViewPrivate::Other;
+ emit countChanged();
+}
+
+void QSGListView::createdItem(int index, QSGItem *item)
+{
+ Q_D(QSGListView);
+ if (d->requestedIndex != index) {
+ item->setParentItem(contentItem());
+ d->unrequestedItems.insert(item, index);
+ if (d->orient == QSGListView::Vertical) {
+ item->setY(d->positionAt(index));
+ } else {
+ if (d->isRightToLeft())
+ item->setX(-d->positionAt(index)-item->width());
+ else
+ item->setX(d->positionAt(index));
+ }
+ }
+}
+
+void QSGListView::destroyingItem(QSGItem *item)
+{
+ Q_D(QSGListView);
+ d->unrequestedItems.remove(item);
+}
+
+void QSGListView::animStopped()
+{
+ Q_D(QSGListView);
+ d->bufferMode = QSGListViewPrivate::NoBuffer;
+ if (d->haveHighlightRange && d->highlightRange == QSGListView::StrictlyEnforceRange)
+ d->updateHighlight();
+}
+
+QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
+{
+ return new QSGListViewAttached(obj);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsglistview_p.h b/src/declarative/items/qsglistview_p.h
new file mode 100644
index 0000000000..2e3df2020f
--- /dev/null
+++ b/src/declarative/items/qsglistview_p.h
@@ -0,0 +1,374 @@
+// Commit: 95814418f9d6adeba365c795462e8afb00138211
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGLISTVIEW_P_H
+#define QSGLISTVIEW_P_H
+
+#include "qsgflickable_p.h"
+
+#include <private/qdeclarativeguard_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_AUTOTEST_EXPORT QSGViewSection : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged)
+ Q_PROPERTY(SectionCriteria criteria READ criteria WRITE setCriteria NOTIFY criteriaChanged)
+ Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_ENUMS(SectionCriteria)
+public:
+ QSGViewSection(QObject *parent=0) : QObject(parent), m_criteria(FullString), m_delegate(0) {}
+
+ QString property() const { return m_property; }
+ void setProperty(const QString &);
+
+ enum SectionCriteria { FullString, FirstCharacter };
+ SectionCriteria criteria() const { return m_criteria; }
+ void setCriteria(SectionCriteria);
+
+ QDeclarativeComponent *delegate() const { return m_delegate; }
+ void setDelegate(QDeclarativeComponent *delegate);
+
+ QString sectionString(const QString &value);
+
+Q_SIGNALS:
+ void propertyChanged();
+ void criteriaChanged();
+ void delegateChanged();
+
+private:
+ QString m_property;
+ SectionCriteria m_criteria;
+ QDeclarativeComponent *m_delegate;
+};
+
+
+class QSGVisualModel;
+class QSGListViewAttached;
+class QSGListViewPrivate;
+class Q_AUTOTEST_EXPORT QSGListView : public QSGFlickable
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGListView)
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(QSGItem *currentItem READ currentItem NOTIFY currentIndexChanged)
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+
+ Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
+ Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged)
+ Q_PROPERTY(bool highlightFollowsCurrentItem READ highlightFollowsCurrentItem WRITE setHighlightFollowsCurrentItem NOTIFY highlightFollowsCurrentItemChanged)
+ Q_PROPERTY(qreal highlightMoveSpeed READ highlightMoveSpeed WRITE setHighlightMoveSpeed NOTIFY highlightMoveSpeedChanged)
+ Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged)
+ Q_PROPERTY(qreal highlightResizeSpeed READ highlightResizeSpeed WRITE setHighlightResizeSpeed NOTIFY highlightResizeSpeedChanged)
+ Q_PROPERTY(int highlightResizeDuration READ highlightResizeDuration WRITE setHighlightResizeDuration NOTIFY highlightResizeDurationChanged)
+
+ Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged RESET resetPreferredHighlightBegin)
+ Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged RESET resetPreferredHighlightEnd)
+ Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged)
+
+ Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
+ Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged)
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
+ Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
+ Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged)
+ Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged)
+ Q_PROPERTY(QSGViewSection *section READ sectionCriteria CONSTANT)
+ Q_PROPERTY(QString currentSection READ currentSection NOTIFY currentSectionChanged)
+
+ Q_PROPERTY(SnapMode snapMode READ snapMode WRITE setSnapMode NOTIFY snapModeChanged)
+
+ Q_PROPERTY(QDeclarativeComponent *header READ header WRITE setHeader NOTIFY headerChanged)
+ Q_PROPERTY(QDeclarativeComponent *footer READ footer WRITE setFooter NOTIFY footerChanged)
+
+ Q_ENUMS(HighlightRangeMode)
+ Q_ENUMS(Orientation)
+ Q_ENUMS(SnapMode)
+ Q_ENUMS(PositionMode)
+ Q_CLASSINFO("DefaultProperty", "data")
+
+public:
+ QSGListView(QSGItem *parent=0);
+ ~QSGListView();
+
+ QVariant model() const;
+ void setModel(const QVariant &);
+
+ QDeclarativeComponent *delegate() const;
+ void setDelegate(QDeclarativeComponent *);
+
+ int currentIndex() const;
+ void setCurrentIndex(int idx);
+
+ QSGItem *currentItem();
+ QSGItem *highlightItem();
+ int count() const;
+
+ QDeclarativeComponent *highlight() const;
+ void setHighlight(QDeclarativeComponent *highlight);
+
+ bool highlightFollowsCurrentItem() const;
+ void setHighlightFollowsCurrentItem(bool);
+
+ enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange };
+ HighlightRangeMode highlightRangeMode() const;
+ void setHighlightRangeMode(HighlightRangeMode mode);
+
+ qreal preferredHighlightBegin() const;
+ void setPreferredHighlightBegin(qreal);
+ void resetPreferredHighlightBegin();
+
+ qreal preferredHighlightEnd() const;
+ void setPreferredHighlightEnd(qreal);
+ void resetPreferredHighlightEnd();
+
+ qreal spacing() const;
+ void setSpacing(qreal spacing);
+
+ enum Orientation { Horizontal = Qt::Horizontal, Vertical = Qt::Vertical };
+ Orientation orientation() const;
+ void setOrientation(Orientation);
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection(Qt::LayoutDirection);
+ Qt::LayoutDirection effectiveLayoutDirection() const;
+
+ bool isWrapEnabled() const;
+ void setWrapEnabled(bool);
+
+ int cacheBuffer() const;
+ void setCacheBuffer(int);
+
+ QSGViewSection *sectionCriteria();
+ QString currentSection() const;
+
+ qreal highlightMoveSpeed() const;
+ void setHighlightMoveSpeed(qreal);
+
+ int highlightMoveDuration() const;
+ void setHighlightMoveDuration(int);
+
+ qreal highlightResizeSpeed() const;
+ void setHighlightResizeSpeed(qreal);
+
+ int highlightResizeDuration() const;
+ void setHighlightResizeDuration(int);
+
+ enum SnapMode { NoSnap, SnapToItem, SnapOneItem };
+ SnapMode snapMode() const;
+ void setSnapMode(SnapMode mode);
+
+ QDeclarativeComponent *footer() const;
+ void setFooter(QDeclarativeComponent *);
+
+ QDeclarativeComponent *header() const;
+ void setHeader(QDeclarativeComponent *);
+
+ virtual void setContentX(qreal pos);
+ virtual void setContentY(qreal pos);
+
+ static QSGListViewAttached *qmlAttachedProperties(QObject *);
+
+ enum PositionMode { Beginning, Center, End, Visible, Contain };
+
+ Q_INVOKABLE void positionViewAtIndex(int index, int mode);
+ Q_INVOKABLE int indexAt(qreal x, qreal y) const;
+ Q_INVOKABLE void positionViewAtBeginning();
+ Q_INVOKABLE void positionViewAtEnd();
+
+public Q_SLOTS:
+ void incrementCurrentIndex();
+ void decrementCurrentIndex();
+
+Q_SIGNALS:
+ void countChanged();
+ void spacingChanged();
+ void orientationChanged();
+ void layoutDirectionChanged();
+ void effectiveLayoutDirectionChanged();
+ void currentIndexChanged();
+ void currentSectionChanged();
+ void highlightMoveSpeedChanged();
+ void highlightMoveDurationChanged();
+ void highlightResizeSpeedChanged();
+ void highlightResizeDurationChanged();
+ void highlightChanged();
+ void highlightItemChanged();
+ void modelChanged();
+ void delegateChanged();
+ void highlightFollowsCurrentItemChanged();
+ void preferredHighlightBeginChanged();
+ void preferredHighlightEndChanged();
+ void highlightRangeModeChanged();
+ void keyNavigationWrapsChanged();
+ void cacheBufferChanged();
+ void snapModeChanged();
+ void headerChanged();
+ void footerChanged();
+
+protected:
+ virtual void updatePolish();
+ virtual void viewportMoved();
+ virtual qreal minYExtent() const;
+ virtual qreal maxYExtent() const;
+ virtual qreal minXExtent() const;
+ virtual qreal maxXExtent() const;
+ virtual void keyPressEvent(QKeyEvent *);
+ virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry);
+ virtual void componentComplete();
+
+private Q_SLOTS:
+ void updateSections();
+ void refill();
+ void trackedPositionChanged();
+ void itemsInserted(int index, int count);
+ void itemsRemoved(int index, int count);
+ void itemsMoved(int from, int to, int count);
+ void itemsChanged(int index, int count);
+ void modelReset();
+ void destroyRemoved();
+ void createdItem(int index, QSGItem *item);
+ void destroyingItem(QSGItem *item);
+ void animStopped();
+};
+
+class QSGListViewAttached : public QObject
+{
+ Q_OBJECT
+public:
+ QSGListViewAttached(QObject *parent)
+ : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {}
+ ~QSGListViewAttached() {}
+
+ Q_PROPERTY(QSGListView *view READ view NOTIFY viewChanged)
+ QSGListView *view() { return m_view; }
+ void setView(QSGListView *view) {
+ if (view != m_view) {
+ m_view = view;
+ emit viewChanged();
+ }
+ }
+
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
+ bool isCurrentItem() const { return m_isCurrent; }
+ void setIsCurrentItem(bool c) {
+ if (m_isCurrent != c) {
+ m_isCurrent = c;
+ emit currentItemChanged();
+ }
+ }
+
+ Q_PROPERTY(QString previousSection READ prevSection NOTIFY prevSectionChanged)
+ QString prevSection() const { return m_prevSection; }
+ void setPrevSection(const QString &sect) {
+ if (m_prevSection != sect) {
+ m_prevSection = sect;
+ emit prevSectionChanged();
+ }
+ }
+
+ Q_PROPERTY(QString nextSection READ nextSection NOTIFY nextSectionChanged)
+ QString nextSection() const { return m_nextSection; }
+ void setNextSection(const QString &sect) {
+ if (m_nextSection != sect) {
+ m_nextSection = sect;
+ emit nextSectionChanged();
+ }
+ }
+
+ Q_PROPERTY(QString section READ section NOTIFY sectionChanged)
+ QString section() const { return m_section; }
+ void setSection(const QString &sect) {
+ if (m_section != sect) {
+ m_section = sect;
+ emit sectionChanged();
+ }
+ }
+
+ Q_PROPERTY(bool delayRemove READ delayRemove WRITE setDelayRemove NOTIFY delayRemoveChanged)
+ bool delayRemove() const { return m_delayRemove; }
+ void setDelayRemove(bool delay) {
+ if (m_delayRemove != delay) {
+ m_delayRemove = delay;
+ emit delayRemoveChanged();
+ }
+ }
+
+ void emitAdd() { emit add(); }
+ void emitRemove() { emit remove(); }
+
+Q_SIGNALS:
+ void currentItemChanged();
+ void sectionChanged();
+ void prevSectionChanged();
+ void nextSectionChanged();
+ void delayRemoveChanged();
+ void add();
+ void remove();
+ void viewChanged();
+
+public:
+ QDeclarativeGuard<QSGListView> m_view;
+ mutable QString m_section;
+ QString m_prevSection;
+ QString m_nextSection;
+ bool m_isCurrent : 1;
+ bool m_delayRemove : 1;
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPEINFO(QSGListView, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QSGListView)
+QML_DECLARE_TYPE(QSGViewSection)
+
+QT_END_HEADER
+
+#endif // QSGLISTVIEW_P_H
diff --git a/src/declarative/items/qsgloader.cpp b/src/declarative/items/qsgloader.cpp
new file mode 100644
index 0000000000..6717098506
--- /dev/null
+++ b/src/declarative/items/qsgloader.cpp
@@ -0,0 +1,340 @@
+// Commit: 501180c6fbed0857126da2bb0ff1f17ee35472c6
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgloader_p_p.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+
+#include <private/qdeclarativeengine_p.h>
+#include <private/qdeclarativeglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGLoaderPrivate::QSGLoaderPrivate()
+ : item(0), component(0), ownComponent(false), updatingSize(false),
+ itemWidthValid(false), itemHeightValid(false)
+{
+}
+
+QSGLoaderPrivate::~QSGLoaderPrivate()
+{
+}
+
+void QSGLoaderPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ if (resizeItem == item) {
+ if (!updatingSize && newGeometry.width() != oldGeometry.width())
+ itemWidthValid = true;
+ if (!updatingSize && newGeometry.height() != oldGeometry.height())
+ itemHeightValid = true;
+ _q_updateSize(false);
+ }
+ QSGItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+}
+
+void QSGLoaderPrivate::clear()
+{
+ if (ownComponent) {
+ component->deleteLater();
+ component = 0;
+ ownComponent = false;
+ }
+ source = QUrl();
+
+ if (item) {
+ QSGItemPrivate *p = QSGItemPrivate::get(item);
+ p->removeItemChangeListener(this, QSGItemPrivate::Geometry);
+
+ // We can't delete immediately because our item may have triggered
+ // the Loader to load a different item.
+ item->setParentItem(0);
+ item->setVisible(false);
+ item->deleteLater();
+ item = 0;
+ }
+}
+
+void QSGLoaderPrivate::initResize()
+{
+ QSGItemPrivate *p = QSGItemPrivate::get(item);
+ p->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ // We may override the item's size, so we need to remember
+ // whether the item provided its own valid size.
+ itemWidthValid = p->widthValid;
+ itemHeightValid = p->heightValid;
+ _q_updateSize();
+}
+
+QSGLoader::QSGLoader(QSGItem *parent)
+ : QSGImplicitSizeItem(*(new QSGLoaderPrivate), parent)
+{
+ setFlag(ItemIsFocusScope);
+}
+
+QSGLoader::~QSGLoader()
+{
+ Q_D(QSGLoader);
+ if (d->item) {
+ QSGItemPrivate *p = QSGItemPrivate::get(d->item);
+ p->removeItemChangeListener(d, QSGItemPrivate::Geometry);
+ }
+}
+
+QUrl QSGLoader::source() const
+{
+ Q_D(const QSGLoader);
+ return d->source;
+}
+
+void QSGLoader::setSource(const QUrl &url)
+{
+ Q_D(QSGLoader);
+ if (d->source == url)
+ return;
+
+ d->clear();
+
+ d->source = url;
+ if (d->source.isEmpty()) {
+ emit sourceChanged();
+ emit statusChanged();
+ emit progressChanged();
+ emit itemChanged();
+ return;
+ }
+
+ d->component = new QDeclarativeComponent(qmlEngine(this), d->source, this);
+ d->ownComponent = true;
+
+ if (isComponentComplete())
+ d->load();
+}
+
+QDeclarativeComponent *QSGLoader::sourceComponent() const
+{
+ Q_D(const QSGLoader);
+ return d->component;
+}
+
+void QSGLoader::setSourceComponent(QDeclarativeComponent *comp)
+{
+ Q_D(QSGLoader);
+ if (comp == d->component)
+ return;
+
+ d->clear();
+
+ d->component = comp;
+ d->ownComponent = false;
+ if (!d->component) {
+ emit sourceChanged();
+ emit statusChanged();
+ emit progressChanged();
+ emit itemChanged();
+ return;
+ }
+
+ if (isComponentComplete())
+ d->load();
+}
+
+void QSGLoader::resetSourceComponent()
+{
+ setSourceComponent(0);
+}
+
+void QSGLoaderPrivate::load()
+{
+ Q_Q(QSGLoader);
+
+ if (!q->isComponentComplete() || !component)
+ return;
+
+ if (!component->isLoading()) {
+ _q_sourceLoaded();
+ } else {
+ QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
+ q, SLOT(_q_sourceLoaded()));
+ QObject::connect(component, SIGNAL(progressChanged(qreal)),
+ q, SIGNAL(progressChanged()));
+ emit q->statusChanged();
+ emit q->progressChanged();
+ emit q->sourceChanged();
+ emit q->itemChanged();
+ }
+}
+
+void QSGLoaderPrivate::_q_sourceLoaded()
+{
+ Q_Q(QSGLoader);
+
+ if (component) {
+ if (!component->errors().isEmpty()) {
+ QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
+ emit q->sourceChanged();
+ emit q->statusChanged();
+ emit q->progressChanged();
+ return;
+ }
+
+ QDeclarativeContext *creationContext = component->creationContext();
+ if (!creationContext) creationContext = qmlContext(q);
+ QDeclarativeContext *ctxt = new QDeclarativeContext(creationContext);
+ ctxt->setContextObject(q);
+
+ QDeclarativeGuard<QDeclarativeComponent> c = component;
+ QObject *obj = component->beginCreate(ctxt);
+ if (component != c) {
+ // component->create could trigger a change in source that causes
+ // component to be set to something else. In that case we just
+ // need to cleanup.
+ if (c)
+ c->completeCreate();
+ delete obj;
+ delete ctxt;
+ return;
+ }
+ if (obj) {
+ item = qobject_cast<QSGItem *>(obj);
+ if (item) {
+ QDeclarative_setParent_noEvent(ctxt, obj);
+ QDeclarative_setParent_noEvent(item, q);
+ item->setParentItem(q);
+// item->setFocus(true);
+ initResize();
+ } else {
+ qmlInfo(q) << QSGLoader::tr("Loader does not support loading non-visual elements.");
+ delete obj;
+ delete ctxt;
+ }
+ } else {
+ if (!component->errors().isEmpty())
+ QDeclarativeEnginePrivate::warning(qmlEngine(q), component->errors());
+ delete obj;
+ delete ctxt;
+ source = QUrl();
+ }
+ component->completeCreate();
+ emit q->sourceChanged();
+ emit q->statusChanged();
+ emit q->progressChanged();
+ emit q->itemChanged();
+ emit q->loaded();
+ }
+}
+
+QSGLoader::Status QSGLoader::status() const
+{
+ Q_D(const QSGLoader);
+
+ if (d->component)
+ return static_cast<QSGLoader::Status>(d->component->status());
+
+ if (d->item)
+ return Ready;
+
+ return d->source.isEmpty() ? Null : Error;
+}
+
+void QSGLoader::componentComplete()
+{
+ Q_D(QSGLoader);
+ QSGItem::componentComplete();
+ d->load();
+}
+
+qreal QSGLoader::progress() const
+{
+ Q_D(const QSGLoader);
+
+ if (d->item)
+ return 1.0;
+
+ if (d->component)
+ return d->component->progress();
+
+ return 0.0;
+}
+
+void QSGLoaderPrivate::_q_updateSize(bool loaderGeometryChanged)
+{
+ Q_Q(QSGLoader);
+ if (!item || updatingSize)
+ return;
+
+ updatingSize = true;
+
+ if (!itemWidthValid)
+ q->setImplicitWidth(item->implicitWidth());
+ else
+ q->setImplicitWidth(item->width());
+ if (loaderGeometryChanged && q->widthValid())
+ item->setWidth(q->width());
+
+ if (!itemHeightValid)
+ q->setImplicitHeight(item->implicitHeight());
+ else
+ q->setImplicitHeight(item->height());
+ if (loaderGeometryChanged && q->heightValid())
+ item->setHeight(q->height());
+
+ updatingSize = false;
+}
+
+QSGItem *QSGLoader::item() const
+{
+ Q_D(const QSGLoader);
+ return d->item;
+}
+
+void QSGLoader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QSGLoader);
+ if (newGeometry != oldGeometry) {
+ d->_q_updateSize();
+ }
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+#include <moc_qsgloader_p.cpp>
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgloader_p.h b/src/declarative/items/qsgloader_p.h
new file mode 100644
index 0000000000..689971792c
--- /dev/null
+++ b/src/declarative/items/qsgloader_p.h
@@ -0,0 +1,107 @@
+// Commit: 6f78a6080b84cc3ef96b73a4ff58d1b5a72f08f4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGLOADER_P_H
+#define QSGLOADER_P_H
+
+#include "qsgimplicitsizeitem_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGLoaderPrivate;
+class Q_AUTOTEST_EXPORT QSGLoader : public QSGImplicitSizeItem
+{
+ Q_OBJECT
+ Q_ENUMS(Status)
+
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+ Q_PROPERTY(QDeclarativeComponent *sourceComponent READ sourceComponent WRITE setSourceComponent RESET resetSourceComponent NOTIFY sourceChanged)
+ Q_PROPERTY(QSGItem *item READ item NOTIFY itemChanged)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged)
+
+public:
+ QSGLoader(QSGItem *parent=0);
+ virtual ~QSGLoader();
+
+ QUrl source() const;
+ void setSource(const QUrl &);
+
+ QDeclarativeComponent *sourceComponent() const;
+ void setSourceComponent(QDeclarativeComponent *);
+ void resetSourceComponent();
+
+ enum Status { Null, Ready, Loading, Error };
+ Status status() const;
+ qreal progress() const;
+
+ QSGItem *item() const;
+
+Q_SIGNALS:
+ void itemChanged();
+ void sourceChanged();
+ void statusChanged();
+ void progressChanged();
+ void loaded();
+
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ void componentComplete();
+
+private:
+ Q_DISABLE_COPY(QSGLoader)
+ Q_DECLARE_PRIVATE(QSGLoader)
+ Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded())
+ Q_PRIVATE_SLOT(d_func(), void _q_updateSize())
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGLoader)
+
+QT_END_HEADER
+
+#endif // QSGLOADER_P_H
diff --git a/src/declarative/items/qsgloader_p_p.h b/src/declarative/items/qsgloader_p_p.h
new file mode 100644
index 0000000000..63da789dce
--- /dev/null
+++ b/src/declarative/items/qsgloader_p_p.h
@@ -0,0 +1,91 @@
+// Commit: 5d2817cd668a705729df1727de49adf00713ac97
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGLOADER_P_P_H
+#define QSGLOADER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgloader_p.h"
+#include "qsgimplicitsizeitem_p_p.h"
+#include "qsgitemchangelistener_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeContext;
+class QSGLoaderPrivate : public QSGImplicitSizeItemPrivate, public QSGItemChangeListener
+{
+ Q_DECLARE_PUBLIC(QSGLoader)
+
+public:
+ QSGLoaderPrivate();
+ ~QSGLoaderPrivate();
+
+ void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
+ void clear();
+ void initResize();
+ void load();
+
+ QUrl source;
+ QSGItem *item;
+ QDeclarativeComponent *component;
+ bool ownComponent : 1;
+ bool updatingSize: 1;
+ bool itemWidthValid : 1;
+ bool itemHeightValid : 1;
+
+ void _q_sourceLoaded();
+ void _q_updateSize(bool loaderGeometryChanged = true);
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGLOADER_P_P_H
diff --git a/src/declarative/items/qsgmousearea.cpp b/src/declarative/items/qsgmousearea.cpp
new file mode 100644
index 0000000000..887d78a64d
--- /dev/null
+++ b/src/declarative/items/qsgmousearea.cpp
@@ -0,0 +1,800 @@
+// Commit: e1ffbc04131dc6f76fa76821c297d08162e4b1ee
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgmousearea_p.h"
+#include "qsgmousearea_p_p.h"
+#include "qsgcanvas.h"
+#include "qsgevents_p_p.h"
+
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qapplication.h>
+
+#include <float.h>
+
+QT_BEGIN_NAMESPACE
+static const int PressAndHoldDelay = 800;
+
+QSGDrag::QSGDrag(QObject *parent)
+: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX),
+_active(false), _filterChildren(false)
+{
+}
+
+QSGDrag::~QSGDrag()
+{
+}
+
+QSGItem *QSGDrag::target() const
+{
+ return _target;
+}
+
+void QSGDrag::setTarget(QSGItem *t)
+{
+ if (_target == t)
+ return;
+ _target = t;
+ emit targetChanged();
+}
+
+void QSGDrag::resetTarget()
+{
+ if (!_target)
+ return;
+ _target = 0;
+ emit targetChanged();
+}
+
+QSGDrag::Axis QSGDrag::axis() const
+{
+ return _axis;
+}
+
+void QSGDrag::setAxis(QSGDrag::Axis a)
+{
+ if (_axis == a)
+ return;
+ _axis = a;
+ emit axisChanged();
+}
+
+qreal QSGDrag::xmin() const
+{
+ return _xmin;
+}
+
+void QSGDrag::setXmin(qreal m)
+{
+ if (_xmin == m)
+ return;
+ _xmin = m;
+ emit minimumXChanged();
+}
+
+qreal QSGDrag::xmax() const
+{
+ return _xmax;
+}
+
+void QSGDrag::setXmax(qreal m)
+{
+ if (_xmax == m)
+ return;
+ _xmax = m;
+ emit maximumXChanged();
+}
+
+qreal QSGDrag::ymin() const
+{
+ return _ymin;
+}
+
+void QSGDrag::setYmin(qreal m)
+{
+ if (_ymin == m)
+ return;
+ _ymin = m;
+ emit minimumYChanged();
+}
+
+qreal QSGDrag::ymax() const
+{
+ return _ymax;
+}
+
+void QSGDrag::setYmax(qreal m)
+{
+ if (_ymax == m)
+ return;
+ _ymax = m;
+ emit maximumYChanged();
+}
+
+bool QSGDrag::active() const
+{
+ return _active;
+}
+
+void QSGDrag::setActive(bool drag)
+{
+ if (_active == drag)
+ return;
+ _active = drag;
+ emit activeChanged();
+}
+
+bool QSGDrag::filterChildren() const
+{
+ return _filterChildren;
+}
+
+void QSGDrag::setFilterChildren(bool filter)
+{
+ if (_filterChildren == filter)
+ return;
+ _filterChildren = filter;
+ emit filterChildrenChanged();
+}
+
+QSGMouseAreaPrivate::QSGMouseAreaPrivate()
+: absorb(true), hovered(false), pressed(false), longPress(false),
+ moved(false), stealMouse(false), doubleClick(false), preventStealing(false), drag(0)
+{
+ Q_Q(QSGMouseArea);
+ forwardTo = QDeclarativeListProperty<QSGItem>(q, forwardToList);
+}
+
+QSGMouseAreaPrivate::~QSGMouseAreaPrivate()
+{
+ delete drag;
+}
+
+void QSGMouseAreaPrivate::init()
+{
+ Q_Q(QSGMouseArea);
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFiltersChildMouseEvents(true);
+}
+
+void QSGMouseAreaPrivate::saveEvent(QGraphicsSceneMouseEvent *event)
+{
+ lastPos = event->pos();
+ lastScenePos = event->scenePos();
+ lastButton = event->button();
+ lastButtons = event->buttons();
+ lastModifiers = event->modifiers();
+}
+
+void QSGMouseAreaPrivate::forwardEvent(QGraphicsSceneMouseEvent* event)
+{
+ Q_Q(QSGMouseArea);
+ for(int i=0; i < forwardToList.count(); i++){
+ event->setPos(forwardToList[i]->mapFromScene(event->scenePos()));
+ forwardToList[i]->canvas()->sendEvent(forwardToList[i], event);
+ if(event->isAccepted())
+ break;
+ }
+ event->setPos(q->mapFromScene(event->scenePos()));
+}
+
+bool QSGMouseAreaPrivate::isPressAndHoldConnected()
+{
+ Q_Q(QSGMouseArea);
+ static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QSGMouseEvent*)");
+ return QObjectPrivate::get(q)->isSignalConnected(idx);
+}
+
+bool QSGMouseAreaPrivate::isDoubleClickConnected()
+{
+ Q_Q(QSGMouseArea);
+ static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QSGMouseEvent*)");
+ return QObjectPrivate::get(q)->isSignalConnected(idx);
+}
+
+bool QSGMouseAreaPrivate::isClickConnected()
+{
+ Q_Q(QSGMouseArea);
+ static int idx = QObjectPrivate::get(q)->signalIndex("clicked(QSGMouseEvent*)");
+ return QObjectPrivate::get(q)->isSignalConnected(idx);
+}
+
+void QSGMouseAreaPrivate::propagate(QSGMouseEvent* event, PropagateType t)
+{
+ Q_Q(QSGMouseArea);
+ QPointF scenePos = q->mapToScene(QPointF(event->x(), event->y()));
+ propagateHelper(event, canvas->rootItem(), scenePos, t);
+}
+
+bool QSGMouseAreaPrivate::propagateHelper(QSGMouseEvent *ev, QSGItem *item,const QPointF &sp, PropagateType sig)
+{
+ //Based off of QSGCanvas::deliverInitialMousePressEvent
+ //But specific to MouseArea, so doesn't belong in canvas
+ Q_Q(const QSGMouseArea);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ if (itemPrivate->opacity == 0.0)
+ return false;
+
+ if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
+ QPointF p = item->mapFromScene(sp);
+ if (!QRectF(0, 0, item->width(), item->height()).contains(p))
+ return false;
+ }
+
+ QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
+ for (int ii = children.count() - 1; ii >= 0; --ii) {
+ QSGItem *child = children.at(ii);
+ if (!child->isVisible() || !child->isEnabled())
+ continue;
+ if (propagateHelper(ev, child, sp, sig))
+ return true;
+ }
+
+ QSGMouseArea* ma = qobject_cast<QSGMouseArea*>(item);
+ if (ma && ma != q && itemPrivate->acceptedMouseButtons & ev->button()) {
+ switch(sig){
+ case Click:
+ if (!ma->d_func()->isClickConnected())
+ return false;
+ break;
+ case DoubleClick:
+ if (!ma->d_func()->isDoubleClickConnected())
+ return false;
+ break;
+ case PressAndHold:
+ if (!ma->d_func()->isPressAndHoldConnected())
+ return false;
+ break;
+ }
+ QPointF p = item->mapFromScene(sp);
+ if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
+ ev->setX(p.x());
+ ev->setY(p.y());
+ ev->setAccepted(true);//It is connected, they have to explicitly ignore to let it slide
+ switch(sig){
+ case Click: emit ma->clicked(ev); break;
+ case DoubleClick: emit ma->doubleClicked(ev); break;
+ case PressAndHold: emit ma->pressAndHold(ev); break;
+ }
+ if (ev->isAccepted())
+ return true;
+ }
+ }
+ return false;
+
+}
+
+/*
+ Behavioral Change in QtQuick 2.0
+
+ From QtQuick 2.0, the signals clicked, doubleClicked and pressAndHold have a different interaction
+ model with regards to the delivery of events to multiple overlapping MouseAreas. These signals will now propagate
+ to all MouseAreas in the area, in painting order, until accepted by one of them. A signal is accepted by
+ default if there is a signal handler for it, use mouse.accepted = false; to ignore. This propagation
+ can send the signal to MouseAreas other than the one which accepted the press event, although that MouseArea
+ will receive the signal first.
+
+ Note that to get the same behavior as a QtQuick 1.0 MouseArea{} with regard to absorbing all mouse events, you will
+ now need to add empty signal handlers for these three signals.
+ */
+QSGMouseArea::QSGMouseArea(QSGItem *parent)
+ : QSGItem(*(new QSGMouseAreaPrivate), parent)
+{
+ Q_D(QSGMouseArea);
+ d->init();
+}
+
+QSGMouseArea::~QSGMouseArea()
+{
+}
+
+qreal QSGMouseArea::mouseX() const
+{
+ Q_D(const QSGMouseArea);
+ return d->lastPos.x();
+}
+
+qreal QSGMouseArea::mouseY() const
+{
+ Q_D(const QSGMouseArea);
+ return d->lastPos.y();
+}
+
+bool QSGMouseArea::isEnabled() const
+{
+ Q_D(const QSGMouseArea);
+ return d->absorb;
+}
+
+void QSGMouseArea::setEnabled(bool a)
+{
+ Q_D(QSGMouseArea);
+ if (a != d->absorb) {
+ d->absorb = a;
+ emit enabledChanged();
+ }
+}
+
+bool QSGMouseArea::preventStealing() const
+{
+ Q_D(const QSGMouseArea);
+ return d->preventStealing;
+}
+
+void QSGMouseArea::setPreventStealing(bool prevent)
+{
+ Q_D(QSGMouseArea);
+ if (prevent != d->preventStealing) {
+ d->preventStealing = prevent;
+ setKeepMouseGrab(d->preventStealing && d->absorb);
+ emit preventStealingChanged();
+ }
+}
+
+Qt::MouseButtons QSGMouseArea::pressedButtons() const
+{
+ Q_D(const QSGMouseArea);
+ return d->lastButtons;
+}
+
+void QSGMouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGMouseArea);
+ d->moved = false;
+ d->stealMouse = d->preventStealing;
+ if (!d->absorb)
+ QSGItem::mousePressEvent(event);
+ else {
+ d->longPress = false;
+ d->saveEvent(event);
+ if (d->drag) {
+ d->dragX = drag()->axis() & QSGDrag::XAxis;
+ d->dragY = drag()->axis() & QSGDrag::YAxis;
+ }
+ if (d->drag)
+ d->drag->setActive(false);
+ setHovered(true);
+ d->startScene = event->scenePos();
+ d->pressAndHoldTimer.start(PressAndHoldDelay, this);
+ setKeepMouseGrab(d->stealMouse);
+ event->setAccepted(setPressed(true));
+
+ if(!event->isAccepted() && d->forwardToList.count())
+ d->forwardEvent(event);
+ }
+}
+
+void QSGMouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGMouseArea);
+ if (!d->absorb) {
+ QSGItem::mouseMoveEvent(event);
+ return;
+ }
+
+ d->saveEvent(event);
+
+ // ### we should skip this if these signals aren't used
+ // ### can GV handle this for us?
+ bool contains = boundingRect().contains(d->lastPos);
+ if (d->hovered && !contains)
+ setHovered(false);
+ else if (!d->hovered && contains)
+ setHovered(true);
+
+ if (d->drag && d->drag->target()) {
+ if (!d->moved) {
+ d->startX = drag()->target()->x();
+ d->startY = drag()->target()->y();
+ }
+
+ QPointF startLocalPos;
+ QPointF curLocalPos;
+ if (drag()->target()->parentItem()) {
+ startLocalPos = drag()->target()->parentItem()->mapFromScene(d->startScene);
+ curLocalPos = drag()->target()->parentItem()->mapFromScene(event->scenePos());
+ } else {
+ startLocalPos = d->startScene;
+ curLocalPos = event->scenePos();
+ }
+
+ const int dragThreshold = QApplication::startDragDistance();
+ qreal dx = qAbs(curLocalPos.x() - startLocalPos.x());
+ qreal dy = qAbs(curLocalPos.y() - startLocalPos.y());
+
+ if (keepMouseGrab() && d->stealMouse)
+ d->drag->setActive(true);
+
+ if (d->dragX && d->drag->active()) {
+ qreal x = (curLocalPos.x() - startLocalPos.x()) + d->startX;
+ if (x < drag()->xmin())
+ x = drag()->xmin();
+ else if (x > drag()->xmax())
+ x = drag()->xmax();
+ drag()->target()->setX(x);
+ }
+ if (d->dragY && d->drag->active()) {
+ qreal y = (curLocalPos.y() - startLocalPos.y()) + d->startY;
+ if (y < drag()->ymin())
+ y = drag()->ymin();
+ else if (y > drag()->ymax())
+ y = drag()->ymax();
+ drag()->target()->setY(y);
+ }
+
+ if (!keepMouseGrab()) {
+ if ((!d->dragY && dy < dragThreshold && d->dragX && dx > dragThreshold)
+ || (!d->dragX && dx < dragThreshold && d->dragY && dy > dragThreshold)
+ || (d->dragX && d->dragY && (dx > dragThreshold || dy > dragThreshold))) {
+ setKeepMouseGrab(true);
+ d->stealMouse = true;
+ }
+ }
+
+ d->moved = true;
+ }
+ QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+ emit mousePositionChanged(&me);
+ me.setX(d->lastPos.x());
+ me.setY(d->lastPos.y());
+ emit positionChanged(&me);
+
+ if(!event->isAccepted() && d->forwardToList.count())
+ d->forwardEvent(event);
+}
+
+void QSGMouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGMouseArea);
+ d->stealMouse = false;
+ if (!d->absorb) {
+ QSGItem::mouseReleaseEvent(event);
+ } else {
+ d->saveEvent(event);
+ setPressed(false);
+ if (d->drag)
+ d->drag->setActive(false);
+ // If we don't accept hover, we need to reset containsMouse.
+ if (!acceptHoverEvents())
+ setHovered(false);
+ QSGCanvas *c = canvas();
+ if (c && c->mouseGrabberItem() == this)
+ ungrabMouse();
+ setKeepMouseGrab(false);
+
+ if(!event->isAccepted() && d->forwardToList.count())
+ d->forwardEvent(event);
+ }
+ d->doubleClick = false;
+}
+
+void QSGMouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGMouseArea);
+ if (!d->absorb) {
+ QSGItem::mouseDoubleClickEvent(event);
+ } else {
+ d->saveEvent(event);
+ QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false);
+ me.setAccepted(d->isDoubleClickConnected());
+ emit this->doubleClicked(&me);
+ if (!me.isAccepted())
+ d->propagate(&me, QSGMouseAreaPrivate::DoubleClick);
+ d->doubleClick = d->isDoubleClickConnected() || me.isAccepted();
+ QSGItem::mouseDoubleClickEvent(event);
+ }
+}
+
+void QSGMouseArea::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_D(QSGMouseArea);
+ if (!d->absorb) {
+ QSGItem::hoverEnterEvent(event);
+ } else {
+ d->lastPos = event->pos();
+ setHovered(true);
+ QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
+ emit mousePositionChanged(&me);
+ }
+}
+
+void QSGMouseArea::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_D(QSGMouseArea);
+ if (!d->absorb) {
+ QSGItem::hoverMoveEvent(event);
+ } else {
+ d->lastPos = event->pos();
+ QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false);
+ emit mousePositionChanged(&me);
+ me.setX(d->lastPos.x());
+ me.setY(d->lastPos.y());
+ emit positionChanged(&me);
+ }
+}
+
+void QSGMouseArea::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
+{
+ Q_D(QSGMouseArea);
+ if (!d->absorb)
+ QSGItem::hoverLeaveEvent(event);
+ else
+ setHovered(false);
+}
+
+void QSGMouseArea::mouseUngrabEvent()
+{
+ Q_D(QSGMouseArea);
+ if (d->pressed) {
+ // if our mouse grab has been removed (probably by Flickable), fix our
+ // state
+ d->pressed = false;
+ d->stealMouse = false;
+ setKeepMouseGrab(false);
+ emit canceled();
+ emit pressedChanged();
+ if (d->hovered) {
+ d->hovered = false;
+ emit hoveredChanged();
+ }
+ }
+}
+
+bool QSGMouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGMouseArea);
+ QGraphicsSceneMouseEvent mouseEvent(event->type());
+ QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+
+ QSGCanvas *c = canvas();
+ QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
+ bool stealThisEvent = d->stealMouse;
+ if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
+ mouseEvent.setAccepted(false);
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
+ }
+ }
+ mouseEvent.setScenePos(event->scenePos());
+ mouseEvent.setLastScenePos(event->lastScenePos());
+ mouseEvent.setPos(mapFromScene(event->scenePos()));
+ mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
+
+ switch(mouseEvent.type()) {
+ case QEvent::GraphicsSceneMouseMove:
+ mouseMoveEvent(&mouseEvent);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ mousePressEvent(&mouseEvent);
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ mouseReleaseEvent(&mouseEvent);
+ break;
+ default:
+ break;
+ }
+ grabber = c->mouseGrabberItem();
+ if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
+ grabMouse();
+
+ return stealThisEvent;
+ }
+ if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
+ if (d->pressed) {
+ d->pressed = false;
+ d->stealMouse = false;
+ if (c && c->mouseGrabberItem() == this)
+ ungrabMouse();
+ emit canceled();
+ emit pressedChanged();
+ if (d->hovered) {
+ d->hovered = false;
+ emit hoveredChanged();
+ }
+ }
+ }
+ return false;
+}
+
+bool QSGMouseArea::childMouseEventFilter(QSGItem *i, QEvent *e)
+{
+ Q_D(QSGMouseArea);
+ if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren())
+ return QSGItem::childMouseEventFilter(i, e);
+ switch (e->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
+ default:
+ break;
+ }
+
+ return QSGItem::childMouseEventFilter(i, e);
+}
+
+void QSGMouseArea::timerEvent(QTimerEvent *event)
+{
+ Q_D(QSGMouseArea);
+ if (event->timerId() == d->pressAndHoldTimer.timerId()) {
+ d->pressAndHoldTimer.stop();
+ bool dragged = d->drag && d->drag->active();
+ if (d->pressed && dragged == false && d->hovered == true) {
+ d->longPress = true;
+ QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress);
+ me.setAccepted(d->isPressAndHoldConnected());
+ emit pressAndHold(&me);
+ if (!me.isAccepted())
+ d->propagate(&me, QSGMouseAreaPrivate::PressAndHold);
+ }
+ }
+}
+
+void QSGMouseArea::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ Q_D(QSGMouseArea);
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+
+ if (d->lastScenePos.isNull)
+ d->lastScenePos = mapToScene(d->lastPos);
+ else if (newGeometry.x() != oldGeometry.x() || newGeometry.y() != oldGeometry.y())
+ d->lastPos = mapFromScene(d->lastScenePos);
+}
+
+void QSGMouseArea::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QSGMouseArea);
+ switch (change) {
+ case ItemVisibleHasChanged:
+ if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse()))
+ setHovered(!d->hovered);
+ break;
+ default:
+ break;
+ }
+
+ QSGItem::itemChange(change, value);
+}
+
+bool QSGMouseArea::hoverEnabled() const
+{
+ return acceptHoverEvents();
+}
+
+void QSGMouseArea::setHoverEnabled(bool h)
+{
+ Q_D(QSGMouseArea);
+ if (h == acceptHoverEvents())
+ return;
+
+ setAcceptHoverEvents(h);
+ emit hoverEnabledChanged();
+ if (d->hovered != isUnderMouse())
+ setHovered(!d->hovered);
+}
+
+bool QSGMouseArea::hovered() const
+{
+ Q_D(const QSGMouseArea);
+ return d->hovered;
+}
+
+bool QSGMouseArea::pressed() const
+{
+ Q_D(const QSGMouseArea);
+ return d->pressed;
+}
+
+void QSGMouseArea::setHovered(bool h)
+{
+ Q_D(QSGMouseArea);
+ if (d->hovered != h) {
+ d->hovered = h;
+ emit hoveredChanged();
+ d->hovered ? emit entered() : emit exited();
+ }
+}
+
+Qt::MouseButtons QSGMouseArea::acceptedButtons() const
+{
+ return acceptedMouseButtons();
+}
+
+void QSGMouseArea::setAcceptedButtons(Qt::MouseButtons buttons)
+{
+ if (buttons != acceptedMouseButtons()) {
+ setAcceptedMouseButtons(buttons);
+ emit acceptedButtonsChanged();
+ }
+}
+
+bool QSGMouseArea::setPressed(bool p)
+{
+ Q_D(QSGMouseArea);
+ bool dragged = d->drag && d->drag->active();
+ bool isclick = d->pressed == true && p == false && dragged == false && d->hovered == true;
+
+ if (d->pressed != p) {
+ d->pressed = p;
+ QSGMouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, isclick, d->longPress);
+ if (d->pressed) {
+ if (!d->doubleClick)
+ emit pressed(&me);
+ me.setX(d->lastPos.x());
+ me.setY(d->lastPos.y());
+ emit mousePositionChanged(&me);
+ emit pressedChanged();
+ } else {
+ emit released(&me);
+ me.setX(d->lastPos.x());
+ me.setY(d->lastPos.y());
+ emit pressedChanged();
+ if (isclick && !d->longPress && !d->doubleClick){
+ me.setAccepted(d->isClickConnected());
+ emit clicked(&me);
+ if (!me.isAccepted())
+ d->propagate(&me, QSGMouseAreaPrivate::Click);
+ }
+ }
+
+ return me.isAccepted();
+ }
+ return false;
+}
+
+QSGDrag *QSGMouseArea::drag()
+{
+ Q_D(QSGMouseArea);
+ if (!d->drag)
+ d->drag = new QSGDrag;
+ return d->drag;
+}
+
+QDeclarativeListProperty<QSGItem> QSGMouseArea::forwardTo()
+{
+ Q_D(QSGMouseArea);
+ return d->forwardTo;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgmousearea_p.h b/src/declarative/items/qsgmousearea_p.h
new file mode 100644
index 0000000000..469b9f7168
--- /dev/null
+++ b/src/declarative/items/qsgmousearea_p.h
@@ -0,0 +1,219 @@
+// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGMOUSEAREA_P_H
+#define QSGMOUSEAREA_P_H
+
+#include "qsgitem.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_AUTOTEST_EXPORT QSGDrag : public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS(Axis)
+ Q_PROPERTY(QSGItem *target READ target WRITE setTarget NOTIFY targetChanged RESET resetTarget)
+ Q_PROPERTY(Axis axis READ axis WRITE setAxis NOTIFY axisChanged)
+ Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged)
+ Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged)
+ Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged)
+ Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged)
+ Q_PROPERTY(bool filterChildren READ filterChildren WRITE setFilterChildren NOTIFY filterChildrenChanged)
+ //### consider drag and drop
+
+public:
+ QSGDrag(QObject *parent=0);
+ ~QSGDrag();
+
+ QSGItem *target() const;
+ void setTarget(QSGItem *);
+ void resetTarget();
+
+ enum Axis { XAxis=0x01, YAxis=0x02, XandYAxis=0x03 };
+ Axis axis() const;
+ void setAxis(Axis);
+
+ qreal xmin() const;
+ void setXmin(qreal);
+ qreal xmax() const;
+ void setXmax(qreal);
+ qreal ymin() const;
+ void setYmin(qreal);
+ qreal ymax() const;
+ void setYmax(qreal);
+
+ bool active() const;
+ void setActive(bool);
+
+ bool filterChildren() const;
+ void setFilterChildren(bool);
+
+Q_SIGNALS:
+ void targetChanged();
+ void axisChanged();
+ void minimumXChanged();
+ void maximumXChanged();
+ void minimumYChanged();
+ void maximumYChanged();
+ void activeChanged();
+ void filterChildrenChanged();
+
+private:
+ QSGItem *_target;
+ Axis _axis;
+ qreal _xmin;
+ qreal _xmax;
+ qreal _ymin;
+ qreal _ymax;
+ bool _active : 1;
+ bool _filterChildren: 1;
+ Q_DISABLE_COPY(QSGDrag)
+};
+
+class QSGMouseEvent;
+class QSGMouseAreaPrivate;
+class Q_AUTOTEST_EXPORT QSGMouseArea : public QSGItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal mouseX READ mouseX NOTIFY mousePositionChanged)
+ Q_PROPERTY(qreal mouseY READ mouseY NOTIFY mousePositionChanged)
+ Q_PROPERTY(bool containsMouse READ hovered NOTIFY hoveredChanged)
+ Q_PROPERTY(bool pressed READ pressed NOTIFY pressedChanged)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons NOTIFY pressedChanged)
+ Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
+ Q_PROPERTY(bool hoverEnabled READ hoverEnabled WRITE setHoverEnabled NOTIFY hoverEnabledChanged)
+ Q_PROPERTY(QSGDrag *drag READ drag CONSTANT) //### add flicking to QSGDrag or add a QDeclarativeFlick ???
+ Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged)
+ Q_PROPERTY(QDeclarativeListProperty<QSGItem> forwardTo READ forwardTo);
+
+public:
+ QSGMouseArea(QSGItem *parent=0);
+ ~QSGMouseArea();
+
+ qreal mouseX() const;
+ qreal mouseY() const;
+
+ bool isEnabled() const;
+ void setEnabled(bool);
+
+ bool hovered() const;
+ bool pressed() const;
+
+ Qt::MouseButtons pressedButtons() const;
+
+ Qt::MouseButtons acceptedButtons() const;
+ void setAcceptedButtons(Qt::MouseButtons buttons);
+
+ bool hoverEnabled() const;
+ void setHoverEnabled(bool h);
+
+ QSGDrag *drag();
+
+ bool preventStealing() const;
+ void setPreventStealing(bool prevent);
+
+ QDeclarativeListProperty<QSGItem> forwardTo();
+
+Q_SIGNALS:
+ void hoveredChanged();
+ void pressedChanged();
+ void enabledChanged();
+ void acceptedButtonsChanged();
+ void hoverEnabledChanged();
+ void positionChanged(QSGMouseEvent *mouse);
+ void mousePositionChanged(QSGMouseEvent *mouse);
+ void preventStealingChanged();
+
+ void pressed(QSGMouseEvent *mouse);
+ void pressAndHold(QSGMouseEvent *mouse);
+ void released(QSGMouseEvent *mouse);
+ void clicked(QSGMouseEvent *mouse);
+ void doubleClicked(QSGMouseEvent *mouse);
+ void entered();
+ void exited();
+ void canceled();
+
+protected:
+ void setHovered(bool);
+ bool setPressed(bool);
+ bool sendMouseEvent(QGraphicsSceneMouseEvent *event);
+
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseUngrabEvent();
+ virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
+ virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
+ virtual bool childMouseEventFilter(QSGItem *i, QEvent *e);
+ virtual void timerEvent(QTimerEvent *event);
+
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+ virtual void itemChange(ItemChange change, const ItemChangeData& value);
+
+private:
+ void handlePress();
+ void handleRelease();
+
+private:
+ Q_DISABLE_COPY(QSGMouseArea)
+ Q_DECLARE_PRIVATE(QSGMouseArea)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGDrag)
+QML_DECLARE_TYPE(QSGMouseArea)
+
+QT_END_HEADER
+
+#endif // QSGMOUSEAREA_P_H
diff --git a/src/declarative/items/qsgmousearea_p_p.h b/src/declarative/items/qsgmousearea_p_p.h
new file mode 100644
index 0000000000..e736c059a2
--- /dev/null
+++ b/src/declarative/items/qsgmousearea_p_p.h
@@ -0,0 +1,115 @@
+// Commit: c6e6a35aeb8794d68a3ca0c4e27a3a1181c066b5
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGMOUSEAREA_P_P_H
+#define QSGMOUSEAREA_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgitem_p.h"
+
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGMouseEvent;
+class QSGMouseArea;
+class QSGMouseAreaPrivate : public QSGItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGMouseArea)
+
+public:
+ QSGMouseAreaPrivate();
+ ~QSGMouseAreaPrivate();
+ void init();
+
+ void saveEvent(QGraphicsSceneMouseEvent *event);
+ enum PropagateType{
+ Click,
+ DoubleClick,
+ PressAndHold
+ };
+ void propagate(QSGMouseEvent* event, PropagateType);
+ bool propagateHelper(QSGMouseEvent*, QSGItem*,const QPointF &, PropagateType);
+ void forwardEvent(QGraphicsSceneMouseEvent* event);
+
+ bool isPressAndHoldConnected();
+ bool isDoubleClickConnected();
+ bool isClickConnected();
+
+ bool absorb : 1;
+ bool hovered : 1;
+ bool pressed : 1;
+ bool longPress : 1;
+ bool moved : 1;
+ bool dragX : 1;
+ bool dragY : 1;
+ bool stealMouse : 1;
+ bool doubleClick : 1;
+ bool preventStealing : 1;
+ QSGDrag *drag;
+ QPointF startScene;
+ qreal startX;
+ qreal startY;
+ QPointF lastPos;
+ QDeclarativeNullableValue<QPointF> lastScenePos;
+ Qt::MouseButton lastButton;
+ Qt::MouseButtons lastButtons;
+ Qt::KeyboardModifiers lastModifiers;
+ QBasicTimer pressAndHoldTimer;
+ QDeclarativeListProperty<QSGItem> forwardTo;
+ QList<QSGItem*> forwardToList;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGMOUSEAREA_P_P_H
diff --git a/src/declarative/items/qsgninepatchnode.cpp b/src/declarative/items/qsgninepatchnode.cpp
new file mode 100644
index 0000000000..eaa97178f4
--- /dev/null
+++ b/src/declarative/items/qsgninepatchnode.cpp
@@ -0,0 +1,292 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgninepatchnode_p.h"
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qmath_p.h>
+
+QSGNinePatchNode::QSGNinePatchNode()
+ : m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
+ , m_horizontalTileMode(QSGBorderImage::Stretch)
+ , m_verticalTileMode(QSGBorderImage::Stretch)
+ , m_dirtyGeometry(false)
+ , m_mirror(false)
+{
+ setOpaqueMaterial(&m_material);
+ setMaterial(&m_materialO);
+ setGeometry(&m_geometry);
+ m_geometry.setDrawingMode(GL_TRIANGLES);
+}
+
+void QSGNinePatchNode::setInnerRect(const QRectF &rect)
+{
+ if (m_innerRect == rect)
+ return;
+ m_innerRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGNinePatchNode::setRect(const QRectF &rect)
+{
+ if (m_targetRect == rect)
+ return;
+ m_targetRect = rect;
+ m_dirtyGeometry = true;
+}
+
+void QSGNinePatchNode::setHorzontalTileMode(QSGBorderImage::TileMode mode)
+{
+ if (mode == QSGBorderImage::TileMode(m_horizontalTileMode))
+ return;
+ m_horizontalTileMode = mode;
+ m_dirtyGeometry = true;
+}
+
+
+void QSGNinePatchNode::setVerticalTileMode(QSGBorderImage::TileMode mode)
+{
+ if (mode == QSGBorderImage::TileMode(m_verticalTileMode))
+ return;
+ m_verticalTileMode = mode;
+ m_dirtyGeometry = true;
+}
+
+
+void QSGNinePatchNode::setFiltering(QSGTexture::Filtering filtering)
+{
+ if (m_material.filtering() == filtering)
+ return;
+
+ m_material.setFiltering(filtering);
+ m_materialO.setFiltering(filtering);
+ markDirty(DirtyMaterial);
+}
+
+QSGTexture::Filtering QSGNinePatchNode::filtering() const
+{
+ return m_material.filtering();
+}
+
+void QSGNinePatchNode::setTexture(QSGTexture *texture)
+{
+ if (texture == m_material.texture())
+ return;
+ m_material.setTexture(texture);
+ m_materialO.setTexture(texture);
+ markDirty(DirtyMaterial);
+}
+
+QSGTexture *QSGNinePatchNode::texture() const
+{
+ return m_material.texture();
+}
+
+void QSGNinePatchNode::setMirror(bool m)
+{
+ if (m_mirror == m)
+ return;
+ m_mirror = m;
+ m_dirtyGeometry = true;
+}
+
+
+void QSGNinePatchNode::update()
+{
+ if (!m_dirtyGeometry)
+ return;
+
+ // For stretch this algorithm could be simplified to use less vertices
+ // as more vertices could be reused then, but I doubt its where our main
+ // problem will lie. This way, we at least share the algorithm between all
+
+ Q_ASSERT(m_material.texture());
+
+ float tw = m_material.texture()->textureSize().width();
+ float th = m_material.texture()->textureSize().height();
+
+ float rightBorder = tw - m_innerRect.right();
+ float bottomBorder = th - m_innerRect.bottom();
+
+// qDebug() << m_innerRect << m_targetRect << m_horizontalTileMode << m_verticalTileMode;
+
+ int xChunkCount = 0; // Number of chunks
+ float xChunkSize = 0; // Size of chunk in pixels
+ float xTexSize = m_innerRect.width(); // Size of the texture to stretch/tile
+ float xSize = m_targetRect.width() - m_innerRect.left() - rightBorder; // Size of area to fill with chunks
+
+ if (m_horizontalTileMode == QSGBorderImage::Repeat) {
+ xChunkCount = qCeil(xSize / xTexSize);
+ xChunkSize = xTexSize;
+ } else if (m_horizontalTileMode == QSGBorderImage::Round) {
+ xChunkCount = qCeil(xSize / xTexSize);
+ qreal fullWidth = xChunkCount * xTexSize;
+ xChunkSize = xTexSize * xSize / fullWidth;
+ } else {
+ xChunkCount = 1;
+ xChunkSize = xSize;
+ }
+
+ int yChunkCount = 0;
+ float yChunkSize = 0; // Relative to target rect.
+ float yTexSize = m_innerRect.height(); // Size of the texture to stretch/tile
+ float ySize = m_targetRect.height() - m_innerRect.top() - bottomBorder;
+
+ if (m_verticalTileMode == QSGBorderImage::Repeat) {
+ yChunkCount = qCeil(ySize / yTexSize);
+ yChunkSize = yTexSize;
+ } else if (m_verticalTileMode == QSGBorderImage::Round) {
+ yChunkCount = qCeil(ySize / yTexSize);
+ qreal fullHeight = yChunkCount * yTexSize;
+ yChunkSize = yTexSize * ySize / fullHeight;
+ } else {
+ yChunkCount = 1;
+ yChunkSize = ySize;
+ }
+
+ int xTotalChunkCount = xChunkCount + 2;
+ int yTotalChunkCount = yChunkCount + 2;
+
+ int totalChunkCount = xTotalChunkCount * yTotalChunkCount;
+ int vertexCount = totalChunkCount * 4;
+ int indexCount = totalChunkCount * 6;
+
+ if (vertexCount != m_geometry.vertexCount() || indexCount != m_geometry.indexCount())
+ m_geometry.allocate(vertexCount, indexCount);
+
+ QSGGeometry::TexturedPoint2D *v = m_geometry.vertexDataAsTexturedPoint2D();
+
+
+ // Fill in the vertices.. The loop below is pretty much an exact replica
+ // of the one inside fillRow.
+ float yTexChunk1 = m_innerRect.top() / th;
+ float yTexChunk2 = m_innerRect.bottom() / th;
+
+ fillRow(v, 0, 0, xChunkCount, xChunkSize);
+ fillRow(v, m_innerRect.y(), yTexChunk1, xChunkCount, xChunkSize);
+
+ for (int yc=0; yc<yChunkCount; ++yc) {
+ float yy = m_innerRect.y() + yChunkSize * yc;
+ fillRow(v, yy, yTexChunk1, xChunkCount, xChunkSize);
+
+ // Special case the last one
+ if (yc == yChunkCount - 1) {
+ float t = m_verticalTileMode == QSGBorderImage::Repeat
+ ? yTexChunk1 + (yTexChunk2 - yTexChunk1) * (m_targetRect.height() - bottomBorder - yy) / yChunkSize
+ : yTexChunk2;
+ fillRow(v, m_targetRect.height() - bottomBorder, t, xChunkCount, xChunkSize);
+ } else {
+ fillRow(v, yy + yChunkSize, yTexChunk2, xChunkCount, xChunkSize);
+ }
+ }
+
+ fillRow(v, m_targetRect.height() - bottomBorder, yTexChunk2, xChunkCount, xChunkSize);
+ fillRow(v, m_targetRect.height(), 1, xChunkCount, xChunkSize);
+
+ if (m_mirror) {
+ v = m_geometry.vertexDataAsTexturedPoint2D();
+ for (int i=0; i<m_geometry.vertexCount(); ++i) {
+ v->x = m_targetRect.width() - v->x;
+ ++v;
+ }
+ }
+
+// v = m_geometry.vertexDataAsTexturedPoint2D();
+// for (int i=0; i<m_geometry.vertexCount(); ++i) {
+// printf("Vertex: %d: (%.3f, %.3f) - (%.3f, %.3f)\n",
+// i,
+// v->x, v->y, v->tx, v->ty);
+// ++v;
+// }
+
+ quint16 *i = m_geometry.indexDataAsUShort();
+ int row = xTotalChunkCount * 2;
+ for (int r=0; r<yTotalChunkCount; ++r) {
+ int offset = r * row * 2;
+ for (int c=0; c<xTotalChunkCount; ++c) {
+ *i++ = offset + c * 2;
+ *i++ = offset + c * 2 + 1;
+ *i++ = offset + c * 2 + row;
+ *i++ = offset + c * 2 + 1;
+ *i++ = offset + c * 2 + row + 1;
+ *i++ = offset + c * 2 + row;
+ }
+ }
+
+// i = m_geometry.indexDataAsUShort();
+// for (int idx=0; idx<m_geometry.indexCount(); idx+=6) {
+// printf("%2d: ", idx / 6);
+// for (int s=0; s<6; ++s)
+// printf(" %d", i[idx + s]);
+// printf("\n");
+// }
+
+ markDirty(QSGNode::DirtyGeometry);
+}
+
+void QSGNinePatchNode::fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize)
+{
+ float tw = m_material.texture()->textureSize().width();
+ float rightBorder = tw - m_innerRect.right();
+ float xTexChunk1 = m_innerRect.left() / tw;
+ float xTexChunk2 = m_innerRect.right() / tw;
+
+ v++->set(0, y, 0, ty);
+ v++->set(m_innerRect.x(), y, xTexChunk1, ty);
+
+ for (int xc=0; xc<xChunkCount; ++xc) {
+ float xx = m_innerRect.x() + xChunkSize * xc;
+ v++->set(xx, y, xTexChunk1, ty);
+
+ // Special case the last one
+ if (xc == xChunkCount - 1) {
+ float t = m_horizontalTileMode == QSGBorderImage::Repeat
+ ? xTexChunk1 + (xTexChunk2 - xTexChunk1) * (m_targetRect.width() - rightBorder - xx) / xChunkSize
+ : xTexChunk2;
+ v->set(m_targetRect.width() - rightBorder, y, t, ty);
+ } else {
+ v->set(xx + xChunkSize, y, xTexChunk2, ty);
+ }
+ ++v;
+ }
+
+ v++->set(m_targetRect.width() - rightBorder, y, xTexChunk2, ty);
+ v++->set(m_targetRect.width(), y, 1, ty);
+}
diff --git a/src/declarative/items/qsgninepatchnode_p.h b/src/declarative/items/qsgninepatchnode_p.h
new file mode 100644
index 0000000000..ef1686d528
--- /dev/null
+++ b/src/declarative/items/qsgninepatchnode_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGNINEPATCHNODE_H
+#define QSGNINEPATCHNODE_H
+
+#include "qsgnode.h"
+#include "qsgtexturematerial.h"
+#include "qsgborderimage_p.h"
+
+class TextureReference;
+
+class QSGNinePatchNode : public QSGGeometryNode
+{
+public:
+ QSGNinePatchNode();
+
+ void setTexture(QSGTexture *texture);
+ QSGTexture *texture() const;
+
+ void setRect(const QRectF &rect);
+ QRectF rect() const { return m_targetRect; }
+
+ void setInnerRect(const QRectF &rect);
+ QRectF innerRect() const { return m_innerRect; }
+
+ void setFiltering(QSGTexture::Filtering filtering);
+ QSGTexture::Filtering filtering() const;
+
+ void setHorzontalTileMode(QSGBorderImage::TileMode mode);
+ QSGBorderImage::TileMode horizontalTileMode() const {
+ return (QSGBorderImage::TileMode) m_horizontalTileMode;
+ }
+
+ void setVerticalTileMode(QSGBorderImage::TileMode mode);
+ QSGBorderImage::TileMode verticalTileMode() const {
+ return (QSGBorderImage::TileMode) m_verticalTileMode;
+ }
+
+ void setMirror(bool m);
+ bool mirror() const { return m_mirror; }
+
+ void update();
+
+private:
+ void fillRow(QSGGeometry::TexturedPoint2D *&v, float y, float ty, int xChunkCount, float xChunkSize);
+ QRectF m_targetRect;
+ QRectF m_innerRect;
+ QSGOpaqueTextureMaterial m_material;
+ QSGTextureMaterial m_materialO;
+ QSGGeometry m_geometry;
+
+ uint m_horizontalTileMode : 2;
+ uint m_verticalTileMode : 2;
+
+ uint m_dirtyGeometry : 1;
+ uint m_mirror : 1;
+};
+
+#endif // QSGNINEPATCHNODE_H
diff --git a/src/declarative/items/qsgpainteditem.cpp b/src/declarative/items/qsgpainteditem.cpp
new file mode 100644
index 0000000000..775405c1fb
--- /dev/null
+++ b/src/declarative/items/qsgpainteditem.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgpainteditem.h"
+#include <private/qsgpainteditem_p.h>
+#include <private/qsgpainternode_p.h>
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSGPaintedItem
+ \brief The QSGPaintedItem class provides a way to use the QPainter API in the
+ QML Scene Graph.
+
+ The QSGPaintedItem makes it possible to use the QPainter API with the QML Scene Graph.
+ It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint
+ onto the texture. The render target can be either a QImage or a QGLFramebufferObject.
+ When the render target is a QImage, QPainter first renders into the image then
+ the content is uploaded to the texture.
+ When a QGLFramebufferObject is used, QPainter paints directly onto the texture.
+ Call update() to trigger a repaint.
+
+ To enable QPainter to do anti-aliased rendering, use setAntialiasing().
+
+ QSGPaintedItem is meant to make it easier to port old code that is using the
+ QPainter API to the QML Scene Graph API and it should be used only for that purpose.
+
+ To write your own painted item, you first create a subclass of QSGPaintedItem, and then
+ start by implementing its only pure virtual public function: paint(), which implements
+ the actual painting. To get the size of the area painted by the item, use
+ contentsBoundingRect().
+*/
+
+/*!
+ \enum QSGPaintedItem::RenderTarget
+
+ This enum describes QSGPaintedItem's render targets. The render target is the
+ surface QPainter paints onto before the item is rendered on screen.
+
+ \value Image The default; QPainter paints into a QImage using the raster paint engine.
+ The image's content needs to be uploaded to graphics memory afterward, this operation
+ can potentially be slow if the item is large. This render target allows high quality
+ anti-aliasing and fast item resizing.
+
+ \value FramebufferObject QPainter paints into a QGLFramebufferObject using the GL
+ paint engine. Painting can be faster as no texture upload is required, but anti-aliasing
+ quality is not as good as if using an image. This render target allows faster rendering
+ in some cases, but you should avoid using it if the item is resized often.
+
+ \sa setRenderTarget()
+*/
+
+/*!
+ \internal
+*/
+QSGPaintedItemPrivate::QSGPaintedItemPrivate()
+ : QSGItemPrivate()
+ , contentsScale(1.0)
+ , fillColor(Qt::transparent)
+ , renderTarget(QSGPaintedItem::Image)
+ , geometryDirty(false)
+ , contentsDirty(false)
+ , opaquePainting(false)
+ , antialiasing(false)
+ , mipmap(false)
+{
+}
+
+/*!
+ Constructs a QSGPaintedItem with the given \a parent item.
+ */
+QSGPaintedItem::QSGPaintedItem(QSGItem *parent)
+ : QSGItem(*(new QSGPaintedItemPrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+/*!
+ \internal
+*/
+QSGPaintedItem::QSGPaintedItem(QSGPaintedItemPrivate &dd, QSGItem *parent)
+ : QSGItem(dd, parent)
+{
+ setFlag(ItemHasContents);
+}
+
+/*!
+ Destroys the QSGPaintedItem.
+*/
+QSGPaintedItem::~QSGPaintedItem()
+{
+}
+
+/*!
+ Schedules a redraw of the area covered by \a rect in this item. You can call this function
+ whenever your item needs to be redrawn, such as if it changes appearance or size.
+
+ This function does not cause an immediate paint; instead it schedules a paint request that
+ is processed by the QML Scene Graph when the next frame is rendered. The item will only be
+ redrawn if it is visible.
+
+ Note that calling this function will trigger a repaint of the whole scene.
+
+ \sa paint()
+*/
+void QSGPaintedItem::update(const QRect &rect)
+{
+ Q_D(QSGPaintedItem);
+ d->contentsDirty = true;
+
+ QRect srect(qCeil(rect.x()*d->contentsScale),
+ qCeil(rect.y()*d->contentsScale),
+ qCeil(rect.width()*d->contentsScale),
+ qCeil(rect.height()*d->contentsScale));
+
+ if (srect.isNull() && !d->dirtyRect.isNull())
+ d->dirtyRect = contentsBoundingRect().toAlignedRect();
+ else
+ d->dirtyRect |= (contentsBoundingRect() & srect).toAlignedRect();
+ QSGItem::update();
+}
+
+/*!
+ Returns true if this item is opaque; otherwise, false is returned.
+
+ By default, painted items are not opaque.
+
+ \sa setOpaquePainting()
+*/
+bool QSGPaintedItem::opaquePainting() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->opaquePainting;
+}
+
+/*!
+ If \a opaque is true, the item is opaque; otherwise, it is considered as translucent.
+
+ Opaque items are not blended with the rest of the scene, you should set this to true
+ if the content of the item is opaque to speed up rendering.
+
+ By default, painted items are not opaque.
+
+ \sa opaquePainting()
+*/
+void QSGPaintedItem::setOpaquePainting(bool opaque)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->opaquePainting == opaque)
+ return;
+
+ d->opaquePainting = opaque;
+ QSGItem::update();
+}
+
+/*!
+ Returns true if antialiased painting is enabled; otherwise, false is returned.
+
+ By default, antialiasing is not enabled.
+
+ \sa setAntialiasing()
+*/
+bool QSGPaintedItem::antialiasing() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->antialiasing;
+}
+
+/*!
+ If \a enable is true, antialiased painting is enabled.
+
+ By default, antialiasing is not enabled.
+
+ \sa antialiasing()
+*/
+void QSGPaintedItem::setAntialiasing(bool enable)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->antialiasing == enable)
+ return;
+
+ d->antialiasing = enable;
+ update();
+}
+
+/*!
+ Returns true if mipmaps are enabled; otherwise, false is returned.
+
+ By default, mipmapping is not enabled.
+
+ \sa setMipmap()
+*/
+bool QSGPaintedItem::mipmap() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->mipmap;
+}
+
+/*!
+ If \a enable is true, mipmapping is enabled on the associated texture.
+
+ Mipmapping increases rendering speed and reduces aliasing artifacts when the item is
+ scaled down.
+
+ By default, mipmapping is not enabled.
+
+ \sa mipmap()
+*/
+void QSGPaintedItem::setMipmap(bool enable)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->mipmap == enable)
+ return;
+
+ d->mipmap = enable;
+ update();
+}
+
+/*!
+ This function returns the outer bounds of the item as a rectangle; all painting must be
+ restricted to inside an item's bounding rect.
+
+ If the contents size has not been set it reflects the size of the item; otherwise
+ it reflects the contents size scaled by the contents scale.
+
+ Use this function to know the area painted by the item.
+
+ \sa QSGItem::width(), QSGItem::height(), contentsSize(), contentsScale()
+*/
+QRectF QSGPaintedItem::contentsBoundingRect() const
+{
+ Q_D(const QSGPaintedItem);
+
+ qreal w = d->width;
+ QSizeF sz = d->contentsSize * d->contentsScale;
+ if (w < sz.width())
+ w = sz.width();
+ qreal h = d->height;
+ if (h < sz.height())
+ h = sz.height();
+
+ return QRectF(0, 0, w, h);
+}
+
+/*!
+ \property QSGPaintedItem::contentsSize
+ \brief The size of the contents
+
+ The contents size is the size of the item in regards to how it is painted
+ using the paint() function. This is distinct from the size of the
+ item in regards to height() and width().
+*/
+QSize QSGPaintedItem::contentsSize() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->contentsSize;
+}
+
+void QSGPaintedItem::setContentsSize(const QSize &size)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->contentsSize == size)
+ return;
+
+ d->contentsSize = size;
+ update();
+}
+
+/*!
+ This convenience function is equivalent to calling setContentsSize(QSize()).
+*/
+void QSGPaintedItem::resetContentsSize()
+{
+ setContentsSize(QSize());
+}
+
+/*!
+ \property QSGPaintedItem::contentsScale
+ \brief The scale of the contents
+
+ All painting happening in paint() is scaled by the contents scale. This is distinct
+ from the scale of the item in regards to scale().
+
+ The default value is 1.
+*/
+qreal QSGPaintedItem::contentsScale() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->contentsScale;
+}
+
+void QSGPaintedItem::setContentsScale(qreal scale)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->contentsScale == scale)
+ return;
+
+ d->contentsScale = scale;
+ update();
+}
+
+/*!
+ \property QSGPaintedItem::fillColor
+ \brief The item's background fill color.
+
+ By default, the fill color is set to Qt::transparent.
+*/
+QColor QSGPaintedItem::fillColor() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->fillColor;
+}
+
+void QSGPaintedItem::setFillColor(const QColor &c)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->fillColor == c)
+ return;
+
+ d->fillColor = c;
+ update();
+
+ emit fillColorChanged();
+}
+
+/*!
+ \property QSGPaintedItem::renderTarget
+ \brief The item's render target.
+
+ This property defines which render target the QPainter renders into, it can be either
+ QSGPaintedItem::Image or QSGPaintedItem::FramebufferObject. Both have certains benefits,
+ typically performance versus quality. Using a framebuffer object avoids a costly upload
+ of the image contents to the texture in graphics memory, while using an image enables
+ high quality anti-aliasing.
+
+ \warning Resizing a framebuffer object is a costly operation, avoid using
+ the QSGPaintedItem::FramebufferObject render target if the item gets resized often.
+
+ By default, the render target is QSGPaintedItem::Image.
+*/
+QSGPaintedItem::RenderTarget QSGPaintedItem::renderTarget() const
+{
+ Q_D(const QSGPaintedItem);
+ return d->renderTarget;
+}
+
+void QSGPaintedItem::setRenderTarget(RenderTarget target)
+{
+ Q_D(QSGPaintedItem);
+
+ if (d->renderTarget == target)
+ return;
+
+ d->renderTarget = target;
+ update();
+
+ emit renderTargetChanged();
+}
+
+/*!
+ \fn virtual void QSGPaintedItem::paint(QPainter *painter) = 0
+
+ This function, which is usually called by the QML Scene Graph, paints the
+ contents of an item in local coordinates.
+
+ The function is called after the item has been filled with the fillColor.
+
+ Reimplement this function in a QSGPaintedItem subclass to provide the
+ item's painting implementation, using \a painter.
+
+ \note The QML Scene Graph uses two separate threads, the main thread does things such as
+ processing events or updating animations while a second thread does the actual OpenGL rendering.
+ As a consequence, paint() is not called from the main GUI thread but from the GL enabled
+ renderer thread. At the moment paint() is called, the GUI thread is blocked and this is
+ therefore thread-safe.
+*/
+
+/*!
+ This function is called after the item's geometry has changed.
+*/
+void QSGPaintedItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QSGPaintedItem);
+ d->geometryDirty = true;
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+
+/*!
+ This function is called when the Scene Graph node associated to the item needs to
+ be updated.
+*/
+QSGNode *QSGPaintedItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
+{
+ Q_UNUSED(data);
+ Q_D(QSGPaintedItem);
+
+ if (width() <= 0 || height() <= 0) {
+ delete oldNode;
+ return 0;
+ }
+
+ QSGPainterNode *node = static_cast<QSGPainterNode *>(oldNode);
+ if (!node)
+ node = new QSGPainterNode(this);
+
+ QRectF br = contentsBoundingRect();
+
+ node->setPreferredRenderTarget(d->renderTarget);
+ node->setSize(QSize(qRound(br.width()), qRound(br.height())));
+ node->setSmoothPainting(d->antialiasing);
+ node->setLinearFiltering(d->smooth);
+ node->setMipmapping(d->mipmap);
+ node->setOpaquePainting(d->opaquePainting);
+ node->setFillColor(d->fillColor);
+ node->setContentsScale(d->contentsScale);
+ node->setDirty(d->contentsDirty || d->geometryDirty, d->dirtyRect);
+ node->update();
+
+ d->contentsDirty = false;
+ d->geometryDirty = false;
+ d->dirtyRect = QRect();
+
+ return node;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgpainteditem.h b/src/declarative/items/qsgpainteditem.h
new file mode 100644
index 0000000000..a2d3a09e76
--- /dev/null
+++ b/src/declarative/items/qsgpainteditem.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPAINTEDITEM_P_H
+#define QSGPAINTEDITEM_P_H
+
+#include <qsgitem.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QSGPaintedItemPrivate;
+class Q_DECLARATIVE_EXPORT QSGPaintedItem : public QSGItem
+{
+ Q_OBJECT
+ Q_ENUMS(RenderTarget)
+
+ Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize NOTIFY contentsSizeChanged)
+ Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged)
+ Q_PROPERTY(qreal contentsScale READ contentsScale WRITE setContentsScale NOTIFY contentsScaleChanged)
+ Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged)
+public:
+ QSGPaintedItem(QSGItem *parent = 0);
+ virtual ~QSGPaintedItem();
+
+ enum RenderTarget {
+ Image,
+ FramebufferObject
+ };
+
+ void update(const QRect &rect = QRect());
+
+ bool opaquePainting() const;
+ void setOpaquePainting(bool opaque);
+
+ bool antialiasing() const;
+ void setAntialiasing(bool enable);
+
+ bool mipmap() const;
+ void setMipmap(bool enable);
+
+ QRectF contentsBoundingRect() const;
+
+ QSize contentsSize() const;
+ void setContentsSize(const QSize &);
+ void resetContentsSize();
+
+ qreal contentsScale() const;
+ void setContentsScale(qreal);
+
+ QColor fillColor() const;
+ void setFillColor(const QColor&);
+
+ RenderTarget renderTarget() const;
+ void setRenderTarget(RenderTarget target);
+
+ virtual void paint(QPainter *painter) = 0;
+
+Q_SIGNALS:
+ void fillColorChanged();
+ void contentsSizeChanged();
+ void contentsScaleChanged();
+ void renderTargetChanged();
+
+protected:
+ QSGPaintedItem(QSGPaintedItemPrivate &dd, QSGItem *parent = 0);
+ virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private:
+ Q_DISABLE_COPY(QSGPaintedItem);
+ Q_DECLARE_PRIVATE(QSGPaintedItem);
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSGPAINTEDITEM_P_H
diff --git a/src/declarative/items/qsgpainteditem_p.h b/src/declarative/items/qsgpainteditem_p.h
new file mode 100644
index 0000000000..9a170d0167
--- /dev/null
+++ b/src/declarative/items/qsgpainteditem_p.h
@@ -0,0 +1,71 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPAINTEDITEM_P_P_H
+#define QSGPAINTEDITEM_P_P_H
+
+#include "qsgitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGPaintedItemPrivate : public QSGItemPrivate
+{
+public:
+ QSGPaintedItemPrivate();
+
+ QSize contentsSize;
+ qreal contentsScale;
+ QColor fillColor;
+ QSGPaintedItem::RenderTarget renderTarget;
+
+ QRect dirtyRect;
+
+ bool geometryDirty : 1;
+ bool contentsDirty : 1;
+ bool opaquePainting: 1;
+ bool antialiasing: 1;
+ bool mipmap: 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGPAINTEDITEM_P_P_H
diff --git a/src/declarative/items/qsgpathview.cpp b/src/declarative/items/qsgpathview.cpp
new file mode 100644
index 0000000000..fc3d39aef7
--- /dev/null
+++ b/src/declarative/items/qsgpathview.cpp
@@ -0,0 +1,1416 @@
+// Commit: 806f031efeda71d3f4d7d2f949b437493e79cf52
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgpathview_p.h"
+#include "qsgpathview_p_p.h"
+#include "qsgcanvas.h"
+
+#include <private/qdeclarativestate_p.h>
+#include <private/qdeclarativeopenmetaobject_p.h>
+#include <private/qlistmodelinterface_p.h>
+
+#include <QtGui/qevent.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qapplication.h>
+#include <QtCore/qmath.h>
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+inline qreal qmlMod(qreal x, qreal y)
+{
+#ifdef QT_USE_MATH_H_FLOATS
+ if(sizeof(qreal) == sizeof(float))
+ return fmodf(float(x), float(y));
+ else
+#endif
+ return fmod(x, y);
+}
+
+static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0;
+
+QSGPathViewAttached::QSGPathViewAttached(QObject *parent)
+: QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
+{
+ if (qPathViewAttachedType) {
+ m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType);
+ m_metaobject->setCached(true);
+ } else {
+ m_metaobject = new QDeclarativeOpenMetaObject(this);
+ }
+}
+
+QSGPathViewAttached::~QSGPathViewAttached()
+{
+}
+
+QVariant QSGPathViewAttached::value(const QByteArray &name) const
+{
+ return m_metaobject->value(name);
+}
+void QSGPathViewAttached::setValue(const QByteArray &name, const QVariant &val)
+{
+ m_metaobject->setValue(name, val);
+}
+
+
+void QSGPathViewPrivate::init()
+{
+ Q_Q(QSGPathView);
+ offset = 0;
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFlag(QSGItem::ItemIsFocusScope);
+ q->setFiltersChildMouseEvents(true);
+ q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked()));
+ lastPosTime.invalidate();
+ static int timelineCompletedIdx = -1;
+ static int movementEndingIdx = -1;
+ if (timelineCompletedIdx == -1) {
+ timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
+ movementEndingIdx = QSGPathView::staticMetaObject.indexOfSlot("movementEnding()");
+ }
+ QMetaObject::connect(&tl, timelineCompletedIdx,
+ q, movementEndingIdx, Qt::DirectConnection);
+}
+
+QSGItem *QSGPathViewPrivate::getItem(int modelIndex)
+{
+ Q_Q(QSGPathView);
+ requestedIndex = modelIndex;
+ QSGItem *item = model->item(modelIndex, false);
+ if (item) {
+ if (!attType) {
+ // pre-create one metatype to share with all attached objects
+ attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(q));
+ foreach(const QString &attr, path->attributes())
+ attType->createProperty(attr.toUtf8());
+ }
+ qPathViewAttachedType = attType;
+ QSGPathViewAttached *att = static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item));
+ qPathViewAttachedType = 0;
+ if (att) {
+ att->m_view = q;
+ att->setOnPath(true);
+ }
+ item->setParentItem(q);
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ }
+ requestedIndex = -1;
+ return item;
+}
+
+void QSGPathViewPrivate::releaseItem(QSGItem *item)
+{
+ if (!item || !model)
+ return;
+ QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
+ itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
+ if (model->release(item) == 0) {
+ // item was not destroyed, and we no longer reference it.
+ if (QSGPathViewAttached *att = attached(item))
+ att->setOnPath(false);
+ }
+}
+
+QSGPathViewAttached *QSGPathViewPrivate::attached(QSGItem *item)
+{
+ return static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item, false));
+}
+
+void QSGPathViewPrivate::clear()
+{
+ for (int i=0; i<items.count(); i++){
+ QSGItem *p = items[i];
+ releaseItem(p);
+ }
+ items.clear();
+}
+
+void QSGPathViewPrivate::updateMappedRange()
+{
+ if (model && pathItems != -1 && pathItems < modelCount)
+ mappedRange = qreal(pathItems)/modelCount;
+ else
+ mappedRange = 1.0;
+}
+
+qreal QSGPathViewPrivate::positionOfIndex(qreal index) const
+{
+ qreal pos = -1.0;
+
+ if (model && index >= 0 && index < modelCount) {
+ qreal start = 0.0;
+ if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange)
+ start = highlightRangeStart;
+ qreal globalPos = index + offset;
+ globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
+ if (pathItems != -1 && pathItems < modelCount) {
+ globalPos += start * mappedRange;
+ globalPos = qmlMod(globalPos, 1.0);
+ if (globalPos < mappedRange)
+ pos = globalPos / mappedRange;
+ } else {
+ pos = qmlMod(globalPos + start, 1.0);
+ }
+ }
+
+ return pos;
+}
+
+void QSGPathViewPrivate::createHighlight()
+{
+ Q_Q(QSGPathView);
+ if (!q->isComponentComplete())
+ return;
+
+ bool changed = false;
+ if (highlightItem) {
+ highlightItem->setParentItem(0);
+ highlightItem->deleteLater();
+ highlightItem = 0;
+ changed = true;
+ }
+
+ QSGItem *item = 0;
+ if (highlightComponent) {
+ QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
+ QObject *nobj = highlightComponent->create(highlightContext);
+ if (nobj) {
+ QDeclarative_setParent_noEvent(highlightContext, nobj);
+ item = qobject_cast<QSGItem *>(nobj);
+ if (!item)
+ delete nobj;
+ } else {
+ delete highlightContext;
+ }
+ } else {
+ item = new QSGItem;
+ }
+ if (item) {
+ QDeclarative_setParent_noEvent(item, q);
+ item->setParentItem(q);
+ highlightItem = item;
+ changed = true;
+ }
+ if (changed)
+ emit q->highlightItemChanged();
+}
+
+void QSGPathViewPrivate::updateHighlight()
+{
+ Q_Q(QSGPathView);
+ if (!q->isComponentComplete() || !isValid())
+ return;
+ if (highlightItem) {
+ if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
+ updateItem(highlightItem, highlightRangeStart);
+ } else {
+ qreal target = currentIndex;
+
+ offsetAdj = 0.0;
+ tl.reset(moveHighlight);
+ moveHighlight.setValue(highlightPosition);
+
+ const int duration = highlightMoveDuration;
+
+ if (target - highlightPosition > modelCount/2) {
+ highlightUp = false;
+ qreal distance = modelCount - target + highlightPosition;
+ tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
+ tl.set(moveHighlight, modelCount-0.01);
+ tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
+ } else if (target - highlightPosition <= -modelCount/2) {
+ highlightUp = true;
+ qreal distance = modelCount - highlightPosition + target;
+ tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
+ tl.set(moveHighlight, 0.0);
+ tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
+ } else {
+ highlightUp = highlightPosition - target < 0;
+ tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
+ }
+ }
+ }
+}
+
+void QSGPathViewPrivate::setHighlightPosition(qreal pos)
+{
+ if (pos != highlightPosition) {
+ qreal start = 0.0;
+ qreal end = 1.0;
+ if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange) {
+ start = highlightRangeStart;
+ end = highlightRangeEnd;
+ }
+
+ qreal range = qreal(modelCount);
+ // calc normalized position of highlight relative to offset
+ qreal relativeHighlight = qmlMod(pos + offset, range) / range;
+
+ if (!highlightUp && relativeHighlight > end * mappedRange) {
+ qreal diff = 1.0 - relativeHighlight;
+ setOffset(offset + diff * range);
+ } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
+ qreal diff = relativeHighlight - (end - start) * mappedRange;
+ setOffset(offset - diff * range - 0.00001);
+ }
+
+ highlightPosition = pos;
+ qreal pathPos = positionOfIndex(pos);
+ updateItem(highlightItem, pathPos);
+ if (QSGPathViewAttached *att = attached(highlightItem))
+ att->setOnPath(pathPos != -1.0);
+ }
+}
+
+void QSGPathView::pathUpdated()
+{
+ Q_D(QSGPathView);
+ QList<QSGItem*>::iterator it = d->items.begin();
+ while (it != d->items.end()) {
+ QSGItem *item = *it;
+ if (QSGPathViewAttached *att = d->attached(item))
+ att->m_percent = -1;
+ ++it;
+ }
+ refill();
+}
+
+void QSGPathViewPrivate::updateItem(QSGItem *item, qreal percent)
+{
+ if (QSGPathViewAttached *att = attached(item)) {
+ if (qFuzzyCompare(att->m_percent, percent))
+ return;
+ att->m_percent = percent;
+ foreach(const QString &attr, path->attributes())
+ att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
+ }
+ QPointF pf = path->pointAt(percent);
+ item->setX(qRound(pf.x() - item->width()/2));
+ item->setY(qRound(pf.y() - item->height()/2));
+}
+
+void QSGPathViewPrivate::regenerate()
+{
+ Q_Q(QSGPathView);
+ if (!q->isComponentComplete())
+ return;
+
+ clear();
+
+ if (!isValid())
+ return;
+
+ firstIndex = -1;
+ updateMappedRange();
+ q->refill();
+}
+
+QSGPathView::QSGPathView(QSGItem *parent)
+ : QSGItem(*(new QSGPathViewPrivate), parent)
+{
+ Q_D(QSGPathView);
+ d->init();
+}
+
+QSGPathView::~QSGPathView()
+{
+ Q_D(QSGPathView);
+ d->clear();
+ if (d->attType)
+ d->attType->release();
+ if (d->ownModel)
+ delete d->model;
+}
+
+QVariant QSGPathView::model() const
+{
+ Q_D(const QSGPathView);
+ return d->modelVariant;
+}
+
+void QSGPathView::setModel(const QVariant &model)
+{
+ Q_D(QSGPathView);
+ if (d->modelVariant == model)
+ return;
+
+ if (d->model) {
+ disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ for (int i=0; i<d->items.count(); i++){
+ QSGItem *p = d->items[i];
+ d->model->release(p);
+ }
+ d->items.clear();
+ }
+
+ d->modelVariant = model;
+ QObject *object = qvariant_cast<QObject*>(model);
+ QSGVisualModel *vim = 0;
+ if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
+ if (d->ownModel) {
+ delete d->model;
+ d->ownModel = false;
+ }
+ d->model = vim;
+ } else {
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this), this);
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ dataModel->setModel(model);
+ }
+ d->modelCount = 0;
+ if (d->model) {
+ connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ d->modelCount = d->model->count();
+ if (d->model->count())
+ d->offset = qmlMod(d->offset, qreal(d->model->count()));
+ if (d->offset < 0)
+ d->offset = d->model->count() + d->offset;
+}
+ d->regenerate();
+ d->fixOffset();
+ emit countChanged();
+ emit modelChanged();
+}
+
+int QSGPathView::count() const
+{
+ Q_D(const QSGPathView);
+ return d->model ? d->modelCount : 0;
+}
+
+QDeclarativePath *QSGPathView::path() const
+{
+ Q_D(const QSGPathView);
+ return d->path;
+}
+
+void QSGPathView::setPath(QDeclarativePath *path)
+{
+ Q_D(QSGPathView);
+ if (d->path == path)
+ return;
+ if (d->path)
+ disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
+ d->path = path;
+ connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
+ if (d->isValid() && isComponentComplete()) {
+ d->clear();
+ if (d->attType) {
+ d->attType->release();
+ d->attType = 0;
+ }
+ d->regenerate();
+ }
+ emit pathChanged();
+}
+
+int QSGPathView::currentIndex() const
+{
+ Q_D(const QSGPathView);
+ return d->currentIndex;
+}
+
+void QSGPathView::setCurrentIndex(int idx)
+{
+ Q_D(QSGPathView);
+ if (d->model && d->modelCount)
+ idx = qAbs(idx % d->modelCount);
+ if (d->model && idx != d->currentIndex) {
+ if (d->modelCount) {
+ int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount;
+ if (itemIndex < d->items.count()) {
+ if (QSGItem *item = d->items.at(itemIndex)) {
+ if (QSGPathViewAttached *att = d->attached(item))
+ att->setIsCurrentItem(false);
+ }
+ }
+ }
+ d->currentItem = 0;
+ d->moveReason = QSGPathViewPrivate::SetIndex;
+ d->currentIndex = idx;
+ if (d->modelCount) {
+ if (d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange)
+ d->snapToCurrent();
+ int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount;
+ if (itemIndex < d->items.count()) {
+ d->currentItem = d->items.at(itemIndex);
+ d->currentItem->setFocus(true);
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setIsCurrentItem(true);
+ }
+ d->currentItemOffset = d->positionOfIndex(d->currentIndex);
+ d->updateHighlight();
+ }
+ emit currentIndexChanged();
+ }
+}
+
+void QSGPathView::incrementCurrentIndex()
+{
+ Q_D(QSGPathView);
+ d->moveDirection = QSGPathViewPrivate::Positive;
+ setCurrentIndex(currentIndex()+1);
+}
+
+void QSGPathView::decrementCurrentIndex()
+{
+ Q_D(QSGPathView);
+ if (d->model && d->modelCount) {
+ int idx = currentIndex()-1;
+ if (idx < 0)
+ idx = d->modelCount - 1;
+ d->moveDirection = QSGPathViewPrivate::Negative;
+ setCurrentIndex(idx);
+ }
+}
+
+qreal QSGPathView::offset() const
+{
+ Q_D(const QSGPathView);
+ return d->offset;
+}
+
+void QSGPathView::setOffset(qreal offset)
+{
+ Q_D(QSGPathView);
+ d->setOffset(offset);
+ d->updateCurrent();
+}
+
+void QSGPathViewPrivate::setOffset(qreal o)
+{
+ Q_Q(QSGPathView);
+ if (offset != o) {
+ if (isValid() && q->isComponentComplete()) {
+ offset = qmlMod(o, qreal(modelCount));
+ if (offset < 0)
+ offset += qreal(modelCount);
+ q->refill();
+ } else {
+ offset = o;
+ }
+ emit q->offsetChanged();
+ }
+}
+
+void QSGPathViewPrivate::setAdjustedOffset(qreal o)
+{
+ setOffset(o+offsetAdj);
+}
+
+QDeclarativeComponent *QSGPathView::highlight() const
+{
+ Q_D(const QSGPathView);
+ return d->highlightComponent;
+}
+
+void QSGPathView::setHighlight(QDeclarativeComponent *highlight)
+{
+ Q_D(QSGPathView);
+ if (highlight != d->highlightComponent) {
+ d->highlightComponent = highlight;
+ d->createHighlight();
+ d->updateHighlight();
+ emit highlightChanged();
+ }
+}
+
+QSGItem *QSGPathView::highlightItem()
+{
+ Q_D(const QSGPathView);
+ return d->highlightItem;
+}
+
+qreal QSGPathView::preferredHighlightBegin() const
+{
+ Q_D(const QSGPathView);
+ return d->highlightRangeStart;
+}
+
+void QSGPathView::setPreferredHighlightBegin(qreal start)
+{
+ Q_D(QSGPathView);
+ if (d->highlightRangeStart == start || start < 0 || start > 1.0)
+ return;
+ d->highlightRangeStart = start;
+ d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ refill();
+ emit preferredHighlightBeginChanged();
+}
+
+qreal QSGPathView::preferredHighlightEnd() const
+{
+ Q_D(const QSGPathView);
+ return d->highlightRangeEnd;
+}
+
+void QSGPathView::setPreferredHighlightEnd(qreal end)
+{
+ Q_D(QSGPathView);
+ if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
+ return;
+ d->highlightRangeEnd = end;
+ d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ refill();
+ emit preferredHighlightEndChanged();
+}
+
+QSGPathView::HighlightRangeMode QSGPathView::highlightRangeMode() const
+{
+ Q_D(const QSGPathView);
+ return d->highlightRangeMode;
+}
+
+void QSGPathView::setHighlightRangeMode(HighlightRangeMode mode)
+{
+ Q_D(QSGPathView);
+ if (d->highlightRangeMode == mode)
+ return;
+ d->highlightRangeMode = mode;
+ d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit highlightRangeModeChanged();
+}
+
+int QSGPathView::highlightMoveDuration() const
+{
+ Q_D(const QSGPathView);
+ return d->highlightMoveDuration;
+}
+
+void QSGPathView::setHighlightMoveDuration(int duration)
+{
+ Q_D(QSGPathView);
+ if (d->highlightMoveDuration == duration)
+ return;
+ d->highlightMoveDuration = duration;
+ emit highlightMoveDurationChanged();
+}
+
+qreal QSGPathView::dragMargin() const
+{
+ Q_D(const QSGPathView);
+ return d->dragMargin;
+}
+
+void QSGPathView::setDragMargin(qreal dragMargin)
+{
+ Q_D(QSGPathView);
+ if (d->dragMargin == dragMargin)
+ return;
+ d->dragMargin = dragMargin;
+ emit dragMarginChanged();
+}
+
+qreal QSGPathView::flickDeceleration() const
+{
+ Q_D(const QSGPathView);
+ return d->deceleration;
+}
+
+void QSGPathView::setFlickDeceleration(qreal dec)
+{
+ Q_D(QSGPathView);
+ if (d->deceleration == dec)
+ return;
+ d->deceleration = dec;
+ emit flickDecelerationChanged();
+}
+
+bool QSGPathView::isInteractive() const
+{
+ Q_D(const QSGPathView);
+ return d->interactive;
+}
+
+void QSGPathView::setInteractive(bool interactive)
+{
+ Q_D(QSGPathView);
+ if (interactive != d->interactive) {
+ d->interactive = interactive;
+ if (!interactive)
+ d->tl.clear();
+ emit interactiveChanged();
+ }
+}
+
+bool QSGPathView::isMoving() const
+{
+ Q_D(const QSGPathView);
+ return d->moving;
+}
+
+bool QSGPathView::isFlicking() const
+{
+ Q_D(const QSGPathView);
+ return d->flicking;
+}
+
+QDeclarativeComponent *QSGPathView::delegate() const
+{
+ Q_D(const QSGPathView);
+ if (d->model) {
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ return dataModel->delegate();
+ }
+
+ return 0;
+}
+
+void QSGPathView::setDelegate(QDeclarativeComponent *delegate)
+{
+ Q_D(QSGPathView);
+ if (delegate == this->delegate())
+ return;
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this));
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
+ int oldCount = dataModel->count();
+ dataModel->setDelegate(delegate);
+ d->modelCount = dataModel->count();
+ d->regenerate();
+ if (oldCount != dataModel->count())
+ emit countChanged();
+ emit delegateChanged();
+ }
+}
+
+int QSGPathView::pathItemCount() const
+{
+ Q_D(const QSGPathView);
+ return d->pathItems;
+}
+
+void QSGPathView::setPathItemCount(int i)
+{
+ Q_D(QSGPathView);
+ if (i == d->pathItems)
+ return;
+ if (i < 1)
+ i = 1;
+ d->pathItems = i;
+ d->updateMappedRange();
+ if (d->isValid() && isComponentComplete()) {
+ d->regenerate();
+ }
+ emit pathItemCountChanged();
+}
+
+QPointF QSGPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
+{
+ //XXX maybe do recursively at increasing resolution.
+ qreal mindist = 1e10; // big number
+ QPointF nearPoint = path->pointAt(0);
+ qreal nearPc = 0;
+ for (qreal i=1; i < 1000; i++) {
+ QPointF pt = path->pointAt(i/1000.0);
+ QPointF diff = pt - point;
+ qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
+ if (dist < mindist) {
+ nearPoint = pt;
+ nearPc = i;
+ mindist = dist;
+ }
+ }
+
+ if (nearPercent)
+ *nearPercent = nearPc / 1000.0;
+
+ return nearPoint;
+}
+
+void QSGPathView::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPathView);
+ if (d->interactive) {
+ d->handleMousePressEvent(event);
+ event->accept();
+ } else {
+ QSGItem::mousePressEvent(event);
+ }
+}
+
+void QSGPathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGPathView);
+ if (!interactive || !items.count())
+ return;
+ QPointF scenePoint = q->mapToScene(event->pos());
+ int idx = 0;
+ for (; idx < items.count(); ++idx) {
+ QRectF rect = items.at(idx)->boundingRect();
+ rect = items.at(idx)->mapRectToScene(rect);
+ if (rect.contains(scenePoint))
+ break;
+ }
+ if (idx == items.count() && dragMargin == 0.) // didn't click on an item
+ return;
+
+ startPoint = pointNear(event->pos(), &startPc);
+ if (idx == items.count()) {
+ qreal distance = qAbs(event->pos().x() - startPoint.x()) + qAbs(event->pos().y() - startPoint.y());
+ if (distance > dragMargin)
+ return;
+ }
+
+ if (tl.isActive() && flicking)
+ stealMouse = true; // If we've been flicked then steal the click.
+ else
+ stealMouse = false;
+
+ lastElapsed = 0;
+ lastDist = 0;
+ QSGItemPrivate::start(lastPosTime);
+ tl.clear();
+}
+
+void QSGPathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPathView);
+ if (d->interactive) {
+ d->handleMouseMoveEvent(event);
+ if (d->stealMouse)
+ setKeepMouseGrab(true);
+ event->accept();
+ } else {
+ QSGItem::mouseMoveEvent(event);
+ }
+}
+
+void QSGPathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_Q(QSGPathView);
+ if (!interactive || !lastPosTime.isValid())
+ return;
+
+ qreal newPc;
+ QPointF pathPoint = pointNear(event->pos(), &newPc);
+ if (!stealMouse) {
+ QPointF delta = pathPoint - startPoint;
+ if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) {
+ stealMouse = true;
+ startPc = newPc;
+ }
+ }
+
+ if (stealMouse) {
+ moveReason = QSGPathViewPrivate::Mouse;
+ qreal diff = (newPc - startPc)*modelCount*mappedRange;
+ if (diff) {
+ q->setOffset(offset + diff);
+
+ if (diff > modelCount/2)
+ diff -= modelCount;
+ else if (diff < -modelCount/2)
+ diff += modelCount;
+
+ lastElapsed = QSGItemPrivate::restart(lastPosTime);
+ lastDist = diff;
+ startPc = newPc;
+ }
+ if (!moving) {
+ moving = true;
+ emit q->movingChanged();
+ emit q->movementStarted();
+ }
+ }
+}
+
+void QSGPathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPathView);
+ if (d->interactive) {
+ d->handleMouseReleaseEvent(event);
+ event->accept();
+ ungrabMouse();
+ } else {
+ QSGItem::mouseReleaseEvent(event);
+ }
+}
+
+void QSGPathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *)
+{
+ Q_Q(QSGPathView);
+ stealMouse = false;
+ q->setKeepMouseGrab(false);
+ if (!interactive || !lastPosTime.isValid())
+ return;
+
+ qreal elapsed = qreal(lastElapsed + QSGItemPrivate::elapsed(lastPosTime)) / 1000.;
+ qreal velocity = elapsed > 0. ? lastDist / elapsed : 0;
+ if (model && modelCount && qAbs(velocity) > 1.) {
+ qreal count = pathItems == -1 ? modelCount : pathItems;
+ if (qAbs(velocity) > count * 2) // limit velocity
+ velocity = (velocity > 0 ? count : -count) * 2;
+ // Calculate the distance to be travelled
+ qreal v2 = velocity*velocity;
+ qreal accel = deceleration/10;
+ // + 0.25 to encourage moving at least one item in the flick direction
+ qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
+ if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
+ // round to nearest item.
+ if (velocity > 0.)
+ dist = qRound(dist + offset) - offset;
+ else
+ dist = qRound(dist - offset) + offset;
+ // Calculate accel required to stop on item boundary
+ if (dist <= 0.) {
+ dist = 0.;
+ accel = 0.;
+ } else {
+ accel = v2 / (2.0f * qAbs(dist));
+ }
+ }
+ offsetAdj = 0.0;
+ moveOffset.setValue(offset);
+ tl.accel(moveOffset, velocity, accel, dist);
+ tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this));
+ if (!flicking) {
+ flicking = true;
+ emit q->flickingChanged();
+ emit q->flickStarted();
+ }
+ } else {
+ fixOffset();
+ }
+
+ lastPosTime.invalidate();
+ if (!tl.isActive())
+ q->movementEnding();
+}
+
+bool QSGPathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPathView);
+ QGraphicsSceneMouseEvent mouseEvent(event->type());
+ QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+ QSGCanvas *c = canvas();
+ QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
+ bool stealThisEvent = d->stealMouse;
+ if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
+ mouseEvent.setAccepted(false);
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
+ }
+ }
+ mouseEvent.setScenePos(event->scenePos());
+ mouseEvent.setLastScenePos(event->lastScenePos());
+ mouseEvent.setPos(mapFromScene(event->scenePos()));
+ mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
+
+ switch(mouseEvent.type()) {
+ case QEvent::GraphicsSceneMouseMove:
+ d->handleMouseMoveEvent(&mouseEvent);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ d->handleMousePressEvent(&mouseEvent);
+ stealThisEvent = d->stealMouse; // Update stealThisEvent in case changed by function call above
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ d->handleMouseReleaseEvent(&mouseEvent);
+ break;
+ default:
+ break;
+ }
+ grabber = c->mouseGrabberItem();
+ if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
+ grabMouse();
+
+ return d->stealMouse;
+ } else if (d->lastPosTime.isValid()) {
+ d->lastPosTime.invalidate();
+ }
+ if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease)
+ d->stealMouse = false;
+ return false;
+}
+
+bool QSGPathView::childMouseEventFilter(QSGItem *i, QEvent *e)
+{
+ Q_D(QSGPathView);
+ if (!isVisible() || !d->interactive)
+ return QSGItem::childMouseEventFilter(i, e);
+
+ switch (e->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
+ default:
+ break;
+ }
+
+ return QSGItem::childMouseEventFilter(i, e);
+}
+
+void QSGPathView::updatePolish()
+{
+ QSGItem::updatePolish();
+ refill();
+}
+
+void QSGPathView::componentComplete()
+{
+ Q_D(QSGPathView);
+ QSGItem::componentComplete();
+ d->createHighlight();
+ // It is possible that a refill has already happended to to Path
+ // bindings being handled in the componentComplete(). If so
+ // don't do it again.
+ if (d->items.count() == 0 && d->model) {
+ d->modelCount = d->model->count();
+ d->regenerate();
+ }
+ d->updateHighlight();
+}
+
+void QSGPathView::refill()
+{
+ Q_D(QSGPathView);
+ if (!d->isValid() || !isComponentComplete())
+ return;
+
+ d->layoutScheduled = false;
+ bool currentVisible = false;
+
+ // first move existing items and remove items off path
+ int idx = d->firstIndex;
+ QList<QSGItem*>::iterator it = d->items.begin();
+ while (it != d->items.end()) {
+ qreal pos = d->positionOfIndex(idx);
+ QSGItem *item = *it;
+ if (pos >= 0.0) {
+ d->updateItem(item, pos);
+ if (idx == d->currentIndex) {
+ currentVisible = true;
+ d->currentItemOffset = pos;
+ }
+ ++it;
+ } else {
+// qDebug() << "release";
+ d->updateItem(item, 1.0);
+ d->releaseItem(item);
+ if (it == d->items.begin()) {
+ if (++d->firstIndex >= d->modelCount)
+ d->firstIndex = 0;
+ }
+ it = d->items.erase(it);
+ }
+ ++idx;
+ if (idx >= d->modelCount)
+ idx = 0;
+ }
+ if (!d->items.count())
+ d->firstIndex = -1;
+
+ if (d->modelCount) {
+ // add items to beginning and end
+ int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
+ if (d->items.count() < count) {
+ int idx = qRound(d->modelCount - d->offset) % d->modelCount;
+ qreal startPos = 0.0;
+ if (d->haveHighlightRange && d->highlightRangeMode != QSGPathView::NoHighlightRange)
+ startPos = d->highlightRangeStart;
+ if (d->firstIndex >= 0) {
+ startPos = d->positionOfIndex(d->firstIndex);
+ idx = (d->firstIndex + d->items.count()) % d->modelCount;
+ }
+ qreal pos = d->positionOfIndex(idx);
+ while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
+ // qDebug() << "append" << idx;
+ QSGItem *item = d->getItem(idx);
+ if (d->model->completePending())
+ item->setZ(idx+1);
+ if (d->currentIndex == idx) {
+ item->setFocus(true);
+ if (QSGPathViewAttached *att = d->attached(item))
+ att->setIsCurrentItem(true);
+ currentVisible = true;
+ d->currentItemOffset = pos;
+ d->currentItem = item;
+ }
+ if (d->items.count() == 0)
+ d->firstIndex = idx;
+ d->items.append(item);
+ d->updateItem(item, pos);
+ if (d->model->completePending())
+ d->model->completeItem();
+ ++idx;
+ if (idx >= d->modelCount)
+ idx = 0;
+ pos = d->positionOfIndex(idx);
+ }
+
+ idx = d->firstIndex - 1;
+ if (idx < 0)
+ idx = d->modelCount - 1;
+ pos = d->positionOfIndex(idx);
+ while (pos >= 0.0 && pos < startPos) {
+ // qDebug() << "prepend" << idx;
+ QSGItem *item = d->getItem(idx);
+ if (d->model->completePending())
+ item->setZ(idx+1);
+ if (d->currentIndex == idx) {
+ item->setFocus(true);
+ if (QSGPathViewAttached *att = d->attached(item))
+ att->setIsCurrentItem(true);
+ currentVisible = true;
+ d->currentItemOffset = pos;
+ d->currentItem = item;
+ }
+ d->items.prepend(item);
+ d->updateItem(item, pos);
+ if (d->model->completePending())
+ d->model->completeItem();
+ d->firstIndex = idx;
+ idx = d->firstIndex - 1;
+ if (idx < 0)
+ idx = d->modelCount - 1;
+ pos = d->positionOfIndex(idx);
+ }
+ }
+ }
+
+ if (!currentVisible)
+ d->currentItemOffset = 1.0;
+
+ if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
+ d->updateItem(d->highlightItem, d->highlightRangeStart);
+ if (QSGPathViewAttached *att = d->attached(d->highlightItem))
+ att->setOnPath(true);
+ } else if (d->highlightItem && d->moveReason != QSGPathViewPrivate::SetIndex) {
+ d->updateItem(d->highlightItem, d->currentItemOffset);
+ if (QSGPathViewAttached *att = d->attached(d->highlightItem))
+ att->setOnPath(currentVisible);
+ }
+ while (d->itemCache.count())
+ d->releaseItem(d->itemCache.takeLast());
+}
+
+void QSGPathView::itemsInserted(int modelIndex, int count)
+{
+ //XXX support animated insertion
+ Q_D(QSGPathView);
+ if (!d->isValid() || !isComponentComplete())
+ return;
+
+ if (d->modelCount) {
+ d->itemCache += d->items;
+ d->items.clear();
+ if (modelIndex <= d->currentIndex) {
+ d->currentIndex += count;
+ emit currentIndexChanged();
+ } else if (d->offset != 0) {
+ d->offset += count;
+ d->offsetAdj += count;
+ }
+ }
+
+ d->modelCount += count;
+ if (d->flicking || d->moving) {
+ d->regenerate();
+ d->updateCurrent();
+ } else {
+ d->firstIndex = -1;
+ d->updateMappedRange();
+ d->scheduleLayout();
+ }
+ emit countChanged();
+}
+
+void QSGPathView::itemsRemoved(int modelIndex, int count)
+{
+ //XXX support animated removal
+ Q_D(QSGPathView);
+ if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete())
+ return;
+
+ // fix current
+ bool currentChanged = false;
+ if (d->currentIndex >= modelIndex + count) {
+ d->currentIndex -= count;
+ currentChanged = true;
+ } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
+ // current item has been removed.
+ d->currentIndex = qMin(modelIndex, d->modelCount-count-1);
+ if (d->currentItem) {
+ if (QSGPathViewAttached *att = d->attached(d->currentItem))
+ att->setIsCurrentItem(true);
+ }
+ currentChanged = true;
+ }
+
+ d->itemCache += d->items;
+ d->items.clear();
+
+ bool changedOffset = false;
+ if (modelIndex > d->currentIndex) {
+ if (d->offset >= count) {
+ changedOffset = true;
+ d->offset -= count;
+ d->offsetAdj -= count;
+ }
+ }
+
+ d->modelCount -= count;
+ if (!d->modelCount) {
+ while (d->itemCache.count())
+ d->releaseItem(d->itemCache.takeLast());
+ d->offset = 0;
+ changedOffset = true;
+ d->tl.reset(d->moveOffset);
+ } else {
+ d->regenerate();
+ d->updateCurrent();
+ if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange)
+ d->snapToCurrent();
+ }
+ if (changedOffset)
+ emit offsetChanged();
+ if (currentChanged)
+ emit currentIndexChanged();
+ emit countChanged();
+}
+
+void QSGPathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/)
+{
+ Q_D(QSGPathView);
+ if (!d->isValid() || !isComponentComplete())
+ return;
+
+ QList<QSGItem *> removedItems = d->items;
+ d->items.clear();
+ d->regenerate();
+ while (removedItems.count())
+ d->releaseItem(removedItems.takeLast());
+
+ // Fix current index
+ if (d->currentIndex >= 0 && d->currentItem) {
+ int oldCurrent = d->currentIndex;
+ d->currentIndex = d->model->indexOf(d->currentItem, this);
+ if (oldCurrent != d->currentIndex)
+ emit currentIndexChanged();
+ }
+ d->updateCurrent();
+}
+
+void QSGPathView::modelReset()
+{
+ Q_D(QSGPathView);
+ d->modelCount = d->model->count();
+ d->regenerate();
+ emit countChanged();
+}
+
+void QSGPathView::createdItem(int index, QSGItem *item)
+{
+ Q_D(QSGPathView);
+ if (d->requestedIndex != index) {
+ if (!d->attType) {
+ // pre-create one metatype to share with all attached objects
+ d->attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(this));
+ foreach(const QString &attr, d->path->attributes())
+ d->attType->createProperty(attr.toUtf8());
+ }
+ qPathViewAttachedType = d->attType;
+ QSGPathViewAttached *att = static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item));
+ qPathViewAttachedType = 0;
+ if (att) {
+ att->m_view = this;
+ att->setOnPath(false);
+ }
+ item->setParentItem(this);
+ d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
+ }
+}
+
+void QSGPathView::destroyingItem(QSGItem *item)
+{
+ Q_UNUSED(item);
+}
+
+void QSGPathView::ticked()
+{
+ Q_D(QSGPathView);
+ d->updateCurrent();
+}
+
+void QSGPathView::movementEnding()
+{
+ Q_D(QSGPathView);
+ if (d->flicking) {
+ d->flicking = false;
+ emit flickingChanged();
+ emit flickEnded();
+ }
+ if (d->moving && !d->stealMouse) {
+ d->moving = false;
+ emit movingChanged();
+ emit movementEnded();
+ }
+}
+
+// find the item closest to the snap position
+int QSGPathViewPrivate::calcCurrentIndex()
+{
+ int current = -1;
+ if (modelCount && model && items.count()) {
+ offset = qmlMod(offset, modelCount);
+ if (offset < 0)
+ offset += modelCount;
+ current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
+ current = current % modelCount;
+ }
+
+ return current;
+}
+
+void QSGPathViewPrivate::updateCurrent()
+{
+ Q_Q(QSGPathView);
+ if (moveReason != Mouse)
+ return;
+ if (!modelCount || !haveHighlightRange || highlightRangeMode != QSGPathView::StrictlyEnforceRange)
+ return;
+
+ int idx = calcCurrentIndex();
+ if (model && idx != currentIndex) {
+ int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
+ if (itemIndex < items.count()) {
+ if (QSGItem *item = items.at(itemIndex)) {
+ if (QSGPathViewAttached *att = attached(item))
+ att->setIsCurrentItem(false);
+ }
+ }
+ currentIndex = idx;
+ currentItem = 0;
+ itemIndex = (idx - firstIndex + modelCount) % modelCount;
+ if (itemIndex < items.count()) {
+ currentItem = items.at(itemIndex);
+ currentItem->setFocus(true);
+ if (QSGPathViewAttached *att = attached(currentItem))
+ att->setIsCurrentItem(true);
+ }
+ emit q->currentIndexChanged();
+ }
+}
+
+void QSGPathViewPrivate::fixOffsetCallback(void *d)
+{
+ ((QSGPathViewPrivate *)d)->fixOffset();
+}
+
+void QSGPathViewPrivate::fixOffset()
+{
+ Q_Q(QSGPathView);
+ if (model && items.count()) {
+ if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
+ int curr = calcCurrentIndex();
+ if (curr != currentIndex)
+ q->setCurrentIndex(curr);
+ else
+ snapToCurrent();
+ }
+ }
+}
+
+void QSGPathViewPrivate::snapToCurrent()
+{
+ if (!model || modelCount <= 0)
+ return;
+
+ qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
+
+ moveReason = Other;
+ offsetAdj = 0.0;
+ tl.reset(moveOffset);
+ moveOffset.setValue(offset);
+
+ const int duration = highlightMoveDuration;
+
+ if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
+ qreal distance = modelCount - targetOffset + offset;
+ if (targetOffset > moveOffset) {
+ tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
+ tl.set(moveOffset, modelCount);
+ tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
+ } else {
+ tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
+ }
+ } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
+ qreal distance = modelCount - offset + targetOffset;
+ if (targetOffset < moveOffset) {
+ tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
+ tl.set(moveOffset, 0.0);
+ tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
+ } else {
+ tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
+ }
+ } else {
+ tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
+ }
+ moveDirection = Shortest;
+}
+
+QSGPathViewAttached *QSGPathView::qmlAttachedProperties(QObject *obj)
+{
+ return new QSGPathViewAttached(obj);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/declarative/items/qsgpathview_p.h b/src/declarative/items/qsgpathview_p.h
new file mode 100644
index 0000000000..d75063395a
--- /dev/null
+++ b/src/declarative/items/qsgpathview_p.h
@@ -0,0 +1,254 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPATHVIEW_P_H
+#define QSGPATHVIEW_P_H
+
+#include "qsgitem.h"
+
+#include <private/qdeclarativepath_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGPathViewPrivate;
+class QSGPathViewAttached;
+class Q_AUTOTEST_EXPORT QSGPathView : public QSGItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QDeclarativePath *path READ path WRITE setPath NOTIFY pathChanged)
+ Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(qreal offset READ offset WRITE setOffset NOTIFY offsetChanged)
+
+ Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
+ Q_PROPERTY(QSGItem *highlightItem READ highlightItem NOTIFY highlightItemChanged)
+
+ Q_PROPERTY(qreal preferredHighlightBegin READ preferredHighlightBegin WRITE setPreferredHighlightBegin NOTIFY preferredHighlightBeginChanged)
+ Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged)
+ Q_PROPERTY(HighlightRangeMode highlightRangeMode READ highlightRangeMode WRITE setHighlightRangeMode NOTIFY highlightRangeModeChanged)
+ Q_PROPERTY(int highlightMoveDuration READ highlightMoveDuration WRITE setHighlightMoveDuration NOTIFY highlightMoveDurationChanged)
+
+ Q_PROPERTY(qreal dragMargin READ dragMargin WRITE setDragMargin NOTIFY dragMarginChanged)
+ Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged)
+ Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
+
+ Q_PROPERTY(bool moving READ isMoving NOTIFY movingChanged)
+ Q_PROPERTY(bool flicking READ isFlicking NOTIFY flickingChanged)
+
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(int pathItemCount READ pathItemCount WRITE setPathItemCount NOTIFY pathItemCountChanged)
+
+ Q_ENUMS(HighlightRangeMode)
+
+public:
+ QSGPathView(QSGItem *parent=0);
+ virtual ~QSGPathView();
+
+ QVariant model() const;
+ void setModel(const QVariant &);
+
+ QDeclarativePath *path() const;
+ void setPath(QDeclarativePath *);
+
+ int currentIndex() const;
+ void setCurrentIndex(int idx);
+
+ qreal offset() const;
+ void setOffset(qreal offset);
+
+ QDeclarativeComponent *highlight() const;
+ void setHighlight(QDeclarativeComponent *highlight);
+ QSGItem *highlightItem();
+
+ enum HighlightRangeMode { NoHighlightRange, ApplyRange, StrictlyEnforceRange };
+ HighlightRangeMode highlightRangeMode() const;
+ void setHighlightRangeMode(HighlightRangeMode mode);
+
+ qreal preferredHighlightBegin() const;
+ void setPreferredHighlightBegin(qreal);
+
+ qreal preferredHighlightEnd() const;
+ void setPreferredHighlightEnd(qreal);
+
+ int highlightMoveDuration() const;
+ void setHighlightMoveDuration(int);
+
+ qreal dragMargin() const;
+ void setDragMargin(qreal margin);
+
+ qreal flickDeceleration() const;
+ void setFlickDeceleration(qreal dec);
+
+ bool isInteractive() const;
+ void setInteractive(bool);
+
+ bool isMoving() const;
+ bool isFlicking() const;
+
+ int count() const;
+
+ QDeclarativeComponent *delegate() const;
+ void setDelegate(QDeclarativeComponent *);
+
+ int pathItemCount() const;
+ void setPathItemCount(int);
+
+ static QSGPathViewAttached *qmlAttachedProperties(QObject *);
+
+public Q_SLOTS:
+ void incrementCurrentIndex();
+ void decrementCurrentIndex();
+
+Q_SIGNALS:
+ void currentIndexChanged();
+ void offsetChanged();
+ void modelChanged();
+ void countChanged();
+ void pathChanged();
+ void preferredHighlightBeginChanged();
+ void preferredHighlightEndChanged();
+ void highlightRangeModeChanged();
+ void dragMarginChanged();
+ void snapPositionChanged();
+ void delegateChanged();
+ void pathItemCountChanged();
+ void flickDecelerationChanged();
+ void interactiveChanged();
+ void movingChanged();
+ void flickingChanged();
+ void highlightChanged();
+ void highlightItemChanged();
+ void highlightMoveDurationChanged();
+ void movementStarted();
+ void movementEnded();
+ void flickStarted();
+ void flickEnded();
+
+protected:
+ virtual void updatePolish();
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *);
+ bool sendMouseEvent(QGraphicsSceneMouseEvent *event);
+ bool childMouseEventFilter(QSGItem *, QEvent *);
+ void componentComplete();
+
+private Q_SLOTS:
+ void refill();
+ void ticked();
+ void movementEnding();
+ void itemsInserted(int index, int count);
+ void itemsRemoved(int index, int count);
+ void itemsMoved(int,int,int);
+ void modelReset();
+ void createdItem(int index, QSGItem *item);
+ void destroyingItem(QSGItem *item);
+ void pathUpdated();
+
+private:
+ friend class QSGPathViewAttached;
+ Q_DISABLE_COPY(QSGPathView)
+ Q_DECLARE_PRIVATE(QSGPathView)
+};
+
+class QDeclarativeOpenMetaObject;
+class QSGPathViewAttached : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QSGPathView *view READ view CONSTANT)
+ Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged)
+ Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged)
+
+public:
+ QSGPathViewAttached(QObject *parent);
+ ~QSGPathViewAttached();
+
+ QSGPathView *view() { return m_view; }
+
+ bool isCurrentItem() const { return m_isCurrent; }
+ void setIsCurrentItem(bool c) {
+ if (m_isCurrent != c) {
+ m_isCurrent = c;
+ emit currentItemChanged();
+ }
+ }
+
+ QVariant value(const QByteArray &name) const;
+ void setValue(const QByteArray &name, const QVariant &val);
+
+ bool isOnPath() const { return m_onPath; }
+ void setOnPath(bool on) {
+ if (on != m_onPath) {
+ m_onPath = on;
+ emit pathChanged();
+ }
+ }
+ qreal m_percent;
+
+Q_SIGNALS:
+ void currentItemChanged();
+ void pathChanged();
+
+private:
+ friend class QSGPathViewPrivate;
+ friend class QSGPathView;
+ QSGPathView *m_view;
+ QDeclarativeOpenMetaObject *m_metaobject;
+ bool m_onPath : 1;
+ bool m_isCurrent : 1;
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGPathView)
+QML_DECLARE_TYPEINFO(QSGPathView, QML_HAS_ATTACHED_PROPERTIES)
+QT_END_HEADER
+
+#endif // QSGPATHVIEW_P_H
diff --git a/src/declarative/items/qsgpathview_p_p.h b/src/declarative/items/qsgpathview_p_p.h
new file mode 100644
index 0000000000..ed54f29abe
--- /dev/null
+++ b/src/declarative/items/qsgpathview_p_p.h
@@ -0,0 +1,193 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEPATHVIEW_P_H
+#define QDECLARATIVEPATHVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgpathview_p.h"
+#include "qsgitem_p.h"
+#include "qsgvisualitemmodel_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <private/qdeclarativeanimation_p_p.h>
+#include <private/qdeclarativeguard_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeOpenMetaObjectType;
+class QSGPathViewAttached;
+class QSGPathViewPrivate : public QSGItemPrivate, public QSGItemChangeListener
+{
+ Q_DECLARE_PUBLIC(QSGPathView)
+
+public:
+ QSGPathViewPrivate()
+ : path(0), currentIndex(0), currentItemOffset(0.0), startPc(0), lastDist(0)
+ , lastElapsed(0), offset(0.0), offsetAdj(0.0), mappedRange(1.0)
+ , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true)
+ , autoHighlight(true), highlightUp(false), layoutScheduled(false)
+ , moving(false), flicking(false)
+ , dragMargin(0), deceleration(100)
+ , moveOffset(this, &QSGPathViewPrivate::setAdjustedOffset)
+ , firstIndex(-1), pathItems(-1), requestedIndex(-1)
+ , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0)
+ , moveHighlight(this, &QSGPathViewPrivate::setHighlightPosition)
+ , highlightPosition(0)
+ , highlightRangeStart(0), highlightRangeEnd(0)
+ , highlightRangeMode(QSGPathView::StrictlyEnforceRange)
+ , highlightMoveDuration(300), modelCount(0)
+ {
+ }
+
+ void init();
+
+ void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
+ if ((newGeometry.size() != oldGeometry.size())
+ && (!highlightItem || item != highlightItem)) {
+ if (QSGPathViewAttached *att = attached(item))
+ att->m_percent = -1;
+ scheduleLayout();
+ }
+ }
+
+ void scheduleLayout() {
+ Q_Q(QSGPathView);
+ if (!layoutScheduled) {
+ layoutScheduled = true;
+ q->polish();
+ }
+ }
+
+ QSGItem *getItem(int modelIndex);
+ void releaseItem(QSGItem *item);
+ QSGPathViewAttached *attached(QSGItem *item);
+ void clear();
+ void updateMappedRange();
+ qreal positionOfIndex(qreal index) const;
+ void createHighlight();
+ void updateHighlight();
+ void setHighlightPosition(qreal pos);
+ bool isValid() const {
+ return model && model->count() > 0 && model->isValid() && path;
+ }
+
+ void handleMousePressEvent(QGraphicsSceneMouseEvent *event);
+ void handleMouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void handleMouseReleaseEvent(QGraphicsSceneMouseEvent *);
+
+ int calcCurrentIndex();
+ void updateCurrent();
+ static void fixOffsetCallback(void*);
+ void fixOffset();
+ void setOffset(qreal offset);
+ void setAdjustedOffset(qreal offset);
+ void regenerate();
+ void updateItem(QSGItem *, qreal);
+ void snapToCurrent();
+ QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const;
+
+ QDeclarativePath *path;
+ int currentIndex;
+ QDeclarativeGuard<QSGItem> currentItem;
+ qreal currentItemOffset;
+ qreal startPc;
+ QPointF startPoint;
+ qreal lastDist;
+ int lastElapsed;
+ qreal offset;
+ qreal offsetAdj;
+ qreal mappedRange;
+ bool stealMouse : 1;
+ bool ownModel : 1;
+ bool interactive : 1;
+ bool haveHighlightRange : 1;
+ bool autoHighlight : 1;
+ bool highlightUp : 1;
+ bool layoutScheduled : 1;
+ bool moving : 1;
+ bool flicking : 1;
+ QElapsedTimer lastPosTime;
+ QPointF lastPos;
+ qreal dragMargin;
+ qreal deceleration;
+ QDeclarativeTimeLine tl;
+ QDeclarativeTimeLineValueProxy<QSGPathViewPrivate> moveOffset;
+ int firstIndex;
+ int pathItems;
+ int requestedIndex;
+ QList<QSGItem *> items;
+ QList<QSGItem *> itemCache;
+ QDeclarativeGuard<QSGVisualModel> model;
+ QVariant modelVariant;
+ enum MovementReason { Other, SetIndex, Mouse };
+ MovementReason moveReason;
+ enum MovementDirection { Shortest, Negative, Positive };
+ MovementDirection moveDirection;
+ QDeclarativeOpenMetaObjectType *attType;
+ QDeclarativeComponent *highlightComponent;
+ QSGItem *highlightItem;
+ QDeclarativeTimeLineValueProxy<QSGPathViewPrivate> moveHighlight;
+ qreal highlightPosition;
+ qreal highlightRangeStart;
+ qreal highlightRangeEnd;
+ QSGPathView::HighlightRangeMode highlightRangeMode;
+ int highlightMoveDuration;
+ int modelCount;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/declarative/items/qsgpincharea.cpp b/src/declarative/items/qsgpincharea.cpp
new file mode 100644
index 0000000000..f21d873e5e
--- /dev/null
+++ b/src/declarative/items/qsgpincharea.cpp
@@ -0,0 +1,421 @@
+// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgpincharea_p_p.h"
+#include "qsgcanvas.h"
+
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qapplication.h>
+
+#include <float.h>
+#include <math.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGPinch::QSGPinch()
+ : m_target(0), m_minScale(1.0), m_maxScale(1.0)
+ , m_minRotation(0.0), m_maxRotation(0.0)
+ , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX)
+ , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false)
+{
+}
+
+QSGPinchAreaPrivate::~QSGPinchAreaPrivate()
+{
+ delete pinch;
+}
+
+QSGPinchArea::QSGPinchArea(QSGItem *parent)
+ : QSGItem(*(new QSGPinchAreaPrivate), parent)
+{
+ Q_D(QSGPinchArea);
+ d->init();
+}
+
+QSGPinchArea::~QSGPinchArea()
+{
+}
+
+bool QSGPinchArea::isEnabled() const
+{
+ Q_D(const QSGPinchArea);
+ return d->absorb;
+}
+
+void QSGPinchArea::setEnabled(bool a)
+{
+ Q_D(QSGPinchArea);
+ if (a != d->absorb) {
+ d->absorb = a;
+ emit enabledChanged();
+ }
+}
+
+void QSGPinchArea::touchEvent(QTouchEvent *event)
+{
+ Q_D(QSGPinchArea);
+ if (!d->absorb || !isVisible()) {
+ QSGItem::event(event);
+ return;
+ }
+
+ switch (event->type()) {
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate:
+ d->touchPoints.clear();
+ for (int i = 0; i < event->touchPoints().count(); ++i) {
+ if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) {
+ d->touchPoints << event->touchPoints().at(i);
+ }
+ }
+ updatePinch();
+ break;
+ case QEvent::TouchEnd:
+ d->touchPoints.clear();
+ updatePinch();
+ break;
+ default:
+ QSGItem::event(event);
+ }
+}
+
+void QSGPinchArea::updatePinch()
+{
+ Q_D(QSGPinchArea);
+ if (d->touchPoints.count() == 0) {
+ if (d->inPinch) {
+ d->stealMouse = false;
+ setKeepMouseGrab(false);
+ d->inPinch = false;
+ QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
+ QSGPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
+ pe.setStartCenter(d->pinchStartCenter);
+ pe.setPreviousCenter(pinchCenter);
+ pe.setPreviousAngle(d->pinchLastAngle);
+ pe.setPreviousScale(d->pinchLastScale);
+ pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
+ pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
+ pe.setPoint1(mapFromScene(d->lastPoint1));
+ pe.setPoint2(mapFromScene(d->lastPoint2));
+ emit pinchFinished(&pe);
+ d->pinchStartDist = 0;
+ d->pinchActivated = false;
+ if (d->pinch && d->pinch->target())
+ d->pinch->setActive(false);
+ }
+ return;
+ }
+ QTouchEvent::TouchPoint touchPoint1 = d->touchPoints.at(0);
+ QTouchEvent::TouchPoint touchPoint2 = d->touchPoints.at(d->touchPoints. count() >= 2 ? 1 : 0);
+ if (d->touchPoints.count() == 2
+ && (touchPoint1.state() & Qt::TouchPointPressed || touchPoint2.state() & Qt::TouchPointPressed)) {
+ d->id1 = touchPoint1.id();
+ d->sceneStartPoint1 = touchPoint1.scenePos();
+ d->sceneStartPoint2 = touchPoint2.scenePos();
+ d->inPinch = false;
+ d->pinchRejected = false;
+ d->pinchActivated = true;
+ } else if (d->pinchActivated && !d->pinchRejected){
+ const int dragThreshold = QApplication::startDragDistance();
+ QPointF p1 = touchPoint1.scenePos();
+ QPointF p2 = touchPoint2.scenePos();
+ qreal dx = p1.x() - p2.x();
+ qreal dy = p1.y() - p2.y();
+ qreal dist = sqrt(dx*dx + dy*dy);
+ QPointF sceneCenter = (p1 + p2)/2;
+ qreal angle = QLineF(p1, p2).angle();
+ if (d->touchPoints.count() == 1) {
+ // If we only have one point then just move the center
+ if (d->id1 == touchPoint1.id())
+ sceneCenter = d->sceneLastCenter + touchPoint1.scenePos() - d->lastPoint1;
+ else
+ sceneCenter = d->sceneLastCenter + touchPoint2.scenePos() - d->lastPoint2;
+ angle = d->pinchLastAngle;
+ }
+ d->id1 = touchPoint1.id();
+ if (angle > 180)
+ angle -= 360;
+ if (!d->inPinch) {
+ if (d->touchPoints.count() >= 2
+ && (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold
+ || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold
+ || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold
+ || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold)) {
+ d->sceneStartCenter = sceneCenter;
+ d->sceneLastCenter = sceneCenter;
+ d->pinchStartCenter = mapFromScene(sceneCenter);
+ d->pinchStartDist = dist;
+ d->pinchStartAngle = angle;
+ d->pinchLastScale = 1.0;
+ d->pinchLastAngle = angle;
+ d->pinchRotation = 0.0;
+ d->lastPoint1 = p1;
+ d->lastPoint2 = p2;
+ QSGPinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0);
+ pe.setStartCenter(d->pinchStartCenter);
+ pe.setPreviousCenter(d->pinchStartCenter);
+ pe.setPreviousAngle(d->pinchLastAngle);
+ pe.setPreviousScale(d->pinchLastScale);
+ pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
+ pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
+ pe.setPoint1(mapFromScene(d->lastPoint1));
+ pe.setPoint2(mapFromScene(d->lastPoint2));
+ pe.setPointCount(d->touchPoints.count());
+ emit pinchStarted(&pe);
+ if (pe.accepted()) {
+ d->inPinch = true;
+ d->stealMouse = true;
+ QSGCanvas *c = canvas();
+ if (c && c->mouseGrabberItem() != this)
+ grabMouse();
+ setKeepMouseGrab(true);
+ if (d->pinch && d->pinch->target()) {
+ d->pinchStartPos = pinch()->target()->pos();
+ d->pinchStartScale = d->pinch->target()->scale();
+ d->pinchStartRotation = d->pinch->target()->rotation();
+ d->pinch->setActive(true);
+ }
+ } else {
+ d->pinchRejected = true;
+ }
+ }
+ } else if (d->pinchStartDist > 0) {
+ qreal scale = dist ? dist / d->pinchStartDist : d->pinchLastScale;
+ qreal da = d->pinchLastAngle - angle;
+ if (da > 180)
+ da -= 360;
+ else if (da < -180)
+ da += 360;
+ d->pinchRotation += da;
+ QPointF pinchCenter = mapFromScene(sceneCenter);
+ QSGPinchEvent pe(pinchCenter, scale, angle, d->pinchRotation);
+ pe.setStartCenter(d->pinchStartCenter);
+ pe.setPreviousCenter(mapFromScene(d->sceneLastCenter));
+ pe.setPreviousAngle(d->pinchLastAngle);
+ pe.setPreviousScale(d->pinchLastScale);
+ pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
+ pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
+ pe.setPoint1(touchPoint1.pos());
+ pe.setPoint2(touchPoint2.pos());
+ pe.setPointCount(d->touchPoints.count());
+ d->pinchLastScale = scale;
+ d->sceneLastCenter = sceneCenter;
+ d->pinchLastAngle = angle;
+ d->lastPoint1 = touchPoint1.scenePos();
+ d->lastPoint2 = touchPoint2.scenePos();
+ emit pinchUpdated(&pe);
+ if (d->pinch && d->pinch->target()) {
+ qreal s = d->pinchStartScale * scale;
+ s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
+ pinch()->target()->setScale(s);
+ QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos;
+ if (pinch()->axis() & QSGPinch::XAxis) {
+ qreal x = pos.x();
+ if (x < pinch()->xmin())
+ x = pinch()->xmin();
+ else if (x > pinch()->xmax())
+ x = pinch()->xmax();
+ pinch()->target()->setX(x);
+ }
+ if (pinch()->axis() & QSGPinch::YAxis) {
+ qreal y = pos.y();
+ if (y < pinch()->ymin())
+ y = pinch()->ymin();
+ else if (y > pinch()->ymax())
+ y = pinch()->ymax();
+ pinch()->target()->setY(y);
+ }
+ if (d->pinchStartRotation >= pinch()->minimumRotation()
+ && d->pinchStartRotation <= pinch()->maximumRotation()) {
+ qreal r = d->pinchRotation + d->pinchStartRotation;
+ r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation());
+ pinch()->target()->setRotation(r);
+ }
+ }
+ }
+ }
+}
+
+void QSGPinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPinchArea);
+ d->stealMouse = false;
+ if (!d->absorb)
+ QSGItem::mousePressEvent(event);
+ else {
+ setKeepMouseGrab(false);
+ event->setAccepted(true);
+ }
+}
+
+void QSGPinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPinchArea);
+ if (!d->absorb) {
+ QSGItem::mouseMoveEvent(event);
+ return;
+ }
+}
+
+void QSGPinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPinchArea);
+ d->stealMouse = false;
+ if (!d->absorb) {
+ QSGItem::mouseReleaseEvent(event);
+ } else {
+ QSGCanvas *c = canvas();
+ if (c && c->mouseGrabberItem() == this)
+ ungrabMouse();
+ setKeepMouseGrab(false);
+ }
+}
+
+void QSGPinchArea::mouseUngrabEvent()
+{
+ setKeepMouseGrab(false);
+}
+
+bool QSGPinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGPinchArea);
+ QGraphicsSceneMouseEvent mouseEvent(event->type());
+ QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
+
+ QSGCanvas *c = canvas();
+ QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
+ bool stealThisEvent = d->stealMouse;
+ if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
+ mouseEvent.setAccepted(false);
+ for (int i = 0x1; i <= 0x10; i <<= 1) {
+ if (event->buttons() & i) {
+ Qt::MouseButton button = Qt::MouseButton(i);
+ mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
+ }
+ }
+ mouseEvent.setScenePos(event->scenePos());
+ mouseEvent.setLastScenePos(event->lastScenePos());
+ mouseEvent.setPos(mapFromScene(event->scenePos()));
+ mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
+
+ switch(mouseEvent.type()) {
+ case QEvent::GraphicsSceneMouseMove:
+ mouseMoveEvent(&mouseEvent);
+ break;
+ case QEvent::GraphicsSceneMousePress:
+ mousePressEvent(&mouseEvent);
+ break;
+ case QEvent::GraphicsSceneMouseRelease:
+ mouseReleaseEvent(&mouseEvent);
+ break;
+ default:
+ break;
+ }
+ grabber = c->mouseGrabberItem();
+ if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
+ grabMouse();
+
+ return stealThisEvent;
+ }
+ if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
+ d->stealMouse = false;
+ if (c && c->mouseGrabberItem() == this)
+ ungrabMouse();
+ setKeepMouseGrab(false);
+ }
+ return false;
+}
+
+bool QSGPinchArea::childMouseEventFilter(QSGItem *i, QEvent *e)
+{
+ Q_D(QSGPinchArea);
+ if (!d->absorb || !isVisible())
+ return QSGItem::childMouseEventFilter(i, e);
+ switch (e->type()) {
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
+ break;
+ case QEvent::TouchBegin:
+ case QEvent::TouchUpdate: {
+ QTouchEvent *touch = static_cast<QTouchEvent*>(e);
+ d->touchPoints.clear();
+ for (int i = 0; i < touch->touchPoints().count(); ++i)
+ if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
+ d->touchPoints << touch->touchPoints().at(i);
+ updatePinch();
+ }
+ return d->inPinch;
+ case QEvent::TouchEnd:
+ d->touchPoints.clear();
+ updatePinch();
+ break;
+ default:
+ break;
+ }
+
+ return QSGItem::childMouseEventFilter(i, e);
+}
+
+void QSGPinchArea::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+void QSGPinchArea::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ QSGItem::itemChange(change, value);
+}
+
+QSGPinch *QSGPinchArea::pinch()
+{
+ Q_D(QSGPinchArea);
+ if (!d->pinch)
+ d->pinch = new QSGPinch;
+ return d->pinch;
+}
+
+
+QT_END_NAMESPACE
+
diff --git a/src/declarative/items/qsgpincharea_p.h b/src/declarative/items/qsgpincharea_p.h
new file mode 100644
index 0000000000..4cba6367e1
--- /dev/null
+++ b/src/declarative/items/qsgpincharea_p.h
@@ -0,0 +1,315 @@
+// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPINCHAREA_H
+#define QSGPINCHAREA_H
+
+#include "qsgitem.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_AUTOTEST_EXPORT QSGPinch : public QObject
+{
+ Q_OBJECT
+
+ Q_ENUMS(Axis)
+ Q_PROPERTY(QSGItem *target READ target WRITE setTarget RESET resetTarget)
+ Q_PROPERTY(qreal minimumScale READ minimumScale WRITE setMinimumScale NOTIFY minimumScaleChanged)
+ Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged)
+ Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged)
+ Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged)
+ Q_PROPERTY(Axis dragAxis READ axis WRITE setAxis NOTIFY dragAxisChanged)
+ Q_PROPERTY(qreal minimumX READ xmin WRITE setXmin NOTIFY minimumXChanged)
+ Q_PROPERTY(qreal maximumX READ xmax WRITE setXmax NOTIFY maximumXChanged)
+ Q_PROPERTY(qreal minimumY READ ymin WRITE setYmin NOTIFY minimumYChanged)
+ Q_PROPERTY(qreal maximumY READ ymax WRITE setYmax NOTIFY maximumYChanged)
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged)
+
+public:
+ QSGPinch();
+
+ QSGItem *target() const { return m_target; }
+ void setTarget(QSGItem *target) {
+ if (target == m_target)
+ return;
+ m_target = target;
+ emit targetChanged();
+ }
+ void resetTarget() {
+ if (!m_target)
+ return;
+ m_target = 0;
+ emit targetChanged();
+ }
+
+ qreal minimumScale() const { return m_minScale; }
+ void setMinimumScale(qreal s) {
+ if (s == m_minScale)
+ return;
+ m_minScale = s;
+ emit minimumScaleChanged();
+ }
+ qreal maximumScale() const { return m_maxScale; }
+ void setMaximumScale(qreal s) {
+ if (s == m_maxScale)
+ return;
+ m_maxScale = s;
+ emit maximumScaleChanged();
+ }
+
+ qreal minimumRotation() const { return m_minRotation; }
+ void setMinimumRotation(qreal r) {
+ if (r == m_minRotation)
+ return;
+ m_minRotation = r;
+ emit minimumRotationChanged();
+ }
+ qreal maximumRotation() const { return m_maxRotation; }
+ void setMaximumRotation(qreal r) {
+ if (r == m_maxRotation)
+ return;
+ m_maxRotation = r;
+ emit maximumRotationChanged();
+ }
+
+ enum Axis { NoDrag=0x00, XAxis=0x01, YAxis=0x02, XandYAxis=0x03 };
+ Axis axis() const { return m_axis; }
+ void setAxis(Axis a) {
+ if (a == m_axis)
+ return;
+ m_axis = a;
+ emit dragAxisChanged();
+ }
+
+ qreal xmin() const { return m_xmin; }
+ void setXmin(qreal x) {
+ if (x == m_xmin)
+ return;
+ m_xmin = x;
+ emit minimumXChanged();
+ }
+ qreal xmax() const { return m_xmax; }
+ void setXmax(qreal x) {
+ if (x == m_xmax)
+ return;
+ m_xmax = x;
+ emit maximumXChanged();
+ }
+ qreal ymin() const { return m_ymin; }
+ void setYmin(qreal y) {
+ if (y == m_ymin)
+ return;
+ m_ymin = y;
+ emit minimumYChanged();
+ }
+ qreal ymax() const { return m_ymax; }
+ void setYmax(qreal y) {
+ if (y == m_ymax)
+ return;
+ m_ymax = y;
+ emit maximumYChanged();
+ }
+
+ bool active() const { return m_active; }
+ void setActive(bool a) {
+ if (a == m_active)
+ return;
+ m_active = a;
+ emit activeChanged();
+ }
+
+signals:
+ void targetChanged();
+ void minimumScaleChanged();
+ void maximumScaleChanged();
+ void minimumRotationChanged();
+ void maximumRotationChanged();
+ void dragAxisChanged();
+ void minimumXChanged();
+ void maximumXChanged();
+ void minimumYChanged();
+ void maximumYChanged();
+ void activeChanged();
+
+private:
+ QSGItem *m_target;
+ qreal m_minScale;
+ qreal m_maxScale;
+ qreal m_minRotation;
+ qreal m_maxRotation;
+ Axis m_axis;
+ qreal m_xmin;
+ qreal m_xmax;
+ qreal m_ymin;
+ qreal m_ymax;
+ bool m_active;
+};
+
+class Q_AUTOTEST_EXPORT QSGPinchEvent : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QPointF center READ center)
+ Q_PROPERTY(QPointF startCenter READ startCenter)
+ Q_PROPERTY(QPointF previousCenter READ previousCenter)
+ Q_PROPERTY(qreal scale READ scale)
+ Q_PROPERTY(qreal previousScale READ previousScale)
+ Q_PROPERTY(qreal angle READ angle)
+ Q_PROPERTY(qreal previousAngle READ previousAngle)
+ Q_PROPERTY(qreal rotation READ rotation)
+ Q_PROPERTY(QPointF point1 READ point1)
+ Q_PROPERTY(QPointF startPoint1 READ startPoint1)
+ Q_PROPERTY(QPointF point2 READ point2)
+ Q_PROPERTY(QPointF startPoint2 READ startPoint2)
+ Q_PROPERTY(int pointCount READ pointCount)
+ Q_PROPERTY(bool accepted READ accepted WRITE setAccepted)
+
+public:
+ QSGPinchEvent(QPointF c, qreal s, qreal a, qreal r)
+ : QObject(), m_center(c), m_scale(s), m_angle(a), m_rotation(r)
+ , m_pointCount(0), m_accepted(true) {}
+
+ QPointF center() const { return m_center; }
+ QPointF startCenter() const { return m_startCenter; }
+ void setStartCenter(QPointF c) { m_startCenter = c; }
+ QPointF previousCenter() const { return m_lastCenter; }
+ void setPreviousCenter(QPointF c) { m_lastCenter = c; }
+ qreal scale() const { return m_scale; }
+ qreal previousScale() const { return m_lastScale; }
+ void setPreviousScale(qreal s) { m_lastScale = s; }
+ qreal angle() const { return m_angle; }
+ qreal previousAngle() const { return m_lastAngle; }
+ void setPreviousAngle(qreal a) { m_lastAngle = a; }
+ qreal rotation() const { return m_rotation; }
+ QPointF point1() const { return m_point1; }
+ void setPoint1(QPointF p) { m_point1 = p; }
+ QPointF startPoint1() const { return m_startPoint1; }
+ void setStartPoint1(QPointF p) { m_startPoint1 = p; }
+ QPointF point2() const { return m_point2; }
+ void setPoint2(QPointF p) { m_point2 = p; }
+ QPointF startPoint2() const { return m_startPoint2; }
+ void setStartPoint2(QPointF p) { m_startPoint2 = p; }
+ int pointCount() const { return m_pointCount; }
+ void setPointCount(int count) { m_pointCount = count; }
+
+ bool accepted() const { return m_accepted; }
+ void setAccepted(bool a) { m_accepted = a; }
+
+private:
+ QPointF m_center;
+ QPointF m_startCenter;
+ QPointF m_lastCenter;
+ qreal m_scale;
+ qreal m_lastScale;
+ qreal m_angle;
+ qreal m_lastAngle;
+ qreal m_rotation;
+ QPointF m_point1;
+ QPointF m_point2;
+ QPointF m_startPoint1;
+ QPointF m_startPoint2;
+ int m_pointCount;
+ bool m_accepted;
+};
+
+
+class QSGMouseEvent;
+class QSGPinchAreaPrivate;
+class Q_AUTOTEST_EXPORT QSGPinchArea : public QSGItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(QSGPinch *pinch READ pinch CONSTANT)
+
+public:
+ QSGPinchArea(QSGItem *parent=0);
+ ~QSGPinchArea();
+
+ bool isEnabled() const;
+ void setEnabled(bool);
+
+ QSGPinch *pinch();
+
+Q_SIGNALS:
+ void enabledChanged();
+ void pinchStarted(QSGPinchEvent *pinch);
+ void pinchUpdated(QSGPinchEvent *pinch);
+ void pinchFinished(QSGPinchEvent *pinch);
+
+protected:
+ virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ virtual void mouseUngrabEvent();
+ virtual bool sendMouseEvent(QGraphicsSceneMouseEvent *event);
+ virtual bool childMouseEventFilter(QSGItem *i, QEvent *e);
+ virtual void touchEvent(QTouchEvent *event);
+
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+ virtual void itemChange(ItemChange change, const ItemChangeData& value);
+
+private:
+ void updatePinch();
+ void handlePress();
+ void handleRelease();
+
+private:
+ Q_DISABLE_COPY(QSGPinchArea)
+ Q_DECLARE_PRIVATE(QSGPinchArea)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGPinch)
+QML_DECLARE_TYPE(QSGPinchEvent)
+QML_DECLARE_TYPE(QSGPinchArea)
+
+QT_END_HEADER
+
+#endif // QSGPINCHAREA_H
+
diff --git a/src/declarative/items/qsgpincharea_p_p.h b/src/declarative/items/qsgpincharea_p_p.h
new file mode 100644
index 0000000000..b93d095e0e
--- /dev/null
+++ b/src/declarative/items/qsgpincharea_p_p.h
@@ -0,0 +1,115 @@
+// Commit: f707672eb4c51ea82fbd98e1da16ece61a74c690
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtSG module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPINCHAREA_P_H
+#define QSGPINCHAREA_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qevent.h>
+
+#include "qsgitem_p.h"
+#include "qsgpincharea_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGPinch;
+class QSGPinchAreaPrivate : public QSGItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGPinchArea)
+public:
+ QSGPinchAreaPrivate()
+ : absorb(true), stealMouse(false), inPinch(false)
+ , pinchRejected(false), pinchActivated(false)
+ , pinch(0), pinchStartDist(0), pinchStartScale(1.0)
+ , pinchLastScale(1.0), pinchStartRotation(0.0), pinchStartAngle(0.0)
+ , pinchLastAngle(0.0), pinchRotation(0.0)
+ {
+ }
+
+ ~QSGPinchAreaPrivate();
+
+ void init()
+ {
+ Q_Q(QSGPinchArea);
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFiltersChildMouseEvents(true);
+ }
+
+ bool absorb : 1;
+ bool stealMouse : 1;
+ bool inPinch : 1;
+ bool pinchRejected : 1;
+ bool pinchActivated : 1;
+ QSGPinch *pinch;
+ QPointF sceneStartPoint1;
+ QPointF sceneStartPoint2;
+ QPointF lastPoint1;
+ QPointF lastPoint2;
+ qreal pinchStartDist;
+ qreal pinchStartScale;
+ qreal pinchLastScale;
+ qreal pinchStartRotation;
+ qreal pinchStartAngle;
+ qreal pinchLastAngle;
+ qreal pinchRotation;
+ QPointF sceneStartCenter;
+ QPointF pinchStartCenter;
+ QPointF sceneLastCenter;
+ QPointF pinchStartPos;
+ QList<QTouchEvent::TouchPoint> touchPoints;
+ int id1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGPINCHAREA_P_H
+
diff --git a/src/declarative/items/qsgpositioners.cpp b/src/declarative/items/qsgpositioners.cpp
new file mode 100644
index 0000000000..f174612af6
--- /dev/null
+++ b/src/declarative/items/qsgpositioners.cpp
@@ -0,0 +1,788 @@
+// Commit: 1f38d41854fa2daa51d938a4eb398752b1751e0b
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgpositioners_p.h"
+#include "qsgpositioners_p_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+#include <QtCore/qcoreapplication.h>
+
+#include <private/qdeclarativestate_p.h>
+#include <private/qdeclarativestategroup_p.h>
+#include <private/qdeclarativestateoperations_p.h>
+
+QT_BEGIN_NAMESPACE
+
+static const QSGItemPrivate::ChangeTypes watchedChanges
+ = QSGItemPrivate::Geometry
+ | QSGItemPrivate::SiblingOrder
+ | QSGItemPrivate::Visibility
+ | QSGItemPrivate::Opacity
+ | QSGItemPrivate::Destroyed;
+
+void QSGBasePositionerPrivate::watchChanges(QSGItem *other)
+{
+ QSGItemPrivate *otherPrivate = QSGItemPrivate::get(other);
+ otherPrivate->addItemChangeListener(this, watchedChanges);
+}
+
+void QSGBasePositionerPrivate::unwatchChanges(QSGItem* other)
+{
+ QSGItemPrivate *otherPrivate = QSGItemPrivate::get(other);
+ otherPrivate->removeItemChangeListener(this, watchedChanges);
+}
+
+QSGBasePositioner::QSGBasePositioner(PositionerType at, QSGItem *parent)
+ : QSGImplicitSizeItem(*(new QSGBasePositionerPrivate), parent)
+{
+ Q_D(QSGBasePositioner);
+ d->init(at);
+}
+
+QSGBasePositioner::QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent)
+ : QSGImplicitSizeItem(dd, parent)
+{
+ Q_D(QSGBasePositioner);
+ d->init(at);
+}
+
+QSGBasePositioner::~QSGBasePositioner()
+{
+ Q_D(QSGBasePositioner);
+ for (int i = 0; i < positionedItems.count(); ++i)
+ d->unwatchChanges(positionedItems.at(i).item);
+ positionedItems.clear();
+}
+
+int QSGBasePositioner::spacing() const
+{
+ Q_D(const QSGBasePositioner);
+ return d->spacing;
+}
+
+void QSGBasePositioner::setSpacing(int s)
+{
+ Q_D(QSGBasePositioner);
+ if (s==d->spacing)
+ return;
+ d->spacing = s;
+ prePositioning();
+ emit spacingChanged();
+}
+
+QDeclarativeTransition *QSGBasePositioner::move() const
+{
+ Q_D(const QSGBasePositioner);
+ return d->moveTransition;
+}
+
+void QSGBasePositioner::setMove(QDeclarativeTransition *mt)
+{
+ Q_D(QSGBasePositioner);
+ if (mt == d->moveTransition)
+ return;
+ d->moveTransition = mt;
+ emit moveChanged();
+}
+
+QDeclarativeTransition *QSGBasePositioner::add() const
+{
+ Q_D(const QSGBasePositioner);
+ return d->addTransition;
+}
+
+void QSGBasePositioner::setAdd(QDeclarativeTransition *add)
+{
+ Q_D(QSGBasePositioner);
+ if (add == d->addTransition)
+ return;
+
+ d->addTransition = add;
+ emit addChanged();
+}
+
+void QSGBasePositioner::componentComplete()
+{
+ QSGItem::componentComplete();
+ positionedItems.reserve(childItems().count());
+ prePositioning();
+ reportConflictingAnchors();
+}
+
+void QSGBasePositioner::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QSGBasePositioner);
+ if (change == ItemChildAddedChange){
+ prePositioning();
+ } else if (change == ItemChildRemovedChange) {
+ QSGItem *child = value.item;
+ QSGBasePositioner::PositionedItem posItem(child);
+ int idx = positionedItems.find(posItem);
+ if (idx >= 0) {
+ d->unwatchChanges(child);
+ positionedItems.remove(idx);
+ }
+ prePositioning();
+ }
+
+ QSGItem::itemChange(change, value);
+}
+
+void QSGBasePositioner::prePositioning()
+{
+ Q_D(QSGBasePositioner);
+ if (!isComponentComplete())
+ return;
+
+ if (d->doingPositioning)
+ return;
+
+ d->queuedPositioning = false;
+ d->doingPositioning = true;
+ //Need to order children by creation order modified by stacking order
+ QList<QSGItem *> children = childItems();
+
+ QPODVector<PositionedItem,8> oldItems;
+ positionedItems.copyAndClear(oldItems);
+ for (int ii = 0; ii < children.count(); ++ii) {
+ QSGItem *child = children.at(ii);
+ QSGItemPrivate *childPrivate = QSGItemPrivate::get(child);
+ PositionedItem *item = 0;
+ PositionedItem posItem(child);
+ int wIdx = oldItems.find(posItem);
+ if (wIdx < 0) {
+ d->watchChanges(child);
+ positionedItems.append(posItem);
+ item = &positionedItems[positionedItems.count()-1];
+ item->isNew = true;
+ if (child->opacity() <= 0.0 || !childPrivate->explicitVisible || !child->width() || !child->height())
+ item->isVisible = false;
+ } else {
+ item = &oldItems[wIdx];
+ // Items are only omitted from positioning if they are explicitly hidden
+ // i.e. their positioning is not affected if an ancestor is hidden.
+ if (child->opacity() <= 0.0 || !childPrivate->explicitVisible || !child->width() || !child->height()) {
+ item->isVisible = false;
+ } else if (!item->isVisible) {
+ item->isVisible = true;
+ item->isNew = true;
+ } else {
+ item->isNew = false;
+ }
+ positionedItems.append(*item);
+ }
+ }
+ QSizeF contentSize;
+ doPositioning(&contentSize);
+ if(d->addTransition || d->moveTransition)
+ finishApplyTransitions();
+ d->doingPositioning = false;
+ //Set implicit size to the size of its children
+ setImplicitHeight(contentSize.height());
+ setImplicitWidth(contentSize.width());
+}
+
+void QSGBasePositioner::positionX(int x, const PositionedItem &target)
+{
+ Q_D(QSGBasePositioner);
+ if(d->type == Horizontal || d->type == Both){
+ if (target.isNew) {
+ if (!d->addTransition)
+ target.item->setX(x);
+ else
+ d->addActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
+ } else if (x != target.item->x()) {
+ if (!d->moveTransition)
+ target.item->setX(x);
+ else
+ d->moveActions << QDeclarativeAction(target.item, QLatin1String("x"), QVariant(x));
+ }
+ }
+}
+
+void QSGBasePositioner::positionY(int y, const PositionedItem &target)
+{
+ Q_D(QSGBasePositioner);
+ if(d->type == Vertical || d->type == Both){
+ if (target.isNew) {
+ if (!d->addTransition)
+ target.item->setY(y);
+ else
+ d->addActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
+ } else if (y != target.item->y()) {
+ if (!d->moveTransition)
+ target.item->setY(y);
+ else
+ d->moveActions << QDeclarativeAction(target.item, QLatin1String("y"), QVariant(y));
+ }
+ }
+}
+
+void QSGBasePositioner::finishApplyTransitions()
+{
+ Q_D(QSGBasePositioner);
+ // Note that if a transition is not set the transition manager will
+ // apply the changes directly, in the case add/move aren't set
+ d->addTransitionManager.transition(d->addActions, d->addTransition);
+ d->moveTransitionManager.transition(d->moveActions, d->moveTransition);
+ d->addActions.clear();
+ d->moveActions.clear();
+}
+
+QSGColumn::QSGColumn(QSGItem *parent)
+: QSGBasePositioner(Vertical, parent)
+{
+}
+
+void QSGColumn::doPositioning(QSizeF *contentSize)
+{
+ int voffset = 0;
+
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (!child.item || !child.isVisible)
+ continue;
+
+ if(child.item->y() != voffset)
+ positionY(voffset, child);
+
+ contentSize->setWidth(qMax(contentSize->width(), child.item->width()));
+
+ voffset += child.item->height();
+ voffset += spacing();
+ }
+
+ contentSize->setHeight(voffset - spacing());
+}
+
+void QSGColumn::reportConflictingAnchors()
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (child.item) {
+ QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
+ if (anchors) {
+ QSGAnchors::Anchors usedAnchors = anchors->usedAnchors();
+ if (usedAnchors & QSGAnchors::TopAnchor ||
+ usedAnchors & QSGAnchors::BottomAnchor ||
+ usedAnchors & QSGAnchors::VCenterAnchor ||
+ anchors->fill() || anchors->centerIn()) {
+ d->anchorConflict = true;
+ break;
+ }
+ }
+ }
+ }
+ if (d->anchorConflict) {
+ qmlInfo(this) << "Cannot specify top, bottom, verticalCenter, fill or centerIn anchors for items inside Column";
+ }
+}
+
+QSGRow::QSGRow(QSGItem *parent)
+: QSGBasePositioner(Horizontal, parent)
+{
+}
+
+Qt::LayoutDirection QSGRow::layoutDirection() const
+{
+ return QSGBasePositionerPrivate::getLayoutDirection(this);
+}
+
+void QSGRow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate* >(QSGBasePositionerPrivate::get(this));
+ if (d->layoutDirection != layoutDirection) {
+ d->layoutDirection = layoutDirection;
+ // For RTL layout the positioning changes when the width changes.
+ if (d->layoutDirection == Qt::RightToLeft)
+ d->addItemChangeListener(d, QSGItemPrivate::Geometry);
+ else
+ d->removeItemChangeListener(d, QSGItemPrivate::Geometry);
+ prePositioning();
+ emit layoutDirectionChanged();
+ emit effectiveLayoutDirectionChanged();
+ }
+}
+
+Qt::LayoutDirection QSGRow::effectiveLayoutDirection() const
+{
+ return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
+}
+
+void QSGRow::doPositioning(QSizeF *contentSize)
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate* >(QSGBasePositionerPrivate::get(this));
+ int hoffset = 0;
+
+ QList<int> hoffsets;
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (!child.item || !child.isVisible)
+ continue;
+
+ if (d->isLeftToRight()) {
+ if (child.item->x() != hoffset)
+ positionX(hoffset, child);
+ } else {
+ hoffsets << hoffset;
+ }
+
+ contentSize->setHeight(qMax(contentSize->height(), child.item->height()));
+
+ hoffset += child.item->width();
+ hoffset += spacing();
+ }
+
+ contentSize->setWidth(hoffset - spacing());
+
+ if (d->isLeftToRight())
+ return;
+
+ //Right to Left layout
+ int end = 0;
+ if (!widthValid())
+ end = contentSize->width();
+ else
+ end = width();
+
+ int acc = 0;
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (!child.item || !child.isVisible)
+ continue;
+ hoffset = end - hoffsets[acc++] - child.item->width();
+ if (child.item->x() != hoffset)
+ positionX(hoffset, child);
+ }
+}
+
+void QSGRow::reportConflictingAnchors()
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (child.item) {
+ QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
+ if (anchors) {
+ QSGAnchors::Anchors usedAnchors = anchors->usedAnchors();
+ if (usedAnchors & QSGAnchors::LeftAnchor ||
+ usedAnchors & QSGAnchors::RightAnchor ||
+ usedAnchors & QSGAnchors::HCenterAnchor ||
+ anchors->fill() || anchors->centerIn()) {
+ d->anchorConflict = true;
+ break;
+ }
+ }
+ }
+ }
+ if (d->anchorConflict)
+ qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside Row";
+}
+
+QSGGrid::QSGGrid(QSGItem *parent) :
+ QSGBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight)
+{
+}
+
+void QSGGrid::setColumns(const int columns)
+{
+ if (columns == m_columns)
+ return;
+ m_columns = columns;
+ prePositioning();
+ emit columnsChanged();
+}
+
+void QSGGrid::setRows(const int rows)
+{
+ if (rows == m_rows)
+ return;
+ m_rows = rows;
+ prePositioning();
+ emit rowsChanged();
+}
+
+QSGGrid::Flow QSGGrid::flow() const
+{
+ return m_flow;
+}
+
+void QSGGrid::setFlow(Flow flow)
+{
+ if (m_flow != flow) {
+ m_flow = flow;
+ prePositioning();
+ emit flowChanged();
+ }
+}
+
+Qt::LayoutDirection QSGGrid::layoutDirection() const
+{
+ return QSGBasePositionerPrivate::getLayoutDirection(this);
+}
+
+void QSGGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection)
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
+ if (d->layoutDirection != layoutDirection) {
+ d->layoutDirection = layoutDirection;
+ // For RTL layout the positioning changes when the width changes.
+ if (d->layoutDirection == Qt::RightToLeft)
+ d->addItemChangeListener(d, QSGItemPrivate::Geometry);
+ else
+ d->removeItemChangeListener(d, QSGItemPrivate::Geometry);
+ prePositioning();
+ emit layoutDirectionChanged();
+ emit effectiveLayoutDirectionChanged();
+ }
+}
+
+Qt::LayoutDirection QSGGrid::effectiveLayoutDirection() const
+{
+ return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
+}
+
+void QSGGrid::doPositioning(QSizeF *contentSize)
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
+ int c = m_columns;
+ int r = m_rows;
+ //Is allocating the extra QPODVector too much overhead?
+ QPODVector<PositionedItem, 8> visibleItems;//we aren't concerned with invisible items
+ visibleItems.reserve(positionedItems.count());
+ for(int i=0; i<positionedItems.count(); i++)
+ if(positionedItems[i].item && positionedItems[i].isVisible)
+ visibleItems.append(positionedItems[i]);
+
+ int numVisible = visibleItems.count();
+ if (m_columns <= 0 && m_rows <= 0){
+ c = 4;
+ r = (numVisible+3)/4;
+ } else if (m_rows <= 0){
+ r = (numVisible+(m_columns-1))/m_columns;
+ } else if (m_columns <= 0){
+ c = (numVisible+(m_rows-1))/m_rows;
+ }
+
+ if(r==0 || c==0)
+ return; //Nothing to do
+
+ QList<int> maxColWidth;
+ QList<int> maxRowHeight;
+ int childIndex =0;
+ if (m_flow == LeftToRight) {
+ for (int i=0; i < r; i++){
+ for (int j=0; j < c; j++){
+ if (j==0)
+ maxRowHeight << 0;
+ if (i==0)
+ maxColWidth << 0;
+
+ if (childIndex == visibleItems.count())
+ break;
+
+ const PositionedItem &child = visibleItems.at(childIndex++);
+ if (child.item->width() > maxColWidth[j])
+ maxColWidth[j] = child.item->width();
+ if (child.item->height() > maxRowHeight[i])
+ maxRowHeight[i] = child.item->height();
+ }
+ }
+ } else {
+ for (int j=0; j < c; j++){
+ for (int i=0; i < r; i++){
+ if (j==0)
+ maxRowHeight << 0;
+ if (i==0)
+ maxColWidth << 0;
+
+ if (childIndex == visibleItems.count())
+ break;
+
+ const PositionedItem &child = visibleItems.at(childIndex++);
+ if (child.item->width() > maxColWidth[j])
+ maxColWidth[j] = child.item->width();
+ if (child.item->height() > maxRowHeight[i])
+ maxRowHeight[i] = child.item->height();
+ }
+ }
+ }
+
+ int widthSum = 0;
+ for (int j=0; j < maxColWidth.size(); j++){
+ if (j)
+ widthSum += spacing();
+ widthSum += maxColWidth[j];
+ }
+
+ int heightSum = 0;
+ for (int i=0; i < maxRowHeight.size(); i++){
+ if (i)
+ heightSum += spacing();
+ heightSum += maxRowHeight[i];
+ }
+
+ contentSize->setHeight(heightSum);
+ contentSize->setWidth(widthSum);
+
+ int end = 0;
+ if (widthValid())
+ end = width();
+ else
+ end = widthSum;
+
+ int xoffset=0;
+ if (!d->isLeftToRight())
+ xoffset = end;
+ int yoffset=0;
+ int curRow =0;
+ int curCol =0;
+ for (int i = 0; i < visibleItems.count(); ++i) {
+ const PositionedItem &child = visibleItems.at(i);
+ int childXOffset = xoffset;
+ if (!d->isLeftToRight())
+ childXOffset -= child.item->width();
+ if ((child.item->x() != childXOffset) || (child.item->y() != yoffset)){
+ positionX(childXOffset, child);
+ positionY(yoffset, child);
+ }
+
+ if (m_flow == LeftToRight) {
+ if (d->isLeftToRight())
+ xoffset += maxColWidth[curCol]+spacing();
+ else
+ xoffset -= maxColWidth[curCol]+spacing();
+ curCol++;
+ curCol%=c;
+ if (!curCol){
+ yoffset += maxRowHeight[curRow]+spacing();
+ if (d->isLeftToRight())
+ xoffset = 0;
+ else
+ xoffset = end;
+ curRow++;
+ if (curRow>=r)
+ break;
+ }
+ } else {
+ yoffset+=maxRowHeight[curRow]+spacing();
+ curRow++;
+ curRow%=r;
+ if (!curRow){
+ if (d->isLeftToRight())
+ xoffset += maxColWidth[curCol]+spacing();
+ else
+ xoffset -= maxColWidth[curCol]+spacing();
+ yoffset=0;
+ curCol++;
+ if (curCol>=c)
+ break;
+ }
+ }
+ }
+}
+
+void QSGGrid::reportConflictingAnchors()
+{
+ QSGBasePositionerPrivate *d = static_cast<QSGBasePositionerPrivate*>(QSGBasePositionerPrivate::get(this));
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (child.item) {
+ QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
+ if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
+ d->anchorConflict = true;
+ break;
+ }
+ }
+ }
+ if (d->anchorConflict)
+ qmlInfo(this) << "Cannot specify anchors for items inside Grid";
+}
+
+class QSGFlowPrivate : public QSGBasePositionerPrivate
+{
+ Q_DECLARE_PUBLIC(QSGFlow)
+
+public:
+ QSGFlowPrivate()
+ : QSGBasePositionerPrivate(), flow(QSGFlow::LeftToRight)
+ {}
+
+ QSGFlow::Flow flow;
+};
+
+QSGFlow::QSGFlow(QSGItem *parent)
+: QSGBasePositioner(*(new QSGFlowPrivate), Both, parent)
+{
+ Q_D(QSGFlow);
+ // Flow layout requires relayout if its own size changes too.
+ d->addItemChangeListener(d, QSGItemPrivate::Geometry);
+}
+
+QSGFlow::Flow QSGFlow::flow() const
+{
+ Q_D(const QSGFlow);
+ return d->flow;
+}
+
+void QSGFlow::setFlow(Flow flow)
+{
+ Q_D(QSGFlow);
+ if (d->flow != flow) {
+ d->flow = flow;
+ prePositioning();
+ emit flowChanged();
+ }
+}
+
+Qt::LayoutDirection QSGFlow::layoutDirection() const
+{
+ Q_D(const QSGFlow);
+ return d->layoutDirection;
+}
+
+void QSGFlow::setLayoutDirection(Qt::LayoutDirection layoutDirection)
+{
+ Q_D(QSGFlow);
+ if (d->layoutDirection != layoutDirection) {
+ d->layoutDirection = layoutDirection;
+ prePositioning();
+ emit layoutDirectionChanged();
+ emit effectiveLayoutDirectionChanged();
+ }
+}
+
+Qt::LayoutDirection QSGFlow::effectiveLayoutDirection() const
+{
+ return QSGBasePositionerPrivate::getEffectiveLayoutDirection(this);
+}
+
+void QSGFlow::doPositioning(QSizeF *contentSize)
+{
+ Q_D(QSGFlow);
+
+ int hoffset = 0;
+ int voffset = 0;
+ int linemax = 0;
+ QList<int> hoffsets;
+
+ for (int i = 0; i < positionedItems.count(); ++i) {
+ const PositionedItem &child = positionedItems.at(i);
+ if (!child.item || !child.isVisible)
+ continue;
+
+ if (d->flow == LeftToRight) {
+ if (widthValid() && hoffset && hoffset + child.item->width() > width()) {
+ hoffset = 0;
+ voffset += linemax + spacing();
+ linemax = 0;
+ }
+ } else {
+ if (heightValid() && voffset && voffset + child.item->height() > height()) {
+ voffset = 0;
+ hoffset += linemax + spacing();
+ linemax = 0;
+ }
+ }
+
+ if (d->isLeftToRight()) {
+ if (child.item->x() != hoffset)
+ positionX(hoffset, child);
+ } else {
+ hoffsets << hoffset;
+ }
+ if (child.item->y() != voffset)
+ positionY(voffset, child);
+
+ contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width()));
+ contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height()));
+
+ if (d->flow == LeftToRight) {
+ hoffset += child.item->width();
+ hoffset += spacing();
+ linemax = qMax(linemax, qCeil(child.item->height()));
+ } else {
+ voffset += child.item->height();
+ voffset += spacing();
+ linemax = qMax(linemax, qCeil(child.item->width()));
+ }
+ }
+ if (d->isLeftToRight())
+ return;
+
+ int end;
+ if (widthValid())
+ end = width();
+ else
+ end = contentSize->width();
+ int acc = 0;
+ for (int i = 0; i < positionedItems.count(); ++i) {
+ const PositionedItem &child = positionedItems.at(i);
+ if (!child.item || !child.isVisible)
+ continue;
+ hoffset = end - hoffsets[acc++] - child.item->width();
+ if (child.item->x() != hoffset)
+ positionX(hoffset, child);
+ }
+}
+
+void QSGFlow::reportConflictingAnchors()
+{
+ Q_D(QSGFlow);
+ for (int ii = 0; ii < positionedItems.count(); ++ii) {
+ const PositionedItem &child = positionedItems.at(ii);
+ if (child.item) {
+ QSGAnchors *anchors = QSGItemPrivate::get(static_cast<QSGItem *>(child.item))->_anchors;
+ if (anchors && (anchors->usedAnchors() || anchors->fill() || anchors->centerIn())) {
+ d->anchorConflict = true;
+ break;
+ }
+ }
+ }
+ if (d->anchorConflict)
+ qmlInfo(this) << "Cannot specify anchors for items inside Flow";
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgpositioners_p.h b/src/declarative/items/qsgpositioners_p.h
new file mode 100644
index 0000000000..eb0e73456b
--- /dev/null
+++ b/src/declarative/items/qsgpositioners_p.h
@@ -0,0 +1,242 @@
+// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPOSITIONERS_P_H
+#define QSGPOSITIONERS_P_H
+
+#include "qsgimplicitsizeitem_p.h"
+
+#include <private/qdeclarativestate_p.h>
+#include <private/qpodvector_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGBasePositionerPrivate;
+
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGBasePositioner : public QSGImplicitSizeItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int spacing READ spacing WRITE setSpacing NOTIFY spacingChanged)
+ Q_PROPERTY(QDeclarativeTransition *move READ move WRITE setMove NOTIFY moveChanged)
+ Q_PROPERTY(QDeclarativeTransition *add READ add WRITE setAdd NOTIFY addChanged)
+public:
+ enum PositionerType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 };
+ QSGBasePositioner(PositionerType, QSGItem *parent);
+ ~QSGBasePositioner();
+
+ int spacing() const;
+ void setSpacing(int);
+
+ QDeclarativeTransition *move() const;
+ void setMove(QDeclarativeTransition *);
+
+ QDeclarativeTransition *add() const;
+ void setAdd(QDeclarativeTransition *);
+
+protected:
+ QSGBasePositioner(QSGBasePositionerPrivate &dd, PositionerType at, QSGItem *parent);
+ virtual void componentComplete();
+ virtual void itemChange(ItemChange, const ItemChangeData &);
+ void finishApplyTransitions();
+
+Q_SIGNALS:
+ void spacingChanged();
+ void moveChanged();
+ void addChanged();
+
+protected Q_SLOTS:
+ void prePositioning();
+
+protected:
+ virtual void doPositioning(QSizeF *contentSize)=0;
+ virtual void reportConflictingAnchors()=0;
+ class PositionedItem {
+ public :
+ PositionedItem(QSGItem *i) : item(i), isNew(false), isVisible(true) {}
+ bool operator==(const PositionedItem &other) const { return other.item == item; }
+ QSGItem *item;
+ bool isNew;
+ bool isVisible;
+ };
+
+ QPODVector<PositionedItem,8> positionedItems;
+ void positionX(int,const PositionedItem &target);
+ void positionY(int,const PositionedItem &target);
+
+private:
+ Q_DISABLE_COPY(QSGBasePositioner)
+ Q_DECLARE_PRIVATE(QSGBasePositioner)
+};
+
+class Q_AUTOTEST_EXPORT QSGColumn : public QSGBasePositioner
+{
+ Q_OBJECT
+public:
+ QSGColumn(QSGItem *parent=0);
+protected:
+ virtual void doPositioning(QSizeF *contentSize);
+ virtual void reportConflictingAnchors();
+private:
+ Q_DISABLE_COPY(QSGColumn)
+};
+
+class Q_AUTOTEST_EXPORT QSGRow: public QSGBasePositioner
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
+ Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
+public:
+ QSGRow(QSGItem *parent=0);
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection (Qt::LayoutDirection);
+ Qt::LayoutDirection effectiveLayoutDirection() const;
+
+Q_SIGNALS:
+ void layoutDirectionChanged();
+ void effectiveLayoutDirectionChanged();
+
+protected:
+ virtual void doPositioning(QSizeF *contentSize);
+ virtual void reportConflictingAnchors();
+private:
+ Q_DISABLE_COPY(QSGRow)
+};
+
+class Q_AUTOTEST_EXPORT QSGGrid : public QSGBasePositioner
+{
+ Q_OBJECT
+ Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged)
+ Q_PROPERTY(int columns READ columns WRITE setColumns NOTIFY columnsChanged)
+ Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
+ Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
+
+public:
+ QSGGrid(QSGItem *parent=0);
+
+ int rows() const {return m_rows;}
+ void setRows(const int rows);
+
+ int columns() const {return m_columns;}
+ void setColumns(const int columns);
+
+ Q_ENUMS(Flow)
+ enum Flow { LeftToRight, TopToBottom };
+ Flow flow() const;
+ void setFlow(Flow);
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection (Qt::LayoutDirection);
+ Qt::LayoutDirection effectiveLayoutDirection() const;
+
+Q_SIGNALS:
+ void rowsChanged();
+ void columnsChanged();
+ void flowChanged();
+ void layoutDirectionChanged();
+ void effectiveLayoutDirectionChanged();
+
+protected:
+ virtual void doPositioning(QSizeF *contentSize);
+ virtual void reportConflictingAnchors();
+
+private:
+ int m_rows;
+ int m_columns;
+ Flow m_flow;
+ Q_DISABLE_COPY(QSGGrid)
+};
+
+class QSGFlowPrivate;
+class Q_AUTOTEST_EXPORT QSGFlow: public QSGBasePositioner
+{
+ Q_OBJECT
+ Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged)
+ Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged)
+ Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged)
+public:
+ QSGFlow(QSGItem *parent=0);
+
+ Q_ENUMS(Flow)
+ enum Flow { LeftToRight, TopToBottom };
+ Flow flow() const;
+ void setFlow(Flow);
+
+ Qt::LayoutDirection layoutDirection() const;
+ void setLayoutDirection (Qt::LayoutDirection);
+ Qt::LayoutDirection effectiveLayoutDirection() const;
+
+Q_SIGNALS:
+ void flowChanged();
+ void layoutDirectionChanged();
+ void effectiveLayoutDirectionChanged();
+
+protected:
+ virtual void doPositioning(QSizeF *contentSize);
+ virtual void reportConflictingAnchors();
+protected:
+ QSGFlow(QSGFlowPrivate &dd, QSGItem *parent);
+private:
+ Q_DISABLE_COPY(QSGFlow)
+ Q_DECLARE_PRIVATE(QSGFlow)
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGColumn)
+QML_DECLARE_TYPE(QSGRow)
+QML_DECLARE_TYPE(QSGGrid)
+QML_DECLARE_TYPE(QSGFlow)
+
+QT_END_HEADER
+
+#endif // QSGPOSITIONERS_P_H
diff --git a/src/declarative/items/qsgpositioners_p_p.h b/src/declarative/items/qsgpositioners_p_p.h
new file mode 100644
index 0000000000..49de12a1fd
--- /dev/null
+++ b/src/declarative/items/qsgpositioners_p_p.h
@@ -0,0 +1,174 @@
+// Commit: 2c7cab4172f1acc86fd49345a2847417e162f2c3
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGPOSITIONERS_P_P_H
+#define QSGPOSITIONERS_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgpositioners_p.h"
+#include "qsgimplicitsizeitem_p_p.h"
+
+#include <private/qdeclarativestate_p.h>
+#include <private/qdeclarativetransitionmanager_p_p.h>
+#include <private/qdeclarativestateoperations_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qtimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGBasePositionerPrivate : public QSGImplicitSizeItemPrivate, public QSGItemChangeListener
+{
+ Q_DECLARE_PUBLIC(QSGBasePositioner)
+
+public:
+ QSGBasePositionerPrivate()
+ : spacing(0), type(QSGBasePositioner::None)
+ , moveTransition(0), addTransition(0), queuedPositioning(false)
+ , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight)
+ {
+ }
+
+ void init(QSGBasePositioner::PositionerType at)
+ {
+ type = at;
+ childrenDoNotOverlap = true;
+ }
+
+ int spacing;
+
+ QSGBasePositioner::PositionerType type;
+ QDeclarativeTransition *moveTransition;
+ QDeclarativeTransition *addTransition;
+ QDeclarativeStateOperation::ActionList addActions;
+ QDeclarativeStateOperation::ActionList moveActions;
+ QDeclarativeTransitionManager addTransitionManager;
+ QDeclarativeTransitionManager moveTransitionManager;
+
+ void watchChanges(QSGItem *other);
+ void unwatchChanges(QSGItem* other);
+ bool queuedPositioning : 1;
+ bool doingPositioning : 1;
+ bool anchorConflict : 1;
+
+ Qt::LayoutDirection layoutDirection;
+
+ void schedulePositioning()
+ {
+ Q_Q(QSGBasePositioner);
+ if(!queuedPositioning){
+ QTimer::singleShot(0,q,SLOT(prePositioning()));
+ queuedPositioning = true;
+ }
+ }
+
+ void mirrorChange() {
+ Q_Q(QSGBasePositioner);
+ if (type != QSGBasePositioner::Vertical)
+ q->prePositioning();
+ }
+ bool isLeftToRight() const {
+ if (type == QSGBasePositioner::Vertical)
+ return true;
+ else
+ return effectiveLayoutMirror ? layoutDirection == Qt::RightToLeft : layoutDirection == Qt::LeftToRight;
+ }
+
+ virtual void itemSiblingOrderChanged(QSGItem* other)
+ {
+ Q_UNUSED(other);
+ //Delay is due to many children often being reordered at once
+ //And we only want to reposition them all once
+ schedulePositioning();
+ }
+
+ void itemGeometryChanged(QSGItem *, const QRectF &newGeometry, const QRectF &oldGeometry)
+ {
+ Q_Q(QSGBasePositioner);
+ if (newGeometry.size() != oldGeometry.size())
+ q->prePositioning();
+ }
+
+ virtual void itemVisibilityChanged(QSGItem *)
+ {
+ schedulePositioning();
+ }
+ virtual void itemOpacityChanged(QSGItem *)
+ {
+ Q_Q(QSGBasePositioner);
+ q->prePositioning();
+ }
+
+ void itemDestroyed(QSGItem *item)
+ {
+ Q_Q(QSGBasePositioner);
+ q->positionedItems.removeOne(QSGBasePositioner::PositionedItem(item));
+ }
+
+ static Qt::LayoutDirection getLayoutDirection(const QSGBasePositioner *positioner)
+ {
+ return positioner->d_func()->layoutDirection;
+ }
+
+ static Qt::LayoutDirection getEffectiveLayoutDirection(const QSGBasePositioner *positioner)
+ {
+ if (positioner->d_func()->effectiveLayoutMirror)
+ return positioner->d_func()->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
+ else
+ return positioner->d_func()->layoutDirection;
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGPOSITIONERS_P_P_H
diff --git a/src/declarative/items/qsgrectangle.cpp b/src/declarative/items/qsgrectangle.cpp
new file mode 100644
index 0000000000..e97abe3e1c
--- /dev/null
+++ b/src/declarative/items/qsgrectangle.cpp
@@ -0,0 +1,308 @@
+// Commit: acc903853d5ac54d646d324b7386c998bc07d464
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrectangle_p.h"
+#include "qsgrectangle_p_p.h"
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+
+#include <QtGui/qpixmapcache.h>
+#include <QtCore/qstringbuilder.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+// XXX todo - should we change rectangle to draw entirely within its width/height?
+
+QSGPen::QSGPen(QObject *parent)
+ : QObject(parent)
+ , m_width(1)
+ , m_color("#000000")
+ , m_aligned(true)
+ , m_valid(false)
+{
+}
+
+qreal QSGPen::width() const
+{
+ return m_width;
+}
+
+void QSGPen::setWidth(qreal w)
+{
+ if (m_width == w && m_valid)
+ return;
+
+ m_width = w;
+ m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0));
+ emit penChanged();
+}
+
+QColor QSGPen::color() const
+{
+ return m_color;
+}
+
+void QSGPen::setColor(const QColor &c)
+{
+ m_color = c;
+ m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0));
+ emit penChanged();
+}
+
+bool QSGPen::aligned() const
+{
+ return m_aligned;
+}
+
+void QSGPen::setAligned(bool aligned)
+{
+ if (aligned == m_aligned)
+ return;
+ m_aligned = aligned;
+ m_valid = m_color.alpha() && (qRound(m_width) >= 1 || (!m_aligned && m_width > 0));
+ emit penChanged();
+}
+
+bool QSGPen::isValid() const
+{
+ return m_valid;
+}
+
+QSGGradientStop::QSGGradientStop(QObject *parent)
+ : QObject(parent)
+{
+}
+
+qreal QSGGradientStop::position() const
+{
+ return m_position;
+}
+
+void QSGGradientStop::setPosition(qreal position)
+{
+ m_position = position; updateGradient();
+}
+
+QColor QSGGradientStop::color() const
+{
+ return m_color;
+}
+
+void QSGGradientStop::setColor(const QColor &color)
+{
+ m_color = color; updateGradient();
+}
+
+void QSGGradientStop::updateGradient()
+{
+ if (QSGGradient *grad = qobject_cast<QSGGradient*>(parent()))
+ grad->doUpdate();
+}
+
+QSGGradient::QSGGradient(QObject *parent)
+: QObject(parent), m_gradient(0)
+{
+}
+
+QSGGradient::~QSGGradient()
+{
+ delete m_gradient;
+}
+
+QDeclarativeListProperty<QSGGradientStop> QSGGradient::stops()
+{
+ return QDeclarativeListProperty<QSGGradientStop>(this, m_stops);
+}
+
+const QGradient *QSGGradient::gradient() const
+{
+ if (!m_gradient && !m_stops.isEmpty()) {
+ m_gradient = new QLinearGradient(0,0,0,1.0);
+ for (int i = 0; i < m_stops.count(); ++i) {
+ const QSGGradientStop *stop = m_stops.at(i);
+ m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode);
+ m_gradient->setColorAt(stop->position(), stop->color());
+ }
+ }
+
+ return m_gradient;
+}
+
+void QSGGradient::doUpdate()
+{
+ delete m_gradient;
+ m_gradient = 0;
+ emit updated();
+}
+
+int QSGRectanglePrivate::doUpdateSlotIdx = -1;
+
+QSGRectangle::QSGRectangle(QSGItem *parent)
+: QSGItem(*(new QSGRectanglePrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+void QSGRectangle::doUpdate()
+{
+ Q_D(QSGRectangle);
+ const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0;
+ d->setPaintMargin((pw+1)/2);
+ update();
+}
+
+QSGPen *QSGRectangle::border()
+{
+ Q_D(QSGRectangle);
+ return d->getPen();
+}
+
+QSGGradient *QSGRectangle::gradient() const
+{
+ Q_D(const QSGRectangle);
+ return d->gradient;
+}
+
+void QSGRectangle::setGradient(QSGGradient *gradient)
+{
+ Q_D(QSGRectangle);
+ if (d->gradient == gradient)
+ return;
+ static int updatedSignalIdx = -1;
+ if (updatedSignalIdx < 0)
+ updatedSignalIdx = QSGGradient::staticMetaObject.indexOfSignal("updated()");
+ if (d->doUpdateSlotIdx < 0)
+ d->doUpdateSlotIdx = QSGRectangle::staticMetaObject.indexOfSlot("doUpdate()");
+ if (d->gradient)
+ QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+ d->gradient = gradient;
+ if (d->gradient)
+ QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+ update();
+}
+
+qreal QSGRectangle::radius() const
+{
+ Q_D(const QSGRectangle);
+ return d->radius;
+}
+
+void QSGRectangle::setRadius(qreal radius)
+{
+ Q_D(QSGRectangle);
+ if (d->radius == radius)
+ return;
+
+ d->radius = radius;
+ update();
+ emit radiusChanged();
+}
+
+QColor QSGRectangle::color() const
+{
+ Q_D(const QSGRectangle);
+ return d->color;
+}
+
+void QSGRectangle::setColor(const QColor &c)
+{
+ Q_D(QSGRectangle);
+ if (d->color == c)
+ return;
+
+ d->color = c;
+ update();
+ emit colorChanged();
+}
+
+QSGNode *QSGRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
+{
+ Q_UNUSED(data);
+ Q_D(QSGRectangle);
+
+ if (width() <= 0 || height() <= 0) {
+ delete oldNode;
+ return 0;
+ }
+
+ QSGRectangleNode *rectangle = static_cast<QSGRectangleNode *>(oldNode);
+ if (!rectangle) rectangle = d->sceneGraphContext()->createRectangleNode();
+
+ rectangle->setRect(QRectF(0, 0, width(), height()));
+ rectangle->setColor(d->color);
+
+ if (d->pen && d->pen->isValid()) {
+ rectangle->setPenColor(d->pen->color());
+ rectangle->setPenWidth(d->pen->width());
+ rectangle->setAligned(d->pen->aligned());
+ } else {
+ rectangle->setPenWidth(0);
+ }
+
+ rectangle->setRadius(d->radius);
+
+ QGradientStops stops;
+ if (d->gradient) {
+ QList<QSGGradientStop *> qxstops = d->gradient->m_stops;
+ for (int i = 0; i < qxstops.size(); ++i){
+ int j = 0;
+ while (j < stops.size() && stops.at(j).first < qxstops[i]->position())
+ j++;
+ stops.insert(j, QGradientStop(qxstops.at(i)->position(), qxstops.at(i)->color()));
+ }
+ }
+ rectangle->setGradientStops(stops);
+
+ rectangle->update();
+
+ return rectangle;
+}
+
+QRectF QSGRectangle::boundingRect() const
+{
+ Q_D(const QSGRectangle);
+ return QRectF(-d->paintmargin, -d->paintmargin, width()+d->paintmargin*2, height()+d->paintmargin*2);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgrectangle_p.h b/src/declarative/items/qsgrectangle_p.h
new file mode 100644
index 0000000000..6157d82442
--- /dev/null
+++ b/src/declarative/items/qsgrectangle_p.h
@@ -0,0 +1,189 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRECTANGLE_P_H
+#define QSGRECTANGLE_P_H
+
+#include "qsgitem.h"
+
+#include <QtGui/qbrush.h>
+
+#include <private/qdeclarativeglobal_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGPen : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY penChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY penChanged)
+ Q_PROPERTY(bool aligned READ aligned WRITE setAligned NOTIFY penChanged)
+public:
+ QSGPen(QObject *parent=0);
+
+ qreal width() const;
+ void setWidth(qreal w);
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ bool aligned() const;
+ void setAligned(bool aligned);
+
+ bool isValid() const;
+
+Q_SIGNALS:
+ void penChanged();
+
+private:
+ qreal m_width;
+ QColor m_color;
+ bool m_aligned : 1;
+ bool m_valid : 1;
+};
+
+class Q_AUTOTEST_EXPORT QSGGradientStop : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal position READ position WRITE setPosition)
+ Q_PROPERTY(QColor color READ color WRITE setColor)
+
+public:
+ QSGGradientStop(QObject *parent=0);
+
+ qreal position() const;
+ void setPosition(qreal position);
+
+ QColor color() const;
+ void setColor(const QColor &color);
+
+private:
+ void updateGradient();
+
+private:
+ qreal m_position;
+ QColor m_color;
+};
+
+class Q_AUTOTEST_EXPORT QSGGradient : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QDeclarativeListProperty<QSGGradientStop> stops READ stops)
+ Q_CLASSINFO("DefaultProperty", "stops")
+
+public:
+ QSGGradient(QObject *parent=0);
+ ~QSGGradient();
+
+ QDeclarativeListProperty<QSGGradientStop> stops();
+
+ const QGradient *gradient() const;
+
+Q_SIGNALS:
+ void updated();
+
+private:
+ void doUpdate();
+
+private:
+ QList<QSGGradientStop *> m_stops;
+ mutable QGradient *m_gradient;
+ friend class QSGRectangle;
+ friend class QSGGradientStop;
+};
+
+class QSGRectanglePrivate;
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGRectangle : public QSGItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(QSGGradient *gradient READ gradient WRITE setGradient)
+ Q_PROPERTY(QSGPen * border READ border CONSTANT)
+ Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
+public:
+ QSGRectangle(QSGItem *parent=0);
+
+ QColor color() const;
+ void setColor(const QColor &);
+
+ QSGPen *border();
+
+ QSGGradient *gradient() const;
+ void setGradient(QSGGradient *gradient);
+
+ qreal radius() const;
+ void setRadius(qreal radius);
+
+ QRectF boundingRect() const;
+
+Q_SIGNALS:
+ void colorChanged();
+ void radiusChanged();
+
+protected:
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private Q_SLOTS:
+ void doUpdate();
+
+private:
+ Q_DISABLE_COPY(QSGRectangle)
+ Q_DECLARE_PRIVATE(QSGRectangle)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGPen)
+QML_DECLARE_TYPE(QSGGradientStop)
+QML_DECLARE_TYPE(QSGGradient)
+QML_DECLARE_TYPE(QSGRectangle)
+
+QT_END_HEADER
+
+#endif // QSGRECTANGLE_P_H
diff --git a/src/declarative/items/qsgrectangle_p_p.h b/src/declarative/items/qsgrectangle_p_p.h
new file mode 100644
index 0000000000..15bbd1f032
--- /dev/null
+++ b/src/declarative/items/qsgrectangle_p_p.h
@@ -0,0 +1,109 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGRECTANGLE_P_P_H
+#define QSGRECTANGLE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGGradient;
+class QSGRectangle;
+class QSGRectanglePrivate : public QSGItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGRectangle)
+
+public:
+ QSGRectanglePrivate() :
+ color(Qt::white), gradient(0), pen(0), radius(0), paintmargin(0)
+ {
+ }
+
+ ~QSGRectanglePrivate()
+ {
+ delete pen;
+ }
+
+ QColor color;
+ QSGGradient *gradient;
+ QSGPen *pen;
+ qreal radius;
+ qreal paintmargin;
+ static int doUpdateSlotIdx;
+
+ QSGPen *getPen() {
+ if (!pen) {
+ Q_Q(QSGRectangle);
+ pen = new QSGPen;
+ static int penChangedSignalIdx = -1;
+ if (penChangedSignalIdx < 0)
+ penChangedSignalIdx = QSGPen::staticMetaObject.indexOfSignal("penChanged()");
+ if (doUpdateSlotIdx < 0)
+ doUpdateSlotIdx = QSGRectangle::staticMetaObject.indexOfSlot("doUpdate()");
+ QMetaObject::connect(pen, penChangedSignalIdx, q, doUpdateSlotIdx);
+ }
+ return pen;
+ }
+
+ void setPaintMargin(qreal margin)
+ {
+ if (margin == paintmargin)
+ return;
+ paintmargin = margin;
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGRECTANGLE_P_P_H
diff --git a/src/declarative/items/qsgrepeater.cpp b/src/declarative/items/qsgrepeater.cpp
new file mode 100644
index 0000000000..ac6086fc62
--- /dev/null
+++ b/src/declarative/items/qsgrepeater.cpp
@@ -0,0 +1,294 @@
+// Commit: a9f1eaa6a368bf7a72b52c428728a3e3e0a76209
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgrepeater_p.h"
+#include "qsgrepeater_p_p.h"
+#include "qsgvisualitemmodel_p.h"
+
+#include <private/qdeclarativeglobal_p.h>
+#include <private/qdeclarativelistaccessor_p.h>
+#include <private/qlistmodelinterface_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGRepeaterPrivate::QSGRepeaterPrivate()
+: model(0), ownModel(false)
+{
+}
+
+QSGRepeaterPrivate::~QSGRepeaterPrivate()
+{
+ if (ownModel)
+ delete model;
+}
+
+QSGRepeater::QSGRepeater(QSGItem *parent)
+ : QSGItem(*(new QSGRepeaterPrivate), parent)
+{
+}
+
+QSGRepeater::~QSGRepeater()
+{
+}
+
+QVariant QSGRepeater::model() const
+{
+ Q_D(const QSGRepeater);
+ return d->dataSource;
+}
+
+void QSGRepeater::setModel(const QVariant &model)
+{
+ Q_D(QSGRepeater);
+ if (d->dataSource == model)
+ return;
+
+ clear();
+ if (d->model) {
+ disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ /*
+ disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
+ */
+ }
+ d->dataSource = model;
+ QObject *object = qvariant_cast<QObject*>(model);
+ QSGVisualModel *vim = 0;
+ if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
+ if (d->ownModel) {
+ delete d->model;
+ d->ownModel = false;
+ }
+ d->model = vim;
+ } else {
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this));
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ dataModel->setModel(model);
+ }
+ if (d->model) {
+ connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
+ connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
+ connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
+ connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+ /*
+ connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
+ connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
+ */
+ regenerate();
+ }
+ emit modelChanged();
+ emit countChanged();
+}
+
+QDeclarativeComponent *QSGRepeater::delegate() const
+{
+ Q_D(const QSGRepeater);
+ if (d->model) {
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ return dataModel->delegate();
+ }
+
+ return 0;
+}
+
+void QSGRepeater::setDelegate(QDeclarativeComponent *delegate)
+{
+ Q_D(QSGRepeater);
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
+ if (delegate == dataModel->delegate())
+ return;
+
+ if (!d->ownModel) {
+ d->model = new QSGVisualDataModel(qmlContext(this));
+ d->ownModel = true;
+ }
+ if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
+ dataModel->setDelegate(delegate);
+ regenerate();
+ emit delegateChanged();
+ }
+}
+
+int QSGRepeater::count() const
+{
+ Q_D(const QSGRepeater);
+ if (d->model)
+ return d->model->count();
+ return 0;
+}
+
+QSGItem *QSGRepeater::itemAt(int index) const
+{
+ Q_D(const QSGRepeater);
+ if (index >= 0 && index < d->deletables.count())
+ return d->deletables[index];
+ return 0;
+}
+
+void QSGRepeater::componentComplete()
+{
+ QSGItem::componentComplete();
+ regenerate();
+}
+
+void QSGRepeater::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ QSGItem::itemChange(change, value);
+ if (change == ItemParentHasChanged) {
+ regenerate();
+ }
+}
+
+void QSGRepeater::clear()
+{
+ Q_D(QSGRepeater);
+ bool complete = isComponentComplete();
+
+ if (d->model) {
+ while (d->deletables.count() > 0) {
+ QSGItem *item = d->deletables.takeLast();
+ if (complete)
+ emit itemRemoved(d->deletables.count()-1, item);
+ d->model->release(item);
+ }
+ }
+ d->deletables.clear();
+}
+
+void QSGRepeater::regenerate()
+{
+ Q_D(QSGRepeater);
+ if (!isComponentComplete())
+ return;
+
+ clear();
+
+ if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete())
+ return;
+
+ for (int ii = 0; ii < count(); ++ii) {
+ QSGItem *item = d->model->item(ii);
+ if (item) {
+ QDeclarative_setParent_noEvent(item, parentItem());
+ item->setParentItem(parentItem());
+ item->stackBefore(this);
+ d->deletables << item;
+ emit itemAdded(ii, item);
+ }
+ }
+}
+
+void QSGRepeater::itemsInserted(int index, int count)
+{
+ Q_D(QSGRepeater);
+ if (!isComponentComplete())
+ return;
+ for (int i = 0; i < count; ++i) {
+ int modelIndex = index + i;
+ QSGItem *item = d->model->item(modelIndex);
+ if (item) {
+ QDeclarative_setParent_noEvent(item, parentItem());
+ item->setParentItem(parentItem());
+ if (modelIndex < d->deletables.count())
+ item->stackBefore(d->deletables.at(modelIndex));
+ else
+ item->stackBefore(this);
+ d->deletables.insert(modelIndex, item);
+ emit itemAdded(modelIndex, item);
+ }
+ }
+ emit countChanged();
+}
+
+void QSGRepeater::itemsRemoved(int index, int count)
+{
+ Q_D(QSGRepeater);
+ if (!isComponentComplete() || count <= 0)
+ return;
+ while (count--) {
+ QSGItem *item = d->deletables.takeAt(index);
+ emit itemRemoved(index, item);
+ if (item)
+ d->model->release(item);
+ else
+ break;
+ }
+ emit countChanged();
+}
+
+void QSGRepeater::itemsMoved(int from, int to, int count)
+{
+ Q_D(QSGRepeater);
+ if (!isComponentComplete() || count <= 0)
+ return;
+ if (from + count > d->deletables.count()) {
+ regenerate();
+ return;
+ }
+ QList<QSGItem*> removed;
+ int removedCount = count;
+ while (removedCount--)
+ removed << d->deletables.takeAt(from);
+ for (int i = 0; i < count; ++i)
+ d->deletables.insert(to + i, removed.at(i));
+ d->deletables.last()->stackBefore(this);
+ for (int i = d->model->count()-1; i > 0; --i) {
+ QSGItem *item = d->deletables.at(i-1);
+ item->stackBefore(d->deletables.at(i));
+ }
+}
+
+void QSGRepeater::modelReset()
+{
+ if (!isComponentComplete())
+ return;
+ regenerate();
+ emit countChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgrepeater_p.h b/src/declarative/items/qsgrepeater_p.h
new file mode 100644
index 0000000000..3a53fa4177
--- /dev/null
+++ b/src/declarative/items/qsgrepeater_p.h
@@ -0,0 +1,111 @@
+// Commit: ebd4bc73c46c2962742a682b6a391fb68c482aec
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGREPEATER_P_H
+#define QSGREPEATER_P_H
+
+#include "qsgitem.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGRepeaterPrivate;
+class Q_AUTOTEST_EXPORT QSGRepeater : public QSGItem
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+
+public:
+ QSGRepeater(QSGItem *parent=0);
+ virtual ~QSGRepeater();
+
+ QVariant model() const;
+ void setModel(const QVariant &);
+
+ QDeclarativeComponent *delegate() const;
+ void setDelegate(QDeclarativeComponent *);
+
+ int count() const;
+
+ Q_INVOKABLE QSGItem *itemAt(int index) const;
+
+Q_SIGNALS:
+ void modelChanged();
+ void delegateChanged();
+ void countChanged();
+
+ void itemAdded(int index, QSGItem *item);
+ void itemRemoved(int index, QSGItem *item);
+
+private:
+ void clear();
+ void regenerate();
+
+protected:
+ virtual void componentComplete();
+ void itemChange(ItemChange change, const ItemChangeData &value);
+
+private Q_SLOTS:
+ void itemsInserted(int,int);
+ void itemsRemoved(int,int);
+ void itemsMoved(int,int,int);
+ void modelReset();
+
+private:
+ Q_DISABLE_COPY(QSGRepeater)
+ Q_DECLARE_PRIVATE(QSGRepeater)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGRepeater)
+
+QT_END_HEADER
+
+#endif // QSGREPEATER_P_H
diff --git a/src/declarative/items/qsgrepeater_p_p.h b/src/declarative/items/qsgrepeater_p_p.h
new file mode 100644
index 0000000000..155f1b8c6d
--- /dev/null
+++ b/src/declarative/items/qsgrepeater_p_p.h
@@ -0,0 +1,83 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGREPEATER_P_P_H
+#define QSGREPEATER_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgrepeater_p.h"
+#include "qsgitem_p.h"
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDeclarativeContext;
+class QSGVisualModel;
+class QSGRepeaterPrivate : public QSGItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGRepeater)
+
+public:
+ QSGRepeaterPrivate();
+ ~QSGRepeaterPrivate();
+
+ QSGVisualModel *model;
+ QVariant dataSource;
+ bool ownModel;
+
+ QList<QPointer<QSGItem> > deletables;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGREPEATER_P_P_H
diff --git a/src/declarative/items/qsgscalegrid.cpp b/src/declarative/items/qsgscalegrid.cpp
new file mode 100644
index 0000000000..4a73801159
--- /dev/null
+++ b/src/declarative/items/qsgscalegrid.cpp
@@ -0,0 +1,213 @@
+// Commit: 7ddec9f3179bfd854ae53e23ab292de1f9a26377
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgscalegrid_p_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \class QSGScaleGrid
+ \brief The QSGScaleGrid class allows you to specify a 3x3 grid to use in scaling an image.
+*/
+
+QSGScaleGrid::QSGScaleGrid(QObject *parent) : QObject(parent), _left(0), _top(0), _right(0), _bottom(0)
+{
+}
+
+QSGScaleGrid::~QSGScaleGrid()
+{
+}
+
+bool QSGScaleGrid::isNull() const
+{
+ return !_left && !_top && !_right && !_bottom;
+}
+
+void QSGScaleGrid::setLeft(int pos)
+{
+ if (_left != pos) {
+ _left = pos;
+ emit borderChanged();
+ }
+}
+
+void QSGScaleGrid::setTop(int pos)
+{
+ if (_top != pos) {
+ _top = pos;
+ emit borderChanged();
+ }
+}
+
+void QSGScaleGrid::setRight(int pos)
+{
+ if (_right != pos) {
+ _right = pos;
+ emit borderChanged();
+ }
+}
+
+void QSGScaleGrid::setBottom(int pos)
+{
+ if (_bottom != pos) {
+ _bottom = pos;
+ emit borderChanged();
+ }
+}
+
+QSGGridScaledImage::QSGGridScaledImage()
+: _l(-1), _r(-1), _t(-1), _b(-1),
+ _h(QSGBorderImage::Stretch), _v(QSGBorderImage::Stretch)
+{
+}
+
+QSGGridScaledImage::QSGGridScaledImage(const QSGGridScaledImage &o)
+: _l(o._l), _r(o._r), _t(o._t), _b(o._b), _h(o._h), _v(o._v), _pix(o._pix)
+{
+}
+
+QSGGridScaledImage &QSGGridScaledImage::operator=(const QSGGridScaledImage &o)
+{
+ _l = o._l;
+ _r = o._r;
+ _t = o._t;
+ _b = o._b;
+ _h = o._h;
+ _v = o._v;
+ _pix = o._pix;
+ return *this;
+}
+
+QSGGridScaledImage::QSGGridScaledImage(QIODevice *data)
+: _l(-1), _r(-1), _t(-1), _b(-1), _h(QSGBorderImage::Stretch), _v(QSGBorderImage::Stretch)
+{
+ int l = -1;
+ int r = -1;
+ int t = -1;
+ int b = -1;
+ QString imgFile;
+
+ QByteArray raw;
+ while(raw = data->readLine(), !raw.isEmpty()) {
+ QString line = QString::fromUtf8(raw.trimmed());
+ if (line.isEmpty() || line.startsWith(QLatin1Char('#')))
+ continue;
+
+ int colonId = line.indexOf(QLatin1Char(':'));
+ if (colonId <= 0)
+ return;
+
+ QStringList list;
+ list.append(line.left(colonId).trimmed());
+ list.append(line.mid(colonId+1).trimmed());
+
+ if (list[0] == QLatin1String("border.left"))
+ l = list[1].toInt();
+ else if (list[0] == QLatin1String("border.right"))
+ r = list[1].toInt();
+ else if (list[0] == QLatin1String("border.top"))
+ t = list[1].toInt();
+ else if (list[0] == QLatin1String("border.bottom"))
+ b = list[1].toInt();
+ else if (list[0] == QLatin1String("source"))
+ imgFile = list[1];
+ else if (list[0] == QLatin1String("horizontalTileRule"))
+ _h = stringToRule(list[1]);
+ else if (list[0] == QLatin1String("verticalTileRule"))
+ _v = stringToRule(list[1]);
+ }
+
+ if (l < 0 || r < 0 || t < 0 || b < 0 || imgFile.isEmpty())
+ return;
+
+ _l = l; _r = r; _t = t; _b = b;
+
+ _pix = imgFile;
+}
+
+QSGBorderImage::TileMode QSGGridScaledImage::stringToRule(const QString &s)
+{
+ if (s == QLatin1String("Stretch"))
+ return QSGBorderImage::Stretch;
+ if (s == QLatin1String("Repeat"))
+ return QSGBorderImage::Repeat;
+ if (s == QLatin1String("Round"))
+ return QSGBorderImage::Round;
+
+ qWarning("QSGGridScaledImage: Invalid tile rule specified. Using Stretch.");
+ return QSGBorderImage::Stretch;
+}
+
+bool QSGGridScaledImage::isValid() const
+{
+ return _l >= 0;
+}
+
+int QSGGridScaledImage::gridLeft() const
+{
+ return _l;
+}
+
+int QSGGridScaledImage::gridRight() const
+{
+ return _r;
+}
+
+int QSGGridScaledImage::gridTop() const
+{
+ return _t;
+}
+
+int QSGGridScaledImage::gridBottom() const
+{
+ return _b;
+}
+
+QString QSGGridScaledImage::pixmapUrl() const
+{
+ return _pix;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgscalegrid_p_p.h b/src/declarative/items/qsgscalegrid_p_p.h
new file mode 100644
index 0000000000..57beb4b3b0
--- /dev/null
+++ b/src/declarative/items/qsgscalegrid_p_p.h
@@ -0,0 +1,134 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSCALEGRID_P_P_H
+#define QSGSCALEGRID_P_P_H
+
+#include "qsgborderimage_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtCore/qobject.h>
+
+#include <private/qdeclarativepixmapcache_p.h>
+#include <private/qdeclarativeglobal_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGScaleGrid : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(TileRule)
+
+ Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged)
+ Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged)
+ Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged)
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged)
+
+public:
+ QSGScaleGrid(QObject *parent=0);
+ ~QSGScaleGrid();
+
+ bool isNull() const;
+
+ int left() const { return _left; }
+ void setLeft(int);
+
+ int top() const { return _top; }
+ void setTop(int);
+
+ int right() const { return _right; }
+ void setRight(int);
+
+ int bottom() const { return _bottom; }
+ void setBottom(int);
+
+Q_SIGNALS:
+ void borderChanged();
+
+private:
+ int _left;
+ int _top;
+ int _right;
+ int _bottom;
+};
+
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGGridScaledImage
+{
+public:
+ QSGGridScaledImage();
+ QSGGridScaledImage(const QSGGridScaledImage &);
+ QSGGridScaledImage(QIODevice*);
+ QSGGridScaledImage &operator=(const QSGGridScaledImage &);
+ bool isValid() const;
+ int gridLeft() const;
+ int gridRight() const;
+ int gridTop() const;
+ int gridBottom() const;
+ QSGBorderImage::TileMode horizontalTileRule() const { return _h; }
+ QSGBorderImage::TileMode verticalTileRule() const { return _v; }
+
+ QString pixmapUrl() const;
+
+private:
+ static QSGBorderImage::TileMode stringToRule(const QString &);
+
+private:
+ int _l;
+ int _r;
+ int _t;
+ int _b;
+ QSGBorderImage::TileMode _h;
+ QSGBorderImage::TileMode _v;
+ QString _pix;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGScaleGrid)
+
+QT_END_HEADER
+
+#endif // QSGSCALEGRID_P_P_H
diff --git a/src/declarative/items/qsgshadereffectitem.cpp b/src/declarative/items/qsgshadereffectitem.cpp
new file mode 100644
index 0000000000..44e075c6f7
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectitem.cpp
@@ -0,0 +1,590 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qsgshadereffectitem_p.h>
+#include <private/qsgshadereffectnode_p.h>
+
+#include "qsgmaterial.h"
+#include "qsgitem_p.h"
+
+#include <private/qsgcontext_p.h>
+#include <private/qsgtextureprovider_p.h>
+#include "qsgcanvas.h"
+
+#include <QtCore/qsignalmapper.h>
+#include <QtOpenGL/qglframebufferobject.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char qt_default_vertex_code[] =
+ "uniform highp mat4 qt_ModelViewProjectionMatrix; \n"
+ "attribute highp vec4 qt_Vertex; \n"
+ "attribute highp vec2 qt_MultiTexCoord0; \n"
+ "varying highp vec2 qt_TexCoord0; \n"
+ "void main() { \n"
+ " qt_TexCoord0 = qt_MultiTexCoord0; \n"
+ " gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex; \n"
+ "}";
+
+static const char qt_default_fragment_code[] =
+ "varying highp vec2 qt_TexCoord0; \n"
+ "uniform sampler2D source; \n"
+ "uniform lowp float qt_Opacity; \n"
+ "void main() { \n"
+ " gl_FragColor = texture2D(source, qt_TexCoord0) * qt_Opacity; \n"
+ "}";
+
+static const char qt_position_attribute_name[] = "qt_Vertex";
+static const char qt_texcoord_attribute_name[] = "qt_MultiTexCoord0";
+
+const char *qtPositionAttributeName()
+{
+ return qt_position_attribute_name;
+}
+
+const char *qtTexCoordAttributeName()
+{
+ return qt_texcoord_attribute_name;
+}
+
+/*!
+ \qmlclass ShaderEffectItem QSGShaderEffectItem
+ \since 5.0
+ \ingroup qml-basic-visual-elements
+ \brief The ShaderEffectItem element applies custom shaders to a rectangle.
+ \inherits Item
+
+ The ShaderEffectItem element applies a custom OpenGL
+ \l{vertexShader}{vertex} and \l{fragmentShader}{fragment} shader to a
+ rectangle. It allows you to write effects such as drop shadow, blur,
+ colorize and page curl directly in QML.
+
+ There are two types of input to the \l vertexShader:
+ uniform variables and attributes. Some are predefined:
+ \list
+ \o uniform mat4 qt_ModelViewProjectionMatrix - combined transformation
+ matrix, the product of the matrices from the root item to this
+ ShaderEffectItem, and an orthogonal projection.
+ \o uniform float qt_Opacity - combined opacity, the product of the
+ opacities from the root item to this ShaderEffectItem.
+ \o attribute vec4 qt_Vertex - vertex position, the top-left vertex has
+ position (0, 0), the bottom-right (\l{Item::width}{width},
+ \l{Item::height}{height}).
+ \o attribute vec2 qt_MultiTexCoord0 - texture coordinate, the top-left
+ coordinate is (0, 0), the bottom-right (1, 1).
+ \endlist
+
+ In addition, any property that can be mapped to an OpenGL Shading Language
+ (GLSL) type is available as a uniform variable. The following list shows
+ how properties are mapped to GLSL uniform variables:
+ \list
+ \o bool, int, qreal -> bool, int, float - If the type in the shader is not
+ the same as in QML, the value is converted automatically.
+ \o QColor -> vec4 - When colors are passed to the shader, they are first
+ premultiplied. Thus Qt.rgba(0.2, 0.6, 1.0, 0.5) becomes
+ vec4(0.1, 0.3, 0.5, 0.5) in the shader, for example.
+ \o QRect, QRectF -> vec4 - Qt.rect(x, y, w, h) becomes vec4(x, y, w, h) in
+ the shader.
+ \o QPoint, QPointF, QSize, QSizeF -> vec2
+ \o QVector3D -> vec3
+ \o QTransform -> mat4
+ \o \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left
+ corner, and the color values are premultiplied.
+ \endlist
+
+ The output from the \l fragmentShader should be premultiplied. If
+ \l blending is enabled, source-over blending is used. However, additive
+ blending can be achieved by outputting zero in the alpha channel.
+
+ \row
+ \o \image declarative-shadereffectitem.png
+ \o \qml
+ import QtQuick 2.0
+
+ Rectangle {
+ width: 200; height: 100
+ Row {
+ Image { id: img; sourceSize { width: 100; height: 100 } source: "qt-logo.png" }
+ ShaderEffectItem {
+ width: 100; height: 100
+ property variant src: img
+ vertexShader: "
+ uniform highp mat4 qt_ModelViewProjectionMatrix;
+ attribute highp vec4 qt_Vertex;
+ attribute highp vec2 qt_MultiTexCoord0;
+ varying highp vec2 coord;
+ void main() {
+ coord = qt_MultiTexCoord0;
+ gl_Position = qt_ModelViewProjectionMatrix * qt_Vertex;
+ }"
+ fragmentShader: "
+ varying highp vec2 coord;
+ uniform sampler2D src;
+ uniform lowp float qt_Opacity;
+ void main() {
+ lowp vec4 tex = texture2D(src, coord);
+ gl_FragColor = vec4(vec3(dot(tex.rgb, vec3(0.344, 0.5, 0.156))), tex.a) * qt_Opacity;
+ }"
+ }
+ }
+ }
+ \endqml
+ \endrow
+
+ By default, the ShaderEffectItem consists of four vertices, one for each
+ corner. For non-linear vertex transformations, like page curl, you can
+ specify a fine grid of vertices by assigning a \l GridMesh to the \l mesh
+ property.
+
+ \note Scene Graph textures have origin in the top-left corner rather than
+ bottom-left which is common in OpenGL.
+*/
+
+QSGShaderEffectItem::QSGShaderEffectItem(QSGItem *parent)
+ : QSGItem(parent)
+ , m_mesh(0)
+ , m_cullMode(NoCulling)
+ , m_blending(true)
+ , m_dirtyData(true)
+ , m_programDirty(true)
+ , m_dirtyMesh(true)
+ , m_dirtyGeometry(true)
+{
+ setFlag(QSGItem::ItemHasContents);
+}
+
+QSGShaderEffectItem::~QSGShaderEffectItem()
+{
+ reset();
+}
+
+void QSGShaderEffectItem::componentComplete()
+{
+ updateProperties();
+ QSGItem::componentComplete();
+}
+
+/*!
+ \qmlproperty string ShaderEffectItem::fragmentShader
+
+ This property holds the fragment shader's GLSL source code.
+ The default shader passes the texture coordinate along to the fragment
+ shader as "varying highp vec2 qt_TexCoord0".
+*/
+
+void QSGShaderEffectItem::setFragmentShader(const QByteArray &code)
+{
+ if (m_source.fragmentCode.constData() == code.constData())
+ return;
+ m_source.fragmentCode = code;
+ if (isComponentComplete()) {
+ reset();
+ updateProperties();
+ }
+ emit fragmentShaderChanged();
+}
+
+/*!
+ \qmlproperty string ShaderEffectItem::vertexShader
+
+ This property holds the vertex shader's GLSL source code.
+ The default shader expects the texture coordinate to be passed from the
+ vertex shader as "varying highp vec2 qt_TexCoord0", and it samples from a
+ sampler2D named "source".
+*/
+
+void QSGShaderEffectItem::setVertexShader(const QByteArray &code)
+{
+ if (m_source.vertexCode.constData() == code.constData())
+ return;
+ m_source.vertexCode = code;
+ if (isComponentComplete()) {
+ reset();
+ updateProperties();
+ }
+ emit vertexShaderChanged();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectItem::blending
+
+ If this property is true, the output from the \l fragmentShader is blended
+ with the background using source-over blend mode. If false, the background
+ is disregarded. Blending decreases the performance, so you should set this
+ property to false when blending is not needed. The default value is true.
+*/
+
+void QSGShaderEffectItem::setBlending(bool enable)
+{
+ if (blending() == enable)
+ return;
+
+ m_blending = enable;
+ update();
+
+ emit blendingChanged();
+}
+
+/*!
+ \qmlproperty object ShaderEffectItem::mesh
+
+ This property holds the mesh definition. If not set, a simple mesh with one
+ vertex in each corner is used. Assign a \l GridMesh to this property to get
+ a higher resolution grid.
+*/
+
+void QSGShaderEffectItem::setMesh(QSGShaderEffectMesh *mesh)
+{
+ if (mesh == m_mesh)
+ return;
+ if (m_mesh)
+ disconnect(m_mesh, SIGNAL(geometryChanged()), this, 0);
+ m_mesh = mesh;
+ if (m_mesh)
+ connect(m_mesh, SIGNAL(geometryChanged()), this, SLOT(updateGeometry()));
+ m_dirtyMesh = true;
+ update();
+ emit meshChanged();
+}
+
+/*!
+ \qmlproperty enumeration ShaderEffectItem::cullMode
+
+ This property defines which sides of the element should be visible.
+
+ \list
+ \o ShaderEffectItem.NoCulling - Both sides are visible
+ \o ShaderEffectItem.BackFaceCulling - only front side is visible
+ \o ShaderEffectItem.FrontFaceCulling - only back side is visible
+ \endlist
+
+ The default is NoCulling.
+*/
+
+void QSGShaderEffectItem::setCullMode(CullMode face)
+{
+ if (face == m_cullMode)
+ return;
+ m_cullMode = face;
+ update();
+ emit cullModeChanged();
+}
+
+void QSGShaderEffectItem::changeSource(int index)
+{
+ Q_ASSERT(index >= 0 && index < m_sources.size());
+ QVariant v = property(m_sources.at(index).name.constData());
+ setSource(v, index);
+}
+
+void QSGShaderEffectItem::updateData()
+{
+ m_dirtyData = true;
+ update();
+}
+
+void QSGShaderEffectItem::updateGeometry()
+{
+ m_dirtyGeometry = true;
+ update();
+}
+
+void QSGShaderEffectItem::setSource(const QVariant &var, int index)
+{
+ Q_ASSERT(index >= 0 && index < m_sources.size());
+
+ SourceData &source = m_sources[index];
+
+ source.item = 0;
+ if (var.isNull()) {
+ return;
+ } else if (!qVariantCanConvert<QObject *>(var)) {
+ qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
+ return;
+ }
+
+ QObject *obj = qVariantValue<QObject *>(var);
+
+ QSGTextureProvider *int3rface = QSGTextureProvider::from(obj);
+ if (!int3rface) {
+ qWarning("Could not assign property '%s', did not implement QSGTextureProvider.", source.name.constData());
+ }
+
+ source.item = qobject_cast<QSGItem *>(obj);
+
+ // TODO: Find better solution.
+ // 'source.item' needs a canvas to get a scenegraph node.
+ // The easiest way to make sure it gets a canvas is to
+ // make it a part of the same item tree as 'this'.
+ if (source.item && source.item->parentItem() == 0) {
+ source.item->setParentItem(this);
+ source.item->setVisible(false);
+ }
+}
+
+void QSGShaderEffectItem::disconnectPropertySignals()
+{
+ disconnect(this, 0, this, SLOT(updateData()));
+ for (int i = 0; i < m_sources.size(); ++i) {
+ SourceData &source = m_sources[i];
+ disconnect(this, 0, source.mapper, 0);
+ disconnect(source.mapper, 0, this, 0);
+ }
+}
+
+void QSGShaderEffectItem::connectPropertySignals()
+{
+ QSet<QByteArray>::const_iterator it;
+ for (it = m_source.uniformNames.begin(); it != m_source.uniformNames.end(); ++it) {
+ int pi = metaObject()->indexOfProperty(it->constData());
+ if (pi >= 0) {
+ QMetaProperty mp = metaObject()->property(pi);
+ if (!mp.hasNotifySignal())
+ qWarning("QSGShaderEffectItem: property '%s' does not have notification method!", it->constData());
+ QByteArray signalName("2");
+ signalName.append(mp.notifySignal().signature());
+ connect(this, signalName, this, SLOT(updateData()));
+ } else {
+ qWarning("QSGShaderEffectItem: '%s' does not have a matching property!", it->constData());
+ }
+ }
+ for (int i = 0; i < m_sources.size(); ++i) {
+ SourceData &source = m_sources[i];
+ int pi = metaObject()->indexOfProperty(source.name.constData());
+ if (pi >= 0) {
+ QMetaProperty mp = metaObject()->property(pi);
+ QByteArray signalName("2");
+ signalName.append(mp.notifySignal().signature());
+ connect(this, signalName, source.mapper, SLOT(map()));
+ source.mapper->setMapping(this, i);
+ connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
+ } else {
+ qWarning("QSGShaderEffectItem: '%s' does not have a matching source!", source.name.constData());
+ }
+ }
+}
+
+void QSGShaderEffectItem::reset()
+{
+ disconnectPropertySignals();
+
+ m_source.attributeNames.clear();
+ m_source.uniformNames.clear();
+ m_source.respectsOpacity = false;
+ m_source.respectsMatrix = false;
+ m_source.className = metaObject()->className();
+
+ for (int i = 0; i < m_sources.size(); ++i) {
+ const SourceData &source = m_sources.at(i);
+ delete source.mapper;
+ if (source.item && source.item->parentItem() == this)
+ source.item->setParentItem(0);
+ }
+ m_sources.clear();
+
+ m_programDirty = true;
+ m_dirtyMesh = true;
+}
+
+void QSGShaderEffectItem::updateProperties()
+{
+ QByteArray vertexCode = m_source.vertexCode;
+ QByteArray fragmentCode = m_source.fragmentCode;
+ if (vertexCode.isEmpty())
+ vertexCode = qt_default_vertex_code;
+ if (fragmentCode.isEmpty())
+ fragmentCode = qt_default_fragment_code;
+
+ lookThroughShaderCode(vertexCode);
+ lookThroughShaderCode(fragmentCode);
+
+ if (!m_mesh && !m_source.attributeNames.contains(qt_position_attribute_name))
+ qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_position_attribute_name);
+ if (!m_mesh && !m_source.attributeNames.contains(qt_texcoord_attribute_name))
+ qWarning("QSGShaderEffectItem: Missing reference to \'%s\'.", qt_texcoord_attribute_name);
+ if (!m_source.respectsMatrix)
+ qWarning("QSGShaderEffectItem: Missing reference to \'qt_ModelViewProjectionMatrix\'.");
+ if (!m_source.respectsOpacity)
+ qWarning("QSGShaderEffectItem: Missing reference to \'qt_Opacity\'.");
+
+ for (int i = 0; i < m_sources.size(); ++i) {
+ QVariant v = property(m_sources.at(i).name);
+ setSource(v, i);
+ }
+
+ connectPropertySignals();
+}
+
+void QSGShaderEffectItem::lookThroughShaderCode(const QByteArray &code)
+{
+ // Regexp for matching attributes and uniforms.
+ // In human readable form: attribute|uniform [lowp|mediump|highp] <type> <name>
+ static QRegExp re(QLatin1String("\\b(attribute|uniform)\\b\\s*\\b(?:lowp|mediump|highp)?\\b\\s*\\b(\\w+)\\b\\s*\\b(\\w+)"));
+ Q_ASSERT(re.isValid());
+
+ int pos = -1;
+
+ QString wideCode = QString::fromLatin1(code.constData(), code.size());
+
+ while ((pos = re.indexIn(wideCode, pos + 1)) != -1) {
+ QByteArray decl = re.cap(1).toLatin1(); // uniform or attribute
+ QByteArray type = re.cap(2).toLatin1(); // type
+ QByteArray name = re.cap(3).toLatin1(); // variable name
+
+ if (decl == "attribute") {
+ m_source.attributeNames.append(name);
+ } else {
+ Q_ASSERT(decl == "uniform");
+
+ if (name == "qt_ModelViewProjectionMatrix") {
+ m_source.respectsMatrix = true;
+ } else if (name == "qt_Opacity") {
+ m_source.respectsOpacity = true;
+ } else {
+ m_source.uniformNames.insert(name);
+ if (type == "sampler2D") {
+ SourceData d;
+ d.mapper = new QSignalMapper;
+ d.name = name;
+ d.item = 0;
+ m_sources.append(d);
+ }
+ }
+ }
+ }
+}
+
+void QSGShaderEffectItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ m_dirtyGeometry = true;
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+QSGNode *QSGShaderEffectItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ QSGShaderEffectNode *node = static_cast<QSGShaderEffectNode *>(oldNode);
+
+ if (!node) {
+ node = new QSGShaderEffectNode;
+ node->setMaterial(&m_material);
+ m_programDirty = true;
+ m_dirtyData = true;
+ m_dirtyGeometry = true;
+ }
+
+ if (m_dirtyMesh) {
+ node->setGeometry(0);
+ m_dirtyMesh = false;
+ m_dirtyGeometry = true;
+ }
+
+ if (m_dirtyGeometry) {
+ node->setFlag(QSGNode::OwnsGeometry, false);
+ QSGGeometry *geometry = node->geometry();
+ QRectF rect(0, 0, width(), height());
+ QSGShaderEffectMesh *mesh = m_mesh ? m_mesh : &m_defaultMesh;
+
+ geometry = mesh->updateGeometry(geometry, m_source.attributeNames, rect);
+ if (!geometry) {
+ delete node;
+ return 0;
+ }
+
+ node->setGeometry(geometry);
+ node->setFlag(QSGNode::OwnsGeometry, true);
+
+ m_dirtyGeometry = false;
+ }
+
+ if (m_programDirty) {
+ QSGShaderEffectProgram s = m_source;
+ if (s.fragmentCode.isEmpty())
+ s.fragmentCode = qt_default_fragment_code;
+ if (s.vertexCode.isEmpty())
+ s.vertexCode = qt_default_vertex_code;
+
+ m_material.setProgramSource(s);
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_programDirty = false;
+ }
+
+ // Update blending
+ if (bool(m_material.flags() & QSGMaterial::Blending) != m_blending) {
+ m_material.setFlag(QSGMaterial::Blending, m_blending);
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (int(m_material.cullMode()) != int(m_cullMode)) {
+ m_material.setCullMode(QSGShaderEffectMaterial::CullMode(m_cullMode));
+ node->markDirty(QSGNode::DirtyMaterial);
+ }
+
+ if (m_dirtyData) {
+ QVector<QPair<QByteArray, QVariant> > values;
+ QVector<QPair<QByteArray, QPointer<QSGItem> > > textures;
+ const QVector<QPair<QByteArray, QPointer<QSGItem> > > &oldTextures = m_material.textureProviders();
+
+ for (QSet<QByteArray>::const_iterator it = m_source.uniformNames.begin();
+ it != m_source.uniformNames.end(); ++it) {
+ values.append(qMakePair(*it, property(*it)));
+ }
+ for (int i = 0; i < oldTextures.size(); ++i) {
+ QSGTextureProvider *oldSource = QSGTextureProvider::from(oldTextures.at(i).second);
+ if (oldSource && oldSource->textureChangedSignal())
+ disconnect(oldTextures.at(i).second, oldSource->textureChangedSignal(), node, SLOT(markDirtyTexture()));
+ }
+ for (int i = 0; i < m_sources.size(); ++i) {
+ const SourceData &source = m_sources.at(i);
+ textures.append(qMakePair(source.name, source.item));
+ QSGTextureProvider *t = QSGTextureProvider::from(source.item);
+ if (t && t->textureChangedSignal())
+ connect(source.item, t->textureChangedSignal(), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
+ }
+ m_material.setUniforms(values);
+ m_material.setTextureProviders(textures);
+ node->markDirty(QSGNode::DirtyMaterial);
+ m_dirtyData = false;
+ }
+
+ return node;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgshadereffectitem_p.h b/src/declarative/items/qsgshadereffectitem_p.h
new file mode 100644
index 0000000000..4017d35c64
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectitem_p.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SHADEREFFECTITEM_H
+#define SHADEREFFECTITEM_H
+
+#include "qsgitem.h"
+
+#include "qsgmaterial.h"
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgshadereffectnode_p.h>
+#include "qsgshadereffectmesh_p.h"
+
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+const char *qtPositionAttributeName();
+const char *qtTexCoordAttributeName();
+
+class QSGContext;
+class QSignalMapper;
+class QSGCustomMaterialShader;
+
+class QSGShaderEffectItem : public QSGItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QByteArray fragmentShader READ fragmentShader WRITE setFragmentShader NOTIFY fragmentShaderChanged)
+ Q_PROPERTY(QByteArray vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged)
+ Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged)
+ Q_PROPERTY(QSGShaderEffectMesh *mesh READ mesh WRITE setMesh NOTIFY meshChanged)
+ Q_PROPERTY(CullMode culling READ cullMode WRITE setCullMode NOTIFY cullModeChanged)
+ Q_ENUMS(CullMode)
+
+public:
+ enum CullMode
+ {
+ NoCulling = QSGShaderEffectMaterial::NoCulling,
+ BackFaceCulling = QSGShaderEffectMaterial::BackFaceCulling,
+ FrontFaceCulling = QSGShaderEffectMaterial::FrontFaceCulling
+ };
+
+ QSGShaderEffectItem(QSGItem *parent = 0);
+ ~QSGShaderEffectItem();
+
+ virtual void componentComplete();
+
+ QByteArray fragmentShader() const { return m_source.fragmentCode; }
+ void setFragmentShader(const QByteArray &code);
+
+ QByteArray vertexShader() const { return m_source.vertexCode; }
+ void setVertexShader(const QByteArray &code);
+
+ bool blending() const { return m_blending; }
+ void setBlending(bool enable);
+
+ QSGShaderEffectMesh *mesh() const { return m_mesh; }
+ void setMesh(QSGShaderEffectMesh *mesh);
+
+ CullMode cullMode() const { return m_cullMode; }
+ void setCullMode(CullMode face);
+
+Q_SIGNALS:
+ void fragmentShaderChanged();
+ void vertexShaderChanged();
+ void blendingChanged();
+ void meshChanged();
+ void cullModeChanged();
+
+protected:
+ virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private Q_SLOTS:
+ void changeSource(int index);
+ void updateData();
+ void updateGeometry();
+
+private:
+ friend class QSGCustomMaterialShader;
+ friend class QSGShaderEffectNode;
+
+ void setSource(const QVariant &var, int index);
+ void disconnectPropertySignals();
+ void connectPropertySignals();
+ void reset();
+ void updateProperties();
+ void lookThroughShaderCode(const QByteArray &code);
+
+ QSGShaderEffectProgram m_source;
+ QSGShaderEffectMesh *m_mesh;
+ QSGGridMesh m_defaultMesh;
+ CullMode m_cullMode;
+
+ struct SourceData
+ {
+ QSignalMapper *mapper;
+ QPointer<QSGItem> item;
+ QByteArray name;
+ };
+ QVector<SourceData> m_sources;
+ QSGShaderEffectMaterial m_material;
+
+ uint m_blending : 1;
+ uint m_dirtyData : 1;
+
+ uint m_programDirty : 1;
+ uint m_dirtyMesh : 1;
+ uint m_dirtyGeometry : 1;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SHADEREFFECTITEM_H
diff --git a/src/declarative/items/qsgshadereffectmesh.cpp b/src/declarative/items/qsgshadereffectmesh.cpp
new file mode 100644
index 0000000000..4900755e46
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectmesh.cpp
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgshadereffectmesh_p.h"
+#include "qsggeometry.h"
+#include "qsgshadereffectitem_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSGShaderEffectMesh::QSGShaderEffectMesh(QObject *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ \qmlclass GridMesh QSGGridMesh
+ \since 5.0
+ \ingroup qml-utility-elements
+ \brief GridMesh defines a mesh to be used with \l ShaderEffectItem.
+
+ GridMesh defines a rectangular mesh consisting of vertices arranged in an
+ evenly spaced grid. It can be assigned to the \l ShaderEffectItem's mesh
+ property. The grid resolution is specified with the \l resolution property.
+
+ \row
+ \o \image declarative-gridmesh.png
+ \o \qml
+ import QtQuick 2.0
+
+ ShaderEffectItem {
+ width: 200
+ height: 200
+ mesh: GridMesh { resolution: Qt.size(20, 20) }
+ property variant source: Image {
+ source: "qt-logo.png"
+ sourceSize {width: 200; height: 200 }
+ smooth: true
+ }
+ vertexShader: "
+ uniform highp mat4 qt_ModelViewProjectionMatrix;
+ attribute highp vec4 qt_Vertex;
+ attribute highp vec2 qt_MultiTexCoord0;
+ varying highp vec2 qt_TexCoord0;
+ uniform highp float width;
+ void main() {
+ highp vec4 pos = qt_Vertex;
+ highp float d = .5 * smoothstep(0., 1., qt_MultiTexCoord0.y);
+ pos.x = width * mix(d, 1.0 - d, qt_MultiTexCoord0.x);
+ gl_Position = qt_ModelViewProjectionMatrix * pos;
+ qt_TexCoord0 = qt_MultiTexCoord0;
+ }"
+ }
+ \endqml
+ \endrow
+
+*/
+
+QSGGridMesh::QSGGridMesh(QObject *parent)
+ : QSGShaderEffectMesh(parent)
+ , m_resolution(1, 1)
+{
+ connect(this, SIGNAL(resolutionChanged()), this, SIGNAL(geometryChanged()));
+}
+
+QSGGeometry *QSGGridMesh::updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &dstRect) const
+{
+ int vmesh = m_resolution.height();
+ int hmesh = m_resolution.width();
+ int attrCount = attributes.count();
+
+ if (!geometry) {
+ bool error = true;
+ switch (attrCount) {
+ case 0:
+ qWarning("QSGGridMesh:: No attributes specified.");
+ break;
+ case 1:
+ if (attributes.at(0) == qtPositionAttributeName()) {
+ error = false;
+ break;
+ }
+ qWarning("QSGGridMesh:: Missing \'%s\' attribute.",
+ qtPositionAttributeName());
+ break;
+ case 2:
+ if (attributes.contains(qtPositionAttributeName())
+ && attributes.contains(qtTexCoordAttributeName()))
+ {
+ error = false;
+ break;
+ }
+ qWarning("QSGGridMesh:: Missing \'%s\' or \'%s\' attribute.",
+ qtPositionAttributeName(), qtTexCoordAttributeName());
+ break;
+ default:
+ qWarning("QSGGridMesh:: Too many attributes specified.");
+ break;
+ }
+
+ if (error) {
+ delete geometry;
+ return 0;
+ }
+
+ geometry = new QSGGeometry(attrCount == 1
+ ? QSGGeometry::defaultAttributes_Point2D()
+ : QSGGeometry::defaultAttributes_TexturedPoint2D(),
+ (vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2),
+ GL_UNSIGNED_SHORT);
+
+ } else {
+ geometry->allocate((vmesh + 1) * (hmesh + 1), vmesh * 2 * (hmesh + 2));
+ }
+
+ QSGGeometry::Point2D *vdata = static_cast<QSGGeometry::Point2D *>(geometry->vertexData());
+
+ bool positionFirst = attributes.at(0) == qtPositionAttributeName();
+
+ QRectF srcRect(0, 0, 1, 1);
+ for (int iy = 0; iy <= vmesh; ++iy) {
+ float fy = iy / float(vmesh);
+ float y = float(dstRect.top()) + fy * float(dstRect.height());
+ float ty = float(srcRect.top()) + fy * float(srcRect.height());
+ for (int ix = 0; ix <= hmesh; ++ix) {
+ float fx = ix / float(hmesh);
+ for (int ia = 0; ia < attrCount; ++ia) {
+ if (positionFirst == (ia == 0)) {
+ vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
+ vdata->y = y;
+ ++vdata;
+ } else {
+ vdata->x = float(srcRect.left()) + fx * float(srcRect.width());
+ vdata->y = ty;
+ ++vdata;
+ }
+ }
+ }
+ }
+
+ quint16 *indices = (quint16 *)geometry->indexDataAsUShort();
+ int i = 0;
+ for (int iy = 0; iy < vmesh; ++iy) {
+ *(indices++) = i + hmesh + 1;
+ for (int ix = 0; ix <= hmesh; ++ix, ++i) {
+ *(indices++) = i + hmesh + 1;
+ *(indices++) = i;
+ }
+ *(indices++) = i - 1;
+ }
+
+ return geometry;
+}
+
+/*!
+ \qmlproperty size GridMesh::resolution
+
+ This property holds the grid resolution. The resolution's width and height
+ specify the number of cells or spacings between vertices horizontally and
+ vertically respectively. The minimum and default is 1x1, which corresponds
+ to four vertices in total, one in each corner.
+*/
+
+void QSGGridMesh::setResolution(const QSize &res)
+{
+ if (res == m_resolution)
+ return;
+ if (res.width() < 1 || res.height() < 1) {
+ qWarning("QSGGridMesh: Resolution must be at least 1x1");
+ return;
+ }
+ m_resolution = res;
+ emit resolutionChanged();
+}
+
+QSize QSGGridMesh::resolution() const
+{
+ return m_resolution;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgshadereffectmesh_p.h b/src/declarative/items/qsgshadereffectmesh_p.h
new file mode 100644
index 0000000000..88198b5c40
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectmesh_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativeparserstatus.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qsize.h>
+#include <QtCore/qvariant.h>
+#include <QtOpenGL/qglfunctions.h>
+
+#ifndef SHADEREFFECTMESH_H
+#define SHADEREFFECTMESH_H
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGGeometry;
+class QRectF;
+
+class QSGShaderEffectMesh : public QObject
+{
+ Q_OBJECT
+public:
+ QSGShaderEffectMesh(QObject *parent = 0);
+ // If 'geometry' != 0, 'attributes' is the same as last time the function was called.
+ virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &rect) const = 0;
+
+Q_SIGNALS:
+ // Emitted when the geometry needs to be updated.
+ void geometryChanged();
+};
+
+class QSGGridMesh : public QSGShaderEffectMesh
+{
+ Q_OBJECT
+ Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
+public:
+ QSGGridMesh(QObject *parent = 0);
+ virtual QSGGeometry *updateGeometry(QSGGeometry *geometry, const QVector<QByteArray> &attributes, const QRectF &rect) const;
+
+ void setResolution(const QSize &res);
+ QSize resolution() const;
+
+Q_SIGNALS:
+ void resolutionChanged();
+
+private:
+ QSize m_resolution;
+};
+
+inline QColor qt_premultiply_color(const QColor &c)
+{
+ return QColor::fromRgbF(c.redF() * c.alphaF(), c.greenF() * c.alphaF(), c.blueF() * c.alphaF(), c.alphaF());
+}
+
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SHADEREFFECTITEM_H
diff --git a/src/declarative/items/qsgshadereffectnode.cpp b/src/declarative/items/qsgshadereffectnode.cpp
new file mode 100644
index 0000000000..74cd995d0d
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectnode.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qsgshadereffectnode_p.h>
+
+#include "qsgshadereffectmesh_p.h"
+#include <private/qsgtextureprovider_p.h>
+#include <private/qsgrenderer_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGCustomMaterialShader : public QSGMaterialShader
+{
+public:
+ QSGCustomMaterialShader(const QSGShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes);
+ virtual void deactivate();
+ virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
+ virtual char const *const *attributeNames() const;
+
+protected:
+ friend class QSGShaderEffectNode;
+
+ virtual void initialize();
+ virtual const char *vertexShader() const;
+ virtual const char *fragmentShader() const;
+
+ const QSGShaderEffectMaterialKey m_key;
+ QVector<const char *> m_attributeNames;
+ const QVector<QByteArray> m_attributes;
+
+ QVector<int> m_uniformLocs;
+ int m_opacityLoc;
+ int m_matrixLoc;
+ uint m_textureIndicesSet;
+};
+
+QSGCustomMaterialShader::QSGCustomMaterialShader(const QSGShaderEffectMaterialKey &key, const QVector<QByteArray> &attributes)
+ : m_key(key)
+ , m_attributes(attributes)
+ , m_textureIndicesSet(false)
+{
+ for (int i = 0; i < attributes.count(); ++i)
+ m_attributeNames.append(attributes.at(i).constData());
+ m_attributeNames.append(0);
+}
+
+void QSGCustomMaterialShader::deactivate()
+{
+ glDisable(GL_CULL_FACE);
+}
+
+void QSGCustomMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
+{
+ Q_ASSERT(newEffect != 0);
+
+ const QSGShaderEffectMaterial *material = static_cast<const QSGShaderEffectMaterial *>(newEffect);
+
+ if (!m_textureIndicesSet) {
+ for (int i = 0; i < material->m_textures.size(); ++i)
+ program()->setUniformValue(material->m_textures.at(i).first.constData(), i);
+ m_textureIndicesSet = true;
+ }
+
+ if (m_uniformLocs.size() != material->m_uniformValues.size()) {
+ m_uniformLocs.reserve(material->m_uniformValues.size());
+ for (int i = 0; i < material->m_uniformValues.size(); ++i) {
+ const QByteArray &name = material->m_uniformValues.at(i).first;
+ m_uniformLocs.append(program()->uniformLocation(name.constData()));
+ }
+ }
+
+ QGLFunctions *functions = state.context()->functions();
+ for (int i = material->m_textures.size() - 1; i >= 0; --i) {
+ QPointer<QSGItem> source = material->m_textures.at(i).second;
+ QSGTextureProvider *provider = QSGTextureProvider::from(source);
+ QSGTexture *texture = provider ? provider->texture() : 0;
+ if (!source || !provider || !texture) {
+ qWarning("ShaderEffectItem: source or provider missing when binding textures");
+ continue;
+ }
+ functions->glActiveTexture(GL_TEXTURE0 + i);
+ provider->texture()->bind();
+ }
+
+ if (material->m_source.respectsOpacity)
+ program()->setUniformValue(m_opacityLoc, state.opacity());
+
+ for (int i = 0; i < material->m_uniformValues.count(); ++i) {
+ const QVariant &v = material->m_uniformValues.at(i).second;
+
+ switch (v.type()) {
+ case QVariant::Color:
+ program()->setUniformValue(m_uniformLocs.at(i), qt_premultiply_color(qvariant_cast<QColor>(v)));
+ break;
+ case QVariant::Double:
+ program()->setUniformValue(m_uniformLocs.at(i), (float) qvariant_cast<double>(v));
+ break;
+ case QVariant::Transform:
+ program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QTransform>(v));
+ break;
+ case QVariant::Int:
+ program()->setUniformValue(m_uniformLocs.at(i), v.toInt());
+ break;
+ case QVariant::Bool:
+ program()->setUniformValue(m_uniformLocs.at(i), GLint(v.toBool()));
+ break;
+ case QVariant::Size:
+ case QVariant::SizeF:
+ program()->setUniformValue(m_uniformLocs.at(i), v.toSizeF());
+ break;
+ case QVariant::Point:
+ case QVariant::PointF:
+ program()->setUniformValue(m_uniformLocs.at(i), v.toPointF());
+ break;
+ case QVariant::Rect:
+ case QVariant::RectF:
+ {
+ QRectF r = v.toRectF();
+ program()->setUniformValue(m_uniformLocs.at(i), r.x(), r.y(), r.width(), r.height());
+ }
+ break;
+ case QVariant::Vector3D:
+ program()->setUniformValue(m_uniformLocs.at(i), qvariant_cast<QVector3D>(v));
+ break;
+ default:
+ break;
+ }
+ }
+
+ const QSGShaderEffectMaterial *oldMaterial = static_cast<const QSGShaderEffectMaterial *>(oldEffect);
+ if (oldEffect == 0 || material->cullMode() != oldMaterial->cullMode()) {
+ switch (material->cullMode()) {
+ case QSGShaderEffectMaterial::FrontFaceCulling:
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_FRONT);
+ break;
+ case QSGShaderEffectMaterial::BackFaceCulling:
+ glEnable(GL_CULL_FACE);
+ glCullFace(GL_BACK);
+ break;
+ default:
+ glDisable(GL_CULL_FACE);
+ break;
+ }
+ }
+
+ if ((state.isMatrixDirty()) && material->m_source.respectsMatrix)
+ program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
+}
+
+char const *const *QSGCustomMaterialShader::attributeNames() const
+{
+ return m_attributeNames.constData();
+}
+
+void QSGCustomMaterialShader::initialize()
+{
+ m_opacityLoc = program()->uniformLocation("qt_Opacity");
+ m_matrixLoc = program()->uniformLocation("qt_ModelViewProjectionMatrix");
+}
+
+const char *QSGCustomMaterialShader::vertexShader() const
+{
+ return m_key.vertexCode.constData();
+}
+
+const char *QSGCustomMaterialShader::fragmentShader() const
+{
+ return m_key.fragmentCode.constData();
+}
+
+
+bool QSGShaderEffectMaterialKey::operator == (const QSGShaderEffectMaterialKey &other) const
+{
+ return vertexCode == other.vertexCode && fragmentCode == other.fragmentCode && className == other.className;
+}
+
+uint qHash(const QSGShaderEffectMaterialKey &key)
+{
+ return qHash(qMakePair(qMakePair(key.vertexCode, key.fragmentCode), key.className));
+}
+
+
+QHash<QSGShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > QSGShaderEffectMaterial::materialMap;
+
+QSGShaderEffectMaterial::QSGShaderEffectMaterial()
+ : m_cullMode(NoCulling)
+{
+ setFlag(Blending, true);
+}
+
+QSGMaterialType *QSGShaderEffectMaterial::type() const
+{
+ return m_type.data();
+}
+
+QSGMaterialShader *QSGShaderEffectMaterial::createShader() const
+{
+ return new QSGCustomMaterialShader(m_source, m_source.attributeNames);
+}
+
+int QSGShaderEffectMaterial::compare(const QSGMaterial *other) const
+{
+ return this - static_cast<const QSGShaderEffectMaterial *>(other);
+}
+
+void QSGShaderEffectMaterial::setCullMode(QSGShaderEffectMaterial::CullMode face)
+{
+ m_cullMode = face;
+}
+
+QSGShaderEffectMaterial::CullMode QSGShaderEffectMaterial::cullMode() const
+{
+ return m_cullMode;
+}
+
+void QSGShaderEffectMaterial::setProgramSource(const QSGShaderEffectProgram &source)
+{
+ m_source = source;
+ m_type = materialMap.value(m_source);
+ if (m_type.isNull()) {
+ m_type = QSharedPointer<QSGMaterialType>(new QSGMaterialType);
+ materialMap.insert(m_source, m_type);
+ }
+}
+
+void QSGShaderEffectMaterial::setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues)
+{
+ m_uniformValues = uniformValues;
+}
+
+void QSGShaderEffectMaterial::setTextureProviders(const QVector<QPair<QByteArray, QPointer<QSGItem> > > &textures)
+{
+ m_textures = textures;
+}
+
+const QVector<QPair<QByteArray, QPointer<QSGItem> > > &QSGShaderEffectMaterial::textureProviders() const
+{
+ return m_textures;
+}
+
+void QSGShaderEffectMaterial::updateTextures() const
+{
+ for (int i = 0; i < m_textures.size(); ++i) {
+ QSGItem *item = m_textures.at(i).second;
+ if (item) {
+ QSGTextureProvider *provider = QSGTextureProvider::from(item);
+ if (provider) {
+ QSGTexture *texture = provider->texture();
+ if (!texture) {
+ qWarning("QSGShaderEffectMaterial: no texture from %s [%s]",
+ qPrintable(item->objectName()),
+ item->metaObject()->className());
+ }
+ if (QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(provider->texture())) {
+ t->updateTexture();
+ }
+ }
+ }
+ }
+}
+
+
+QSGShaderEffectNode::QSGShaderEffectNode()
+{
+ QSGNode::setFlag(UsePreprocess, true);
+}
+
+QSGShaderEffectNode::~QSGShaderEffectNode()
+{
+}
+
+void QSGShaderEffectNode::markDirtyTexture()
+{
+ markDirty(DirtyMaterial);
+}
+
+void QSGShaderEffectNode::preprocess()
+{
+ Q_ASSERT(material());
+ static_cast<QSGShaderEffectMaterial *>(material())->updateTextures();
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgshadereffectnode_p.h b/src/declarative/items/qsgshadereffectnode_p.h
new file mode 100644
index 0000000000..0be4b36294
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectnode_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SHADEREFFECTNODE_H
+#define SHADEREFFECTNODE_H
+
+#include "qsgnode.h"
+#include "qsgmaterial.h"
+#include <private/qsgtextureprovider_p.h>
+#include <qsgitem.h>
+
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/qpointer.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+struct QSGShaderEffectMaterialKey {
+ QByteArray vertexCode;
+ QByteArray fragmentCode;
+ const char *className;
+
+ bool operator == (const QSGShaderEffectMaterialKey &other) const;
+};
+
+uint qHash(const QSGShaderEffectMaterialKey &key);
+
+// TODO: Implement support for multisampling.
+struct QSGShaderEffectProgram : public QSGShaderEffectMaterialKey
+{
+ QSGShaderEffectProgram() : respectsOpacity(false), respectsMatrix(false) {}
+
+ QVector<QByteArray> attributeNames;
+ QSet<QByteArray> uniformNames;
+
+ uint respectsOpacity : 1;
+ uint respectsMatrix : 1;
+};
+
+
+class QSGCustomMaterialShader;
+class QSGShaderEffectMaterial : public QSGMaterial
+{
+public:
+ enum CullMode
+ {
+ NoCulling,
+ BackFaceCulling,
+ FrontFaceCulling
+ };
+
+ QSGShaderEffectMaterial();
+ virtual QSGMaterialType *type() const;
+ virtual QSGMaterialShader *createShader() const;
+ virtual int compare(const QSGMaterial *other) const;
+
+ void setCullMode(CullMode face);
+ CullMode cullMode() const;
+
+ void setProgramSource(const QSGShaderEffectProgram &);
+ void setUniforms(const QVector<QPair<QByteArray, QVariant> > &uniformValues);
+ void setTextureProviders(const QVector<QPair<QByteArray, QPointer<QSGItem> > > &textures);
+ const QVector<QPair<QByteArray, QPointer<QSGItem> > > &textureProviders() const;
+ void updateTextures() const;
+
+protected:
+ friend class QSGShaderEffectItem;
+ friend class QSGCustomMaterialShader;
+
+ // The type pointer needs to be unique. It is not safe to let the type object be part of the
+ // QSGShaderEffectMaterial, since it can be deleted and a new one constructed on top of the old
+ // one. The new QSGShaderEffectMaterial would then get the same type pointer as the old one, and
+ // CustomMaterialShaders based on the old one would incorrectly be used together with the new
+ // one. To guarantee that the type pointer is unique, the type object must live as long as
+ // there are any CustomMaterialShaders of that type.
+ QSharedPointer<QSGMaterialType> m_type;
+
+ QSGShaderEffectProgram m_source;
+ QVector<QPair<QByteArray, QVariant> > m_uniformValues;
+ QVector<QPair<QByteArray, QPointer<QSGItem> > > m_textures;
+ CullMode m_cullMode;
+
+ static QHash<QSGShaderEffectMaterialKey, QSharedPointer<QSGMaterialType> > materialMap;
+};
+
+
+class QSGShaderEffectMesh;
+
+class QSGShaderEffectNode : public QObject, public QSGGeometryNode
+{
+ Q_OBJECT
+public:
+ QSGShaderEffectNode();
+ virtual ~QSGShaderEffectNode();
+
+ virtual void preprocess();
+
+private Q_SLOTS:
+ void markDirtyTexture();
+
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SHADEREFFECTNODE_H
diff --git a/src/declarative/items/qsgshadereffectsource.cpp b/src/declarative/items/qsgshadereffectsource.cpp
new file mode 100644
index 0000000000..e2c50bb80e
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectsource.cpp
@@ -0,0 +1,818 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgshadereffectsource_p.h"
+
+#include "qsgitem_p.h"
+#include "qsgcanvas_p.h"
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgrenderer_p.h>
+
+#include "qglframebufferobject.h"
+#include "qmath.h"
+#include <private/qsgtexture_p.h>
+
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
+
+QSGShaderEffectSourceNode::QSGShaderEffectSourceNode()
+{
+ setFlag(UsePreprocess, true);
+}
+
+void QSGShaderEffectSourceNode::markDirtyTexture()
+{
+ markDirty(DirtyMaterial);
+}
+
+
+QSGShaderEffectTexture::QSGShaderEffectTexture(QSGItem *shaderSource)
+ : QSGDynamicTexture()
+ , m_item(0)
+ , m_format(GL_RGBA)
+ , m_shaderSource(shaderSource)
+ , m_renderer(0)
+ , m_fbo(0)
+ , m_secondaryFbo(0)
+#ifdef QSG_DEBUG_FBO_OVERLAY
+ , m_debugOverlay(0)
+#endif
+ , m_mipmap(false)
+ , m_live(true)
+ , m_recursive(false)
+ , m_dirtyTexture(true)
+ , m_multisamplingSupportChecked(false)
+ , m_multisampling(false)
+ , m_grab(false)
+{
+}
+
+QSGShaderEffectTexture::~QSGShaderEffectTexture()
+{
+ delete m_renderer;
+ delete m_fbo;
+ delete m_secondaryFbo;
+#ifdef QSG_DEBUG_FBO_OVERLAY
+ delete m_debugOverlay;
+#endif
+}
+
+
+int QSGShaderEffectTexture::textureId() const
+{
+ return m_fbo ? m_fbo->texture() : 0;
+}
+
+bool QSGShaderEffectTexture::hasAlphaChannel() const
+{
+ return m_format != GL_RGB;
+}
+
+bool QSGShaderEffectTexture::hasMipmaps() const
+{
+ return m_mipmap;
+}
+
+
+void QSGShaderEffectTexture::bind()
+{
+#ifndef QT_NO_DEBUG
+ if (!m_recursive && m_fbo && ((m_multisampling && m_secondaryFbo->isBound()) || m_fbo->isBound()))
+ qWarning("ShaderEffectSource: \'recursive\' must be set to true when rendering recursively.");
+#endif
+ glBindTexture(GL_TEXTURE_2D, m_fbo ? m_fbo->texture() : 0);
+ updateBindOptions();
+}
+
+bool QSGShaderEffectTexture::updateTexture()
+{
+ if ((m_live || m_grab) && m_dirtyTexture) {
+ grab();
+ m_grab = false;
+ return true;
+ }
+ return false;
+}
+
+void QSGShaderEffectTexture::setHasMipmaps(bool mipmap)
+{
+ if (mipmap == m_mipmap)
+ return;
+ m_mipmap = mipmap;
+ if (m_mipmap && m_fbo && !m_fbo->format().mipmap())
+ markDirtyTexture();
+}
+
+
+void QSGShaderEffectTexture::setItem(QSGNode *item)
+{
+ if (item == m_item)
+ return;
+ m_item = item;
+ markDirtyTexture();
+}
+
+void QSGShaderEffectTexture::setRect(const QRectF &rect)
+{
+ if (rect == m_rect)
+ return;
+ m_rect = rect;
+ markDirtyTexture();
+}
+
+void QSGShaderEffectTexture::setSize(const QSize &size)
+{
+ if (size == m_size)
+ return;
+ m_size = size;
+ markDirtyTexture();
+}
+
+void QSGShaderEffectTexture::setFormat(GLenum format)
+{
+ if (format == m_format)
+ return;
+ m_format = format;
+ markDirtyTexture();
+}
+
+void QSGShaderEffectTexture::setLive(bool live)
+{
+ if (live == m_live)
+ return;
+ m_live = live;
+ markDirtyTexture();
+}
+
+void QSGShaderEffectTexture::scheduleUpdate()
+{
+ if (m_grab)
+ return;
+ m_grab = true;
+ if (m_dirtyTexture)
+ emit textureChanged();
+}
+
+void QSGShaderEffectTexture::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+void QSGShaderEffectTexture::markDirtyTexture()
+{
+ m_dirtyTexture = true;
+ if (m_live || m_grab)
+ emit textureChanged();
+}
+
+void QSGShaderEffectTexture::grab()
+{
+ if (!m_item || m_size.isNull()) {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = m_secondaryFbo = 0;
+ m_dirtyTexture = false;
+ return;
+ }
+ QSGNode *root = m_item;
+ while (root->childCount() && root->type() != QSGNode::RootNodeType)
+ root = root->childAtIndex(0);
+ if (root->type() != QSGNode::RootNodeType)
+ return;
+
+ if (m_size.isEmpty()) {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_secondaryFbo = m_fbo = 0;
+ return;
+ }
+
+ QSGContext *context = QSGItemPrivate::get(m_shaderSource)->sceneGraphContext();
+
+ if (!m_renderer) {
+ m_renderer = context->createRenderer();
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()), Qt::DirectConnection);
+ }
+ m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
+
+ bool deleteFboLater = false;
+ if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
+ || (!m_fbo->format().mipmap() && m_mipmap))
+ {
+ if (!m_multisamplingSupportChecked) {
+ QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
+ m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
+ && extensions.contains("GL_EXT_framebuffer_blit");
+ m_multisamplingSupportChecked = true;
+ }
+ if (m_multisampling) {
+ // Don't delete the FBO right away in case it is used recursively.
+ deleteFboLater = true;
+ delete m_secondaryFbo;
+ QGLFramebufferObjectFormat format;
+
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(m_format);
+ format.setSamples(8);
+ m_secondaryFbo = new QGLFramebufferObject(m_size, format);
+ } else {
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(m_format);
+ format.setMipmap(m_mipmap);
+ if (m_recursive) {
+ deleteFboLater = true;
+ delete m_secondaryFbo;
+ m_secondaryFbo = new QGLFramebufferObject(m_size, format);
+ glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
+ updateBindOptions(true);
+ } else {
+ delete m_fbo;
+ delete m_secondaryFbo;
+ m_fbo = new QGLFramebufferObject(m_size, format);
+ m_secondaryFbo = 0;
+ glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(true);
+ }
+ }
+ }
+
+ if (m_recursive && !m_secondaryFbo) {
+ // m_fbo already created, m_recursive was just set.
+ Q_ASSERT(m_fbo);
+ Q_ASSERT(!m_multisampling);
+
+ m_secondaryFbo = new QGLFramebufferObject(m_size, m_fbo->format());
+ glBindTexture(GL_TEXTURE_2D, m_secondaryFbo->texture());
+ updateBindOptions(true);
+ }
+
+ // Render texture.
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip and opacity update.
+ m_renderer->nodeChanged(root, QSGNode::DirtyForceUpdate); // Force render list update.
+
+#ifdef QSG_DEBUG_FBO_OVERLAY
+ if (qmlFboOverlay()) {
+ if (!m_debugOverlay)
+ m_debugOverlay = context->createRectangleNode();
+ m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
+ m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
+ m_debugOverlay->setPenColor(QColor());
+ m_debugOverlay->setPenWidth(0);
+ m_debugOverlay->setRadius(0);
+ m_debugOverlay->update();
+ root->appendChildNode(m_debugOverlay);
+ }
+#endif
+
+ m_dirtyTexture = false;
+
+ const QGLContext *ctx = QGLContext::currentContext();
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setViewportRect(m_size);
+ QRectF mirrored(m_rect.left(), m_rect.bottom(), m_rect.width(), -m_rect.height());
+ m_renderer->setProjectMatrixToRect(mirrored);
+ m_renderer->setClearColor(Qt::transparent);
+
+ if (m_multisampling) {
+ m_renderer->renderScene(BindableFbo(m_secondaryFbo));
+
+ if (deleteFboLater) {
+ delete m_fbo;
+ QGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(m_format);
+ format.setAttachment(QGLFramebufferObject::NoAttachment);
+ format.setMipmap(m_mipmap);
+ format.setSamples(0);
+ m_fbo = new QGLFramebufferObject(m_size, format);
+ glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(true);
+ }
+
+ QRect r(QPoint(), m_size);
+ QGLFramebufferObject::blitFramebuffer(m_fbo, r, m_secondaryFbo, r);
+ } else {
+ if (m_recursive) {
+ m_renderer->renderScene(BindableFbo(m_secondaryFbo));
+
+ if (deleteFboLater) {
+ delete m_fbo;
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(m_format);
+ format.setMipmap(m_mipmap);
+ m_fbo = new QGLFramebufferObject(m_size, format);
+ glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(true);
+ }
+ qSwap(m_fbo, m_secondaryFbo);
+ } else {
+ m_renderer->renderScene(BindableFbo(m_fbo));
+ }
+ }
+
+ if (m_mipmap) {
+ glBindTexture(GL_TEXTURE_2D, textureId());
+ ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
+ }
+
+ root->markDirty(QSGNode::DirtyForceUpdate); // Force matrix, clip, opacity and render list update.
+
+#ifdef QSG_DEBUG_FBO_OVERLAY
+ if (qmlFboOverlay())
+ root->removeChildNode(m_debugOverlay);
+#endif
+ if (m_recursive)
+ markDirtyTexture(); // Continuously update if 'live' and 'recursive'.
+}
+
+/*!
+ \qmlclass ShaderEffectSource QSGShaderEffectSource
+ \since 5.0
+ \ingroup qml-basic-visual-elements
+ \brief The ShaderEffectSource element renders a QML element into a texture
+ and displays it.
+ \inherits Item
+
+ The ShaderEffectSource element renders \l sourceItem into a texture and
+ displays it in the scene. \l sourceItem is drawn into the texture as though
+ it was a fully opaque root element. Thus \l sourceItem itself can be
+ invisible, but still appear in the texture.
+
+ ShaderEffectSource can be used as:
+ \list
+ \o a texture source in a \l ShaderEffectItem.
+ This allows you to apply custom shader effects to any QML element.
+ \o a cache for a complex element.
+ The complex element can be rendered once into the texture, which can
+ then be animated freely without the need to render the complex element
+ again every frame.
+ \o an opacity layer.
+ ShaderEffectSource allows you to apply an opacity to elements as a group
+ rather than each element individually.
+ \endlist
+
+ \row
+ \o \image declarative-shadereffectsource.png
+ \o \qml
+ import QtQuick 2.0
+
+ Rectangle {
+ width: 200
+ height: 100
+ gradient: Gradient {
+ GradientStop { position: 0; color: "white" }
+ GradientStop { position: 1; color: "black" }
+ }
+ Row {
+ opacity: 0.5
+ Item {
+ id: foo
+ width: 100; height: 100
+ Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
+ Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
+ Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
+ }
+ ShaderEffectSource {
+ width: 100; height: 100
+ sourceItem: foo
+ }
+ }
+ }
+ \endqml
+ \endrow
+
+ The ShaderEffectSource element does not redirect any mouse or keyboard
+ input to \l sourceItem. If you hide the \l sourceItem by setting
+ \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
+ it will no longer react to input. In cases where the ShaderEffectSource is
+ meant to replace the \l sourceItem, you typically want to hide the
+ \l sourceItem while still handling input. For this, you can use
+ the \l hideSource property.
+
+ \note If \l sourceItem is a \l Rectangle with border, by default half the
+ border width falls outside the texture. To get the whole border, you can
+ extend the \l sourceRect.
+
+ \warning In most cases, using a ShaderEffectSource will decrease
+ performance, and in all cases, it will increase video memory usage.
+ Rendering through a ShaderEffectSource might also lead to lower quality
+ since some OpenGL implementations support multisampled backbuffer,
+ but not multisampled framebuffer objects.
+*/
+
+QSGShaderEffectSource::QSGShaderEffectSource(QSGItem *parent)
+ : QSGItem(parent)
+ , m_wrapMode(ClampToEdge)
+ , m_sourceItem(0)
+ , m_textureSize(0, 0)
+ , m_format(RGBA)
+ , m_live(true)
+ , m_hideSource(false)
+ , m_mipmap(false)
+ , m_recursive(false)
+ , m_grab(true)
+{
+ setFlag(ItemHasContents);
+ m_texture = new QSGShaderEffectTexture(this);
+ connect(m_texture, SIGNAL(textureChanged()), this, SIGNAL(textureChanged()), Qt::DirectConnection);
+ connect(m_texture, SIGNAL(textureChanged()), this, SLOT(update()));
+}
+
+QSGShaderEffectSource::~QSGShaderEffectSource()
+{
+ delete m_texture;
+ if (m_sourceItem)
+ QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
+}
+
+/*!
+ \qmlproperty enumeration ShaderEffectSource::wrapMode
+
+ This property defines the OpenGL wrap modes associated with the texture.
+ Modifying this property makes most sense when the element is used as a
+ source texture of a \l ShaderEffectItem.
+
+ \list
+ \o ShaderEffectSource.ClampToEdge - GL_CLAMP_TO_EDGE both horizontally and vertically
+ \o ShaderEffectSource.RepeatHorizontally - GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
+ \o ShaderEffectSource.RepeatVertically - GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
+ \o ShaderEffectSource.Repeat - GL_REPEAT both horizontally and vertically
+ \endlist
+
+ \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
+ wrap mode with non-power-of-two textures.
+*/
+
+QSGShaderEffectSource::WrapMode QSGShaderEffectSource::wrapMode() const
+{
+ return m_wrapMode;
+}
+
+void QSGShaderEffectSource::setWrapMode(WrapMode mode)
+{
+ if (mode == m_wrapMode)
+ return;
+ m_wrapMode = mode;
+ update();
+ emit wrapModeChanged();
+}
+
+/*!
+ \qmlproperty Item ShaderEffectSource::sourceItem
+
+ This property holds the element to be rendered into the texture.
+*/
+
+QSGItem *QSGShaderEffectSource::sourceItem() const
+{
+ return m_sourceItem;
+}
+
+void QSGShaderEffectSource::setSourceItem(QSGItem *item)
+{
+ if (item == m_sourceItem)
+ return;
+ if (m_sourceItem)
+ QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
+ m_sourceItem = item;
+ if (m_sourceItem) {
+ // TODO: Find better solution.
+ // 'm_sourceItem' needs a canvas to get a scenegraph node.
+ // The easiest way to make sure it gets a canvas is to
+ // make it a part of the same item tree as 'this'.
+ if (m_sourceItem->parentItem() == 0) {
+ m_sourceItem->setParentItem(this);
+ m_sourceItem->setVisible(false);
+ }
+ QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(m_hideSource);
+ }
+ update();
+ emit sourceItemChanged();
+}
+
+/*!
+ \qmlproperty rect ShaderEffectSource::sourceRect
+
+ This property defines which rectangular area of the \l sourceItem to
+ render into the texture. The source rectangle can be larger than
+ \l sourceItem itself. If the rectangle is null, which is the default,
+ the whole \l sourceItem is rendered to texture.
+*/
+
+QRectF QSGShaderEffectSource::sourceRect() const
+{
+ return m_sourceRect;
+}
+
+void QSGShaderEffectSource::setSourceRect(const QRectF &rect)
+{
+ if (rect == m_sourceRect)
+ return;
+ m_sourceRect = rect;
+ update();
+ emit sourceRectChanged();
+}
+
+/*!
+ \qmlproperty size ShaderEffectSource::textureSize
+
+ This property holds the size of the texture. If it is empty, which is the
+ default, the size of the source rectangle is used.
+*/
+
+QSize QSGShaderEffectSource::textureSize() const
+{
+ return m_textureSize;
+}
+
+void QSGShaderEffectSource::setTextureSize(const QSize &size)
+{
+ if (size == m_textureSize)
+ return;
+ m_textureSize = size;
+ update();
+ emit textureSizeChanged();
+}
+
+/*!
+ \qmlproperty enumeration ShaderEffectSource::format
+
+ This property defines the internal OpenGL format of the texture.
+ Modifying this property makes most sense when the element is used as a
+ source texture of a \l ShaderEffectItem. Depending on the OpenGL
+ implementation, this property might allow you to save some texture memory.
+
+ \list
+ \o ShaderEffectSource.Alpha - GL_ALPHA
+ \o ShaderEffectSource.RGB - GL_RGB
+ \o ShaderEffectSource.RGBA - GL_RGBA
+ \endlist
+
+ \note Some OpenGL implementations do not support the GL_ALPHA format.
+*/
+
+QSGShaderEffectSource::Format QSGShaderEffectSource::format() const
+{
+ return m_format;
+}
+
+void QSGShaderEffectSource::setFormat(QSGShaderEffectSource::Format format)
+{
+ if (format == m_format)
+ return;
+ m_format = format;
+ update();
+ emit formatChanged();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectSource::live
+
+ If this property is true, the texture is updated whenever the
+ \l sourceItem changes. Otherwise, it will be a frozen image of the
+ \l sourceItem. The property is true by default.
+*/
+
+bool QSGShaderEffectSource::live() const
+{
+ return m_live;
+}
+
+void QSGShaderEffectSource::setLive(bool live)
+{
+ if (live == m_live)
+ return;
+ m_live = live;
+ update();
+ emit liveChanged();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectSource::hideSource
+
+ If this property is true, the \l sourceItem is hidden, though it will still
+ be rendered into the texture. As opposed to hiding the \l sourceItem by
+ setting \l{Item::visible}{visible} to false, setting this property to true
+ will not prevent mouse or keyboard input from reaching \l sourceItem.
+ The property is useful when the ShaderEffectSource is anchored on top of,
+ and meant to replace the \l sourceItem.
+*/
+
+bool QSGShaderEffectSource::hideSource() const
+{
+ return m_hideSource;
+}
+
+void QSGShaderEffectSource::setHideSource(bool hide)
+{
+ if (hide == m_hideSource)
+ return;
+ if (m_sourceItem) {
+ QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
+ QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
+ }
+ m_hideSource = hide;
+ update();
+ emit hideSourceChanged();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectSource::mipmap
+
+ If this property is true, mipmaps are generated for the texture.
+
+ \note Some OpenGL ES 2 implementations do not support mipmapping of
+ non-power-of-two textures.
+*/
+
+bool QSGShaderEffectSource::mipmap() const
+{
+ return m_mipmap;
+}
+
+void QSGShaderEffectSource::setMipmap(bool enabled)
+{
+ if (enabled == m_mipmap)
+ return;
+ m_mipmap = enabled;
+ update();
+ emit mipmapChanged();
+}
+
+/*!
+ \qmlproperty bool ShaderEffectSource::recursive
+
+ Set this property to true if the ShaderEffectSource has a dependency on
+ itself. ShaderEffectSources form a dependency chain, where one
+ ShaderEffectSource can be part of the \l sourceItem of another.
+ If there is a loop in this chain, a ShaderEffectSource could end up trying
+ to render into the same texture it is using as source, which is not allowed
+ by OpenGL. When this property is set to true, an extra texture is allocated
+ so that ShaderEffectSource can keep a copy of the texture from the previous
+ frame. It can then render into one texture and use the texture from the
+ previous frame as source.
+
+ Setting both this property and \l live to true will cause the scene graph
+ to render continuously. Since the ShaderEffectSource depends on itself,
+ updating it means that it immediately becomes dirty again.
+*/
+
+bool QSGShaderEffectSource::recursive() const
+{
+ return m_recursive;
+}
+
+void QSGShaderEffectSource::setRecursive(bool enabled)
+{
+ if (enabled == m_recursive)
+ return;
+ m_recursive = enabled;
+ emit recursiveChanged();
+}
+
+/*!
+ \qmlmethod ShaderEffectSource::scheduleUpdate()
+
+ Schedules a re-rendering of the texture for the next frame.
+ Use this to update the texture when \l live is false.
+*/
+
+void QSGShaderEffectSource::scheduleUpdate()
+{
+ if (m_grab)
+ return;
+ m_grab = true;
+ update();
+}
+
+static void get_wrap_mode(QSGShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
+{
+ switch (mode) {
+ case QSGShaderEffectSource::RepeatHorizontally:
+ *hWrap = QSGTexture::Repeat;
+ *vWrap = QSGTexture::ClampToEdge;
+ break;
+ case QSGShaderEffectSource::RepeatVertically:
+ *vWrap = QSGTexture::Repeat;
+ *hWrap = QSGTexture::ClampToEdge;
+ break;
+ case QSGShaderEffectSource::Repeat:
+ *hWrap = *vWrap = QSGTexture::Repeat;
+ break;
+ default:
+ // QSGShaderEffectSource::ClampToEdge
+ *hWrap = *vWrap = QSGTexture::ClampToEdge;
+ break;
+ }
+}
+
+
+QSGTexture *QSGShaderEffectSource::texture() const
+{
+ m_texture->setMipmapFiltering(m_mipmap ? QSGTexture::Linear : QSGTexture::None);
+ m_texture->setFiltering(QSGItemPrivate::get(this)->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
+ QSGTexture::WrapMode h, v;
+ get_wrap_mode(m_wrapMode, &h, &v);
+ m_texture->setHorizontalWrapMode(h);
+ m_texture->setVerticalWrapMode(v);
+ return m_texture;
+}
+
+QSGNode *QSGShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ if (!m_sourceItem) {
+ delete oldNode;
+ return 0;
+ }
+
+ QSGShaderEffectSourceNode *node = static_cast<QSGShaderEffectSourceNode *>(oldNode);
+ if (!node) {
+ node = new QSGShaderEffectSourceNode;
+ node->setTexture(m_texture);
+ connect(m_texture, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
+ }
+
+ // If live and recursive, update continuously.
+ if (m_live && m_recursive)
+ node->markDirty(QSGNode::DirtyMaterial);
+
+ QSGShaderEffectTexture *tex = qobject_cast<QSGShaderEffectTexture *>(m_texture);
+
+ tex->setLive(m_live);
+ tex->setItem(QSGItemPrivate::get(m_sourceItem)->itemNode());
+ QRectF sourceRect = m_sourceRect.isNull()
+ ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
+ : m_sourceRect;
+ tex->setRect(sourceRect);
+ QSize textureSize = m_textureSize.isEmpty()
+ ? QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())))
+ : m_textureSize;
+ tex->setSize(textureSize);
+ tex->setRecursive(m_recursive);
+ tex->setFormat(GLenum(m_format));
+ tex->setHasMipmaps(m_mipmap);
+
+ if (m_grab)
+ tex->scheduleUpdate();
+ m_grab = false;
+
+ QSGTexture::Filtering filtering = QSGItemPrivate::get(this)->smooth
+ ? QSGTexture::Linear
+ : QSGTexture::Nearest;
+ QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
+ node->setMipmapFiltering(mmFiltering);
+ node->setFiltering(filtering);
+
+ QSGTexture::WrapMode hWrap, vWrap;
+ get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
+
+ node->setHorizontalWrapMode(hWrap);
+ node->setVerticalWrapMode(vWrap);
+ node->setTargetRect(QRectF(0, 0, width(), height()));
+ node->setSourceRect(QRectF(0, 0, 1, 1));
+ node->update();
+
+ return node;
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgshadereffectsource_p.h b/src/declarative/items/qsgshadereffectsource_p.h
new file mode 100644
index 0000000000..5842ec7295
--- /dev/null
+++ b/src/declarative/items/qsgshadereffectsource_p.h
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SHADEREFFECTSOURCE_H
+#define SHADEREFFECTSOURCE_H
+
+#include "qsgitem.h"
+#include <private/qsgtextureprovider_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgdefaultimagenode_p.h>
+
+#include "qpointer.h"
+#include "qsize.h"
+#include "qrect.h"
+
+#define QSG_DEBUG_FBO_OVERLAY
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGNode;
+class UpdatePaintNodeData;
+class QGLFramebufferObject;
+
+class QSGShaderEffectSourceNode : public QObject, public QSGDefaultImageNode
+{
+ Q_OBJECT
+
+public:
+ QSGShaderEffectSourceNode();
+
+private Q_SLOTS:
+ void markDirtyTexture();
+};
+
+class QSGShaderEffectTexture : public QSGDynamicTexture
+{
+ Q_OBJECT
+public:
+ QSGShaderEffectTexture(QSGItem *shaderSource);
+ ~QSGShaderEffectTexture();
+
+ virtual bool updateTexture();
+
+ // The item's "paint node", not effect node.
+ QSGNode *item() const { return m_item; }
+ void setItem(QSGNode *item);
+
+ QRectF rect() const { return m_rect; }
+ void setRect(const QRectF &rect);
+
+ QSize size() const { return m_size; }
+ void setSize(const QSize &size);
+
+ void setHasMipmaps(bool mipmap);
+
+ void bind();
+
+ bool hasAlphaChannel() const;
+ bool hasMipmaps() const;
+ int textureId() const;
+ QSize textureSize() const { return m_size; }
+
+ GLenum format() const { return m_format; }
+ void setFormat(GLenum format);
+
+ bool live() const { return bool(m_live); }
+ void setLive(bool live);
+
+ bool recursive() const { return bool(m_recursive); }
+ void setRecursive(bool recursive);
+
+ void scheduleUpdate();
+
+Q_SIGNALS:
+ void textureChanged();
+
+public Q_SLOTS:
+ void markDirtyTexture();
+
+private:
+ void grab();
+
+ QSGNode *m_item;
+ QRectF m_rect;
+ QSize m_size;
+ GLenum m_format;
+
+ QSGItem *m_shaderSource;
+ QSGRenderer *m_renderer;
+ QGLFramebufferObject *m_fbo;
+ QGLFramebufferObject *m_secondaryFbo;
+
+#ifdef QSG_DEBUG_FBO_OVERLAY
+ QSGRectangleNode *m_debugOverlay;
+#endif
+
+ uint m_mipmap : 1;
+ uint m_live : 1;
+ uint m_recursive : 1;
+ uint m_dirtyTexture : 1;
+ uint m_multisamplingSupportChecked : 1;
+ uint m_multisampling : 1;
+ uint m_grab : 1;
+};
+
+class QSGShaderEffectSource : public QSGItem, public QSGTextureProvider
+{
+ Q_OBJECT
+ Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
+ Q_PROPERTY(QSGItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged)
+ Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged)
+ Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged)
+ Q_PROPERTY(Format format READ format WRITE setFormat NOTIFY formatChanged)
+ Q_PROPERTY(bool live READ live WRITE setLive NOTIFY liveChanged)
+ Q_PROPERTY(bool hideSource READ hideSource WRITE setHideSource NOTIFY hideSourceChanged)
+ Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged)
+ Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged)
+ Q_INTERFACES(QSGTextureProvider)
+ Q_ENUMS(Format WrapMode)
+public:
+ enum WrapMode {
+ ClampToEdge,
+ RepeatHorizontally,
+ RepeatVertically,
+ Repeat
+ };
+
+ enum Format {
+ Alpha = GL_ALPHA,
+ RGB = GL_RGB,
+ RGBA = GL_RGBA
+ };
+
+ QSGShaderEffectSource(QSGItem *parent = 0);
+ ~QSGShaderEffectSource();
+
+ WrapMode wrapMode() const;
+ void setWrapMode(WrapMode mode);
+
+ QSGItem *sourceItem() const;
+ void setSourceItem(QSGItem *item);
+
+ QRectF sourceRect() const;
+ void setSourceRect(const QRectF &rect);
+
+ QSize textureSize() const;
+ void setTextureSize(const QSize &size);
+
+ Format format() const;
+ void setFormat(Format format);
+
+ bool live() const;
+ void setLive(bool live);
+
+ bool hideSource() const;
+ void setHideSource(bool hide);
+
+ bool mipmap() const;
+ void setMipmap(bool enabled);
+
+ bool recursive() const;
+ void setRecursive(bool enabled);
+
+ QSGTexture *texture() const;
+ const char *textureChangedSignal() const { return SIGNAL(textureChanged()); }
+
+ Q_INVOKABLE void scheduleUpdate();
+
+Q_SIGNALS:
+ void wrapModeChanged();
+ void sourceItemChanged();
+ void sourceRectChanged();
+ void textureSizeChanged();
+ void formatChanged();
+ void liveChanged();
+ void hideSourceChanged();
+ void mipmapChanged();
+ void recursiveChanged();
+
+ void textureChanged();
+
+protected:
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private:
+ QSGTexture *m_texture;
+ WrapMode m_wrapMode;
+ QPointer<QSGItem> m_sourceItem;
+ QRectF m_sourceRect;
+ QSize m_textureSize;
+ Format m_format;
+ uint m_live : 1;
+ uint m_hideSource : 1;
+ uint m_mipmap : 1;
+ uint m_recursive : 1;
+ uint m_grab : 1;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // SHADEREFFECTSOURCE_H
diff --git a/src/declarative/items/qsgstateoperations.cpp b/src/declarative/items/qsgstateoperations.cpp
new file mode 100644
index 0000000000..5390440e39
--- /dev/null
+++ b/src/declarative/items/qsgstateoperations.cpp
@@ -0,0 +1,1347 @@
+// Commit: 726a8b16c52fe4608c89d740b47361a2b073ce01
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgstateoperations_p.h"
+#include "qsgitem_p.h"
+
+#include <private/qdeclarativestate_p_p.h>
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGParentChangePrivate : public QDeclarativeStateOperationPrivate
+{
+ Q_DECLARE_PUBLIC(QSGParentChange)
+public:
+ QSGParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0),
+ rewindParent(0), rewindStackBefore(0) {}
+
+ QSGItem *target;
+ QDeclarativeGuard<QSGItem> parent;
+ QDeclarativeGuard<QSGItem> origParent;
+ QDeclarativeGuard<QSGItem> origStackBefore;
+ QSGItem *rewindParent;
+ QSGItem *rewindStackBefore;
+
+ QDeclarativeNullableValue<QDeclarativeScriptString> xString;
+ QDeclarativeNullableValue<QDeclarativeScriptString> yString;
+ QDeclarativeNullableValue<QDeclarativeScriptString> widthString;
+ QDeclarativeNullableValue<QDeclarativeScriptString> heightString;
+ QDeclarativeNullableValue<QDeclarativeScriptString> scaleString;
+ QDeclarativeNullableValue<QDeclarativeScriptString> rotationString;
+
+ void doChange(QSGItem *targetParent, QSGItem *stackBefore = 0);
+};
+
+void QSGParentChangePrivate::doChange(QSGItem *targetParent, QSGItem *stackBefore)
+{
+ if (targetParent && target && target->parentItem()) {
+ Q_Q(QSGParentChange);
+ bool ok;
+ const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok);
+ if (transform.type() >= QTransform::TxShear || !ok) {
+ qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under complex transform");
+ ok = false;
+ }
+
+ qreal scale = 1;
+ qreal rotation = 0;
+ bool isRotate = (transform.type() == QTransform::TxRotate) || (transform.m11() < 0);
+ if (ok && !isRotate) {
+ if (transform.m11() == transform.m22())
+ scale = transform.m11();
+ else {
+ qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under non-uniform scale");
+ ok = false;
+ }
+ } else if (ok && isRotate) {
+ if (transform.m11() == transform.m22())
+ scale = qSqrt(transform.m11()*transform.m11() + transform.m12()*transform.m12());
+ else {
+ qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under non-uniform scale");
+ ok = false;
+ }
+
+ if (scale != 0)
+ rotation = atan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI;
+ else {
+ qmlInfo(q) << QSGParentChange::tr("Unable to preserve appearance under scale of 0");
+ ok = false;
+ }
+ }
+
+ const QPointF &point = transform.map(QPointF(target->x(),target->y()));
+ qreal x = point.x();
+ qreal y = point.y();
+
+ // setParentItem will update the transformOriginPoint if needed
+ target->setParentItem(targetParent);
+
+ if (ok && target->transformOrigin() != QSGItem::TopLeft) {
+ qreal tempxt = target->transformOriginPoint().x();
+ qreal tempyt = target->transformOriginPoint().y();
+ QTransform t;
+ t.translate(-tempxt, -tempyt);
+ t.rotate(rotation);
+ t.scale(scale, scale);
+ t.translate(tempxt, tempyt);
+ const QPointF &offset = t.map(QPointF(0,0));
+ x += offset.x();
+ y += offset.y();
+ }
+
+ if (ok) {
+ //qDebug() << x << y << rotation << scale;
+ target->setX(x);
+ target->setY(y);
+ target->setRotation(target->rotation() + rotation);
+ target->setScale(target->scale() * scale);
+ }
+ } else if (target) {
+ target->setParentItem(targetParent);
+ }
+
+ //restore the original stack position.
+ //### if stackBefore has also been reparented this won't work
+ if (stackBefore)
+ target->stackBefore(stackBefore);
+}
+
+QSGParentChange::QSGParentChange(QObject *parent)
+ : QDeclarativeStateOperation(*(new QSGParentChangePrivate), parent)
+{
+}
+
+QSGParentChange::~QSGParentChange()
+{
+}
+
+QDeclarativeScriptString QSGParentChange::x() const
+{
+ Q_D(const QSGParentChange);
+ return d->xString.value;
+}
+
+void QSGParentChange::setX(QDeclarativeScriptString x)
+{
+ Q_D(QSGParentChange);
+ d->xString = x;
+}
+
+bool QSGParentChange::xIsSet() const
+{
+ Q_D(const QSGParentChange);
+ return d->xString.isValid();
+}
+
+QDeclarativeScriptString QSGParentChange::y() const
+{
+ Q_D(const QSGParentChange);
+ return d->yString.value;
+}
+
+void QSGParentChange::setY(QDeclarativeScriptString y)
+{
+ Q_D(QSGParentChange);
+ d->yString = y;
+}
+
+bool QSGParentChange::yIsSet() const
+{
+ Q_D(const QSGParentChange);
+ return d->yString.isValid();
+}
+
+QDeclarativeScriptString QSGParentChange::width() const
+{
+ Q_D(const QSGParentChange);
+ return d->widthString.value;
+}
+
+void QSGParentChange::setWidth(QDeclarativeScriptString width)
+{
+ Q_D(QSGParentChange);
+ d->widthString = width;
+}
+
+bool QSGParentChange::widthIsSet() const
+{
+ Q_D(const QSGParentChange);
+ return d->widthString.isValid();
+}
+
+QDeclarativeScriptString QSGParentChange::height() const
+{
+ Q_D(const QSGParentChange);
+ return d->heightString.value;
+}
+
+void QSGParentChange::setHeight(QDeclarativeScriptString height)
+{
+ Q_D(QSGParentChange);
+ d->heightString = height;
+}
+
+bool QSGParentChange::heightIsSet() const
+{
+ Q_D(const QSGParentChange);
+ return d->heightString.isValid();
+}
+
+QDeclarativeScriptString QSGParentChange::scale() const
+{
+ Q_D(const QSGParentChange);
+ return d->scaleString.value;
+}
+
+void QSGParentChange::setScale(QDeclarativeScriptString scale)
+{
+ Q_D(QSGParentChange);
+ d->scaleString = scale;
+}
+
+bool QSGParentChange::scaleIsSet() const
+{
+ Q_D(const QSGParentChange);
+ return d->scaleString.isValid();
+}
+
+QDeclarativeScriptString QSGParentChange::rotation() const
+{
+ Q_D(const QSGParentChange);
+ return d->rotationString.value;
+}
+
+void QSGParentChange::setRotation(QDeclarativeScriptString rotation)
+{
+ Q_D(QSGParentChange);
+ d->rotationString = rotation;
+}
+
+bool QSGParentChange::rotationIsSet() const
+{
+ Q_D(const QSGParentChange);
+ return d->rotationString.isValid();
+}
+
+QSGItem *QSGParentChange::originalParent() const
+{
+ Q_D(const QSGParentChange);
+ return d->origParent;
+}
+
+QSGItem *QSGParentChange::object() const
+{
+ Q_D(const QSGParentChange);
+ return d->target;
+}
+
+void QSGParentChange::setObject(QSGItem *target)
+{
+ Q_D(QSGParentChange);
+ d->target = target;
+}
+
+QSGItem *QSGParentChange::parent() const
+{
+ Q_D(const QSGParentChange);
+ return d->parent;
+}
+
+void QSGParentChange::setParent(QSGItem *parent)
+{
+ Q_D(QSGParentChange);
+ d->parent = parent;
+}
+
+QDeclarativeStateOperation::ActionList QSGParentChange::actions()
+{
+ Q_D(QSGParentChange);
+ if (!d->target || !d->parent)
+ return ActionList();
+
+ ActionList actions;
+
+ QDeclarativeAction a;
+ a.event = this;
+ actions << a;
+
+ if (d->xString.isValid()) {
+ bool ok = false;
+ QString script = d->xString.value.script();
+ qreal x = script.toFloat(&ok);
+ if (ok) {
+ QDeclarativeAction xa(d->target, QLatin1String("x"), x);
+ actions << xa;
+ } else {
+ QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this));
+ newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("x")));
+ QDeclarativeAction xa;
+ xa.property = newBinding->property();
+ xa.toBinding = newBinding;
+ xa.fromValue = xa.property.read();
+ xa.deletableToBinding = true;
+ actions << xa;
+ }
+ }
+
+ if (d->yString.isValid()) {
+ bool ok = false;
+ QString script = d->yString.value.script();
+ qreal y = script.toFloat(&ok);
+ if (ok) {
+ QDeclarativeAction ya(d->target, QLatin1String("y"), y);
+ actions << ya;
+ } else {
+ QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this));
+ newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("y")));
+ QDeclarativeAction ya;
+ ya.property = newBinding->property();
+ ya.toBinding = newBinding;
+ ya.fromValue = ya.property.read();
+ ya.deletableToBinding = true;
+ actions << ya;
+ }
+ }
+
+ if (d->scaleString.isValid()) {
+ bool ok = false;
+ QString script = d->scaleString.value.script();
+ qreal scale = script.toFloat(&ok);
+ if (ok) {
+ QDeclarativeAction sa(d->target, QLatin1String("scale"), scale);
+ actions << sa;
+ } else {
+ QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this));
+ newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("scale")));
+ QDeclarativeAction sa;
+ sa.property = newBinding->property();
+ sa.toBinding = newBinding;
+ sa.fromValue = sa.property.read();
+ sa.deletableToBinding = true;
+ actions << sa;
+ }
+ }
+
+ if (d->rotationString.isValid()) {
+ bool ok = false;
+ QString script = d->rotationString.value.script();
+ qreal rotation = script.toFloat(&ok);
+ if (ok) {
+ QDeclarativeAction ra(d->target, QLatin1String("rotation"), rotation);
+ actions << ra;
+ } else {
+ QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this));
+ newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("rotation")));
+ QDeclarativeAction ra;
+ ra.property = newBinding->property();
+ ra.toBinding = newBinding;
+ ra.fromValue = ra.property.read();
+ ra.deletableToBinding = true;
+ actions << ra;
+ }
+ }
+
+ if (d->widthString.isValid()) {
+ bool ok = false;
+ QString script = d->widthString.value.script();
+ qreal width = script.toFloat(&ok);
+ if (ok) {
+ QDeclarativeAction wa(d->target, QLatin1String("width"), width);
+ actions << wa;
+ } else {
+ QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this));
+ newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("width")));
+ QDeclarativeAction wa;
+ wa.property = newBinding->property();
+ wa.toBinding = newBinding;
+ wa.fromValue = wa.property.read();
+ wa.deletableToBinding = true;
+ actions << wa;
+ }
+ }
+
+ if (d->heightString.isValid()) {
+ bool ok = false;
+ QString script = d->heightString.value.script();
+ qreal height = script.toFloat(&ok);
+ if (ok) {
+ QDeclarativeAction ha(d->target, QLatin1String("height"), height);
+ actions << ha;
+ } else {
+ QDeclarativeBinding *newBinding = new QDeclarativeBinding(script, d->target, qmlContext(this));
+ newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("height")));
+ QDeclarativeAction ha;
+ ha.property = newBinding->property();
+ ha.toBinding = newBinding;
+ ha.fromValue = ha.property.read();
+ ha.deletableToBinding = true;
+ actions << ha;
+ }
+ }
+
+ return actions;
+}
+
+void QSGParentChange::saveOriginals()
+{
+ Q_D(QSGParentChange);
+ saveCurrentValues();
+ d->origParent = d->rewindParent;
+ d->origStackBefore = d->rewindStackBefore;
+}
+
+/*void QSGParentChange::copyOriginals(QDeclarativeActionEvent *other)
+{
+ Q_D(QSGParentChange);
+ QSGParentChange *pc = static_cast<QSGParentChange*>(other);
+
+ d->origParent = pc->d_func()->rewindParent;
+ d->origStackBefore = pc->d_func()->rewindStackBefore;
+
+ saveCurrentValues();
+}*/
+
+void QSGParentChange::execute(Reason)
+{
+ Q_D(QSGParentChange);
+ d->doChange(d->parent);
+}
+
+bool QSGParentChange::isReversable()
+{
+ return true;
+}
+
+void QSGParentChange::reverse(Reason)
+{
+ Q_D(QSGParentChange);
+ d->doChange(d->origParent, d->origStackBefore);
+}
+
+QString QSGParentChange::typeName() const
+{
+ return QLatin1String("ParentChange");
+}
+
+bool QSGParentChange::override(QDeclarativeActionEvent*other)
+{
+ Q_D(QSGParentChange);
+ if (other->typeName() != QLatin1String("ParentChange"))
+ return false;
+ if (QSGParentChange *otherPC = static_cast<QSGParentChange*>(other))
+ return (d->target == otherPC->object());
+ return false;
+}
+
+void QSGParentChange::saveCurrentValues()
+{
+ Q_D(QSGParentChange);
+ if (!d->target) {
+ d->rewindParent = 0;
+ d->rewindStackBefore = 0;
+ return;
+ }
+
+ d->rewindParent = d->target->parentItem();
+ d->rewindStackBefore = 0;
+
+ if (!d->rewindParent)
+ return;
+
+ QList<QSGItem *> children = d->rewindParent->childItems();
+ for (int ii = 0; ii < children.count() - 1; ++ii) {
+ if (children.at(ii) == d->target) {
+ d->rewindStackBefore = children.at(ii + 1);
+ break;
+ }
+ }
+}
+
+void QSGParentChange::rewind()
+{
+ Q_D(QSGParentChange);
+ d->doChange(d->rewindParent, d->rewindStackBefore);
+}
+
+class QSGAnchorSetPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGAnchorSet)
+public:
+ QSGAnchorSetPrivate()
+ : usedAnchors(0), resetAnchors(0), fill(0),
+ centerIn(0)/*, leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0),
+ margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)*/
+ {
+ }
+
+ QSGAnchors::Anchors usedAnchors;
+ QSGAnchors::Anchors resetAnchors;
+
+ QSGItem *fill;
+ QSGItem *centerIn;
+
+ QDeclarativeScriptString leftScript;
+ QDeclarativeScriptString rightScript;
+ QDeclarativeScriptString topScript;
+ QDeclarativeScriptString bottomScript;
+ QDeclarativeScriptString hCenterScript;
+ QDeclarativeScriptString vCenterScript;
+ QDeclarativeScriptString baselineScript;
+
+ /*qreal leftMargin;
+ qreal rightMargin;
+ qreal topMargin;
+ qreal bottomMargin;
+ qreal margins;
+ qreal vCenterOffset;
+ qreal hCenterOffset;
+ qreal baselineOffset;*/
+};
+
+QSGAnchorSet::QSGAnchorSet(QObject *parent)
+ : QObject(*new QSGAnchorSetPrivate, parent)
+{
+}
+
+QSGAnchorSet::~QSGAnchorSet()
+{
+}
+
+QDeclarativeScriptString QSGAnchorSet::top() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->topScript;
+}
+
+void QSGAnchorSet::setTop(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::TopAnchor;
+ d->topScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetTop();
+}
+
+void QSGAnchorSet::resetTop()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::TopAnchor;
+ d->topScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::TopAnchor;
+}
+
+QDeclarativeScriptString QSGAnchorSet::bottom() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->bottomScript;
+}
+
+void QSGAnchorSet::setBottom(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::BottomAnchor;
+ d->bottomScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetBottom();
+}
+
+void QSGAnchorSet::resetBottom()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::BottomAnchor;
+ d->bottomScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::BottomAnchor;
+}
+
+QDeclarativeScriptString QSGAnchorSet::verticalCenter() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->vCenterScript;
+}
+
+void QSGAnchorSet::setVerticalCenter(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::VCenterAnchor;
+ d->vCenterScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetVerticalCenter();
+}
+
+void QSGAnchorSet::resetVerticalCenter()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::VCenterAnchor;
+ d->vCenterScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::VCenterAnchor;
+}
+
+QDeclarativeScriptString QSGAnchorSet::baseline() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->baselineScript;
+}
+
+void QSGAnchorSet::setBaseline(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::BaselineAnchor;
+ d->baselineScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetBaseline();
+}
+
+void QSGAnchorSet::resetBaseline()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::BaselineAnchor;
+ d->baselineScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::BaselineAnchor;
+}
+
+QDeclarativeScriptString QSGAnchorSet::left() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->leftScript;
+}
+
+void QSGAnchorSet::setLeft(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::LeftAnchor;
+ d->leftScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetLeft();
+}
+
+void QSGAnchorSet::resetLeft()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::LeftAnchor;
+ d->leftScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::LeftAnchor;
+}
+
+QDeclarativeScriptString QSGAnchorSet::right() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->rightScript;
+}
+
+void QSGAnchorSet::setRight(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::RightAnchor;
+ d->rightScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetRight();
+}
+
+void QSGAnchorSet::resetRight()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::RightAnchor;
+ d->rightScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::RightAnchor;
+}
+
+QDeclarativeScriptString QSGAnchorSet::horizontalCenter() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->hCenterScript;
+}
+
+void QSGAnchorSet::setHorizontalCenter(const QDeclarativeScriptString &edge)
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors |= QSGAnchors::HCenterAnchor;
+ d->hCenterScript = edge;
+ if (edge.script() == QLatin1String("undefined"))
+ resetHorizontalCenter();
+}
+
+void QSGAnchorSet::resetHorizontalCenter()
+{
+ Q_D(QSGAnchorSet);
+ d->usedAnchors &= ~QSGAnchors::HCenterAnchor;
+ d->hCenterScript = QDeclarativeScriptString();
+ d->resetAnchors |= QSGAnchors::HCenterAnchor;
+}
+
+QSGItem *QSGAnchorSet::fill() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->fill;
+}
+
+void QSGAnchorSet::setFill(QSGItem *f)
+{
+ Q_D(QSGAnchorSet);
+ d->fill = f;
+}
+
+void QSGAnchorSet::resetFill()
+{
+ setFill(0);
+}
+
+QSGItem *QSGAnchorSet::centerIn() const
+{
+ Q_D(const QSGAnchorSet);
+ return d->centerIn;
+}
+
+void QSGAnchorSet::setCenterIn(QSGItem* c)
+{
+ Q_D(QSGAnchorSet);
+ d->centerIn = c;
+}
+
+void QSGAnchorSet::resetCenterIn()
+{
+ setCenterIn(0);
+}
+
+
+class QSGAnchorChangesPrivate : public QDeclarativeStateOperationPrivate
+{
+public:
+ QSGAnchorChangesPrivate()
+ : target(0), anchorSet(new QSGAnchorSet),
+ leftBinding(0), rightBinding(0), hCenterBinding(0),
+ topBinding(0), bottomBinding(0), vCenterBinding(0), baselineBinding(0),
+ origLeftBinding(0), origRightBinding(0), origHCenterBinding(0),
+ origTopBinding(0), origBottomBinding(0), origVCenterBinding(0),
+ origBaselineBinding(0)
+ {
+
+ }
+ ~QSGAnchorChangesPrivate() { delete anchorSet; }
+
+ QSGItem *target;
+ QSGAnchorSet *anchorSet;
+
+ QDeclarativeBinding *leftBinding;
+ QDeclarativeBinding *rightBinding;
+ QDeclarativeBinding *hCenterBinding;
+ QDeclarativeBinding *topBinding;
+ QDeclarativeBinding *bottomBinding;
+ QDeclarativeBinding *vCenterBinding;
+ QDeclarativeBinding *baselineBinding;
+
+ QDeclarativeAbstractBinding *origLeftBinding;
+ QDeclarativeAbstractBinding *origRightBinding;
+ QDeclarativeAbstractBinding *origHCenterBinding;
+ QDeclarativeAbstractBinding *origTopBinding;
+ QDeclarativeAbstractBinding *origBottomBinding;
+ QDeclarativeAbstractBinding *origVCenterBinding;
+ QDeclarativeAbstractBinding *origBaselineBinding;
+
+ QSGAnchorLine rewindLeft;
+ QSGAnchorLine rewindRight;
+ QSGAnchorLine rewindHCenter;
+ QSGAnchorLine rewindTop;
+ QSGAnchorLine rewindBottom;
+ QSGAnchorLine rewindVCenter;
+ QSGAnchorLine rewindBaseline;
+
+ qreal fromX;
+ qreal fromY;
+ qreal fromWidth;
+ qreal fromHeight;
+
+ qreal toX;
+ qreal toY;
+ qreal toWidth;
+ qreal toHeight;
+
+ qreal rewindX;
+ qreal rewindY;
+ qreal rewindWidth;
+ qreal rewindHeight;
+
+ bool applyOrigLeft;
+ bool applyOrigRight;
+ bool applyOrigHCenter;
+ bool applyOrigTop;
+ bool applyOrigBottom;
+ bool applyOrigVCenter;
+ bool applyOrigBaseline;
+
+ QDeclarativeNullableValue<qreal> origWidth;
+ QDeclarativeNullableValue<qreal> origHeight;
+ qreal origX;
+ qreal origY;
+
+ QList<QDeclarativeAbstractBinding*> oldBindings;
+
+ QDeclarativeProperty leftProp;
+ QDeclarativeProperty rightProp;
+ QDeclarativeProperty hCenterProp;
+ QDeclarativeProperty topProp;
+ QDeclarativeProperty bottomProp;
+ QDeclarativeProperty vCenterProp;
+ QDeclarativeProperty baselineProp;
+};
+
+QSGAnchorChanges::QSGAnchorChanges(QObject *parent)
+ : QDeclarativeStateOperation(*(new QSGAnchorChangesPrivate), parent)
+{
+}
+
+QSGAnchorChanges::~QSGAnchorChanges()
+{
+}
+
+QSGAnchorChanges::ActionList QSGAnchorChanges::actions()
+{
+ Q_D(QSGAnchorChanges);
+ d->leftBinding = d->rightBinding = d->hCenterBinding = d->topBinding
+ = d->bottomBinding = d->vCenterBinding = d->baselineBinding = 0;
+
+ d->leftProp = QDeclarativeProperty(d->target, QLatin1String("anchors.left"));
+ d->rightProp = QDeclarativeProperty(d->target, QLatin1String("anchors.right"));
+ d->hCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.horizontalCenter"));
+ d->topProp = QDeclarativeProperty(d->target, QLatin1String("anchors.top"));
+ d->bottomProp = QDeclarativeProperty(d->target, QLatin1String("anchors.bottom"));
+ d->vCenterProp = QDeclarativeProperty(d->target, QLatin1String("anchors.verticalCenter"));
+ d->baselineProp = QDeclarativeProperty(d->target, QLatin1String("anchors.baseline"));
+
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::LeftAnchor) {
+ d->leftBinding = new QDeclarativeBinding(d->anchorSet->d_func()->leftScript.script(), d->target, qmlContext(this));
+ d->leftBinding->setTarget(d->leftProp);
+ }
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::RightAnchor) {
+ d->rightBinding = new QDeclarativeBinding(d->anchorSet->d_func()->rightScript.script(), d->target, qmlContext(this));
+ d->rightBinding->setTarget(d->rightProp);
+ }
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::HCenterAnchor) {
+ d->hCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->hCenterScript.script(), d->target, qmlContext(this));
+ d->hCenterBinding->setTarget(d->hCenterProp);
+ }
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::TopAnchor) {
+ d->topBinding = new QDeclarativeBinding(d->anchorSet->d_func()->topScript.script(), d->target, qmlContext(this));
+ d->topBinding->setTarget(d->topProp);
+ }
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::BottomAnchor) {
+ d->bottomBinding = new QDeclarativeBinding(d->anchorSet->d_func()->bottomScript.script(), d->target, qmlContext(this));
+ d->bottomBinding->setTarget(d->bottomProp);
+ }
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::VCenterAnchor) {
+ d->vCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->vCenterScript.script(), d->target, qmlContext(this));
+ d->vCenterBinding->setTarget(d->vCenterProp);
+ }
+ if (d->anchorSet->d_func()->usedAnchors & QSGAnchors::BaselineAnchor) {
+ d->baselineBinding = new QDeclarativeBinding(d->anchorSet->d_func()->baselineScript.script(), d->target, qmlContext(this));
+ d->baselineBinding->setTarget(d->baselineProp);
+ }
+
+ QDeclarativeAction a;
+ a.event = this;
+ return ActionList() << a;
+}
+
+QSGAnchorSet *QSGAnchorChanges::anchors()
+{
+ Q_D(QSGAnchorChanges);
+ return d->anchorSet;
+}
+
+QSGItem *QSGAnchorChanges::object() const
+{
+ Q_D(const QSGAnchorChanges);
+ return d->target;
+}
+
+void QSGAnchorChanges::setObject(QSGItem *target)
+{
+ Q_D(QSGAnchorChanges);
+ d->target = target;
+}
+
+void QSGAnchorChanges::execute(Reason reason)
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target);
+ //incorporate any needed "reverts"
+ if (d->applyOrigLeft) {
+ if (!d->origLeftBinding)
+ targetPrivate->anchors()->resetLeft();
+ QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding);
+ }
+ if (d->applyOrigRight) {
+ if (!d->origRightBinding)
+ targetPrivate->anchors()->resetRight();
+ QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding);
+ }
+ if (d->applyOrigHCenter) {
+ if (!d->origHCenterBinding)
+ targetPrivate->anchors()->resetHorizontalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding);
+ }
+ if (d->applyOrigTop) {
+ if (!d->origTopBinding)
+ targetPrivate->anchors()->resetTop();
+ QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding);
+ }
+ if (d->applyOrigBottom) {
+ if (!d->origBottomBinding)
+ targetPrivate->anchors()->resetBottom();
+ QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding);
+ }
+ if (d->applyOrigVCenter) {
+ if (!d->origVCenterBinding)
+ targetPrivate->anchors()->resetVerticalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding);
+ }
+ if (d->applyOrigBaseline) {
+ if (!d->origBaselineBinding)
+ targetPrivate->anchors()->resetBaseline();
+ QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding);
+ }
+
+ //destroy old bindings
+ if (reason == ActualChange) {
+ for (int i = 0; i < d->oldBindings.size(); ++i) {
+ QDeclarativeAbstractBinding *binding = d->oldBindings.at(i);
+ if (binding)
+ binding->destroy();
+ }
+ d->oldBindings.clear();
+ }
+
+ //reset any anchors that have been specified as "undefined"
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::LeftAnchor) {
+ targetPrivate->anchors()->resetLeft();
+ QDeclarativePropertyPrivate::setBinding(d->leftProp, 0);
+ }
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::RightAnchor) {
+ targetPrivate->anchors()->resetRight();
+ QDeclarativePropertyPrivate::setBinding(d->rightProp, 0);
+ }
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::HCenterAnchor) {
+ targetPrivate->anchors()->resetHorizontalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0);
+ }
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::TopAnchor) {
+ targetPrivate->anchors()->resetTop();
+ QDeclarativePropertyPrivate::setBinding(d->topProp, 0);
+ }
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::BottomAnchor) {
+ targetPrivate->anchors()->resetBottom();
+ QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0);
+ }
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::VCenterAnchor) {
+ targetPrivate->anchors()->resetVerticalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0);
+ }
+ if (d->anchorSet->d_func()->resetAnchors & QSGAnchors::BaselineAnchor) {
+ targetPrivate->anchors()->resetBaseline();
+ QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0);
+ }
+
+ //set any anchors that have been specified
+ if (d->leftBinding)
+ QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), d->leftBinding);
+ if (d->rightBinding)
+ QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), d->rightBinding);
+ if (d->hCenterBinding)
+ QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), d->hCenterBinding);
+ if (d->topBinding)
+ QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), d->topBinding);
+ if (d->bottomBinding)
+ QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), d->bottomBinding);
+ if (d->vCenterBinding)
+ QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), d->vCenterBinding);
+ if (d->baselineBinding)
+ QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), d->baselineBinding);
+}
+
+bool QSGAnchorChanges::isReversable()
+{
+ return true;
+}
+
+void QSGAnchorChanges::reverse(Reason reason)
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target);
+ //reset any anchors set by the state
+ if (d->leftBinding) {
+ targetPrivate->anchors()->resetLeft();
+ QDeclarativePropertyPrivate::setBinding(d->leftBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->leftBinding->destroy(); d->leftBinding = 0;
+ }
+ }
+ if (d->rightBinding) {
+ targetPrivate->anchors()->resetRight();
+ QDeclarativePropertyPrivate::setBinding(d->rightBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->rightBinding->destroy(); d->rightBinding = 0;
+ }
+ }
+ if (d->hCenterBinding) {
+ targetPrivate->anchors()->resetHorizontalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->hCenterBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->hCenterBinding->destroy(); d->hCenterBinding = 0;
+ }
+ }
+ if (d->topBinding) {
+ targetPrivate->anchors()->resetTop();
+ QDeclarativePropertyPrivate::setBinding(d->topBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->topBinding->destroy(); d->topBinding = 0;
+ }
+ }
+ if (d->bottomBinding) {
+ targetPrivate->anchors()->resetBottom();
+ QDeclarativePropertyPrivate::setBinding(d->bottomBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->bottomBinding->destroy(); d->bottomBinding = 0;
+ }
+ }
+ if (d->vCenterBinding) {
+ targetPrivate->anchors()->resetVerticalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->vCenterBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->vCenterBinding->destroy(); d->vCenterBinding = 0;
+ }
+ }
+ if (d->baselineBinding) {
+ targetPrivate->anchors()->resetBaseline();
+ QDeclarativePropertyPrivate::setBinding(d->baselineBinding->property(), 0);
+ if (reason == ActualChange) {
+ d->baselineBinding->destroy(); d->baselineBinding = 0;
+ }
+ }
+
+ //restore previous anchors
+ if (d->origLeftBinding)
+ QDeclarativePropertyPrivate::setBinding(d->leftProp, d->origLeftBinding);
+ if (d->origRightBinding)
+ QDeclarativePropertyPrivate::setBinding(d->rightProp, d->origRightBinding);
+ if (d->origHCenterBinding)
+ QDeclarativePropertyPrivate::setBinding(d->hCenterProp, d->origHCenterBinding);
+ if (d->origTopBinding)
+ QDeclarativePropertyPrivate::setBinding(d->topProp, d->origTopBinding);
+ if (d->origBottomBinding)
+ QDeclarativePropertyPrivate::setBinding(d->bottomProp, d->origBottomBinding);
+ if (d->origVCenterBinding)
+ QDeclarativePropertyPrivate::setBinding(d->vCenterProp, d->origVCenterBinding);
+ if (d->origBaselineBinding)
+ QDeclarativePropertyPrivate::setBinding(d->baselineProp, d->origBaselineBinding);
+
+ //restore any absolute geometry changed by the state's anchors
+ QSGAnchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QSGAnchors::Vertical_Mask;
+ QSGAnchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QSGAnchors::Vertical_Mask;
+ QSGAnchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QSGAnchors::Horizontal_Mask;
+ QSGAnchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QSGAnchors::Horizontal_Mask;
+
+ bool stateSetWidth = (stateHAnchors &&
+ stateHAnchors != QSGAnchors::LeftAnchor &&
+ stateHAnchors != QSGAnchors::RightAnchor &&
+ stateHAnchors != QSGAnchors::HCenterAnchor);
+ bool origSetWidth = (origHAnchors &&
+ origHAnchors != QSGAnchors::LeftAnchor &&
+ origHAnchors != QSGAnchors::RightAnchor &&
+ origHAnchors != QSGAnchors::HCenterAnchor);
+ if (d->origWidth.isValid() && stateSetWidth && !origSetWidth)
+ d->target->setWidth(d->origWidth.value);
+
+ bool stateSetHeight = (stateVAnchors &&
+ stateVAnchors != QSGAnchors::TopAnchor &&
+ stateVAnchors != QSGAnchors::BottomAnchor &&
+ stateVAnchors != QSGAnchors::VCenterAnchor &&
+ stateVAnchors != QSGAnchors::BaselineAnchor);
+ bool origSetHeight = (origVAnchors &&
+ origVAnchors != QSGAnchors::TopAnchor &&
+ origVAnchors != QSGAnchors::BottomAnchor &&
+ origVAnchors != QSGAnchors::VCenterAnchor &&
+ origVAnchors != QSGAnchors::BaselineAnchor);
+ if (d->origHeight.isValid() && stateSetHeight && !origSetHeight)
+ d->target->setHeight(d->origHeight.value);
+
+ if (stateHAnchors && !origHAnchors)
+ d->target->setX(d->origX);
+
+ if (stateVAnchors && !origVAnchors)
+ d->target->setY(d->origY);
+}
+
+QString QSGAnchorChanges::typeName() const
+{
+ return QLatin1String("AnchorChanges");
+}
+
+QList<QDeclarativeAction> QSGAnchorChanges::additionalActions()
+{
+ Q_D(QSGAnchorChanges);
+ QList<QDeclarativeAction> extra;
+
+ QSGAnchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors;
+ bool hChange = combined & QSGAnchors::Horizontal_Mask;
+ bool vChange = combined & QSGAnchors::Vertical_Mask;
+
+ if (d->target) {
+ QDeclarativeAction a;
+ if (hChange && d->fromX != d->toX) {
+ a.property = QDeclarativeProperty(d->target, QLatin1String("x"));
+ a.toValue = d->toX;
+ extra << a;
+ }
+ if (vChange && d->fromY != d->toY) {
+ a.property = QDeclarativeProperty(d->target, QLatin1String("y"));
+ a.toValue = d->toY;
+ extra << a;
+ }
+ if (hChange && d->fromWidth != d->toWidth) {
+ a.property = QDeclarativeProperty(d->target, QLatin1String("width"));
+ a.toValue = d->toWidth;
+ extra << a;
+ }
+ if (vChange && d->fromHeight != d->toHeight) {
+ a.property = QDeclarativeProperty(d->target, QLatin1String("height"));
+ a.toValue = d->toHeight;
+ extra << a;
+ }
+ }
+
+ return extra;
+}
+
+bool QSGAnchorChanges::changesBindings()
+{
+ return true;
+}
+
+void QSGAnchorChanges::saveOriginals()
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ d->origLeftBinding = QDeclarativePropertyPrivate::binding(d->leftProp);
+ d->origRightBinding = QDeclarativePropertyPrivate::binding(d->rightProp);
+ d->origHCenterBinding = QDeclarativePropertyPrivate::binding(d->hCenterProp);
+ d->origTopBinding = QDeclarativePropertyPrivate::binding(d->topProp);
+ d->origBottomBinding = QDeclarativePropertyPrivate::binding(d->bottomProp);
+ d->origVCenterBinding = QDeclarativePropertyPrivate::binding(d->vCenterProp);
+ d->origBaselineBinding = QDeclarativePropertyPrivate::binding(d->baselineProp);
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target);
+ if (targetPrivate->widthValid)
+ d->origWidth = d->target->width();
+ if (targetPrivate->heightValid)
+ d->origHeight = d->target->height();
+ d->origX = d->target->x();
+ d->origY = d->target->y();
+
+ d->applyOrigLeft = d->applyOrigRight = d->applyOrigHCenter = d->applyOrigTop
+ = d->applyOrigBottom = d->applyOrigVCenter = d->applyOrigBaseline = false;
+
+ saveCurrentValues();
+}
+
+void QSGAnchorChanges::copyOriginals(QDeclarativeActionEvent *other)
+{
+ Q_D(QSGAnchorChanges);
+ QSGAnchorChanges *ac = static_cast<QSGAnchorChanges*>(other);
+ QSGAnchorChangesPrivate *acp = ac->d_func();
+
+ QSGAnchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors |
+ acp->anchorSet->d_func()->resetAnchors;
+
+ //probably also need to revert some things
+ d->applyOrigLeft = (combined & QSGAnchors::LeftAnchor);
+ d->applyOrigRight = (combined & QSGAnchors::RightAnchor);
+ d->applyOrigHCenter = (combined & QSGAnchors::HCenterAnchor);
+ d->applyOrigTop = (combined & QSGAnchors::TopAnchor);
+ d->applyOrigBottom = (combined & QSGAnchors::BottomAnchor);
+ d->applyOrigVCenter = (combined & QSGAnchors::VCenterAnchor);
+ d->applyOrigBaseline = (combined & QSGAnchors::BaselineAnchor);
+
+ d->origLeftBinding = acp->origLeftBinding;
+ d->origRightBinding = acp->origRightBinding;
+ d->origHCenterBinding = acp->origHCenterBinding;
+ d->origTopBinding = acp->origTopBinding;
+ d->origBottomBinding = acp->origBottomBinding;
+ d->origVCenterBinding = acp->origVCenterBinding;
+ d->origBaselineBinding = acp->origBaselineBinding;
+
+ d->origWidth = acp->origWidth;
+ d->origHeight = acp->origHeight;
+ d->origX = acp->origX;
+ d->origY = acp->origY;
+
+ d->oldBindings.clear();
+ d->oldBindings << acp->leftBinding << acp->rightBinding << acp->hCenterBinding
+ << acp->topBinding << acp->bottomBinding << acp->baselineBinding;
+
+ saveCurrentValues();
+}
+
+void QSGAnchorChanges::clearBindings()
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ //### should this (saving "from" values) be moved to saveCurrentValues()?
+ d->fromX = d->target->x();
+ d->fromY = d->target->y();
+ d->fromWidth = d->target->width();
+ d->fromHeight = d->target->height();
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target);
+ //reset any anchors with corresponding reverts
+ //reset any anchors that have been specified as "undefined"
+ //reset any anchors that we'll be setting in the state
+ QSGAnchors::Anchors combined = d->anchorSet->d_func()->resetAnchors |
+ d->anchorSet->d_func()->usedAnchors;
+ if (d->applyOrigLeft || (combined & QSGAnchors::LeftAnchor)) {
+ targetPrivate->anchors()->resetLeft();
+ QDeclarativePropertyPrivate::setBinding(d->leftProp, 0);
+ }
+ if (d->applyOrigRight || (combined & QSGAnchors::RightAnchor)) {
+ targetPrivate->anchors()->resetRight();
+ QDeclarativePropertyPrivate::setBinding(d->rightProp, 0);
+ }
+ if (d->applyOrigHCenter || (combined & QSGAnchors::HCenterAnchor)) {
+ targetPrivate->anchors()->resetHorizontalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0);
+ }
+ if (d->applyOrigTop || (combined & QSGAnchors::TopAnchor)) {
+ targetPrivate->anchors()->resetTop();
+ QDeclarativePropertyPrivate::setBinding(d->topProp, 0);
+ }
+ if (d->applyOrigBottom || (combined & QSGAnchors::BottomAnchor)) {
+ targetPrivate->anchors()->resetBottom();
+ QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0);
+ }
+ if (d->applyOrigVCenter || (combined & QSGAnchors::VCenterAnchor)) {
+ targetPrivate->anchors()->resetVerticalCenter();
+ QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0);
+ }
+ if (d->applyOrigBaseline || (combined & QSGAnchors::BaselineAnchor)) {
+ targetPrivate->anchors()->resetBaseline();
+ QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0);
+ }
+}
+
+bool QSGAnchorChanges::override(QDeclarativeActionEvent*other)
+{
+ if (other->typeName() != QLatin1String("AnchorChanges"))
+ return false;
+ if (static_cast<QDeclarativeActionEvent*>(this) == other)
+ return true;
+ if (static_cast<QSGAnchorChanges*>(other)->object() == object())
+ return true;
+ return false;
+}
+
+void QSGAnchorChanges::rewind()
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target);
+
+ //restore previous values (but not previous bindings, i.e. anchors)
+ d->target->setX(d->rewindX);
+ d->target->setY(d->rewindY);
+ if (targetPrivate->widthValid) {
+ d->target->setWidth(d->rewindWidth);
+ }
+ if (targetPrivate->heightValid) {
+ d->target->setHeight(d->rewindHeight);
+ }
+}
+
+void QSGAnchorChanges::saveCurrentValues()
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ QSGItemPrivate *targetPrivate = QSGItemPrivate::get(d->target);
+ d->rewindLeft = targetPrivate->anchors()->left();
+ d->rewindRight = targetPrivate->anchors()->right();
+ d->rewindHCenter = targetPrivate->anchors()->horizontalCenter();
+ d->rewindTop = targetPrivate->anchors()->top();
+ d->rewindBottom = targetPrivate->anchors()->bottom();
+ d->rewindVCenter = targetPrivate->anchors()->verticalCenter();
+ d->rewindBaseline = targetPrivate->anchors()->baseline();
+
+ d->rewindX = d->target->x();
+ d->rewindY = d->target->y();
+ d->rewindWidth = d->target->width();
+ d->rewindHeight = d->target->height();
+}
+
+void QSGAnchorChanges::saveTargetValues()
+{
+ Q_D(QSGAnchorChanges);
+ if (!d->target)
+ return;
+
+ d->toX = d->target->x();
+ d->toY = d->target->y();
+ d->toWidth = d->target->width();
+ d->toHeight = d->target->height();
+}
+
+#include <moc_qsgstateoperations_p.cpp>
+
+QT_END_NAMESPACE
+
diff --git a/src/declarative/items/qsgstateoperations_p.h b/src/declarative/items/qsgstateoperations_p.h
new file mode 100644
index 0000000000..f816e36b82
--- /dev/null
+++ b/src/declarative/items/qsgstateoperations_p.h
@@ -0,0 +1,275 @@
+// Commit: 84c47bbb133304d7ef35642fa1fbb17619d4a43d
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGSTATEOPERATIONS_H
+#define QSGSTATEOPERATIONS_H
+
+#include "qsgitem.h"
+#include "qsganchors_p.h"
+
+#include <private/qdeclarativestate_p.h>
+
+#include <QtDeclarative/qdeclarativescriptstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGParentChangePrivate;
+class Q_AUTOTEST_EXPORT QSGParentChange : public QDeclarativeStateOperation, public QDeclarativeActionEvent
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGParentChange)
+
+ Q_PROPERTY(QSGItem *target READ object WRITE setObject)
+ Q_PROPERTY(QSGItem *parent READ parent WRITE setParent)
+ Q_PROPERTY(QDeclarativeScriptString x READ x WRITE setX)
+ Q_PROPERTY(QDeclarativeScriptString y READ y WRITE setY)
+ Q_PROPERTY(QDeclarativeScriptString width READ width WRITE setWidth)
+ Q_PROPERTY(QDeclarativeScriptString height READ height WRITE setHeight)
+ Q_PROPERTY(QDeclarativeScriptString scale READ scale WRITE setScale)
+ Q_PROPERTY(QDeclarativeScriptString rotation READ rotation WRITE setRotation)
+public:
+ QSGParentChange(QObject *parent=0);
+ ~QSGParentChange();
+
+ QSGItem *object() const;
+ void setObject(QSGItem *);
+
+ QSGItem *parent() const;
+ void setParent(QSGItem *);
+
+ QSGItem *originalParent() const;
+
+ QDeclarativeScriptString x() const;
+ void setX(QDeclarativeScriptString x);
+ bool xIsSet() const;
+
+ QDeclarativeScriptString y() const;
+ void setY(QDeclarativeScriptString y);
+ bool yIsSet() const;
+
+ QDeclarativeScriptString width() const;
+ void setWidth(QDeclarativeScriptString width);
+ bool widthIsSet() const;
+
+ QDeclarativeScriptString height() const;
+ void setHeight(QDeclarativeScriptString height);
+ bool heightIsSet() const;
+
+ QDeclarativeScriptString scale() const;
+ void setScale(QDeclarativeScriptString scale);
+ bool scaleIsSet() const;
+
+ QDeclarativeScriptString rotation() const;
+ void setRotation(QDeclarativeScriptString rotation);
+ bool rotationIsSet() const;
+
+ virtual ActionList actions();
+
+ virtual void saveOriginals();
+ //virtual void copyOriginals(QDeclarativeActionEvent*);
+ virtual void execute(Reason reason = ActualChange);
+ virtual bool isReversable();
+ virtual void reverse(Reason reason = ActualChange);
+ virtual QString typeName() const;
+ virtual bool override(QDeclarativeActionEvent*other);
+ virtual void rewind();
+ virtual void saveCurrentValues();
+};
+
+class QSGAnchorChanges;
+class QSGAnchorSetPrivate;
+class Q_AUTOTEST_EXPORT QSGAnchorSet : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QDeclarativeScriptString left READ left WRITE setLeft RESET resetLeft)
+ Q_PROPERTY(QDeclarativeScriptString right READ right WRITE setRight RESET resetRight)
+ Q_PROPERTY(QDeclarativeScriptString horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter)
+ Q_PROPERTY(QDeclarativeScriptString top READ top WRITE setTop RESET resetTop)
+ Q_PROPERTY(QDeclarativeScriptString bottom READ bottom WRITE setBottom RESET resetBottom)
+ Q_PROPERTY(QDeclarativeScriptString verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter)
+ Q_PROPERTY(QDeclarativeScriptString baseline READ baseline WRITE setBaseline RESET resetBaseline)
+ //Q_PROPERTY(QSGItem *fill READ fill WRITE setFill RESET resetFill)
+ //Q_PROPERTY(QSGItem *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn)
+
+ /*Q_PROPERTY(qreal margins READ margins WRITE setMargins NOTIFY marginsChanged)
+ Q_PROPERTY(qreal leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
+ Q_PROPERTY(qreal rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
+ Q_PROPERTY(qreal horizontalCenterOffset READ horizontalCenterOffset WRITE setHorizontalCenterOffset NOTIFY horizontalCenterOffsetChanged())
+ Q_PROPERTY(qreal topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged)
+ Q_PROPERTY(qreal bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
+ Q_PROPERTY(qreal verticalCenterOffset READ verticalCenterOffset WRITE setVerticalCenterOffset NOTIFY verticalCenterOffsetChanged())
+ Q_PROPERTY(qreal baselineOffset READ baselineOffset WRITE setBaselineOffset NOTIFY baselineOffsetChanged())*/
+
+public:
+ QSGAnchorSet(QObject *parent=0);
+ virtual ~QSGAnchorSet();
+
+ QDeclarativeScriptString left() const;
+ void setLeft(const QDeclarativeScriptString &edge);
+ void resetLeft();
+
+ QDeclarativeScriptString right() const;
+ void setRight(const QDeclarativeScriptString &edge);
+ void resetRight();
+
+ QDeclarativeScriptString horizontalCenter() const;
+ void setHorizontalCenter(const QDeclarativeScriptString &edge);
+ void resetHorizontalCenter();
+
+ QDeclarativeScriptString top() const;
+ void setTop(const QDeclarativeScriptString &edge);
+ void resetTop();
+
+ QDeclarativeScriptString bottom() const;
+ void setBottom(const QDeclarativeScriptString &edge);
+ void resetBottom();
+
+ QDeclarativeScriptString verticalCenter() const;
+ void setVerticalCenter(const QDeclarativeScriptString &edge);
+ void resetVerticalCenter();
+
+ QDeclarativeScriptString baseline() const;
+ void setBaseline(const QDeclarativeScriptString &edge);
+ void resetBaseline();
+
+ QSGItem *fill() const;
+ void setFill(QSGItem *);
+ void resetFill();
+
+ QSGItem *centerIn() const;
+ void setCenterIn(QSGItem *);
+ void resetCenterIn();
+
+ /*qreal leftMargin() const;
+ void setLeftMargin(qreal);
+
+ qreal rightMargin() const;
+ void setRightMargin(qreal);
+
+ qreal horizontalCenterOffset() const;
+ void setHorizontalCenterOffset(qreal);
+
+ qreal topMargin() const;
+ void setTopMargin(qreal);
+
+ qreal bottomMargin() const;
+ void setBottomMargin(qreal);
+
+ qreal margins() const;
+ void setMargins(qreal);
+
+ qreal verticalCenterOffset() const;
+ void setVerticalCenterOffset(qreal);
+
+ qreal baselineOffset() const;
+ void setBaselineOffset(qreal);*/
+
+ QSGAnchors::Anchors usedAnchors() const;
+
+/*Q_SIGNALS:
+ void leftMarginChanged();
+ void rightMarginChanged();
+ void topMarginChanged();
+ void bottomMarginChanged();
+ void marginsChanged();
+ void verticalCenterOffsetChanged();
+ void horizontalCenterOffsetChanged();
+ void baselineOffsetChanged();*/
+
+private:
+ friend class QSGAnchorChanges;
+ Q_DISABLE_COPY(QSGAnchorSet)
+ Q_DECLARE_PRIVATE(QSGAnchorSet)
+};
+
+class QSGAnchorChangesPrivate;
+class Q_AUTOTEST_EXPORT QSGAnchorChanges : public QDeclarativeStateOperation, public QDeclarativeActionEvent
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGAnchorChanges)
+
+ Q_PROPERTY(QSGItem *target READ object WRITE setObject)
+ Q_PROPERTY(QSGAnchorSet *anchors READ anchors CONSTANT)
+
+public:
+ QSGAnchorChanges(QObject *parent=0);
+ ~QSGAnchorChanges();
+
+ virtual ActionList actions();
+
+ QSGAnchorSet *anchors();
+
+ QSGItem *object() const;
+ void setObject(QSGItem *);
+
+ virtual void execute(Reason reason = ActualChange);
+ virtual bool isReversable();
+ virtual void reverse(Reason reason = ActualChange);
+ virtual QString typeName() const;
+ virtual bool override(QDeclarativeActionEvent*other);
+ virtual bool changesBindings();
+ virtual void saveOriginals();
+ virtual bool needsCopy() { return true; }
+ virtual void copyOriginals(QDeclarativeActionEvent*);
+ virtual void clearBindings();
+ virtual void rewind();
+ virtual void saveCurrentValues();
+
+ QList<QDeclarativeAction> additionalActions();
+ virtual void saveTargetValues();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGParentChange)
+QML_DECLARE_TYPE(QSGAnchorSet)
+QML_DECLARE_TYPE(QSGAnchorChanges)
+
+QT_END_HEADER
+
+#endif // QSGSTATEOPERATIONS_H
+
diff --git a/src/declarative/items/qsgtext.cpp b/src/declarative/items/qsgtext.cpp
new file mode 100644
index 0000000000..e7e655d591
--- /dev/null
+++ b/src/declarative/items/qsgtext.cpp
@@ -0,0 +1,1255 @@
+// Commit: cce89db1e2555cbca8fc28072e1c6dd737cec6c4
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgtext_p.h"
+#include "qsgtext_p_p.h"
+
+#include <private/qsgdistancefieldglyphcache_p.h>
+#include <private/qsgcontext_p.h>
+#include <private/qsgadaptationlayer_p.h>
+#include "qsgtextnode_p.h"
+#include "qsgimage_p_p.h"
+#include <private/qsgtexture_p.h>
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qabstracttextdocumentlayout.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qtextdocument.h>
+#include <QtGui/qtextobject.h>
+#include <QtGui/qtextcursor.h>
+#include <QtGui/qapplication.h>
+
+#include <private/qdeclarativestyledtext_p.h>
+#include <private/qdeclarativepixmapcache_p.h>
+
+#include <qmath.h>
+#include <limits.h>
+
+QT_BEGIN_NAMESPACE
+
+extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled;
+
+class QSGTextDocumentWithImageResources : public QTextDocument {
+ Q_OBJECT
+
+public:
+ QSGTextDocumentWithImageResources(QSGText *parent);
+ virtual ~QSGTextDocumentWithImageResources();
+
+ void setText(const QString &);
+ int resourcesLoading() const { return outstanding; }
+
+protected:
+ QVariant loadResource(int type, const QUrl &name);
+
+private slots:
+ void requestFinished();
+
+private:
+ QHash<QUrl, QDeclarativePixmap *> m_resources;
+
+ int outstanding;
+ static QSet<QUrl> errors;
+};
+
+DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE);
+
+QString QSGTextPrivate::elideChar = QString(0x2026);
+
+QSGTextPrivate::QSGTextPrivate()
+: color((QRgb)0), style(QSGText::Normal), hAlign(QSGText::AlignLeft),
+ vAlign(QSGText::AlignTop), elideMode(QSGText::ElideNone),
+ format(QSGText::AutoText), wrapMode(QSGText::NoWrap), lineHeight(1),
+ lineHeightMode(QSGText::ProportionalHeight), lineCount(1), maximumLineCount(INT_MAX),
+ maximumLineCountValid(false),
+ texture(0),
+ imageCacheDirty(true), updateOnComponentComplete(true),
+ richText(false), singleline(false), cacheAllTextAsImage(true), internalWidthUpdate(false),
+ requireImplicitWidth(false), truncated(false), hAlignImplicit(true), rightToLeftText(false),
+ layoutTextElided(false), naturalWidth(0), doc(0), layoutThread(0), nodeType(NodeIsNull)
+{
+ cacheAllTextAsImage = enableImageCache();
+}
+
+void QSGTextPrivate::init()
+{
+ Q_Q(QSGText);
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFlag(QSGItem::ItemHasContents);
+}
+
+QSGTextDocumentWithImageResources::QSGTextDocumentWithImageResources(QSGText *parent)
+: QTextDocument(parent), outstanding(0)
+{
+ setUndoRedoEnabled(false);
+}
+
+QSGTextDocumentWithImageResources::~QSGTextDocumentWithImageResources()
+{
+ if (!m_resources.isEmpty())
+ qDeleteAll(m_resources);
+}
+
+QVariant QSGTextDocumentWithImageResources::loadResource(int type, const QUrl &name)
+{
+ QDeclarativeContext *context = qmlContext(parent());
+ QUrl url = context->resolvedUrl(name);
+
+ if (type == QTextDocument::ImageResource) {
+ QHash<QUrl, QDeclarativePixmap *>::Iterator iter = m_resources.find(url);
+
+ if (iter == m_resources.end()) {
+ QDeclarativePixmap *p = new QDeclarativePixmap(context->engine(), url);
+ iter = m_resources.insert(name, p);
+
+ if (p->isLoading()) {
+ p->connectFinished(this, SLOT(requestFinished()));
+ outstanding++;
+ }
+ }
+
+ QDeclarativePixmap *p = *iter;
+ if (p->isReady()) {
+ return p->pixmap();
+ } else if (p->isError()) {
+ if (!errors.contains(url)) {
+ errors.insert(url);
+ qmlInfo(parent()) << p->error();
+ }
+ }
+ }
+
+ return QTextDocument::loadResource(type,url); // The *resolved* URL
+}
+
+void QSGTextDocumentWithImageResources::requestFinished()
+{
+ outstanding--;
+ if (outstanding == 0) {
+ QSGText *textItem = static_cast<QSGText*>(parent());
+ QString text = textItem->text();
+#ifndef QT_NO_TEXTHTMLPARSER
+ setHtml(text);
+#else
+ setPlainText(text);
+#endif
+ QSGTextPrivate *d = QSGTextPrivate::get(textItem);
+ d->updateLayout();
+ }
+}
+
+void QSGTextDocumentWithImageResources::setText(const QString &text)
+{
+ if (!m_resources.isEmpty()) {
+ qDeleteAll(m_resources);
+ m_resources.clear();
+ outstanding = 0;
+ }
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ setHtml(text);
+#else
+ setPlainText(text);
+#endif
+}
+
+QSet<QUrl> QSGTextDocumentWithImageResources::errors;
+
+QSGTextPrivate::~QSGTextPrivate()
+{
+}
+
+qreal QSGTextPrivate::getImplicitWidth() const
+{
+ if (!requireImplicitWidth) {
+ // We don't calculate implicitWidth unless it is required.
+ // We need to force a size update now to ensure implicitWidth is calculated
+ QSGTextPrivate *me = const_cast<QSGTextPrivate*>(this);
+ me->requireImplicitWidth = true;
+ me->updateSize();
+ }
+ return implicitWidth;
+}
+
+void QSGTextPrivate::updateLayout()
+{
+ Q_Q(QSGText);
+ if (!q->isComponentComplete()) {
+ updateOnComponentComplete = true;
+ return;
+ }
+
+ layoutTextElided = false;
+ // Setup instance of QTextLayout for all cases other than richtext
+ if (!richText) {
+ layout.clearLayout();
+ layout.setFont(font);
+ if (format != QSGText::StyledText) {
+ QString tmp = text;
+ tmp.replace(QLatin1Char('\n'), QChar::LineSeparator);
+ singleline = !tmp.contains(QChar::LineSeparator);
+ if (singleline && !maximumLineCountValid && elideMode != QSGText::ElideNone && q->widthValid()) {
+ QFontMetrics fm(font);
+ tmp = fm.elidedText(tmp,(Qt::TextElideMode)elideMode,q->width());
+ if (tmp != text) {
+ layoutTextElided = true;
+ if (!truncated) {
+ truncated = true;
+ emit q->truncatedChanged();
+ }
+ }
+ }
+ layout.setText(tmp);
+ } else {
+ singleline = false;
+ QDeclarativeStyledText::parse(text, layout);
+ }
+ } else {
+ ensureDoc();
+ QTextBlockFormat::LineHeightTypes type;
+ type = lineHeightMode == QSGText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
+ QTextBlockFormat blockFormat;
+ blockFormat.setLineHeight((lineHeightMode == QSGText::FixedHeight ? lineHeight : lineHeight * 100), type);
+ for (QTextBlock it = doc->begin(); it != doc->end(); it = it.next()) {
+ QTextCursor cursor(it);
+ cursor.setBlockFormat(blockFormat);
+ }
+ }
+
+ updateSize();
+}
+
+void QSGTextPrivate::updateSize()
+{
+ Q_Q(QSGText);
+
+ if (!q->isComponentComplete()) {
+ updateOnComponentComplete = true;
+ return;
+ }
+
+ if (!requireImplicitWidth) {
+ emit q->implicitWidthChanged();
+ // if the implicitWidth is used, then updateSize() has already been called (recursively)
+ if (requireImplicitWidth)
+ return;
+ }
+
+ invalidateImageCache();
+
+ QFontMetrics fm(font);
+ if (text.isEmpty()) {
+ q->setImplicitWidth(0);
+ q->setImplicitHeight(fm.height());
+ paintedSize = QSize(0, fm.height());
+ emit q->paintedSizeChanged();
+ q->update();
+ return;
+ }
+
+ int dy = q->height();
+ QSize size(0, 0);
+
+ //setup instance of QTextLayout for all cases other than richtext
+ if (!richText) {
+ QRect textRect = setupTextLayout();
+ layedOutTextRect = textRect;
+ size = textRect.size();
+ dy -= size.height();
+ } else {
+ singleline = false; // richtext can't elide or be optimized for single-line case
+ ensureDoc();
+ doc->setDefaultFont(font);
+ QSGText::HAlignment horizontalAlignment = q->effectiveHAlign();
+ if (rightToLeftText) {
+ if (horizontalAlignment == QSGText::AlignLeft)
+ horizontalAlignment = QSGText::AlignRight;
+ else if (horizontalAlignment == QSGText::AlignRight)
+ horizontalAlignment = QSGText::AlignLeft;
+ }
+ QTextOption option;
+ option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
+ option.setWrapMode(QTextOption::WrapMode(wrapMode));
+ doc->setDefaultTextOption(option);
+ if (requireImplicitWidth && q->widthValid()) {
+ doc->setTextWidth(-1);
+ naturalWidth = doc->idealWidth();
+ }
+ if (wrapMode != QSGText::NoWrap && q->widthValid())
+ doc->setTextWidth(q->width());
+ else
+ doc->setTextWidth(doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
+ dy -= (int)doc->size().height();
+ QSize dsize = doc->size().toSize();
+ layedOutTextRect = QRect(QPoint(0,0), dsize);
+ size = QSize(int(doc->idealWidth()),dsize.height());
+ }
+ int yoff = 0;
+
+ if (q->heightValid()) {
+ if (vAlign == QSGText::AlignBottom)
+ yoff = dy;
+ else if (vAlign == QSGText::AlignVCenter)
+ yoff = dy/2;
+ }
+ q->setBaselineOffset(fm.ascent() + yoff);
+
+ //### need to comfirm cost of always setting these for richText
+ internalWidthUpdate = true;
+ if (!q->widthValid())
+ q->setImplicitWidth(size.width());
+ else if (requireImplicitWidth)
+ q->setImplicitWidth(naturalWidth);
+ internalWidthUpdate = false;
+
+ q->setImplicitHeight(size.height());
+ if (paintedSize != size) {
+ paintedSize = size;
+ emit q->paintedSizeChanged();
+ }
+ q->update();
+}
+
+/*!
+ Lays out the QSGTextPrivate::layout QTextLayout in the constraints of the QSGText.
+
+ Returns the size of the final text. This can be used to position the text vertically (the text is
+ already absolutely positioned horizontally).
+*/
+QRect QSGTextPrivate::setupTextLayout()
+{
+ // ### text layout handling should be profiled and optimized as needed
+ // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine);
+ Q_Q(QSGText);
+ layout.setCacheEnabled(true);
+
+ qreal lineWidth = 0;
+ int visibleCount = 0;
+
+ //set manual width
+ if (q->widthValid())
+ lineWidth = q->width();
+
+ QTextOption textOption = layout.textOption();
+ textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
+ textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
+ layout.setTextOption(textOption);
+
+ bool elideText = false;
+ bool truncate = false;
+
+ layoutThread = QThread::currentThread();
+
+ QFontMetrics fm(layout.font());
+ elidePos = QPointF();
+
+ if (requireImplicitWidth && q->widthValid()) {
+ // requires an extra layout
+ QString elidedText;
+ if (layoutTextElided) {
+ // We have provided elided text to the layout, but we must calculate unelided width.
+ elidedText = layout.text();
+ layout.setText(text);
+ }
+ layout.beginLayout();
+ forever {
+ QTextLine line = layout.createLine();
+ if (!line.isValid())
+ break;
+ }
+ layout.endLayout();
+ QRectF br;
+ for (int i = 0; i < layout.lineCount(); ++i) {
+ QTextLine line = layout.lineAt(i);
+ br = br.united(line.naturalTextRect());
+ }
+ naturalWidth = br.width();
+ if (layoutTextElided)
+ layout.setText(elidedText);
+ }
+
+ if (maximumLineCountValid) {
+ layout.beginLayout();
+ if (!lineWidth)
+ lineWidth = INT_MAX;
+ int linesLeft = maximumLineCount;
+ int visibleTextLength = 0;
+ while (linesLeft > 0) {
+ QTextLine line = layout.createLine();
+ if (!line.isValid())
+ break;
+
+ visibleCount++;
+ if (lineWidth)
+ line.setLineWidth(lineWidth);
+ visibleTextLength += line.textLength();
+
+ if (--linesLeft == 0) {
+ if (visibleTextLength < text.length()) {
+ truncate = true;
+ if (elideMode==QSGText::ElideRight && q->widthValid()) {
+ qreal elideWidth = fm.width(elideChar);
+ // Need to correct for alignment
+ line.setLineWidth(lineWidth-elideWidth);
+ if (layout.text().mid(line.textStart(), line.textLength()).isRightToLeft()) {
+ line.setPosition(QPointF(line.position().x() + elideWidth, line.position().y()));
+ elidePos.setX(line.naturalTextRect().left() - elideWidth);
+ } else {
+ elidePos.setX(line.naturalTextRect().right());
+ }
+ elideText = true;
+ }
+ }
+ }
+ }
+ layout.endLayout();
+
+ //Update truncated
+ if (truncated != truncate) {
+ truncated = truncate;
+ emit q->truncatedChanged();
+ }
+ } else {
+ layout.beginLayout();
+ forever {
+ QTextLine line = layout.createLine();
+ if (!line.isValid())
+ break;
+ visibleCount++;
+ if (lineWidth)
+ line.setLineWidth(lineWidth);
+ }
+ layout.endLayout();
+ }
+
+ qreal height = 0;
+ QRectF br;
+ for (int i = 0; i < layout.lineCount(); ++i) {
+ QTextLine line = layout.lineAt(i);
+ // set line spacing
+ line.setPosition(QPointF(line.position().x(), height));
+ if (elideText && i == layout.lineCount()-1) {
+ elidePos.setY(height + fm.ascent());
+ br = br.united(QRectF(elidePos, QSizeF(fm.width(elideChar), fm.ascent())));
+ }
+ br = br.united(line.naturalTextRect());
+ height += (lineHeightMode == QSGText::FixedHeight) ? lineHeight : line.height() * lineHeight;
+ }
+ br.setHeight(height);
+
+ if (!q->widthValid())
+ naturalWidth = br.width();
+
+ //Update the number of visible lines
+ if (lineCount != visibleCount) {
+ lineCount = visibleCount;
+ emit q->lineCountChanged();
+ }
+
+ return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height()));
+}
+
+/*!
+ Returns a painted version of the QSGTextPrivate::layout QTextLayout.
+ If \a drawStyle is true, the style color overrides all colors in the document.
+*/
+QPixmap QSGTextPrivate::textLayoutImage(bool drawStyle)
+{
+ QSize size = layedOutTextRect.size();
+
+ //paint text
+ QPixmap img(size);
+ if (!size.isEmpty()) {
+ img.fill(Qt::transparent);
+#ifdef Q_WS_MAC
+ bool oldSmooth = qt_applefontsmoothing_enabled;
+ qt_applefontsmoothing_enabled = false;
+#endif
+ QPainter p(&img);
+#ifdef Q_WS_MAC
+ qt_applefontsmoothing_enabled = oldSmooth;
+#endif
+ drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle);
+ }
+ return img;
+}
+
+/*!
+ Paints the QSGTextPrivate::layout QTextLayout into \a painter at \a pos. If
+ \a drawStyle is true, the style color overrides all colors in the document.
+*/
+void QSGTextPrivate::drawTextLayout(QPainter *painter, const QPointF &pos, bool drawStyle)
+{
+ if (drawStyle)
+ painter->setPen(styleColor);
+ else
+ painter->setPen(color);
+ painter->setFont(font);
+ layout.draw(painter, pos);
+ if (!elidePos.isNull())
+ painter->drawText(pos + elidePos, elideChar);
+}
+
+/*!
+ Returns a painted version of the QSGTextPrivate::doc QTextDocument.
+ If \a drawStyle is true, the style color overrides all colors in the document.
+*/
+QPixmap QSGTextPrivate::textDocumentImage(bool drawStyle)
+{
+ QSize size = doc->size().toSize();
+
+ //paint text
+ QPixmap img(size);
+ img.fill(Qt::transparent);
+#ifdef Q_WS_MAC
+ bool oldSmooth = qt_applefontsmoothing_enabled;
+ qt_applefontsmoothing_enabled = false;
+#endif
+ QPainter p(&img);
+#ifdef Q_WS_MAC
+ qt_applefontsmoothing_enabled = oldSmooth;
+#endif
+
+ QAbstractTextDocumentLayout::PaintContext context;
+
+ QTextOption oldOption(doc->defaultTextOption());
+ if (drawStyle) {
+ context.palette.setColor(QPalette::Text, styleColor);
+ QTextOption colorOption(doc->defaultTextOption());
+ colorOption.setFlags(QTextOption::SuppressColors);
+ doc->setDefaultTextOption(colorOption);
+ } else {
+ context.palette.setColor(QPalette::Text, color);
+ }
+ doc->documentLayout()->draw(&p, context);
+ if (drawStyle)
+ doc->setDefaultTextOption(oldOption);
+ return img;
+}
+
+/*!
+ Mark the image cache as dirty.
+*/
+void QSGTextPrivate::invalidateImageCache()
+{
+ Q_Q(QSGText);
+
+ if(cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && style != QSGText::Normal)){//If actually using the image cache
+ if (imageCacheDirty)
+ return;
+
+ imageCacheDirty = true;
+ imageCache = QPixmap();
+ }
+ if (q->isComponentComplete())
+ q->update();
+}
+
+/*!
+ Tests if the image cache is dirty, and repaints it if it is.
+*/
+void QSGTextPrivate::checkImageCache()
+{
+ if (!imageCacheDirty)
+ return;
+
+ if (text.isEmpty()) {
+
+ imageCache = QPixmap();
+
+ } else {
+
+ QPixmap textImage;
+ QPixmap styledImage;
+
+ if (richText) {
+ textImage = textDocumentImage(false);
+ if (style != QSGText::Normal)
+ styledImage = textDocumentImage(true); //### should use styleColor
+ } else {
+ textImage = textLayoutImage(false);
+ if (style != QSGText::Normal)
+ styledImage = textLayoutImage(true); //### should use styleColor
+ }
+
+ switch (style) {
+ case QSGText::Outline:
+ imageCache = drawOutline(textImage, styledImage);
+ break;
+ case QSGText::Sunken:
+ imageCache = drawOutline(textImage, styledImage, -1);
+ break;
+ case QSGText::Raised:
+ imageCache = drawOutline(textImage, styledImage, 1);
+ break;
+ default:
+ imageCache = textImage;
+ break;
+ }
+
+ }
+
+ imageCacheDirty = false;
+}
+
+/*!
+ Ensures the QSGTextPrivate::doc variable is set to a valid text document
+*/
+void QSGTextPrivate::ensureDoc()
+{
+ if (!doc) {
+ Q_Q(QSGText);
+ doc = new QSGTextDocumentWithImageResources(q);
+ doc->setDocumentMargin(0);
+ }
+}
+
+/*!
+ Draw \a styleSource as an outline around \a source and return the new image.
+*/
+QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource)
+{
+ QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
+ img.fill(Qt::transparent);
+
+ QPainter ppm(&img);
+
+ QPoint pos(0, 0);
+ pos += QPoint(-1, 0);
+ ppm.drawPixmap(pos, styleSource);
+ pos += QPoint(2, 0);
+ ppm.drawPixmap(pos, styleSource);
+ pos += QPoint(-1, -1);
+ ppm.drawPixmap(pos, styleSource);
+ pos += QPoint(0, 2);
+ ppm.drawPixmap(pos, styleSource);
+
+ pos += QPoint(0, -1);
+ ppm.drawPixmap(pos, source);
+ ppm.end();
+
+ return img;
+}
+
+/*!
+ Draw \a styleSource below \a source at \a yOffset and return the new image.
+*/
+QPixmap QSGTextPrivate::drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset)
+{
+ QPixmap img = QPixmap(styleSource.width() + 2, styleSource.height() + 2);
+ img.fill(Qt::transparent);
+
+ QPainter ppm(&img);
+
+ ppm.drawPixmap(QPoint(0, yOffset), styleSource);
+ ppm.drawPixmap(0, 0, source);
+
+ ppm.end();
+
+ return img;
+}
+
+QSGText::QSGText(QSGItem *parent)
+: QSGImplicitSizeItem(*(new QSGTextPrivate), parent)
+{
+ Q_D(QSGText);
+ d->init();
+}
+
+QSGText::~QSGText()
+{
+}
+
+QFont QSGText::font() const
+{
+ Q_D(const QSGText);
+ return d->sourceFont;
+}
+
+void QSGText::setFont(const QFont &font)
+{
+ Q_D(QSGText);
+ if (d->sourceFont == font)
+ return;
+
+ d->sourceFont = font;
+ QFont oldFont = d->font;
+ d->font = font;
+ if (QSGDistanceFieldGlyphCache::distanceFieldEnabled())
+ d->font.setHintingPreference(QFont::PreferNoHinting);
+
+ if (d->font.pointSizeF() != -1) {
+ // 0.5pt resolution
+ qreal size = qRound(d->font.pointSizeF()*2.0);
+ d->font.setPointSizeF(size/2.0);
+ }
+
+ if (oldFont != d->font)
+ d->updateLayout();
+
+ emit fontChanged(d->sourceFont);
+}
+
+QString QSGText::text() const
+{
+ Q_D(const QSGText);
+ return d->text;
+}
+
+void QSGText::setText(const QString &n)
+{
+ Q_D(QSGText);
+ if (d->text == n)
+ return;
+
+ d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n));
+ d->text = n;
+ if (isComponentComplete()) {
+ if (d->richText) {
+ d->ensureDoc();
+ d->doc->setText(n);
+ d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
+ } else {
+ d->rightToLeftText = d->text.isRightToLeft();
+ }
+ d->determineHorizontalAlignment();
+ }
+ d->updateLayout();
+ emit textChanged(d->text);
+}
+
+QColor QSGText::color() const
+{
+ Q_D(const QSGText);
+ return d->color;
+}
+
+void QSGText::setColor(const QColor &color)
+{
+ Q_D(QSGText);
+ if (d->color == color)
+ return;
+
+ d->color = color;
+ d->invalidateImageCache();
+ emit colorChanged(d->color);
+}
+
+QSGText::TextStyle QSGText::style() const
+{
+ Q_D(const QSGText);
+ return d->style;
+}
+
+void QSGText::setStyle(QSGText::TextStyle style)
+{
+ Q_D(QSGText);
+ if (d->style == style)
+ return;
+
+ // changing to/from Normal requires the boundingRect() to change
+ if (isComponentComplete() && (d->style == Normal || style == Normal))
+ update();
+ d->style = style;
+ d->invalidateImageCache();
+ emit styleChanged(d->style);
+}
+
+QColor QSGText::styleColor() const
+{
+ Q_D(const QSGText);
+ return d->styleColor;
+}
+
+void QSGText::setStyleColor(const QColor &color)
+{
+ Q_D(QSGText);
+ if (d->styleColor == color)
+ return;
+
+ d->styleColor = color;
+ d->invalidateImageCache();
+ emit styleColorChanged(d->styleColor);
+}
+
+QSGText::HAlignment QSGText::hAlign() const
+{
+ Q_D(const QSGText);
+ return d->hAlign;
+}
+
+void QSGText::setHAlign(HAlignment align)
+{
+ Q_D(QSGText);
+ bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
+ d->hAlignImplicit = false;
+ if (d->setHAlign(align, forceAlign) && isComponentComplete())
+ d->updateLayout();
+}
+
+void QSGText::resetHAlign()
+{
+ Q_D(QSGText);
+ d->hAlignImplicit = true;
+ if (d->determineHorizontalAlignment() && isComponentComplete())
+ d->updateLayout();
+}
+
+QSGText::HAlignment QSGText::effectiveHAlign() const
+{
+ Q_D(const QSGText);
+ QSGText::HAlignment effectiveAlignment = d->hAlign;
+ if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
+ switch (d->hAlign) {
+ case QSGText::AlignLeft:
+ effectiveAlignment = QSGText::AlignRight;
+ break;
+ case QSGText::AlignRight:
+ effectiveAlignment = QSGText::AlignLeft;
+ break;
+ default:
+ break;
+ }
+ }
+ return effectiveAlignment;
+}
+
+bool QSGTextPrivate::setHAlign(QSGText::HAlignment alignment, bool forceAlign)
+{
+ Q_Q(QSGText);
+ if (hAlign != alignment || forceAlign) {
+ QSGText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
+ hAlign = alignment;
+
+ emit q->horizontalAlignmentChanged(hAlign);
+ if (oldEffectiveHAlign != q->effectiveHAlign())
+ emit q->effectiveHorizontalAlignmentChanged();
+ return true;
+ }
+ return false;
+}
+
+bool QSGTextPrivate::determineHorizontalAlignment()
+{
+ Q_Q(QSGText);
+ if (hAlignImplicit && q->isComponentComplete()) {
+ bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
+ return setHAlign(alignToRight ? QSGText::AlignRight : QSGText::AlignLeft);
+ }
+ return false;
+}
+
+void QSGTextPrivate::mirrorChange()
+{
+ Q_Q(QSGText);
+ if (q->isComponentComplete()) {
+ if (!hAlignImplicit && (hAlign == QSGText::AlignRight || hAlign == QSGText::AlignLeft)) {
+ updateLayout();
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
+ }
+}
+
+QTextDocument *QSGTextPrivate::textDocument()
+{
+ return doc;
+}
+
+QSGText::VAlignment QSGText::vAlign() const
+{
+ Q_D(const QSGText);
+ return d->vAlign;
+}
+
+void QSGText::setVAlign(VAlignment align)
+{
+ Q_D(QSGText);
+ if (d->vAlign == align)
+ return;
+
+ d->vAlign = align;
+ emit verticalAlignmentChanged(align);
+}
+
+QSGText::WrapMode QSGText::wrapMode() const
+{
+ Q_D(const QSGText);
+ return d->wrapMode;
+}
+
+void QSGText::setWrapMode(WrapMode mode)
+{
+ Q_D(QSGText);
+ if (mode == d->wrapMode)
+ return;
+
+ d->wrapMode = mode;
+ d->updateLayout();
+
+ emit wrapModeChanged();
+}
+
+int QSGText::lineCount() const
+{
+ Q_D(const QSGText);
+ return d->lineCount;
+}
+
+bool QSGText::truncated() const
+{
+ Q_D(const QSGText);
+ return d->truncated;
+}
+
+int QSGText::maximumLineCount() const
+{
+ Q_D(const QSGText);
+ return d->maximumLineCount;
+}
+
+void QSGText::setMaximumLineCount(int lines)
+{
+ Q_D(QSGText);
+
+ d->maximumLineCountValid = lines==INT_MAX ? false : true;
+ if (d->maximumLineCount != lines) {
+ d->maximumLineCount = lines;
+ d->updateLayout();
+ emit maximumLineCountChanged();
+ }
+}
+
+void QSGText::resetMaximumLineCount()
+{
+ Q_D(QSGText);
+ setMaximumLineCount(INT_MAX);
+ d->elidePos = QPointF();
+ if (d->truncated != false) {
+ d->truncated = false;
+ emit truncatedChanged();
+ }
+}
+
+QSGText::TextFormat QSGText::textFormat() const
+{
+ Q_D(const QSGText);
+ return d->format;
+}
+
+void QSGText::setTextFormat(TextFormat format)
+{
+ Q_D(QSGText);
+ if (format == d->format)
+ return;
+ d->format = format;
+ bool wasRich = d->richText;
+ d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
+
+ if (!wasRich && d->richText && isComponentComplete()) {
+ d->ensureDoc();
+ d->doc->setText(d->text);
+ }
+
+ d->updateLayout();
+
+ emit textFormatChanged(d->format);
+}
+
+QSGText::TextElideMode QSGText::elideMode() const
+{
+ Q_D(const QSGText);
+ return d->elideMode;
+}
+
+void QSGText::setElideMode(QSGText::TextElideMode mode)
+{
+ Q_D(QSGText);
+ if (mode == d->elideMode)
+ return;
+
+ d->elideMode = mode;
+ d->updateLayout();
+
+ emit elideModeChanged(d->elideMode);
+}
+
+/*! \internal */
+QRectF QSGText::boundingRect() const
+{
+ Q_D(const QSGText);
+
+ QRect rect = d->layedOutTextRect;
+ if (d->style != Normal)
+ rect.adjust(-1, 0, 1, 2);
+
+ // Could include font max left/right bearings to either side of rectangle.
+
+ int h = height();
+ switch (d->vAlign) {
+ case AlignTop:
+ break;
+ case AlignBottom:
+ rect.moveTop(h - rect.height());
+ break;
+ case AlignVCenter:
+ rect.moveTop((h - rect.height()) / 2);
+ break;
+ }
+
+ return QRectF(rect);
+}
+
+/*! \internal */
+void QSGText::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QSGText);
+ if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width())
+ && (d->wrapMode != QSGText::NoWrap
+ || d->elideMode != QSGText::ElideNone
+ || d->hAlign != QSGText::AlignLeft)) {
+ if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QSGText::ElideNone && widthValid()) {
+ // We need to re-elide
+ d->updateLayout();
+ } else {
+ // We just need to re-layout
+ d->updateSize();
+ }
+ }
+
+ QSGItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+QSGNode *QSGText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
+{
+ Q_UNUSED(data);
+ Q_D(QSGText);
+
+ bool richTextAsImage = false;
+ if (d->richText) {
+ d->ensureDoc();
+ richTextAsImage = QSGTextNode::isComplexRichText(d->doc);
+ }
+
+ QRectF bounds = boundingRect();
+
+ // We need to make sure the layout is done in the current thread
+ if (d->layoutThread != QThread::currentThread())
+ d->updateLayout();
+
+ // XXX todo - some styled text can be done by the QSGTextNode
+ if (richTextAsImage || d->cacheAllTextAsImage || (!QSGDistanceFieldGlyphCache::distanceFieldEnabled() && d->style != Normal)) {
+ bool wasDirty = d->imageCacheDirty;
+
+ d->checkImageCache();
+
+ if (d->imageCache.isNull()) {
+ delete oldNode;
+ return 0;
+ }
+
+ QSGImageNode *node = 0;
+ if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsTexture) {
+ delete oldNode;
+ node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
+ d->texture = new QSGPlainTexture();
+ wasDirty = true;
+ d->nodeType = QSGTextPrivate::NodeIsTexture;
+ } else {
+ node = static_cast<QSGImageNode *>(oldNode);
+ Q_ASSERT(d->texture);
+ }
+
+ if (wasDirty) {
+ qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->imageCache.toImage());
+ node->setTexture(0);
+ node->setTexture(d->texture);
+ }
+
+ node->setTargetRect(QRectF(bounds.x(), bounds.y(), d->imageCache.width(), d->imageCache.height()));
+ node->setSourceRect(QRectF(0, 0, 1, 1));
+ node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ node->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
+ node->update();
+
+ return node;
+
+ } else {
+ QSGTextNode *node = 0;
+ if (!oldNode || d->nodeType != QSGTextPrivate::NodeIsText) {
+ delete oldNode;
+ node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
+ d->nodeType = QSGTextPrivate::NodeIsText;
+ } else {
+ node = static_cast<QSGTextNode *>(oldNode);
+ }
+
+ node->deleteContent();
+ node->setMatrix(QMatrix4x4());
+
+ if (d->richText) {
+
+ d->ensureDoc();
+ node->addTextDocument(bounds.topLeft(), d->doc, QColor(), d->style, d->styleColor);
+
+ } else {
+ node->addTextLayout(QPoint(0, bounds.y()), &d->layout, d->color, d->style, d->styleColor);
+ }
+
+ return node;
+ }
+}
+
+qreal QSGText::paintedWidth() const
+{
+ Q_D(const QSGText);
+ return d->paintedSize.width();
+}
+
+qreal QSGText::paintedHeight() const
+{
+ Q_D(const QSGText);
+ return d->paintedSize.height();
+}
+
+qreal QSGText::lineHeight() const
+{
+ Q_D(const QSGText);
+ return d->lineHeight;
+}
+
+void QSGText::setLineHeight(qreal lineHeight)
+{
+ Q_D(QSGText);
+
+ if ((d->lineHeight == lineHeight) || (lineHeight < 0.0))
+ return;
+
+ d->lineHeight = lineHeight;
+ d->updateLayout();
+ emit lineHeightChanged(lineHeight);
+}
+
+QSGText::LineHeightMode QSGText::lineHeightMode() const
+{
+ Q_D(const QSGText);
+ return d->lineHeightMode;
+}
+
+void QSGText::setLineHeightMode(LineHeightMode mode)
+{
+ Q_D(QSGText);
+ if (mode == d->lineHeightMode)
+ return;
+
+ d->lineHeightMode = mode;
+ d->updateLayout();
+
+ emit lineHeightModeChanged(mode);
+}
+
+/*!
+ Returns the number of resources (images) that are being loaded asynchronously.
+*/
+int QSGText::resourcesLoading() const
+{
+ Q_D(const QSGText);
+ return d->doc ? d->doc->resourcesLoading() : 0;
+}
+
+/*! \internal */
+void QSGText::componentComplete()
+{
+ Q_D(QSGText);
+ QSGItem::componentComplete();
+ if (d->updateOnComponentComplete) {
+ d->updateOnComponentComplete = false;
+ if (d->richText) {
+ d->ensureDoc();
+ d->doc->setText(d->text);
+ d->rightToLeftText = d->doc->toPlainText().isRightToLeft();
+ } else {
+ d->rightToLeftText = d->text.isRightToLeft();
+ }
+ d->determineHorizontalAlignment();
+ d->updateLayout();
+ }
+}
+
+/*! \internal */
+void QSGText::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGText);
+
+ if (!d->richText || !d->doc || d->doc->documentLayout()->anchorAt(event->pos()).isEmpty()) {
+ event->setAccepted(false);
+ d->activeLink.clear();
+ } else {
+ d->activeLink = d->doc->documentLayout()->anchorAt(event->pos());
+ }
+
+ // ### may malfunction if two of the same links are clicked & dragged onto each other)
+
+ if (!event->isAccepted())
+ QSGItem::mousePressEvent(event);
+
+}
+
+/*! \internal */
+void QSGText::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGText);
+
+ // ### confirm the link, and send a signal out
+ if (d->richText && d->doc && d->activeLink == d->doc->documentLayout()->anchorAt(event->pos()))
+ emit linkActivated(d->activeLink);
+ else
+ event->setAccepted(false);
+
+ if (!event->isAccepted())
+ QSGItem::mouseReleaseEvent(event);
+}
+
+QT_END_NAMESPACE
+
+#include "qsgtext.moc"
diff --git a/src/declarative/items/qsgtext_p.h b/src/declarative/items/qsgtext_p.h
new file mode 100644
index 0000000000..090a2b0e67
--- /dev/null
+++ b/src/declarative/items/qsgtext_p.h
@@ -0,0 +1,214 @@
+// Commit: 27e4302b7f45f22180693d26747f419177c81e27
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXT_P_H
+#define QSGTEXT_P_H
+
+#include "qsgimplicitsizeitem_p.h"
+
+#include <private/qdeclarativeglobal_p.h>
+
+#include <QtGui/qtextoption.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+class QSGTextPrivate;
+class Q_DECLARATIVE_PRIVATE_EXPORT QSGText : public QSGImplicitSizeItem
+{
+ Q_OBJECT
+ Q_ENUMS(HAlignment)
+ Q_ENUMS(VAlignment)
+ Q_ENUMS(TextStyle)
+ Q_ENUMS(TextFormat)
+ Q_ENUMS(TextElideMode)
+ Q_ENUMS(WrapMode)
+ Q_ENUMS(LineHeightMode)
+
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(TextStyle style READ style WRITE setStyle NOTIFY styleChanged)
+ Q_PROPERTY(QColor styleColor READ styleColor WRITE setStyleColor NOTIFY styleColorChanged)
+ Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged)
+ Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged)
+ Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged)
+ Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
+ Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged)
+ Q_PROPERTY(bool truncated READ truncated NOTIFY truncatedChanged)
+ Q_PROPERTY(int maximumLineCount READ maximumLineCount WRITE setMaximumLineCount NOTIFY maximumLineCountChanged RESET resetMaximumLineCount)
+
+ Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged)
+ Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode?
+ Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged)
+ Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged)
+ Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged)
+ Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged)
+
+public:
+ QSGText(QSGItem *parent=0);
+ ~QSGText();
+
+ enum HAlignment { AlignLeft = Qt::AlignLeft,
+ AlignRight = Qt::AlignRight,
+ AlignHCenter = Qt::AlignHCenter,
+ AlignJustify = Qt::AlignJustify };
+ enum VAlignment { AlignTop = Qt::AlignTop,
+ AlignBottom = Qt::AlignBottom,
+ AlignVCenter = Qt::AlignVCenter };
+ enum TextStyle { Normal,
+ Outline,
+ Raised,
+ Sunken };
+ enum TextFormat { PlainText = Qt::PlainText,
+ RichText = Qt::RichText,
+ AutoText = Qt::AutoText,
+ StyledText = 4 };
+ enum TextElideMode { ElideLeft = Qt::ElideLeft,
+ ElideRight = Qt::ElideRight,
+ ElideMiddle = Qt::ElideMiddle,
+ ElideNone = Qt::ElideNone };
+
+ enum WrapMode { NoWrap = QTextOption::NoWrap,
+ WordWrap = QTextOption::WordWrap,
+ WrapAnywhere = QTextOption::WrapAnywhere,
+ WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT
+ Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere
+ };
+
+ enum LineHeightMode { ProportionalHeight, FixedHeight };
+
+ QString text() const;
+ void setText(const QString &);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ TextStyle style() const;
+ void setStyle(TextStyle style);
+
+ QColor styleColor() const;
+ void setStyleColor(const QColor &c);
+
+ HAlignment hAlign() const;
+ void setHAlign(HAlignment align);
+ void resetHAlign();
+ HAlignment effectiveHAlign() const;
+
+ VAlignment vAlign() const;
+ void setVAlign(VAlignment align);
+
+ WrapMode wrapMode() const;
+ void setWrapMode(WrapMode w);
+
+ int lineCount() const;
+ bool truncated() const;
+
+ int maximumLineCount() const;
+ void setMaximumLineCount(int lines);
+ void resetMaximumLineCount();
+
+ TextFormat textFormat() const;
+ void setTextFormat(TextFormat format);
+
+ TextElideMode elideMode() const;
+ void setElideMode(TextElideMode);
+
+ qreal lineHeight() const;
+ void setLineHeight(qreal lineHeight);
+
+ LineHeightMode lineHeightMode() const;
+ void setLineHeightMode(LineHeightMode);
+
+ virtual void componentComplete();
+
+ int resourcesLoading() const; // mainly for testing
+
+ qreal paintedWidth() const;
+ qreal paintedHeight() const;
+
+ QRectF boundingRect() const;
+
+Q_SIGNALS:
+ void textChanged(const QString &text);
+ void linkActivated(const QString &link);
+ void fontChanged(const QFont &font);
+ void colorChanged(const QColor &color);
+ void styleChanged(TextStyle style);
+ void styleColorChanged(const QColor &color);
+ void horizontalAlignmentChanged(HAlignment alignment);
+ void verticalAlignmentChanged(VAlignment alignment);
+ void wrapModeChanged();
+ void lineCountChanged();
+ void truncatedChanged();
+ void maximumLineCountChanged();
+ void textFormatChanged(TextFormat textFormat);
+ void elideModeChanged(TextElideMode mode);
+ void paintedSizeChanged();
+ void lineHeightChanged(qreal lineHeight);
+ void lineHeightModeChanged(LineHeightMode mode);
+ void effectiveHorizontalAlignmentChanged();
+
+protected:
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+
+private:
+ Q_DISABLE_COPY(QSGText)
+ Q_DECLARE_PRIVATE(QSGText)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGText)
+
+QT_END_HEADER
+
+#endif // QSGTEXT_P_H
diff --git a/src/declarative/items/qsgtext_p_p.h b/src/declarative/items/qsgtext_p_p.h
new file mode 100644
index 0000000000..a3836a19f8
--- /dev/null
+++ b/src/declarative/items/qsgtext_p_p.h
@@ -0,0 +1,156 @@
+// Commit: 6e5a642c9484536fc173714f560f739944368cf5
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXT_P_P_H
+#define QSGTEXT_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgitem.h"
+#include "qsgimplicitsizeitem_p_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtGui/qtextlayout.h>
+
+#include <private/qdeclarativetextlayout_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextLayout;
+class QSGTextDocumentWithImageResources;
+class QSGPlainTexture;
+
+class Q_AUTOTEST_EXPORT QSGTextPrivate : public QSGImplicitSizeItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGText)
+public:
+ QSGTextPrivate();
+ ~QSGTextPrivate();
+ void init();
+
+ void updateSize();
+ void updateLayout();
+ bool determineHorizontalAlignment();
+ bool setHAlign(QSGText::HAlignment, bool forceAlign = false);
+ void mirrorChange();
+ QTextDocument *textDocument();
+
+ QString text;
+ QFont font;
+ QFont sourceFont;
+ QColor color;
+ QSGText::TextStyle style;
+ QColor styleColor;
+ QString activeLink;
+ QSGText::HAlignment hAlign;
+ QSGText::VAlignment vAlign;
+ QSGText::TextElideMode elideMode;
+ QSGText::TextFormat format;
+ QSGText::WrapMode wrapMode;
+ qreal lineHeight;
+ QSGText::LineHeightMode lineHeightMode;
+ int lineCount;
+ int maximumLineCount;
+ int maximumLineCountValid;
+ QPointF elidePos;
+
+ static QString elideChar;
+
+ void invalidateImageCache();
+ void checkImageCache();
+ QPixmap imageCache;
+ QSGTexture *texture;
+
+ bool imageCacheDirty:1;
+ bool updateOnComponentComplete:1;
+ bool richText:1;
+ bool singleline:1;
+ bool cacheAllTextAsImage:1;
+ bool internalWidthUpdate:1;
+ bool requireImplicitWidth:1;
+ bool truncated:1;
+ bool hAlignImplicit:1;
+ bool rightToLeftText:1;
+ bool layoutTextElided:1;
+
+ QRect layedOutTextRect;
+ QSize paintedSize;
+ qreal naturalWidth;
+ virtual qreal getImplicitWidth() const;
+
+ void ensureDoc();
+ QPixmap textDocumentImage(bool drawStyle);
+ QSGTextDocumentWithImageResources *doc;
+
+ QRect setupTextLayout();
+ QPixmap textLayoutImage(bool drawStyle);
+ void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle);
+ QTextLayout layout;
+ QThread *layoutThread;
+
+ static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource);
+ static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset);
+
+ static inline QSGTextPrivate *get(QSGText *t) {
+ return t->d_func();
+ }
+
+ enum NodeType {
+ NodeIsNull,
+ NodeIsTexture,
+ NodeIsText,
+ };
+ NodeType nodeType;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXT_P_P_H
diff --git a/src/declarative/items/qsgtextedit.cpp b/src/declarative/items/qsgtextedit.cpp
new file mode 100644
index 0000000000..1c199ecc28
--- /dev/null
+++ b/src/declarative/items/qsgtextedit.cpp
@@ -0,0 +1,1232 @@
+// Commit: ec40dd2bb51868bca10dbd0c9116f3224ff2b29b
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgtextedit_p.h"
+#include "qsgtextedit_p_p.h"
+#include "qsgevents_p_p.h"
+#include "qsgcanvas.h"
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qapplication.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qtextobject.h>
+#include <QtCore/qmath.h>
+
+#include <private/qdeclarativeglobal_p.h>
+#include <private/qtextcontrol_p.h>
+#include <private/qtextengine_p.h>
+#include <private/qwidget_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QWidgetPrivate *qt_widget_private(QWidget *widget);
+
+QSGTextEdit::QSGTextEdit(QSGItem *parent)
+: QSGImplicitSizePaintedItem(*(new QSGTextEditPrivate), parent)
+{
+ Q_D(QSGTextEdit);
+ d->init();
+}
+
+QString QSGTextEdit::text() const
+{
+ Q_D(const QSGTextEdit);
+
+#ifndef QT_NO_TEXTHTMLPARSER
+ if (d->richText)
+ return d->document->toHtml();
+ else
+#endif
+ return d->document->toPlainText();
+}
+
+void QSGTextEdit::setText(const QString &text)
+{
+ Q_D(QSGTextEdit);
+ if (QSGTextEdit::text() == text)
+ return;
+
+ d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
+ if (d->richText) {
+#ifndef QT_NO_TEXTHTMLPARSER
+ d->control->setHtml(text);
+#else
+ d->control->setPlainText(text);
+#endif
+ } else {
+ d->control->setPlainText(text);
+ }
+ q_textChanged();
+}
+
+QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
+{
+ Q_D(const QSGTextEdit);
+ return d->format;
+}
+
+void QSGTextEdit::setTextFormat(TextFormat format)
+{
+ Q_D(QSGTextEdit);
+ if (format == d->format)
+ return;
+ bool wasRich = d->richText;
+ d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
+
+ if (wasRich && !d->richText) {
+ d->control->setPlainText(d->text);
+ updateSize();
+ } else if (!wasRich && d->richText) {
+#ifndef QT_NO_TEXTHTMLPARSER
+ d->control->setHtml(d->text);
+#else
+ d->control->setPlainText(d->text);
+#endif
+ updateSize();
+ }
+ d->format = format;
+ d->control->setAcceptRichText(d->format != PlainText);
+ emit textFormatChanged(d->format);
+}
+
+QFont QSGTextEdit::font() const
+{
+ Q_D(const QSGTextEdit);
+ return d->sourceFont;
+}
+
+void QSGTextEdit::setFont(const QFont &font)
+{
+ Q_D(QSGTextEdit);
+ if (d->sourceFont == font)
+ return;
+
+ d->sourceFont = font;
+ QFont oldFont = d->font;
+ d->font = font;
+ if (d->font.pointSizeF() != -1) {
+ // 0.5pt resolution
+ qreal size = qRound(d->font.pointSizeF()*2.0);
+ d->font.setPointSizeF(size/2.0);
+ }
+
+ if (oldFont != d->font) {
+ d->document->setDefaultFont(d->font);
+ if(d->cursor){
+ d->cursor->setHeight(QFontMetrics(d->font).height());
+ moveCursorDelegate();
+ }
+ updateSize();
+ update();
+ }
+ emit fontChanged(d->sourceFont);
+}
+
+QColor QSGTextEdit::color() const
+{
+ Q_D(const QSGTextEdit);
+ return d->color;
+}
+
+void QSGTextEdit::setColor(const QColor &color)
+{
+ Q_D(QSGTextEdit);
+ if (d->color == color)
+ return;
+
+ d->color = color;
+ QPalette pal = d->control->palette();
+ pal.setColor(QPalette::Text, color);
+ d->control->setPalette(pal);
+ update();
+ emit colorChanged(d->color);
+}
+
+QColor QSGTextEdit::selectionColor() const
+{
+ Q_D(const QSGTextEdit);
+ return d->selectionColor;
+}
+
+void QSGTextEdit::setSelectionColor(const QColor &color)
+{
+ Q_D(QSGTextEdit);
+ if (d->selectionColor == color)
+ return;
+
+ d->selectionColor = color;
+ QPalette pal = d->control->palette();
+ pal.setColor(QPalette::Highlight, color);
+ d->control->setPalette(pal);
+ update();
+ emit selectionColorChanged(d->selectionColor);
+}
+
+QColor QSGTextEdit::selectedTextColor() const
+{
+ Q_D(const QSGTextEdit);
+ return d->selectedTextColor;
+}
+
+void QSGTextEdit::setSelectedTextColor(const QColor &color)
+{
+ Q_D(QSGTextEdit);
+ if (d->selectedTextColor == color)
+ return;
+
+ d->selectedTextColor = color;
+ QPalette pal = d->control->palette();
+ pal.setColor(QPalette::HighlightedText, color);
+ d->control->setPalette(pal);
+ update();
+ emit selectedTextColorChanged(d->selectedTextColor);
+}
+
+QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
+{
+ Q_D(const QSGTextEdit);
+ return d->hAlign;
+}
+
+void QSGTextEdit::setHAlign(HAlignment align)
+{
+ Q_D(QSGTextEdit);
+ bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
+ d->hAlignImplicit = false;
+ if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+ d->updateDefaultTextOption();
+ updateSize();
+ }
+}
+
+void QSGTextEdit::resetHAlign()
+{
+ Q_D(QSGTextEdit);
+ d->hAlignImplicit = true;
+ if (d->determineHorizontalAlignment() && isComponentComplete()) {
+ d->updateDefaultTextOption();
+ updateSize();
+ }
+}
+
+QSGTextEdit::HAlignment QSGTextEdit::effectiveHAlign() const
+{
+ Q_D(const QSGTextEdit);
+ QSGTextEdit::HAlignment effectiveAlignment = d->hAlign;
+ if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
+ switch (d->hAlign) {
+ case QSGTextEdit::AlignLeft:
+ effectiveAlignment = QSGTextEdit::AlignRight;
+ break;
+ case QSGTextEdit::AlignRight:
+ effectiveAlignment = QSGTextEdit::AlignLeft;
+ break;
+ default:
+ break;
+ }
+ }
+ return effectiveAlignment;
+}
+
+bool QSGTextEditPrivate::setHAlign(QSGTextEdit::HAlignment alignment, bool forceAlign)
+{
+ Q_Q(QSGTextEdit);
+ if (hAlign != alignment || forceAlign) {
+ QSGTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
+ hAlign = alignment;
+ emit q->horizontalAlignmentChanged(alignment);
+ if (oldEffectiveHAlign != q->effectiveHAlign())
+ emit q->effectiveHorizontalAlignmentChanged();
+ return true;
+ }
+ return false;
+}
+
+bool QSGTextEditPrivate::determineHorizontalAlignment()
+{
+ Q_Q(QSGTextEdit);
+ if (hAlignImplicit && q->isComponentComplete()) {
+ bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
+ return setHAlign(alignToRight ? QSGTextEdit::AlignRight : QSGTextEdit::AlignLeft);
+ }
+ return false;
+}
+
+void QSGTextEditPrivate::mirrorChange()
+{
+ Q_Q(QSGTextEdit);
+ if (q->isComponentComplete()) {
+ if (!hAlignImplicit && (hAlign == QSGTextEdit::AlignRight || hAlign == QSGTextEdit::AlignLeft)) {
+ updateDefaultTextOption();
+ q->updateSize();
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
+ }
+}
+
+QSGTextEdit::VAlignment QSGTextEdit::vAlign() const
+{
+ Q_D(const QSGTextEdit);
+ return d->vAlign;
+}
+
+void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
+{
+ Q_D(QSGTextEdit);
+ if (alignment == d->vAlign)
+ return;
+ d->vAlign = alignment;
+ d->updateDefaultTextOption();
+ updateSize();
+ moveCursorDelegate();
+ emit verticalAlignmentChanged(d->vAlign);
+}
+
+QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
+{
+ Q_D(const QSGTextEdit);
+ return d->wrapMode;
+}
+
+void QSGTextEdit::setWrapMode(WrapMode mode)
+{
+ Q_D(QSGTextEdit);
+ if (mode == d->wrapMode)
+ return;
+ d->wrapMode = mode;
+ d->updateDefaultTextOption();
+ updateSize();
+ emit wrapModeChanged();
+}
+
+int QSGTextEdit::lineCount() const
+{
+ Q_D(const QSGTextEdit);
+ return d->lineCount;
+}
+
+qreal QSGTextEdit::paintedWidth() const
+{
+ Q_D(const QSGTextEdit);
+ return d->paintedSize.width();
+}
+
+qreal QSGTextEdit::paintedHeight() const
+{
+ Q_D(const QSGTextEdit);
+ return d->paintedSize.height();
+}
+
+QRectF QSGTextEdit::positionToRectangle(int pos) const
+{
+ Q_D(const QSGTextEdit);
+ QTextCursor c(d->document);
+ c.setPosition(pos);
+ return d->control->cursorRect(c);
+
+}
+
+int QSGTextEdit::positionAt(int x, int y) const
+{
+ Q_D(const QSGTextEdit);
+ int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit);
+ QTextCursor cursor = d->control->textCursor();
+ if (r > cursor.position()) {
+ // The cursor position includes positions within the preedit text, but only positions in the
+ // same text block are offset so it is possible to get a position that is either part of the
+ // preedit or the next text block.
+ QTextLayout *layout = cursor.block().layout();
+ const int preeditLength = layout
+ ? layout->preeditAreaText().length()
+ : 0;
+ if (preeditLength > 0
+ && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) {
+ r = r > cursor.position() + preeditLength
+ ? r - preeditLength
+ : cursor.position();
+ }
+ }
+ return r;
+}
+
+void QSGTextEdit::moveCursorSelection(int pos)
+{
+ //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
+ Q_D(QSGTextEdit);
+ QTextCursor cursor = d->control->textCursor();
+ if (cursor.position() == pos)
+ return;
+ cursor.setPosition(pos, QTextCursor::KeepAnchor);
+ d->control->setTextCursor(cursor);
+}
+
+void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
+{
+ Q_D(QSGTextEdit);
+ QTextCursor cursor = d->control->textCursor();
+ if (cursor.position() == pos)
+ return;
+ if (mode == SelectCharacters) {
+ cursor.setPosition(pos, QTextCursor::KeepAnchor);
+ } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
+ if (cursor.anchor() > cursor.position()) {
+ cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
+ if (cursor.position() == cursor.anchor())
+ cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
+ else
+ cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
+ } else {
+ cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
+ }
+
+ cursor.setPosition(pos, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
+ if (cursor.position() != pos)
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
+ if (cursor.anchor() < cursor.position()) {
+ cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
+ } else {
+ cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ if (cursor.position() != cursor.anchor()) {
+ cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
+ }
+ }
+
+ cursor.setPosition(pos, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
+ if (cursor.position() != pos) {
+ cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
+ cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
+ }
+ }
+ d->control->setTextCursor(cursor);
+}
+
+bool QSGTextEdit::isCursorVisible() const
+{
+ Q_D(const QSGTextEdit);
+ return d->cursorVisible;
+}
+
+void QSGTextEdit::setCursorVisible(bool on)
+{
+ Q_D(QSGTextEdit);
+ if (d->cursorVisible == on)
+ return;
+ d->cursorVisible = on;
+ QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut);
+ if (!on && !d->persistentSelection)
+ d->control->setCursorIsFocusIndicator(true);
+ d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
+ emit cursorVisibleChanged(d->cursorVisible);
+}
+
+int QSGTextEdit::cursorPosition() const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->textCursor().position();
+}
+
+void QSGTextEdit::setCursorPosition(int pos)
+{
+ Q_D(QSGTextEdit);
+ if (pos < 0 || pos > d->text.length())
+ return;
+ QTextCursor cursor = d->control->textCursor();
+ if (cursor.position() == pos && cursor.anchor() == pos)
+ return;
+ cursor.setPosition(pos);
+ d->control->setTextCursor(cursor);
+}
+
+QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
+{
+ Q_D(const QSGTextEdit);
+ return d->cursorComponent;
+}
+
+void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
+{
+ Q_D(QSGTextEdit);
+ if(d->cursorComponent){
+ if(d->cursor){
+ d->control->setCursorWidth(-1);
+ update(cursorRectangle());
+ delete d->cursor;
+ d->cursor = 0;
+ }
+ }
+ d->cursorComponent = c;
+ if(c && c->isReady()){
+ loadCursorDelegate();
+ }else{
+ if(c)
+ connect(c, SIGNAL(statusChanged()),
+ this, SLOT(loadCursorDelegate()));
+ }
+
+ emit cursorDelegateChanged();
+}
+
+void QSGTextEdit::loadCursorDelegate()
+{
+ Q_D(QSGTextEdit);
+ if(d->cursorComponent->isLoading())
+ return;
+ d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
+ if(d->cursor){
+ d->control->setCursorWidth(0);
+ update(cursorRectangle());
+ QDeclarative_setParent_noEvent(d->cursor, this);
+ d->cursor->setParentItem(this);
+ d->cursor->setHeight(QFontMetrics(d->font).height());
+ moveCursorDelegate();
+ }else{
+ qmlInfo(this) << "Error loading cursor delegate.";
+ }
+}
+
+int QSGTextEdit::selectionStart() const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->textCursor().selectionStart();
+}
+
+int QSGTextEdit::selectionEnd() const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->textCursor().selectionEnd();
+}
+
+QString QSGTextEdit::selectedText() const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->textCursor().selectedText();
+}
+
+bool QSGTextEdit::focusOnPress() const
+{
+ Q_D(const QSGTextEdit);
+ return d->focusOnPress;
+}
+
+void QSGTextEdit::setFocusOnPress(bool on)
+{
+ Q_D(QSGTextEdit);
+ if (d->focusOnPress == on)
+ return;
+ d->focusOnPress = on;
+ emit activeFocusOnPressChanged(d->focusOnPress);
+}
+
+bool QSGTextEdit::persistentSelection() const
+{
+ Q_D(const QSGTextEdit);
+ return d->persistentSelection;
+}
+
+void QSGTextEdit::setPersistentSelection(bool on)
+{
+ Q_D(QSGTextEdit);
+ if (d->persistentSelection == on)
+ return;
+ d->persistentSelection = on;
+ emit persistentSelectionChanged(d->persistentSelection);
+}
+
+qreal QSGTextEdit::textMargin() const
+{
+ Q_D(const QSGTextEdit);
+ return d->textMargin;
+}
+
+void QSGTextEdit::setTextMargin(qreal margin)
+{
+ Q_D(QSGTextEdit);
+ if (d->textMargin == margin)
+ return;
+ d->textMargin = margin;
+ d->document->setDocumentMargin(d->textMargin);
+ emit textMarginChanged(d->textMargin);
+}
+
+void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ if (newGeometry.width() != oldGeometry.width())
+ updateSize();
+ QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+void QSGTextEdit::componentComplete()
+{
+ Q_D(QSGTextEdit);
+ QSGPaintedItem::componentComplete();
+ if (d->dirty) {
+ d->determineHorizontalAlignment();
+ d->updateDefaultTextOption();
+ updateSize();
+ d->dirty = false;
+ }
+}
+
+bool QSGTextEdit::selectByMouse() const
+{
+ Q_D(const QSGTextEdit);
+ return d->selectByMouse;
+}
+
+void QSGTextEdit::setSelectByMouse(bool on)
+{
+ Q_D(QSGTextEdit);
+ if (d->selectByMouse != on) {
+ d->selectByMouse = on;
+ setKeepMouseGrab(on);
+ if (on)
+ setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
+ else
+ setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
+ emit selectByMouseChanged(on);
+ }
+}
+
+QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
+{
+ Q_D(const QSGTextEdit);
+ return d->mouseSelectionMode;
+}
+
+void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
+{
+ Q_D(QSGTextEdit);
+ if (d->mouseSelectionMode != mode) {
+ d->mouseSelectionMode = mode;
+ d->control->setWordSelectionEnabled(mode == SelectWords);
+ emit mouseSelectionModeChanged(mode);
+ }
+}
+
+void QSGTextEdit::setReadOnly(bool r)
+{
+ Q_D(QSGTextEdit);
+ if (r == isReadOnly())
+ return;
+
+ setFlag(QSGItem::ItemAcceptsInputMethod, !r);
+ Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
+ if (d->selectByMouse)
+ flags = flags | Qt::TextSelectableByMouse;
+ if (!r)
+ flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable;
+ d->control->setTextInteractionFlags(flags);
+ if (!r)
+ d->control->moveCursor(QTextCursor::End);
+
+ emit readOnlyChanged(r);
+}
+
+bool QSGTextEdit::isReadOnly() const
+{
+ Q_D(const QSGTextEdit);
+ return !(d->control->textInteractionFlags() & Qt::TextEditable);
+}
+
+void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
+{
+ Q_D(QSGTextEdit);
+ d->control->setTextInteractionFlags(flags);
+}
+
+Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->textInteractionFlags();
+}
+
+QRect QSGTextEdit::cursorRectangle() const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->cursorRect().toRect().translated(0,d->yoff);
+}
+
+bool QSGTextEdit::event(QEvent *event)
+{
+ Q_D(QSGTextEdit);
+ if (event->type() == QEvent::ShortcutOverride) {
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ return event->isAccepted();
+ }
+ return QSGPaintedItem::event(event);
+}
+
+/*!
+\overload
+Handles the given key \a event.
+*/
+void QSGTextEdit::keyPressEvent(QKeyEvent *event)
+{
+ Q_D(QSGTextEdit);
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (!event->isAccepted())
+ QSGPaintedItem::keyPressEvent(event);
+}
+
+/*!
+\overload
+Handles the given key \a event.
+*/
+void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
+{
+ Q_D(QSGTextEdit);
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (!event->isAccepted())
+ QSGPaintedItem::keyReleaseEvent(event);
+}
+
+void QSGTextEdit::deselect()
+{
+ Q_D(QSGTextEdit);
+ QTextCursor c = d->control->textCursor();
+ c.clearSelection();
+ d->control->setTextCursor(c);
+}
+
+void QSGTextEdit::selectAll()
+{
+ Q_D(QSGTextEdit);
+ d->control->selectAll();
+}
+
+void QSGTextEdit::selectWord()
+{
+ Q_D(QSGTextEdit);
+ QTextCursor c = d->control->textCursor();
+ c.select(QTextCursor::WordUnderCursor);
+ d->control->setTextCursor(c);
+}
+
+void QSGTextEdit::select(int start, int end)
+{
+ Q_D(QSGTextEdit);
+ if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
+ return;
+ QTextCursor cursor = d->control->textCursor();
+ cursor.beginEditBlock();
+ cursor.setPosition(start, QTextCursor::MoveAnchor);
+ cursor.setPosition(end, QTextCursor::KeepAnchor);
+ cursor.endEditBlock();
+ d->control->setTextCursor(cursor);
+
+ // QTBUG-11100
+ updateSelectionMarkers();
+}
+
+bool QSGTextEdit::isRightToLeft(int start, int end)
+{
+ Q_D(QSGTextEdit);
+ if (start > end) {
+ qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
+ return false;
+ } else {
+ return d->text.mid(start, end - start).isRightToLeft();
+ }
+}
+
+#ifndef QT_NO_CLIPBOARD
+void QSGTextEdit::cut()
+{
+ Q_D(QSGTextEdit);
+ d->control->cut();
+}
+
+void QSGTextEdit::copy()
+{
+ Q_D(QSGTextEdit);
+ d->control->copy();
+}
+
+void QSGTextEdit::paste()
+{
+ Q_D(QSGTextEdit);
+ d->control->paste();
+}
+#endif // QT_NO_CLIPBOARD
+
+/*!
+\overload
+Handles the given mouse \a event.
+*/
+void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextEdit);
+ if (d->focusOnPress){
+ bool hadActiveFocus = hasActiveFocus();
+ forceActiveFocus();
+ if (d->showInputPanelOnFocus) {
+ if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
+ // re-open input panel on press if already focused
+ openSoftwareInputPanel();
+ }
+ } else { // show input panel on click
+ if (hasActiveFocus() && !hadActiveFocus) {
+ d->clickCausedFocus = true;
+ }
+ }
+ }
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (!event->isAccepted())
+ QSGPaintedItem::mousePressEvent(event);
+}
+
+/*!
+\overload
+Handles the given mouse \a event.
+*/
+void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextEdit);
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (!d->showInputPanelOnFocus) { // input panel on click
+ if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
+ if (canvas() && canvas() == qApp->focusWidget()) {
+ qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
+ }
+ }
+ }
+ d->clickCausedFocus = false;
+
+ if (!event->isAccepted())
+ QSGPaintedItem::mouseReleaseEvent(event);
+}
+
+/*!
+\overload
+Handles the given mouse \a event.
+*/
+void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextEdit);
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (!event->isAccepted())
+ QSGPaintedItem::mouseDoubleClickEvent(event);
+}
+
+/*!
+\overload
+Handles the given mouse \a event.
+*/
+void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextEdit);
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (!event->isAccepted())
+ QSGPaintedItem::mouseMoveEvent(event);
+}
+
+/*!
+\overload
+Handles the given input method \a event.
+*/
+void QSGTextEdit::inputMethodEvent(QInputMethodEvent *event)
+{
+ Q_D(QSGTextEdit);
+ const bool wasComposing = isInputMethodComposing();
+ d->control->processEvent(event, QPointF(0, -d->yoff));
+ if (wasComposing != isInputMethodComposing())
+ emit inputMethodComposingChanged();
+}
+
+void QSGTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QSGTextEdit);
+ if (change == ItemActiveFocusHasChanged) {
+ setCursorVisible(value.boolValue && d->canvas && d->canvas->hasFocus());
+ }
+ QSGItem::itemChange(change, value);
+}
+
+/*!
+\overload
+Returns the value of the given \a property.
+*/
+QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QSGTextEdit);
+ return d->control->inputMethodQuery(property);
+}
+
+/*!
+Draws the contents of the text edit using the given \a painter within
+the given \a bounds.
+*/
+void QSGTextEdit::paint(QPainter *painter)
+{
+ // XXX todo
+ QRect bounds(0, 0, width(), height());
+ Q_D(QSGTextEdit);
+
+ painter->setRenderHint(QPainter::TextAntialiasing, true);
+ painter->translate(0,d->yoff);
+
+ d->control->drawContents(painter, bounds.translated(0,-d->yoff));
+
+ painter->translate(0,-d->yoff);
+}
+
+void QSGTextEdit::updateImgCache(const QRectF &rf)
+{
+ Q_D(const QSGTextEdit);
+ QRect r;
+ if (!rf.isValid()) {
+ r = QRect(0,0,INT_MAX,INT_MAX);
+ } else {
+ r = rf.toRect();
+ if (r.height() > INT_MAX/2) {
+ // Take care of overflow when translating "everything"
+ r.setTop(r.y() + d->yoff);
+ r.setBottom(INT_MAX/2);
+ } else {
+ r = r.translated(0,d->yoff);
+ }
+ }
+ update(r);
+}
+
+bool QSGTextEdit::canPaste() const
+{
+ Q_D(const QSGTextEdit);
+ return d->canPaste;
+}
+
+bool QSGTextEdit::isInputMethodComposing() const
+{
+ Q_D(const QSGTextEdit);
+ if (QTextLayout *layout = d->control->textCursor().block().layout())
+ return layout->preeditAreaText().length() > 0;
+ return false;
+}
+
+void QSGTextEditPrivate::init()
+{
+ Q_Q(QSGTextEdit);
+
+ q->setSmooth(smooth);
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFlag(QSGItem::ItemAcceptsInputMethod);
+
+ control = new QTextControl(q);
+ control->setIgnoreUnusedNavigationEvents(true);
+ control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
+ control->setDragEnabled(false);
+
+ // QTextControl follows the default text color
+ // defined by the platform, declarative text
+ // should be black by default
+ QPalette pal = control->palette();
+ if (pal.color(QPalette::Text) != color) {
+ pal.setColor(QPalette::Text, color);
+ control->setPalette(pal);
+ }
+
+ QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF)));
+
+ QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
+ QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
+ QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
+ QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
+ QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate()));
+ QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
+#ifndef QT_NO_CLIPBOARD
+ QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged()));
+ QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
+ canPaste = control->canPaste();
+#endif
+
+ document = control->document();
+ document->setDefaultFont(font);
+ document->setDocumentMargin(textMargin);
+ document->setUndoRedoEnabled(false); // flush undo buffer.
+ document->setUndoRedoEnabled(true);
+ updateDefaultTextOption();
+}
+
+void QSGTextEdit::q_textChanged()
+{
+ Q_D(QSGTextEdit);
+ d->text = text();
+ d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
+ d->determineHorizontalAlignment();
+ d->updateDefaultTextOption();
+ updateSize();
+ updateTotalLines();
+ emit textChanged(d->text);
+}
+
+void QSGTextEdit::moveCursorDelegate()
+{
+ Q_D(QSGTextEdit);
+ updateMicroFocus();
+ emit cursorRectangleChanged();
+ if(!d->cursor)
+ return;
+ QRectF cursorRect = cursorRectangle();
+ d->cursor->setX(cursorRect.x());
+ d->cursor->setY(cursorRect.y());
+}
+
+void QSGTextEditPrivate::updateSelection()
+{
+ Q_Q(QSGTextEdit);
+ QTextCursor cursor = control->textCursor();
+ bool startChange = (lastSelectionStart != cursor.selectionStart());
+ bool endChange = (lastSelectionEnd != cursor.selectionEnd());
+ cursor.beginEditBlock();
+ cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor);
+ cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor);
+ cursor.endEditBlock();
+ control->setTextCursor(cursor);
+ if(startChange)
+ q->selectionStartChanged();
+ if(endChange)
+ q->selectionEndChanged();
+}
+
+void QSGTextEdit::updateSelectionMarkers()
+{
+ Q_D(QSGTextEdit);
+ if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
+ d->lastSelectionStart = d->control->textCursor().selectionStart();
+ emit selectionStartChanged();
+ }
+ if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
+ d->lastSelectionEnd = d->control->textCursor().selectionEnd();
+ emit selectionEndChanged();
+ }
+}
+
+QRectF QSGTextEdit::boundingRect() const
+{
+ Q_D(const QSGTextEdit);
+ QRectF r = QSGPaintedItem::boundingRect();
+ int cursorWidth = 1;
+ if(d->cursor)
+ cursorWidth = d->cursor->width();
+ if(!d->document->isEmpty())
+ cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
+
+ // Could include font max left/right bearings to either side of rectangle.
+
+ r.setRight(r.right() + cursorWidth);
+ return r.translated(0,d->yoff);
+}
+
+qreal QSGTextEditPrivate::getImplicitWidth() const
+{
+ Q_Q(const QSGTextEdit);
+ if (!requireImplicitWidth) {
+ // We don't calculate implicitWidth unless it is required.
+ // We need to force a size update now to ensure implicitWidth is calculated
+ const_cast<QSGTextEditPrivate*>(this)->requireImplicitWidth = true;
+ const_cast<QSGTextEdit*>(q)->updateSize();
+ }
+ return implicitWidth;
+}
+
+//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
+// need to do all the calculations each time
+void QSGTextEdit::updateSize()
+{
+ Q_D(QSGTextEdit);
+ if (isComponentComplete()) {
+ qreal naturalWidth = d->implicitWidth;
+ // ### assumes that if the width is set, the text will fill to edges
+ // ### (unless wrap is false, then clipping will occur)
+ if (widthValid()) {
+ if (!d->requireImplicitWidth) {
+ emit implicitWidthChanged();
+ // if the implicitWidth is used, then updateSize() has already been called (recursively)
+ if (d->requireImplicitWidth)
+ return;
+ }
+ if (d->requireImplicitWidth) {
+ d->document->setTextWidth(-1);
+ naturalWidth = d->document->idealWidth();
+ }
+ if (d->document->textWidth() != width())
+ d->document->setTextWidth(width());
+ } else {
+ d->document->setTextWidth(-1);
+ }
+ QFontMetrics fm = QFontMetrics(d->font);
+ int dy = height();
+ dy -= (int)d->document->size().height();
+
+ int nyoff;
+ if (heightValid()) {
+ if (d->vAlign == AlignBottom)
+ nyoff = dy;
+ else if (d->vAlign == AlignVCenter)
+ nyoff = dy/2;
+ else
+ nyoff = 0;
+ } else {
+ nyoff = 0;
+ }
+ if (nyoff != d->yoff)
+ d->yoff = nyoff;
+ setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
+
+ //### need to comfirm cost of always setting these
+ int newWidth = qCeil(d->document->idealWidth());
+ if (!widthValid() && d->document->textWidth() != newWidth)
+ d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
+ // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
+ if (!widthValid())
+ setImplicitWidth(newWidth);
+ else if (d->requireImplicitWidth)
+ setImplicitWidth(naturalWidth);
+ qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height();
+ setImplicitHeight(newHeight);
+
+ d->paintedSize = QSize(newWidth, newHeight);
+ setContentsSize(d->paintedSize);
+ emit paintedSizeChanged();
+ } else {
+ d->dirty = true;
+ }
+ update();
+}
+
+void QSGTextEdit::updateTotalLines()
+{
+ Q_D(QSGTextEdit);
+
+ int subLines = 0;
+
+ for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
+ QTextLayout *layout = it.layout();
+ if (!layout)
+ continue;
+ subLines += layout->lineCount()-1;
+ }
+
+ int newTotalLines = d->document->lineCount() + subLines;
+ if (d->lineCount != newTotalLines) {
+ d->lineCount = newTotalLines;
+ emit lineCountChanged();
+ }
+}
+
+void QSGTextEditPrivate::updateDefaultTextOption()
+{
+ Q_Q(QSGTextEdit);
+ QTextOption opt = document->defaultTextOption();
+ int oldAlignment = opt.alignment();
+
+ QSGTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
+ if (rightToLeftText) {
+ if (horizontalAlignment == QSGTextEdit::AlignLeft)
+ horizontalAlignment = QSGTextEdit::AlignRight;
+ else if (horizontalAlignment == QSGTextEdit::AlignRight)
+ horizontalAlignment = QSGTextEdit::AlignLeft;
+ }
+ opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
+
+ QTextOption::WrapMode oldWrapMode = opt.wrapMode();
+ opt.setWrapMode(QTextOption::WrapMode(wrapMode));
+
+ if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
+ return;
+ document->setDefaultTextOption(opt);
+}
+
+
+void QSGTextEdit::openSoftwareInputPanel()
+{
+ if (qApp) {
+ if (canvas() && canvas() == qApp->focusWidget()) {
+ QEvent event(QEvent::RequestSoftwareInputPanel);
+ QApplication::sendEvent(canvas(), &event);
+ }
+ }
+}
+
+void QSGTextEdit::closeSoftwareInputPanel()
+{
+ if (qApp) {
+ if (canvas() && canvas() == qApp->focusWidget()) {
+ QEvent event(QEvent::CloseSoftwareInputPanel);
+ QApplication::sendEvent(canvas(), &event);
+ }
+ }
+}
+
+void QSGTextEdit::focusInEvent(QFocusEvent *event)
+{
+ Q_D(const QSGTextEdit);
+ if (d->showInputPanelOnFocus) {
+ if (d->focusOnPress && !isReadOnly()) {
+ openSoftwareInputPanel();
+ }
+ }
+ QSGPaintedItem::focusInEvent(event);
+}
+
+void QSGTextEdit::q_canPasteChanged()
+{
+ Q_D(QSGTextEdit);
+ bool old = d->canPaste;
+ d->canPaste = d->control->canPaste();
+ if(old!=d->canPaste)
+ emit canPasteChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgtextedit_p.h b/src/declarative/items/qsgtextedit_p.h
new file mode 100644
index 0000000000..547ead18e9
--- /dev/null
+++ b/src/declarative/items/qsgtextedit_p.h
@@ -0,0 +1,303 @@
+// Commit: 27e4302b7f45f22180693d26747f419177c81e27
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXTEDIT_P_H
+#define QSGTEXTEDIT_P_H
+
+#include "qsgimplicitsizeitem_p.h"
+
+#include <QtGui/qtextoption.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGTextEditPrivate;
+class Q_AUTOTEST_EXPORT QSGTextEdit : public QSGImplicitSizePaintedItem
+{
+ Q_OBJECT
+ Q_ENUMS(VAlignment)
+ Q_ENUMS(HAlignment)
+ Q_ENUMS(TextFormat)
+ Q_ENUMS(WrapMode)
+ Q_ENUMS(SelectionMode)
+
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged)
+ Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged)
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
+ Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged)
+ Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged)
+ Q_PROPERTY(VAlignment verticalAlignment READ vAlign WRITE setVAlign NOTIFY verticalAlignmentChanged)
+ Q_PROPERTY(WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
+ Q_PROPERTY(int lineCount READ lineCount NOTIFY lineCountChanged)
+ Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedSizeChanged)
+ Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedSizeChanged)
+ Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged)
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged)
+ Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged)
+ Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
+ Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorRectangleChanged)
+ Q_PROPERTY(QDeclarativeComponent* cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged)
+ Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged)
+ Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged)
+ Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectionChanged)
+ Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged)
+ Q_PROPERTY(bool persistentSelection READ persistentSelection WRITE setPersistentSelection NOTIFY persistentSelectionChanged)
+ Q_PROPERTY(qreal textMargin READ textMargin WRITE setTextMargin NOTIFY textMarginChanged)
+ Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints)
+ Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged)
+ Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged)
+ Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged)
+ Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged)
+
+public:
+ QSGTextEdit(QSGItem *parent=0);
+
+ enum HAlignment {
+ AlignLeft = Qt::AlignLeft,
+ AlignRight = Qt::AlignRight,
+ AlignHCenter = Qt::AlignHCenter,
+ AlignJustify = Qt::AlignJustify
+ };
+
+ enum VAlignment {
+ AlignTop = Qt::AlignTop,
+ AlignBottom = Qt::AlignBottom,
+ AlignVCenter = Qt::AlignVCenter
+ };
+
+ enum TextFormat {
+ PlainText = Qt::PlainText,
+ RichText = Qt::RichText,
+ AutoText = Qt::AutoText
+ };
+
+ enum WrapMode { NoWrap = QTextOption::NoWrap,
+ WordWrap = QTextOption::WordWrap,
+ WrapAnywhere = QTextOption::WrapAnywhere,
+ WrapAtWordBoundaryOrAnywhere = QTextOption::WrapAtWordBoundaryOrAnywhere, // COMPAT
+ Wrap = QTextOption::WrapAtWordBoundaryOrAnywhere
+ };
+
+ enum SelectionMode {
+ SelectCharacters,
+ SelectWords
+ };
+
+ Q_INVOKABLE void openSoftwareInputPanel();
+ Q_INVOKABLE void closeSoftwareInputPanel();
+
+ QString text() const;
+ void setText(const QString &);
+
+ TextFormat textFormat() const;
+ void setTextFormat(TextFormat format);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ QColor selectionColor() const;
+ void setSelectionColor(const QColor &c);
+
+ QColor selectedTextColor() const;
+ void setSelectedTextColor(const QColor &c);
+
+ HAlignment hAlign() const;
+ void setHAlign(HAlignment align);
+ void resetHAlign();
+ HAlignment effectiveHAlign() const;
+
+ VAlignment vAlign() const;
+ void setVAlign(VAlignment align);
+
+ WrapMode wrapMode() const;
+ void setWrapMode(WrapMode w);
+
+ int lineCount() const;
+
+ bool isCursorVisible() const;
+ void setCursorVisible(bool on);
+
+ int cursorPosition() const;
+ void setCursorPosition(int pos);
+
+ QDeclarativeComponent* cursorDelegate() const;
+ void setCursorDelegate(QDeclarativeComponent*);
+
+ int selectionStart() const;
+ int selectionEnd() const;
+
+ QString selectedText() const;
+
+ bool focusOnPress() const;
+ void setFocusOnPress(bool on);
+
+ bool persistentSelection() const;
+ void setPersistentSelection(bool on);
+
+ qreal textMargin() const;
+ void setTextMargin(qreal margin);
+
+ bool selectByMouse() const;
+ void setSelectByMouse(bool);
+
+ SelectionMode mouseSelectionMode() const;
+ void setMouseSelectionMode(SelectionMode mode);
+
+ bool canPaste() const;
+
+ virtual void componentComplete();
+
+ /* FROM EDIT */
+ void setReadOnly(bool);
+ bool isReadOnly() const;
+
+ void setTextInteractionFlags(Qt::TextInteractionFlags flags);
+ Qt::TextInteractionFlags textInteractionFlags() const;
+
+ QRect cursorRectangle() const;
+
+ QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
+
+ qreal paintedWidth() const;
+ qreal paintedHeight() const;
+
+ Q_INVOKABLE QRectF positionToRectangle(int) const;
+ Q_INVOKABLE int positionAt(int x, int y) const;
+ Q_INVOKABLE void moveCursorSelection(int pos);
+ Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
+
+ QRectF boundingRect() const;
+
+ bool isInputMethodComposing() const;
+
+Q_SIGNALS:
+ void textChanged(const QString &);
+ void paintedSizeChanged();
+ void cursorPositionChanged();
+ void cursorRectangleChanged();
+ void selectionStartChanged();
+ void selectionEndChanged();
+ void selectionChanged();
+ void colorChanged(const QColor &color);
+ void selectionColorChanged(const QColor &color);
+ void selectedTextColorChanged(const QColor &color);
+ void fontChanged(const QFont &font);
+ void horizontalAlignmentChanged(HAlignment alignment);
+ void verticalAlignmentChanged(VAlignment alignment);
+ void wrapModeChanged();
+ void lineCountChanged();
+ void textFormatChanged(TextFormat textFormat);
+ void readOnlyChanged(bool isReadOnly);
+ void cursorVisibleChanged(bool isCursorVisible);
+ void cursorDelegateChanged();
+ void activeFocusOnPressChanged(bool activeFocusOnPressed);
+ void persistentSelectionChanged(bool isPersistentSelection);
+ void textMarginChanged(qreal textMargin);
+ void selectByMouseChanged(bool selectByMouse);
+ void mouseSelectionModeChanged(SelectionMode mode);
+ void linkActivated(const QString &link);
+ void canPasteChanged();
+ void inputMethodComposingChanged();
+ void effectiveHorizontalAlignmentChanged();
+
+public Q_SLOTS:
+ void selectAll();
+ void selectWord();
+ void select(int start, int end);
+ void deselect();
+ bool isRightToLeft(int start, int end);
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy();
+ void paste();
+#endif
+
+private Q_SLOTS:
+ void updateImgCache(const QRectF &rect);
+ void q_textChanged();
+ void updateSelectionMarkers();
+ void moveCursorDelegate();
+ void loadCursorDelegate();
+ void q_canPasteChanged();
+
+private:
+ void updateSize();
+ void updateTotalLines();
+
+protected:
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+
+ bool event(QEvent *);
+ void keyPressEvent(QKeyEvent *);
+ void keyReleaseEvent(QKeyEvent *);
+ void focusInEvent(QFocusEvent *event);
+
+ // mouse filter?
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void inputMethodEvent(QInputMethodEvent *e);
+ virtual void itemChange(ItemChange, const ItemChangeData &);
+
+ void paint(QPainter *);
+private:
+ Q_DISABLE_COPY(QSGTextEdit)
+ Q_DECLARE_PRIVATE(QSGTextEdit)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGTextEdit)
+
+QT_END_HEADER
+
+#endif // QSGTEXTEDIT_P_H
diff --git a/src/declarative/items/qsgtextedit_p_p.h b/src/declarative/items/qsgtextedit_p_p.h
new file mode 100644
index 0000000000..63f4bbc341
--- /dev/null
+++ b/src/declarative/items/qsgtextedit_p_p.h
@@ -0,0 +1,143 @@
+// Commit: 27e4302b7f45f22180693d26747f419177c81e27
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXTEDIT_P_P_H
+#define QSGTEXTEDIT_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qsgtextedit_p.h"
+#include "qsgimplicitsizeitem_p_p.h"
+
+#include <QtDeclarative/qdeclarative.h>
+
+QT_BEGIN_NAMESPACE
+class QTextLayout;
+class QTextDocument;
+class QTextControl;
+class QSGTextEditPrivate : public QSGImplicitSizePaintedItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGTextEdit)
+
+public:
+ QSGTextEditPrivate()
+ : color("black"), hAlign(QSGTextEdit::AlignLeft), vAlign(QSGTextEdit::AlignTop),
+ imgDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true),
+ showInputPanelOnFocus(true), clickCausedFocus(false), persistentSelection(true),
+ requireImplicitWidth(false), selectByMouse(false), canPaste(false),
+ hAlignImplicit(true), rightToLeftText(false),
+ textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), cursorComponent(0), cursor(0),
+ format(QSGTextEdit::AutoText), document(0), wrapMode(QSGTextEdit::NoWrap),
+ mouseSelectionMode(QSGTextEdit::SelectCharacters),
+ yoff(0)
+ {
+#ifdef Q_OS_SYMBIAN
+ if (QSysInfo::symbianVersion() == QSysInfo::SV_SF_1 || QSysInfo::symbianVersion() == QSysInfo::SV_SF_3) {
+ showInputPanelOnFocus = false;
+ }
+#endif
+ }
+
+ void init();
+
+ void updateDefaultTextOption();
+ void relayoutDocument();
+ void updateSelection();
+ bool determineHorizontalAlignment();
+ bool setHAlign(QSGTextEdit::HAlignment, bool forceAlign = false);
+ void mirrorChange();
+ qreal getImplicitWidth() const;
+
+ QString text;
+ QFont font;
+ QFont sourceFont;
+ QColor color;
+ QColor selectionColor;
+ QColor selectedTextColor;
+ QString style;
+ QColor styleColor;
+ QPixmap imgCache;
+ QPixmap imgStyleCache;
+ QSGTextEdit::HAlignment hAlign;
+ QSGTextEdit::VAlignment vAlign;
+
+ bool imgDirty : 1;
+ bool dirty : 1;
+ bool richText : 1;
+ bool cursorVisible : 1;
+ bool focusOnPress : 1;
+ bool showInputPanelOnFocus : 1;
+ bool clickCausedFocus : 1;
+ bool persistentSelection : 1;
+ bool requireImplicitWidth:1;
+ bool selectByMouse:1;
+ bool canPaste:1;
+ bool hAlignImplicit:1;
+ bool rightToLeftText:1;
+
+ qreal textMargin;
+ int lastSelectionStart;
+ int lastSelectionEnd;
+ QDeclarativeComponent* cursorComponent;
+ QSGItem* cursor;
+ QSGTextEdit::TextFormat format;
+ QTextDocument *document;
+ QTextControl *control;
+ QSGTextEdit::WrapMode wrapMode;
+ QSGTextEdit::SelectionMode mouseSelectionMode;
+ int lineCount;
+ int yoff;
+ QSize paintedSize;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTEDIT_P_P_H
diff --git a/src/declarative/items/qsgtextinput.cpp b/src/declarative/items/qsgtextinput.cpp
new file mode 100644
index 0000000000..1db4474d4a
--- /dev/null
+++ b/src/declarative/items/qsgtextinput.cpp
@@ -0,0 +1,1295 @@
+// Commit: 47712d1f330e4b22ce6dd30e7557288ef7f7fca0
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgtextinput_p.h"
+#include "qsgtextinput_p_p.h"
+#include "qsgcanvas.h"
+
+#include <private/qdeclarativeglobal_p.h>
+#include <private/qwidget_p.h>
+
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <QtGui/qgraphicssceneevent.h>
+#include <QtGui/qinputcontext.h>
+#include <QTextBoundaryFinder>
+#include <qstyle.h>
+
+QT_BEGIN_NAMESPACE
+
+QWidgetPrivate *qt_widget_private(QWidget *widget);
+
+QSGTextInput::QSGTextInput(QSGItem* parent)
+: QSGImplicitSizePaintedItem(*(new QSGTextInputPrivate), parent)
+{
+ Q_D(QSGTextInput);
+ d->init();
+}
+
+QSGTextInput::~QSGTextInput()
+{
+}
+
+QString QSGTextInput::text() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->text();
+}
+
+void QSGTextInput::setText(const QString &s)
+{
+ Q_D(QSGTextInput);
+ if(s == text())
+ return;
+ d->control->setText(s);
+}
+
+QFont QSGTextInput::font() const
+{
+ Q_D(const QSGTextInput);
+ return d->sourceFont;
+}
+
+void QSGTextInput::setFont(const QFont &font)
+{
+ Q_D(QSGTextInput);
+ if (d->sourceFont == font)
+ return;
+
+ d->sourceFont = font;
+ QFont oldFont = d->font;
+ d->font = font;
+ if (d->font.pointSizeF() != -1) {
+ // 0.5pt resolution
+ qreal size = qRound(d->font.pointSizeF()*2.0);
+ d->font.setPointSizeF(size/2.0);
+ }
+ if (oldFont != d->font) {
+ d->control->setFont(d->font);
+ if(d->cursorItem){
+ d->cursorItem->setHeight(QFontMetrics(d->font).height());
+ moveCursor();
+ }
+ updateSize();
+ }
+ emit fontChanged(d->sourceFont);
+}
+
+QColor QSGTextInput::color() const
+{
+ Q_D(const QSGTextInput);
+ return d->color;
+}
+
+void QSGTextInput::setColor(const QColor &c)
+{
+ Q_D(QSGTextInput);
+ if (c != d->color) {
+ d->color = c;
+ update();
+ emit colorChanged(c);
+ }
+}
+
+QColor QSGTextInput::selectionColor() const
+{
+ Q_D(const QSGTextInput);
+ return d->selectionColor;
+}
+
+void QSGTextInput::setSelectionColor(const QColor &color)
+{
+ Q_D(QSGTextInput);
+ if (d->selectionColor == color)
+ return;
+
+ d->selectionColor = color;
+ QPalette p = d->control->palette();
+ p.setColor(QPalette::Highlight, d->selectionColor);
+ d->control->setPalette(p);
+ if (d->control->hasSelectedText())
+ update();
+ emit selectionColorChanged(color);
+}
+
+QColor QSGTextInput::selectedTextColor() const
+{
+ Q_D(const QSGTextInput);
+ return d->selectedTextColor;
+}
+
+void QSGTextInput::setSelectedTextColor(const QColor &color)
+{
+ Q_D(QSGTextInput);
+ if (d->selectedTextColor == color)
+ return;
+
+ d->selectedTextColor = color;
+ QPalette p = d->control->palette();
+ p.setColor(QPalette::HighlightedText, d->selectedTextColor);
+ d->control->setPalette(p);
+ if (d->control->hasSelectedText())
+ update();
+ emit selectedTextColorChanged(color);
+}
+
+QSGTextInput::HAlignment QSGTextInput::hAlign() const
+{
+ Q_D(const QSGTextInput);
+ return d->hAlign;
+}
+
+void QSGTextInput::setHAlign(HAlignment align)
+{
+ Q_D(QSGTextInput);
+ bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
+ d->hAlignImplicit = false;
+ if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
+ updateRect();
+ d->updateHorizontalScroll();
+ }
+}
+
+void QSGTextInput::resetHAlign()
+{
+ Q_D(QSGTextInput);
+ d->hAlignImplicit = true;
+ if (d->determineHorizontalAlignment() && isComponentComplete()) {
+ updateRect();
+ d->updateHorizontalScroll();
+ }
+}
+
+QSGTextInput::HAlignment QSGTextInput::effectiveHAlign() const
+{
+ Q_D(const QSGTextInput);
+ QSGTextInput::HAlignment effectiveAlignment = d->hAlign;
+ if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
+ switch (d->hAlign) {
+ case QSGTextInput::AlignLeft:
+ effectiveAlignment = QSGTextInput::AlignRight;
+ break;
+ case QSGTextInput::AlignRight:
+ effectiveAlignment = QSGTextInput::AlignLeft;
+ break;
+ default:
+ break;
+ }
+ }
+ return effectiveAlignment;
+}
+
+bool QSGTextInputPrivate::setHAlign(QSGTextInput::HAlignment alignment, bool forceAlign)
+{
+ Q_Q(QSGTextInput);
+ if ((hAlign != alignment || forceAlign) && alignment <= QSGTextInput::AlignHCenter) { // justify not supported
+ QSGTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
+ hAlign = alignment;
+ emit q->horizontalAlignmentChanged(alignment);
+ if (oldEffectiveHAlign != q->effectiveHAlign())
+ emit q->effectiveHorizontalAlignmentChanged();
+ return true;
+ }
+ return false;
+}
+
+bool QSGTextInputPrivate::determineHorizontalAlignment()
+{
+ if (hAlignImplicit) {
+ // if no explicit alignment has been set, follow the natural layout direction of the text
+ QString text = control->text();
+ bool isRightToLeft = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
+ return setHAlign(isRightToLeft ? QSGTextInput::AlignRight : QSGTextInput::AlignLeft);
+ }
+ return false;
+}
+
+void QSGTextInputPrivate::mirrorChange()
+{
+ Q_Q(QSGTextInput);
+ if (q->isComponentComplete()) {
+ if (!hAlignImplicit && (hAlign == QSGTextInput::AlignRight || hAlign == QSGTextInput::AlignLeft)) {
+ q->updateRect();
+ updateHorizontalScroll();
+ emit q->effectiveHorizontalAlignmentChanged();
+ }
+ }
+}
+
+bool QSGTextInput::isReadOnly() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->isReadOnly();
+}
+
+void QSGTextInput::setReadOnly(bool ro)
+{
+ Q_D(QSGTextInput);
+ if (d->control->isReadOnly() == ro)
+ return;
+
+ setFlag(QSGItem::ItemAcceptsInputMethod, !ro);
+ d->control->setReadOnly(ro);
+
+ emit readOnlyChanged(ro);
+}
+
+int QSGTextInput::maxLength() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->maxLength();
+}
+
+void QSGTextInput::setMaxLength(int ml)
+{
+ Q_D(QSGTextInput);
+ if (d->control->maxLength() == ml)
+ return;
+
+ d->control->setMaxLength(ml);
+
+ emit maximumLengthChanged(ml);
+}
+
+bool QSGTextInput::isCursorVisible() const
+{
+ Q_D(const QSGTextInput);
+ return d->cursorVisible;
+}
+
+void QSGTextInput::setCursorVisible(bool on)
+{
+ Q_D(QSGTextInput);
+ if (d->cursorVisible == on)
+ return;
+ d->cursorVisible = on;
+ d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0);
+ QRect r = d->control->cursorRect();
+ if (d->control->inputMask().isEmpty())
+ updateRect(r);
+ else
+ updateRect();
+ emit cursorVisibleChanged(d->cursorVisible);
+}
+
+int QSGTextInput::cursorPosition() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->cursor();
+}
+void QSGTextInput::setCursorPosition(int cp)
+{
+ Q_D(QSGTextInput);
+ if (cp < 0 || cp > d->control->text().length())
+ return;
+ d->control->moveCursor(cp);
+}
+
+QRect QSGTextInput::cursorRectangle() const
+{
+ Q_D(const QSGTextInput);
+ QRect r = d->control->cursorRect();
+ // Scroll and make consistent with TextEdit
+ // QLineControl inexplicably adds 1 to the height and horizontal padding
+ // for unicode direction markers.
+ r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1);
+ return r;
+}
+
+int QSGTextInput::selectionStart() const
+{
+ Q_D(const QSGTextInput);
+ return d->lastSelectionStart;
+}
+
+int QSGTextInput::selectionEnd() const
+{
+ Q_D(const QSGTextInput);
+ return d->lastSelectionEnd;
+}
+
+void QSGTextInput::select(int start, int end)
+{
+ Q_D(QSGTextInput);
+ if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length())
+ return;
+ d->control->setSelection(start, end-start);
+}
+
+QString QSGTextInput::selectedText() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->selectedText();
+}
+
+bool QSGTextInput::focusOnPress() const
+{
+ Q_D(const QSGTextInput);
+ return d->focusOnPress;
+}
+
+void QSGTextInput::setFocusOnPress(bool b)
+{
+ Q_D(QSGTextInput);
+ if (d->focusOnPress == b)
+ return;
+
+ d->focusOnPress = b;
+
+ emit activeFocusOnPressChanged(d->focusOnPress);
+}
+
+bool QSGTextInput::autoScroll() const
+{
+ Q_D(const QSGTextInput);
+ return d->autoScroll;
+}
+
+void QSGTextInput::setAutoScroll(bool b)
+{
+ Q_D(QSGTextInput);
+ if (d->autoScroll == b)
+ return;
+
+ d->autoScroll = b;
+ //We need to repaint so that the scrolling is taking into account.
+ updateSize(true);
+ d->updateHorizontalScroll();
+ emit autoScrollChanged(d->autoScroll);
+}
+
+#ifndef QT_NO_VALIDATOR
+QValidator* QSGTextInput::validator() const
+{
+ Q_D(const QSGTextInput);
+ //###const cast isn't good, but needed for property system?
+ return const_cast<QValidator*>(d->control->validator());
+}
+
+void QSGTextInput::setValidator(QValidator* v)
+{
+ Q_D(QSGTextInput);
+ if (d->control->validator() == v)
+ return;
+
+ d->control->setValidator(v);
+ if(!d->control->hasAcceptableInput()){
+ d->oldValidity = false;
+ emit acceptableInputChanged();
+ }
+
+ emit validatorChanged();
+}
+#endif // QT_NO_VALIDATOR
+
+QString QSGTextInput::inputMask() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->inputMask();
+}
+
+void QSGTextInput::setInputMask(const QString &im)
+{
+ Q_D(QSGTextInput);
+ if (d->control->inputMask() == im)
+ return;
+
+ d->control->setInputMask(im);
+ emit inputMaskChanged(d->control->inputMask());
+}
+
+bool QSGTextInput::hasAcceptableInput() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->hasAcceptableInput();
+}
+
+void QSGTextInputPrivate::updateInputMethodHints()
+{
+ Q_Q(QSGTextInput);
+ Qt::InputMethodHints hints = inputMethodHints;
+ uint echo = control->echoMode();
+ if (echo == QSGTextInput::Password || echo == QSGTextInput::NoEcho)
+ hints |= Qt::ImhHiddenText;
+ else if (echo == QSGTextInput::PasswordEchoOnEdit)
+ hints &= ~Qt::ImhHiddenText;
+ if (echo != QSGTextInput::Normal)
+ hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
+ q->setInputMethodHints(hints);
+}
+
+QSGTextInput::EchoMode QSGTextInput::echoMode() const
+{
+ Q_D(const QSGTextInput);
+ return (QSGTextInput::EchoMode)d->control->echoMode();
+}
+
+void QSGTextInput::setEchoMode(QSGTextInput::EchoMode echo)
+{
+ Q_D(QSGTextInput);
+ if (echoMode() == echo)
+ return;
+ d->control->setEchoMode((uint)echo);
+ d->updateInputMethodHints();
+ q_textChanged();
+ emit echoModeChanged(echoMode());
+}
+
+Qt::InputMethodHints QSGTextInput::imHints() const
+{
+ Q_D(const QSGTextInput);
+ return d->inputMethodHints;
+}
+
+void QSGTextInput::setIMHints(Qt::InputMethodHints hints)
+{
+ Q_D(QSGTextInput);
+ if (d->inputMethodHints == hints)
+ return;
+ d->inputMethodHints = hints;
+ d->updateInputMethodHints();
+}
+
+QDeclarativeComponent* QSGTextInput::cursorDelegate() const
+{
+ Q_D(const QSGTextInput);
+ return d->cursorComponent;
+}
+
+void QSGTextInput::setCursorDelegate(QDeclarativeComponent* c)
+{
+ Q_D(QSGTextInput);
+ if (d->cursorComponent == c)
+ return;
+
+ d->cursorComponent = c;
+ if(!c){
+ //note that the components are owned by something else
+ disconnect(d->control, SIGNAL(cursorPositionChanged(int,int)),
+ this, SLOT(moveCursor()));
+ disconnect(d->control, SIGNAL(updateMicroFocus()),
+ this, SLOT(moveCursor()));
+ delete d->cursorItem;
+ }else{
+ d->startCreatingCursor();
+ }
+
+ emit cursorDelegateChanged();
+}
+
+void QSGTextInputPrivate::startCreatingCursor()
+{
+ Q_Q(QSGTextInput);
+ q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
+ q, SLOT(moveCursor()), Qt::UniqueConnection);
+ q->connect(control, SIGNAL(updateMicroFocus()),
+ q, SLOT(moveCursor()), Qt::UniqueConnection);
+ if(cursorComponent->isReady()){
+ q->createCursor();
+ }else if(cursorComponent->isLoading()){
+ q->connect(cursorComponent, SIGNAL(statusChanged(int)),
+ q, SLOT(createCursor()));
+ }else {//isError
+ qmlInfo(q, cursorComponent->errors()) << QSGTextInput::tr("Could not load cursor delegate");
+ }
+}
+
+void QSGTextInput::createCursor()
+{
+ Q_D(QSGTextInput);
+ if(d->cursorComponent->isError()){
+ qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
+ return;
+ }
+
+ if(!d->cursorComponent->isReady())
+ return;
+
+ if(d->cursorItem)
+ delete d->cursorItem;
+ d->cursorItem = qobject_cast<QSGItem*>(d->cursorComponent->create());
+ if(!d->cursorItem){
+ qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
+ return;
+ }
+
+ QDeclarative_setParent_noEvent(d->cursorItem, this);
+ d->cursorItem->setParentItem(this);
+ d->cursorItem->setX(d->control->cursorToX());
+ d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
+}
+
+void QSGTextInput::moveCursor()
+{
+ Q_D(QSGTextInput);
+ if(!d->cursorItem)
+ return;
+ d->updateHorizontalScroll();
+ d->cursorItem->setX(d->control->cursorToX() - d->hscroll);
+}
+
+QRectF QSGTextInput::positionToRectangle(int pos) const
+{
+ Q_D(const QSGTextInput);
+ if (pos > d->control->cursorPosition())
+ pos += d->control->preeditAreaText().length();
+ return QRectF(d->control->cursorToX(pos)-d->hscroll,
+ 0.0,
+ d->control->cursorWidth(),
+ cursorRectangle().height());
+}
+
+int QSGTextInput::positionAt(int x) const
+{
+ return positionAt(x, CursorBetweenCharacters);
+}
+
+int QSGTextInput::positionAt(int x, CursorPosition position) const
+{
+ Q_D(const QSGTextInput);
+ int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position));
+ const int cursor = d->control->cursor();
+ if (pos > cursor) {
+ const int preeditLength = d->control->preeditAreaText().length();
+ pos = pos > cursor + preeditLength
+ ? pos - preeditLength
+ : cursor;
+ }
+ return pos;
+}
+
+void QSGTextInput::keyPressEvent(QKeyEvent* ev)
+{
+ Q_D(QSGTextInput);
+ // Don't allow MacOSX up/down support, and we don't allow a completer.
+ bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
+ if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
+ // Ignore when moving off the end unless there is a selection,
+ // because then moving will do something (deselect).
+ int cursorPosition = d->control->cursor();
+ if (cursorPosition == 0)
+ ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
+ if (cursorPosition == d->control->text().length())
+ ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
+ }
+ if (ignore) {
+ ev->ignore();
+ } else {
+ d->control->processKeyEvent(ev);
+ }
+ if (!ev->isAccepted())
+ QSGPaintedItem::keyPressEvent(ev);
+}
+
+void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev)
+{
+ Q_D(QSGTextInput);
+ const bool wasComposing = d->control->preeditAreaText().length() > 0;
+ if (d->control->isReadOnly()) {
+ ev->ignore();
+ } else {
+ d->control->processInputMethodEvent(ev);
+ updateSize();
+ d->updateHorizontalScroll();
+ }
+ if (!ev->isAccepted())
+ QSGPaintedItem::inputMethodEvent(ev);
+
+ if (wasComposing != (d->control->preeditAreaText().length() > 0))
+ emit inputMethodComposingChanged();
+}
+
+void QSGTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextInput);
+ if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick))
+ return;
+ if (d->selectByMouse) {
+ int cursor = d->xToPos(event->pos().x());
+ d->control->selectWordAtPos(cursor);
+ event->setAccepted(true);
+ } else {
+ QSGPaintedItem::mouseDoubleClickEvent(event);
+ }
+}
+
+void QSGTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextInput);
+ if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress))
+ return;
+ if(d->focusOnPress){
+ bool hadActiveFocus = hasActiveFocus();
+ forceActiveFocus();
+ if (d->showInputPanelOnFocus) {
+ if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
+ // re-open input panel on press if already focused
+ openSoftwareInputPanel();
+ }
+ } else { // show input panel on click
+ if (hasActiveFocus() && !hadActiveFocus) {
+ d->clickCausedFocus = true;
+ }
+ }
+ }
+ if (d->selectByMouse) {
+ setKeepMouseGrab(false);
+ d->selectPressed = true;
+ d->pressPos = event->pos();
+ }
+ bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
+ int cursor = d->xToPos(event->pos().x());
+ d->control->moveCursor(cursor, mark);
+ event->setAccepted(true);
+}
+
+void QSGTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextInput);
+ if (d->sendMouseEventToInputContext(event, QEvent::MouseMove))
+ return;
+ if (d->selectPressed) {
+ if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance())
+ setKeepMouseGrab(true);
+ moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode);
+ event->setAccepted(true);
+ } else {
+ QSGPaintedItem::mouseMoveEvent(event);
+ }
+}
+
+void QSGTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ Q_D(QSGTextInput);
+ if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease))
+ return;
+ if (d->selectPressed) {
+ d->selectPressed = false;
+ setKeepMouseGrab(false);
+ }
+ if (!d->showInputPanelOnFocus) { // input panel on click
+ if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
+ if (canvas() && canvas() == qApp->focusWidget()) {
+ qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
+ }
+ }
+ }
+ d->clickCausedFocus = false;
+ d->control->processEvent(event);
+ if (!event->isAccepted())
+ QSGPaintedItem::mouseReleaseEvent(event);
+}
+
+bool QSGTextInputPrivate::sendMouseEventToInputContext(
+ QGraphicsSceneMouseEvent *event, QEvent::Type eventType)
+{
+#if !defined QT_NO_IM
+ if (event->widget() && control->composeMode()) {
+ int tmp_cursor = xToPos(event->pos().x());
+ int mousePos = tmp_cursor - control->cursor();
+ if (mousePos < 0 || mousePos > control->preeditAreaText().length()) {
+ mousePos = -1;
+ // don't send move events outside the preedit area
+ if (eventType == QEvent::MouseMove)
+ return true;
+ }
+
+ QInputContext *qic = event->widget()->inputContext();
+ if (qic) {
+ QMouseEvent mouseEvent(
+ eventType,
+ event->widget()->mapFromGlobal(event->screenPos()),
+ event->screenPos(),
+ event->button(),
+ event->buttons(),
+ event->modifiers());
+ // may be causing reset() in some input methods
+ qic->mouseHandler(mousePos, &mouseEvent);
+ event->setAccepted(mouseEvent.isAccepted());
+ }
+ if (!control->preeditAreaText().isEmpty())
+ return true;
+ }
+#else
+ Q_UNUSED(event);
+ Q_UNUSED(eventType)
+#endif
+
+ return false;
+}
+
+void QSGTextInput::mouseUngrabEvent()
+{
+ Q_D(QSGTextInput);
+ d->selectPressed = false;
+ setKeepMouseGrab(false);
+}
+
+bool QSGTextInput::event(QEvent* ev)
+{
+ Q_D(QSGTextInput);
+ //Anything we don't deal with ourselves, pass to the control
+ bool handled = false;
+ switch(ev->type()){
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease://###Should the control be doing anything with release?
+ case QEvent::InputMethod:
+ case QEvent::GraphicsSceneMousePress:
+ case QEvent::GraphicsSceneMouseMove:
+ case QEvent::GraphicsSceneMouseRelease:
+ case QEvent::GraphicsSceneMouseDoubleClick:
+ break;
+ default:
+ handled = d->control->processEvent(ev);
+ }
+ if(!handled)
+ handled = QSGPaintedItem::event(ev);
+ return handled;
+}
+
+void QSGTextInput::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ Q_D(QSGTextInput);
+ if (newGeometry.width() != oldGeometry.width()) {
+ updateSize();
+ d->updateHorizontalScroll();
+ }
+ QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
+}
+
+int QSGTextInputPrivate::calculateTextWidth()
+{
+ return qRound(control->naturalTextWidth());
+}
+
+void QSGTextInputPrivate::updateHorizontalScroll()
+{
+ Q_Q(QSGTextInput);
+ const int preeditLength = control->preeditAreaText().length();
+ int cix = qRound(control->cursorToX(control->cursor() + preeditLength));
+ QRect br(q->boundingRect().toRect());
+ int widthUsed = calculateTextWidth();
+
+ QSGTextInput::HAlignment effectiveHAlign = q->effectiveHAlign();
+ if (autoScroll) {
+ if (widthUsed <= br.width()) {
+ // text fits in br; use hscroll for alignment
+ switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
+ case Qt::AlignRight:
+ hscroll = widthUsed - br.width() - 1;
+ break;
+ case Qt::AlignHCenter:
+ hscroll = (widthUsed - br.width()) / 2;
+ break;
+ default:
+ // Left
+ hscroll = 0;
+ break;
+ }
+ } else if (cix - hscroll >= br.width()) {
+ // text doesn't fit, cursor is to the right of br (scroll right)
+ hscroll = cix - br.width() + 1;
+ } else if (cix - hscroll < 0 && hscroll < widthUsed) {
+ // text doesn't fit, cursor is to the left of br (scroll left)
+ hscroll = cix;
+ } else if (widthUsed - hscroll < br.width()) {
+ // text doesn't fit, text document is to the left of br; align
+ // right
+ hscroll = widthUsed - br.width() + 1;
+ }
+ if (preeditLength > 0) {
+ // check to ensure long pre-edit text doesn't push the cursor
+ // off to the left
+ cix = qRound(control->cursorToX(
+ control->cursor() + qMax(0, control->preeditCursor() - 1)));
+ if (cix < hscroll)
+ hscroll = cix;
+ }
+ } else {
+ switch (effectiveHAlign) {
+ case QSGTextInput::AlignRight:
+ hscroll = q->width() - widthUsed;
+ break;
+ case QSGTextInput::AlignHCenter:
+ hscroll = (q->width() - widthUsed) / 2;
+ break;
+ default:
+ // Left
+ hscroll = 0;
+ break;
+ }
+ }
+}
+
+void QSGTextInput::paint(QPainter *p)
+{
+ // XXX todo
+ QRect r(0, 0, width(), height());
+
+ Q_D(QSGTextInput);
+ p->setRenderHint(QPainter::TextAntialiasing, true);
+ p->save();
+ p->setPen(QPen(d->color));
+ int flags = QLineControl::DrawText;
+ if(!isReadOnly() && d->cursorVisible && !d->cursorItem)
+ flags |= QLineControl::DrawCursor;
+ if (d->control->hasSelectedText())
+ flags |= QLineControl::DrawSelections;
+ QPoint offset = QPoint(0,0);
+ QFontMetrics fm = QFontMetrics(d->font);
+ QRect br(boundingRect().toRect());
+ if (d->autoScroll) {
+ // the y offset is there to keep the baseline constant in case we have script changes in the text.
+ offset = br.topLeft() - QPoint(d->hscroll, d->control->ascent() - fm.ascent());
+ } else {
+ offset = QPoint(d->hscroll, 0);
+ }
+ d->control->draw(p, offset, r, flags);
+ p->restore();
+}
+
+QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
+{
+ Q_D(const QSGTextInput);
+ switch(property) {
+ case Qt::ImMicroFocus:
+ return cursorRectangle();
+ case Qt::ImFont:
+ return font();
+ case Qt::ImCursorPosition:
+ return QVariant(d->control->cursor());
+ case Qt::ImSurroundingText:
+ if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing())
+ return QVariant(displayText());
+ else
+ return QVariant(text());
+ case Qt::ImCurrentSelection:
+ return QVariant(selectedText());
+ case Qt::ImMaximumTextLength:
+ return QVariant(maxLength());
+ case Qt::ImAnchorPosition:
+ if (d->control->selectionStart() == d->control->selectionEnd())
+ return QVariant(d->control->cursor());
+ else if (d->control->selectionStart() == d->control->cursor())
+ return QVariant(d->control->selectionEnd());
+ else
+ return QVariant(d->control->selectionStart());
+ default:
+ return QVariant();
+ }
+}
+
+void QSGTextInput::deselect()
+{
+ Q_D(QSGTextInput);
+ d->control->deselect();
+}
+
+void QSGTextInput::selectAll()
+{
+ Q_D(QSGTextInput);
+ d->control->setSelection(0, d->control->text().length());
+}
+
+bool QSGTextInput::isRightToLeft(int start, int end)
+{
+ Q_D(QSGTextInput);
+ if (start > end) {
+ qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
+ return false;
+ } else {
+ return d->control->text().mid(start, end - start).isRightToLeft();
+ }
+}
+
+#ifndef QT_NO_CLIPBOARD
+void QSGTextInput::cut()
+{
+ Q_D(QSGTextInput);
+ d->control->copy();
+ d->control->del();
+}
+
+void QSGTextInput::copy()
+{
+ Q_D(QSGTextInput);
+ d->control->copy();
+}
+
+void QSGTextInput::paste()
+{
+ Q_D(QSGTextInput);
+ if (!d->control->isReadOnly())
+ d->control->paste();
+}
+#endif // QT_NO_CLIPBOARD
+
+void QSGTextInput::selectWord()
+{
+ Q_D(QSGTextInput);
+ d->control->selectWordAtPos(d->control->cursor());
+}
+
+QString QSGTextInput::passwordCharacter() const
+{
+ Q_D(const QSGTextInput);
+ return QString(d->control->passwordCharacter());
+}
+
+void QSGTextInput::setPasswordCharacter(const QString &str)
+{
+ Q_D(QSGTextInput);
+ if(str.length() < 1)
+ return;
+ d->control->setPasswordCharacter(str.constData()[0]);
+ EchoMode echoMode_ = echoMode();
+ if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) {
+ updateSize();
+ }
+ emit passwordCharacterChanged();
+}
+
+QString QSGTextInput::displayText() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->displayText();
+}
+
+bool QSGTextInput::selectByMouse() const
+{
+ Q_D(const QSGTextInput);
+ return d->selectByMouse;
+}
+
+void QSGTextInput::setSelectByMouse(bool on)
+{
+ Q_D(QSGTextInput);
+ if (d->selectByMouse != on) {
+ d->selectByMouse = on;
+ emit selectByMouseChanged(on);
+ }
+}
+
+QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const
+{
+ Q_D(const QSGTextInput);
+ return d->mouseSelectionMode;
+}
+
+void QSGTextInput::setMouseSelectionMode(SelectionMode mode)
+{
+ Q_D(QSGTextInput);
+ if (d->mouseSelectionMode != mode) {
+ d->mouseSelectionMode = mode;
+ emit mouseSelectionModeChanged(mode);
+ }
+}
+
+bool QSGTextInput::canPaste() const
+{
+ Q_D(const QSGTextInput);
+ return d->canPaste;
+}
+
+void QSGTextInput::moveCursorSelection(int position)
+{
+ Q_D(QSGTextInput);
+ d->control->moveCursor(position, true);
+ d->updateHorizontalScroll();
+}
+
+void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode)
+{
+ Q_D(QSGTextInput);
+
+ if (mode == SelectCharacters) {
+ d->control->moveCursor(pos, true);
+ } else if (pos != d->control->cursor()){
+ const int cursor = d->control->cursor();
+ int anchor;
+ if (!d->control->hasSelectedText())
+ anchor = d->control->cursor();
+ else if (d->control->selectionStart() == d->control->cursor())
+ anchor = d->control->selectionEnd();
+ else
+ anchor = d->control->selectionStart();
+
+ if (anchor < pos || (anchor == pos && cursor < pos)) {
+ const QString text = d->control->text();
+ QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
+ finder.setPosition(anchor);
+
+ const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
+ if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
+ || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
+ finder.toPreviousBoundary();
+ }
+ anchor = finder.position() != -1 ? finder.position() : 0;
+
+ finder.setPosition(pos);
+ if (pos > 0 && !finder.boundaryReasons())
+ finder.toNextBoundary();
+ const int cursor = finder.position() != -1 ? finder.position() : text.length();
+
+ d->control->setSelection(anchor, cursor - anchor);
+ } else if (anchor > pos || (anchor == pos && cursor > pos)) {
+ const QString text = d->control->text();
+ QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
+ finder.setPosition(anchor);
+
+ const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
+ if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
+ || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
+ finder.toNextBoundary();
+ }
+
+ anchor = finder.position() != -1 ? finder.position() : text.length();
+
+ finder.setPosition(pos);
+ if (pos < text.length() && !finder.boundaryReasons())
+ finder.toPreviousBoundary();
+ const int cursor = finder.position() != -1 ? finder.position() : 0;
+
+ d->control->setSelection(anchor, cursor - anchor);
+ }
+ }
+}
+
+void QSGTextInput::openSoftwareInputPanel()
+{
+ QEvent event(QEvent::RequestSoftwareInputPanel);
+ if (qApp) {
+ if (canvas() && canvas() == qApp->focusWidget()) {
+ QEvent event(QEvent::RequestSoftwareInputPanel);
+ QApplication::sendEvent(canvas(), &event);
+ }
+ }
+}
+
+void QSGTextInput::closeSoftwareInputPanel()
+{
+ if (qApp) {
+ if (canvas() && canvas() == qApp->focusWidget()) {
+ QEvent event(QEvent::CloseSoftwareInputPanel);
+ QApplication::sendEvent(canvas(), &event);
+ }
+ }
+}
+
+void QSGTextInput::focusInEvent(QFocusEvent *event)
+{
+ Q_D(const QSGTextInput);
+ if (d->showInputPanelOnFocus) {
+ if (d->focusOnPress && !isReadOnly()) {
+ openSoftwareInputPanel();
+ }
+ }
+ QSGPaintedItem::focusInEvent(event);
+}
+
+void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value)
+{
+ Q_D(QSGTextInput);
+ if (change == ItemActiveFocusHasChanged) {
+ bool hasFocus = value.boolValue;
+ d->focused = hasFocus;
+ setCursorVisible(hasFocus && d->canvas && d->canvas->hasFocus());
+ if(echoMode() == QSGTextInput::PasswordEchoOnEdit && !hasFocus)
+ d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events
+ if (!hasFocus)
+ d->control->deselect();
+ }
+ QSGItem::itemChange(change, value);
+}
+
+bool QSGTextInput::isInputMethodComposing() const
+{
+ Q_D(const QSGTextInput);
+ return d->control->preeditAreaText().length() > 0;
+}
+
+void QSGTextInputPrivate::init()
+{
+ Q_Q(QSGTextInput);
+ control->setParent(q);//Now mandatory due to accessibility changes
+ control->setCursorWidth(1);
+ control->setPasswordCharacter(QLatin1Char('*'));
+ q->setSmooth(smooth);
+ q->setAcceptedMouseButtons(Qt::LeftButton);
+ q->setFlag(QSGItem::ItemAcceptsInputMethod);
+ q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
+ q, SLOT(cursorPosChanged()));
+ q->connect(control, SIGNAL(selectionChanged()),
+ q, SLOT(selectionChanged()));
+ q->connect(control, SIGNAL(textChanged(QString)),
+ q, SLOT(q_textChanged()));
+ q->connect(control, SIGNAL(accepted()),
+ q, SIGNAL(accepted()));
+ q->connect(control, SIGNAL(updateNeeded(QRect)),
+ q, SLOT(updateRect(QRect)));
+#ifndef QT_NO_CLIPBOARD
+ q->connect(q, SIGNAL(readOnlyChanged(bool)),
+ q, SLOT(q_canPasteChanged()));
+ q->connect(QApplication::clipboard(), SIGNAL(dataChanged()),
+ q, SLOT(q_canPasteChanged()));
+ canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
+#endif // QT_NO_CLIPBOARD
+ q->connect(control, SIGNAL(updateMicroFocus()),
+ q, SLOT(updateMicroFocus()));
+ q->connect(control, SIGNAL(displayTextChanged(QString)),
+ q, SLOT(updateRect()));
+ q->updateSize();
+ oldValidity = control->hasAcceptableInput();
+ lastSelectionStart = 0;
+ lastSelectionEnd = 0;
+ QPalette p = control->palette();
+ selectedTextColor = p.color(QPalette::HighlightedText);
+ selectionColor = p.color(QPalette::Highlight);
+ determineHorizontalAlignment();
+}
+
+void QSGTextInput::cursorPosChanged()
+{
+ Q_D(QSGTextInput);
+ d->updateHorizontalScroll();
+ updateRect();//TODO: Only update rect between pos's
+ updateMicroFocus();
+ emit cursorPositionChanged();
+ // XXX todo - not in 4.8?
+#if 0
+ d->control->resetCursorBlinkTimer();
+#endif
+
+ if(!d->control->hasSelectedText()){
+ if(d->lastSelectionStart != d->control->cursor()){
+ d->lastSelectionStart = d->control->cursor();
+ emit selectionStartChanged();
+ }
+ if(d->lastSelectionEnd != d->control->cursor()){
+ d->lastSelectionEnd = d->control->cursor();
+ emit selectionEndChanged();
+ }
+ }
+}
+
+void QSGTextInput::selectionChanged()
+{
+ Q_D(QSGTextInput);
+ updateRect();//TODO: Only update rect in selection
+ emit selectedTextChanged();
+
+ if(d->lastSelectionStart != d->control->selectionStart()){
+ d->lastSelectionStart = d->control->selectionStart();
+ if(d->lastSelectionStart == -1)
+ d->lastSelectionStart = d->control->cursor();
+ emit selectionStartChanged();
+ }
+ if(d->lastSelectionEnd != d->control->selectionEnd()){
+ d->lastSelectionEnd = d->control->selectionEnd();
+ if(d->lastSelectionEnd == -1)
+ d->lastSelectionEnd = d->control->cursor();
+ emit selectionEndChanged();
+ }
+}
+
+void QSGTextInput::q_textChanged()
+{
+ Q_D(QSGTextInput);
+ updateSize();
+ d->determineHorizontalAlignment();
+ d->updateHorizontalScroll();
+ updateMicroFocus();
+ emit textChanged();
+ emit displayTextChanged();
+ if(hasAcceptableInput() != d->oldValidity){
+ d->oldValidity = hasAcceptableInput();
+ emit acceptableInputChanged();
+ }
+}
+
+void QSGTextInput::updateRect(const QRect &r)
+{
+ Q_D(QSGTextInput);
+ if(r == QRect())
+ update();
+ else
+ update(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height()));
+}
+
+QRectF QSGTextInput::boundingRect() const
+{
+ Q_D(const QSGTextInput);
+ QRectF r = QSGPaintedItem::boundingRect();
+
+ int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth();
+
+ // Could include font max left/right bearings to either side of rectangle.
+
+ r.setRight(r.right() + cursorWidth);
+ return r;
+}
+
+void QSGTextInput::updateSize(bool needsRedraw)
+{
+ Q_D(QSGTextInput);
+ int w = width();
+ int h = height();
+ setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
+ setImplicitWidth(d->calculateTextWidth());
+ setContentsSize(QSize(width(), height()));
+ if(w==width() && h==height() && needsRedraw)
+ update();
+}
+
+void QSGTextInput::q_canPasteChanged()
+{
+ Q_D(QSGTextInput);
+ bool old = d->canPaste;
+#ifndef QT_NO_CLIPBOARD
+ d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
+#endif
+ if(d->canPaste != old)
+ emit canPasteChanged();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/declarative/items/qsgtextinput_p.h b/src/declarative/items/qsgtextinput_p.h
new file mode 100644
index 0000000000..ccea485e99
--- /dev/null
+++ b/src/declarative/items/qsgtextinput_p.h
@@ -0,0 +1,302 @@
+// Commit: 2f173e4945dd8414636c1061acfaf9c2d8b718d8
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXTINPUT_P_H
+#define QSGTEXTINPUT_P_H
+
+#include "qsgimplicitsizeitem_p.h"
+#include <QtGui/qvalidator.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGTextInputPrivate;
+class QValidator;
+class Q_AUTOTEST_EXPORT QSGTextInput : public QSGImplicitSizePaintedItem
+{
+ Q_OBJECT
+ Q_ENUMS(HAlignment)
+ Q_ENUMS(EchoMode)
+ Q_ENUMS(SelectionMode)
+
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
+ Q_PROPERTY(QColor selectionColor READ selectionColor WRITE setSelectionColor NOTIFY selectionColorChanged)
+ Q_PROPERTY(QColor selectedTextColor READ selectedTextColor WRITE setSelectedTextColor NOTIFY selectedTextColorChanged)
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
+ Q_PROPERTY(HAlignment horizontalAlignment READ hAlign WRITE setHAlign RESET resetHAlign NOTIFY horizontalAlignmentChanged)
+ Q_PROPERTY(HAlignment effectiveHorizontalAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged)
+
+ Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged)
+ Q_PROPERTY(bool cursorVisible READ isCursorVisible WRITE setCursorVisible NOTIFY cursorVisibleChanged)
+ Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
+ Q_PROPERTY(QRect cursorRectangle READ cursorRectangle NOTIFY cursorPositionChanged)
+ Q_PROPERTY(QDeclarativeComponent *cursorDelegate READ cursorDelegate WRITE setCursorDelegate NOTIFY cursorDelegateChanged)
+ Q_PROPERTY(int selectionStart READ selectionStart NOTIFY selectionStartChanged)
+ Q_PROPERTY(int selectionEnd READ selectionEnd NOTIFY selectionEndChanged)
+ Q_PROPERTY(QString selectedText READ selectedText NOTIFY selectedTextChanged)
+
+ Q_PROPERTY(int maximumLength READ maxLength WRITE setMaxLength NOTIFY maximumLengthChanged)
+#ifndef QT_NO_VALIDATOR
+ Q_PROPERTY(QValidator* validator READ validator WRITE setValidator NOTIFY validatorChanged)
+#endif
+ Q_PROPERTY(QString inputMask READ inputMask WRITE setInputMask NOTIFY inputMaskChanged)
+ Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ imHints WRITE setIMHints)
+
+ Q_PROPERTY(bool acceptableInput READ hasAcceptableInput NOTIFY acceptableInputChanged)
+ Q_PROPERTY(EchoMode echoMode READ echoMode WRITE setEchoMode NOTIFY echoModeChanged)
+ Q_PROPERTY(bool activeFocusOnPress READ focusOnPress WRITE setFocusOnPress NOTIFY activeFocusOnPressChanged)
+ Q_PROPERTY(QString passwordCharacter READ passwordCharacter WRITE setPasswordCharacter NOTIFY passwordCharacterChanged)
+ Q_PROPERTY(QString displayText READ displayText NOTIFY displayTextChanged)
+ Q_PROPERTY(bool autoScroll READ autoScroll WRITE setAutoScroll NOTIFY autoScrollChanged)
+ Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged)
+ Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged)
+ Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged)
+ Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged)
+
+public:
+ QSGTextInput(QSGItem * parent=0);
+ ~QSGTextInput();
+
+ enum EchoMode {//To match QLineEdit::EchoMode
+ Normal,
+ NoEcho,
+ Password,
+ PasswordEchoOnEdit
+ };
+
+ enum HAlignment {
+ AlignLeft = Qt::AlignLeft,
+ AlignRight = Qt::AlignRight,
+ AlignHCenter = Qt::AlignHCenter
+ };
+
+ enum SelectionMode {
+ SelectCharacters,
+ SelectWords
+ };
+
+ enum CursorPosition {
+ CursorBetweenCharacters,
+ CursorOnCharacter
+ };
+
+ //Auxilliary functions needed to control the TextInput from QML
+ Q_INVOKABLE int positionAt(int x) const;
+ Q_INVOKABLE int positionAt(int x, CursorPosition position) const;
+ Q_INVOKABLE QRectF positionToRectangle(int pos) const;
+ Q_INVOKABLE void moveCursorSelection(int pos);
+ Q_INVOKABLE void moveCursorSelection(int pos, SelectionMode mode);
+
+ Q_INVOKABLE void openSoftwareInputPanel();
+ Q_INVOKABLE void closeSoftwareInputPanel();
+
+ QString text() const;
+ void setText(const QString &);
+
+ QFont font() const;
+ void setFont(const QFont &font);
+
+ QColor color() const;
+ void setColor(const QColor &c);
+
+ QColor selectionColor() const;
+ void setSelectionColor(const QColor &c);
+
+ QColor selectedTextColor() const;
+ void setSelectedTextColor(const QColor &c);
+
+ HAlignment hAlign() const;
+ void setHAlign(HAlignment align);
+ void resetHAlign();
+ HAlignment effectiveHAlign() const;
+
+ bool isReadOnly() const;
+ void setReadOnly(bool);
+
+ bool isCursorVisible() const;
+ void setCursorVisible(bool on);
+
+ int cursorPosition() const;
+ void setCursorPosition(int cp);
+
+ QRect cursorRectangle() const;
+
+ int selectionStart() const;
+ int selectionEnd() const;
+
+ QString selectedText() const;
+
+ int maxLength() const;
+ void setMaxLength(int ml);
+
+#ifndef QT_NO_VALIDATOR
+ QValidator * validator() const;
+ void setValidator(QValidator* v);
+#endif
+ QString inputMask() const;
+ void setInputMask(const QString &im);
+
+ EchoMode echoMode() const;
+ void setEchoMode(EchoMode echo);
+
+ QString passwordCharacter() const;
+ void setPasswordCharacter(const QString &str);
+
+ QString displayText() const;
+
+ QDeclarativeComponent* cursorDelegate() const;
+ void setCursorDelegate(QDeclarativeComponent*);
+
+ bool focusOnPress() const;
+ void setFocusOnPress(bool);
+
+ bool autoScroll() const;
+ void setAutoScroll(bool);
+
+ bool selectByMouse() const;
+ void setSelectByMouse(bool);
+
+ SelectionMode mouseSelectionMode() const;
+ void setMouseSelectionMode(SelectionMode mode);
+
+ bool hasAcceptableInput() const;
+
+ void paint(QPainter *p);
+ QVariant inputMethodQuery(Qt::InputMethodQuery property) const;
+
+ QRectF boundingRect() const;
+ bool canPaste() const;
+
+ bool isInputMethodComposing() const;
+
+ Qt::InputMethodHints imHints() const;
+ void setIMHints(Qt::InputMethodHints hints);
+
+Q_SIGNALS:
+ void textChanged();
+ void cursorPositionChanged();
+ void selectionStartChanged();
+ void selectionEndChanged();
+ void selectedTextChanged();
+ void accepted();
+ void acceptableInputChanged();
+ void colorChanged(const QColor &color);
+ void selectionColorChanged(const QColor &color);
+ void selectedTextColorChanged(const QColor &color);
+ void fontChanged(const QFont &font);
+ void horizontalAlignmentChanged(HAlignment alignment);
+ void readOnlyChanged(bool isReadOnly);
+ void cursorVisibleChanged(bool isCursorVisible);
+ void cursorDelegateChanged();
+ void maximumLengthChanged(int maximumLength);
+ void validatorChanged();
+ void inputMaskChanged(const QString &inputMask);
+ void echoModeChanged(EchoMode echoMode);
+ void passwordCharacterChanged();
+ void displayTextChanged();
+ void activeFocusOnPressChanged(bool activeFocusOnPress);
+ void autoScrollChanged(bool autoScroll);
+ void selectByMouseChanged(bool selectByMouse);
+ void mouseSelectionModeChanged(SelectionMode mode);
+ void canPasteChanged();
+ void inputMethodComposingChanged();
+ void effectiveHorizontalAlignmentChanged();
+
+protected:
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+
+ void mousePressEvent(QGraphicsSceneMouseEvent *event);
+ void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+ void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+ void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
+ bool sceneEvent(QEvent *event);
+ void keyPressEvent(QKeyEvent* ev);
+ void inputMethodEvent(QInputMethodEvent *);
+ void mouseUngrabEvent();
+ bool event(QEvent *e);
+ void focusInEvent(QFocusEvent *event);
+ virtual void itemChange(ItemChange, const ItemChangeData &);
+
+public Q_SLOTS:
+ void selectAll();
+ void selectWord();
+ void select(int start, int end);
+ void deselect();
+ bool isRightToLeft(int start, int end);
+#ifndef QT_NO_CLIPBOARD
+ void cut();
+ void copy();
+ void paste();
+#endif
+
+private Q_SLOTS:
+ void updateSize(bool needsRedraw = true);
+ void q_textChanged();
+ void selectionChanged();
+ void createCursor();
+ void moveCursor();
+ void cursorPosChanged();
+ void updateRect(const QRect &r = QRect());
+ void q_canPasteChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QSGTextInput)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGTextInput)
+#ifndef QT_NO_VALIDATOR
+QML_DECLARE_TYPE(QValidator)
+QML_DECLARE_TYPE(QIntValidator)
+QML_DECLARE_TYPE(QDoubleValidator)
+QML_DECLARE_TYPE(QRegExpValidator)
+#endif
+
+QT_END_HEADER
+
+#endif // QSGTEXTINPUT_P_H
diff --git a/src/declarative/items/qsgtextinput_p_p.h b/src/declarative/items/qsgtextinput_p_p.h
new file mode 100644
index 0000000000..6561d28a31
--- /dev/null
+++ b/src/declarative/items/qsgtextinput_p_p.h
@@ -0,0 +1,152 @@
+// Commit: 47712d1f330e4b22ce6dd30e7557288ef7f7fca0
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXTINPUT_P_P_H
+#define QSGTEXTINPUT_P_P_H
+
+#include "qsgtextinput_p.h"
+#include "qsgtext_p.h"
+#include "qsgimplicitsizeitem_p_p.h"
+
+#include <private/qlinecontrol_p.h>
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtCore/qpointer.h>
+
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QSGTextInputPrivate : public QSGImplicitSizePaintedItemPrivate
+{
+ Q_DECLARE_PUBLIC(QSGTextInput)
+public:
+ QSGTextInputPrivate() : control(new QLineControl(QString())),
+ color((QRgb)0), style(QSGText::Normal),
+ styleColor((QRgb)0), hAlign(QSGTextInput::AlignLeft),
+ mouseSelectionMode(QSGTextInput::SelectCharacters), inputMethodHints(Qt::ImhNone),
+ hscroll(0), oldScroll(0), oldValidity(false), focused(false), focusOnPress(true),
+ showInputPanelOnFocus(true), clickCausedFocus(false), cursorVisible(false),
+ autoScroll(true), selectByMouse(false), canPaste(false), hAlignImplicit(true),
+ selectPressed(false)
+ {
+#ifdef Q_OS_SYMBIAN
+ if (QSysInfo::symbianVersion() == QSysInfo::SV_SF_1 || QSysInfo::symbianVersion() == QSysInfo::SV_SF_3) {
+ showInputPanelOnFocus = false;
+ }
+#endif
+ }
+
+ ~QSGTextInputPrivate()
+ {
+ }
+
+ int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const
+ {
+ Q_Q(const QSGTextInput);
+ QRect cr = q->boundingRect().toRect();
+ x-= cr.x() - hscroll;
+ return control->xToPos(x, betweenOrOn);
+ }
+
+ void init();
+ void startCreatingCursor();
+ void updateHorizontalScroll();
+ bool determineHorizontalAlignment();
+ bool setHAlign(QSGTextInput::HAlignment, bool forceAlign = false);
+ void mirrorChange();
+ int calculateTextWidth();
+ bool sendMouseEventToInputContext(QGraphicsSceneMouseEvent *event, QEvent::Type eventType);
+ void updateInputMethodHints();
+
+ QLineControl* control;
+
+ QFont font;
+ QFont sourceFont;
+ QColor color;
+ QColor selectionColor;
+ QColor selectedTextColor;
+ QSGText::TextStyle style;
+ QColor styleColor;
+ QSGTextInput::HAlignment hAlign;
+ QSGTextInput::SelectionMode mouseSelectionMode;
+ Qt::InputMethodHints inputMethodHints;
+ QPointer<QDeclarativeComponent> cursorComponent;
+ QPointer<QSGItem> cursorItem;
+ QPointF pressPos;
+
+ int lastSelectionStart;
+ int lastSelectionEnd;
+ int oldHeight;
+ int oldWidth;
+ int hscroll;
+ int oldScroll;
+
+ bool oldValidity:1;
+ bool focused:1;
+ bool focusOnPress:1;
+ bool showInputPanelOnFocus:1;
+ bool clickCausedFocus:1;
+ bool cursorVisible:1;
+ bool autoScroll:1;
+ bool selectByMouse:1;
+ bool canPaste:1;
+ bool hAlignImplicit:1;
+ bool selectPressed:1;
+
+ static inline QSGTextInputPrivate *get(QSGTextInput *t) {
+ return t->d_func();
+ }
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTINPUT_P_P_H
diff --git a/src/declarative/items/qsgtextnode.cpp b/src/declarative/items/qsgtextnode.cpp
new file mode 100644
index 0000000000..33325a14ab
--- /dev/null
+++ b/src/declarative/items/qsgtextnode.cpp
@@ -0,0 +1,457 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgtextnode_p.h"
+#include "qsgsimplerectnode.h"
+#include <private/qsgadaptationlayer_p.h>
+#include <private/qsgdistancefieldglyphcache_p.h>
+#include <private/qsgdistancefieldglyphnode_p.h>
+
+#include <private/qsgcontext_p.h>
+
+#include <qmath.h>
+#include <qtextdocument.h>
+#include <qtextlayout.h>
+#include <qabstracttextdocumentlayout.h>
+#include <qxmlstream.h>
+#include <qrawfont.h>
+#include <private/qdeclarativestyledtext_p.h>
+#include <private/qfont_p.h>
+#include <private/qfontengine_p.h>
+#include <private/qrawfont_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Creates an empty QSGTextNode
+*/
+QSGTextNode::QSGTextNode(QSGContext *context)
+: m_context(context)
+{
+#if defined(QML_RUNTIME_TESTING)
+ description = QLatin1String("text");
+#endif
+}
+
+QSGTextNode::~QSGTextNode()
+{
+}
+
+#if 0
+void QSGTextNode::setColor(const QColor &color)
+{
+ if (m_usePixmapCache) {
+ setUpdateFlag(UpdateNodes);
+ } else {
+ for (int i=0; i<childCount(); ++i) {
+ QSGNode *childNode = childAtIndex(i);
+ if (childNode->subType() == GlyphNodeSubType) {
+ QSGGlyphNode *glyphNode = static_cast<QSGGlyphNode *>(childNode);
+ if (glyphNode->color() == m_color)
+ glyphNode->setColor(color);
+ } else if (childNode->subType() == SolidRectNodeSubType) {
+ QSGSimpleRectNode *solidRectNode = static_cast<QSGSimpleRectNode *>(childNode);
+ if (solidRectNode->color() == m_color)
+ solidRectNode->setColor(color);
+ }
+ }
+ }
+ m_color = color;
+}
+
+void QSGTextNode::setStyleColor(const QColor &styleColor)
+{
+ if (m_textStyle != QSGTextNode::NormalTextStyle) {
+ if (m_usePixmapCache) {
+ setUpdateFlag(UpdateNodes);
+ } else {
+ for (int i=0; i<childCount(); ++i) {
+ QSGNode *childNode = childAtIndex(i);
+ if (childNode->subType() == GlyphNodeSubType) {
+ QSGGlyphNode *glyphNode = static_cast<QSGGlyphNode *>(childNode);
+ if (glyphNode->color() == m_styleColor)
+ glyphNode->setColor(styleColor);
+ } else if (childNode->subType() == SolidRectNodeSubType) {
+ QSGSimpleRectNode *solidRectNode = static_cast<QSGSimpleRectNode *>(childNode);
+ if (solidRectNode->color() == m_styleColor)
+ solidRectNode->setColor(styleColor);
+ }
+ }
+ }
+ }
+ m_styleColor = styleColor;
+}
+#endif
+
+void QSGTextNode::addTextDecorations(const QPointF &position, const QRawFont &font, const QColor &color,
+ qreal width, bool hasOverline, bool hasStrikeOut, bool hasUnderline)
+{
+ Q_ASSERT(font.isValid());
+ QRawFontPrivate *dptrFont = QRawFontPrivate::get(font);
+ QFontEngine *fontEngine = dptrFont->fontEngine;
+
+ qreal lineThickness = fontEngine->lineThickness().toReal();
+
+ QRectF line(position.x(), position.y() - lineThickness / 2.0, width, lineThickness);
+
+ if (hasUnderline) {
+ int underlinePosition = fontEngine->underlinePosition().ceil().toInt();
+ QRectF underline(line);
+ underline.translate(0.0, underlinePosition);
+ appendChildNode(new QSGSimpleRectNode(underline, color));
+ }
+
+ qreal ascent = font.ascent();
+ if (hasOverline) {
+ QRectF overline(line);
+ overline.translate(0.0, -ascent);
+ appendChildNode(new QSGSimpleRectNode(overline, color));
+ }
+
+ if (hasStrikeOut) {
+ QRectF strikeOut(line);
+ strikeOut.translate(0.0, ascent / -3.0);
+ appendChildNode(new QSGSimpleRectNode(strikeOut, color));
+ }
+}
+
+QSGGlyphNode *QSGTextNode::addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QSGText::TextStyle style, const QColor &styleColor)
+{
+ QSGGlyphNode *node = m_context->createGlyphNode();
+ if (QSGDistanceFieldGlyphCache::distanceFieldEnabled()) {
+ QSGDistanceFieldGlyphNode *dfNode = static_cast<QSGDistanceFieldGlyphNode *>(node);
+ dfNode->setStyle(style);
+ dfNode->setStyleColor(styleColor);
+ }
+ node->setGlyphs(position, glyphs);
+ node->setColor(color);
+
+ appendChildNode(node);
+
+ return node;
+}
+
+void QSGTextNode::addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color,
+ QSGText::TextStyle style, const QColor &styleColor)
+{
+ Q_UNUSED(position)
+ QTextFrame *textFrame = textDocument->rootFrame();
+ QPointF p = textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft();
+
+ QTextFrame::iterator it = textFrame->begin();
+ while (!it.atEnd()) {
+ addTextBlock(p, textDocument, it.currentBlock(), color, style, styleColor);
+ ++it;
+ }
+}
+
+void QSGTextNode::addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color,
+ QSGText::TextStyle style, const QColor &styleColor)
+{
+ QList<QGlyphRun> glyphsList(textLayout->glyphRuns());
+ for (int i=0; i<glyphsList.size(); ++i) {
+ QGlyphRun glyphs = glyphsList.at(i);
+ QRawFont font = glyphs.rawFont();
+ addGlyphs(position + QPointF(0, font.ascent()), glyphs, color, style, styleColor);
+ }
+
+ QFont font = textLayout->font();
+ QRawFont rawFont = QRawFont::fromFont(font);
+ if (font.strikeOut() || font.underline() || font.overline()) {
+ addTextDecorations(position, rawFont, color, textLayout->boundingRect().width(),
+ font.overline(), font.strikeOut(), font.underline());
+ }
+}
+
+
+/*!
+ Returns true if \a text contains any HTML tags, attributes or CSS properties which are unrelated
+ to text, fonts or text layout. Otherwise the function returns false. If the return value is
+ false, \a text is considered to be easily representable in the scenegraph. If it returns true,
+ then the text should be prerendered into a pixmap before it's displayed on screen.
+*/
+bool QSGTextNode::isComplexRichText(QTextDocument *doc)
+{
+ if (doc == 0)
+ return false;
+
+ static QSet<QString> supportedTags;
+ if (supportedTags.isEmpty()) {
+ supportedTags.insert(QLatin1String("i"));
+ supportedTags.insert(QLatin1String("b"));
+ supportedTags.insert(QLatin1String("u"));
+ supportedTags.insert(QLatin1String("div"));
+ supportedTags.insert(QLatin1String("big"));
+ supportedTags.insert(QLatin1String("blockquote"));
+ supportedTags.insert(QLatin1String("body"));
+ supportedTags.insert(QLatin1String("br"));
+ supportedTags.insert(QLatin1String("center"));
+ supportedTags.insert(QLatin1String("cite"));
+ supportedTags.insert(QLatin1String("code"));
+ supportedTags.insert(QLatin1String("tt"));
+ supportedTags.insert(QLatin1String("dd"));
+ supportedTags.insert(QLatin1String("dfn"));
+ supportedTags.insert(QLatin1String("em"));
+ supportedTags.insert(QLatin1String("font"));
+ supportedTags.insert(QLatin1String("h1"));
+ supportedTags.insert(QLatin1String("h2"));
+ supportedTags.insert(QLatin1String("h3"));
+ supportedTags.insert(QLatin1String("h4"));
+ supportedTags.insert(QLatin1String("h5"));
+ supportedTags.insert(QLatin1String("h6"));
+ supportedTags.insert(QLatin1String("head"));
+ supportedTags.insert(QLatin1String("html"));
+ supportedTags.insert(QLatin1String("meta"));
+ supportedTags.insert(QLatin1String("nobr"));
+ supportedTags.insert(QLatin1String("p"));
+ supportedTags.insert(QLatin1String("pre"));
+ supportedTags.insert(QLatin1String("qt"));
+ supportedTags.insert(QLatin1String("s"));
+ supportedTags.insert(QLatin1String("samp"));
+ supportedTags.insert(QLatin1String("small"));
+ supportedTags.insert(QLatin1String("span"));
+ supportedTags.insert(QLatin1String("strong"));
+ supportedTags.insert(QLatin1String("sub"));
+ supportedTags.insert(QLatin1String("sup"));
+ supportedTags.insert(QLatin1String("title"));
+ supportedTags.insert(QLatin1String("var"));
+ supportedTags.insert(QLatin1String("style"));
+ }
+
+ static QSet<QCss::Property> supportedCssProperties;
+ if (supportedCssProperties.isEmpty()) {
+ supportedCssProperties.insert(QCss::Color);
+ supportedCssProperties.insert(QCss::Float);
+ supportedCssProperties.insert(QCss::Font);
+ supportedCssProperties.insert(QCss::FontFamily);
+ supportedCssProperties.insert(QCss::FontSize);
+ supportedCssProperties.insert(QCss::FontStyle);
+ supportedCssProperties.insert(QCss::FontWeight);
+ supportedCssProperties.insert(QCss::Margin);
+ supportedCssProperties.insert(QCss::MarginBottom);
+ supportedCssProperties.insert(QCss::MarginLeft);
+ supportedCssProperties.insert(QCss::MarginRight);
+ supportedCssProperties.insert(QCss::MarginTop);
+ supportedCssProperties.insert(QCss::TextDecoration);
+ supportedCssProperties.insert(QCss::TextIndent);
+ supportedCssProperties.insert(QCss::TextUnderlineStyle);
+ supportedCssProperties.insert(QCss::VerticalAlignment);
+ supportedCssProperties.insert(QCss::Whitespace);
+ supportedCssProperties.insert(QCss::Padding);
+ supportedCssProperties.insert(QCss::PaddingLeft);
+ supportedCssProperties.insert(QCss::PaddingRight);
+ supportedCssProperties.insert(QCss::PaddingTop);
+ supportedCssProperties.insert(QCss::PaddingBottom);
+ supportedCssProperties.insert(QCss::PageBreakBefore);
+ supportedCssProperties.insert(QCss::PageBreakAfter);
+ supportedCssProperties.insert(QCss::Width);
+ supportedCssProperties.insert(QCss::Height);
+ supportedCssProperties.insert(QCss::MinimumWidth);
+ supportedCssProperties.insert(QCss::MinimumHeight);
+ supportedCssProperties.insert(QCss::MaximumWidth);
+ supportedCssProperties.insert(QCss::MaximumHeight);
+ supportedCssProperties.insert(QCss::Left);
+ supportedCssProperties.insert(QCss::Right);
+ supportedCssProperties.insert(QCss::Top);
+ supportedCssProperties.insert(QCss::Bottom);
+ supportedCssProperties.insert(QCss::Position);
+ supportedCssProperties.insert(QCss::TextAlignment);
+ supportedCssProperties.insert(QCss::FontVariant);
+ }
+
+ QXmlStreamReader reader(doc->toHtml("utf-8"));
+ while (!reader.atEnd()) {
+ reader.readNext();
+
+ if (reader.isStartElement()) {
+ if (!supportedTags.contains(reader.name().toString().toLower()))
+ return true;
+
+ QXmlStreamAttributes attributes = reader.attributes();
+ if (attributes.hasAttribute(QLatin1String("bgcolor")))
+ return true;
+ if (attributes.hasAttribute(QLatin1String("style"))) {
+ QCss::StyleSheet styleSheet;
+ QCss::Parser(attributes.value(QLatin1String("style")).toString()).parse(&styleSheet);
+
+ QVector<QCss::Declaration> decls;
+ for (int i=0; i<styleSheet.pageRules.size(); ++i)
+ decls += styleSheet.pageRules.at(i).declarations;
+
+ QVector<QCss::StyleRule> styleRules =
+ styleSheet.styleRules
+ + styleSheet.idIndex.values().toVector()
+ + styleSheet.nameIndex.values().toVector();
+ for (int i=0; i<styleSheet.mediaRules.size(); ++i)
+ styleRules += styleSheet.mediaRules.at(i).styleRules;
+
+ for (int i=0; i<styleRules.size(); ++i)
+ decls += styleRules.at(i).declarations;
+
+ for (int i=0; i<decls.size(); ++i) {
+ if (!supportedCssProperties.contains(decls.at(i).d->propertyId))
+ return true;
+ }
+
+ }
+ }
+ }
+
+ return reader.hasError();
+}
+
+void QSGTextNode::addTextBlock(const QPointF &position, QTextDocument *textDocument, const QTextBlock &block,
+ const QColor &overrideColor, QSGText::TextStyle style, const QColor &styleColor)
+{
+ if (!block.isValid())
+ return;
+
+ QPointF blockPosition = textDocument->documentLayout()->blockBoundingRect(block).topLeft();
+
+ QTextBlock::iterator it = block.begin();
+ while (!it.atEnd()) {
+ QTextFragment fragment = it.fragment();
+ if (!fragment.text().isEmpty()) {
+ QTextCharFormat charFormat = fragment.charFormat();
+ QColor color = overrideColor.isValid()
+ ? overrideColor
+ : charFormat.foreground().color();
+
+ QList<QGlyphRun> glyphsList = fragment.glyphRuns();
+ for (int i=0; i<glyphsList.size(); ++i) {
+ QGlyphRun glyphs = glyphsList.at(i);
+ QRawFont font = glyphs.rawFont();
+ QSGGlyphNode *glyphNode = addGlyphs(position + blockPosition + QPointF(0, font.ascent()),
+ glyphs, color, style, styleColor);
+
+ QPointF baseLine = glyphNode->baseLine();
+ qreal width = glyphNode->boundingRect().width();
+ addTextDecorations(baseLine, font, color, width,
+ glyphs.overline(), glyphs.strikeOut(), glyphs.underline());
+ }
+ }
+
+ ++it;
+ }
+}
+
+void QSGTextNode::deleteContent()
+{
+ while (childCount() > 0)
+ delete childAtIndex(0);
+}
+
+#if 0
+void QSGTextNode::updateNodes()
+{
+ return;
+ deleteContent();
+ if (m_text.isEmpty())
+ return;
+
+ if (m_usePixmapCache) {
+ // ### gunnar: port properly
+// QPixmap pixmap = generatedPixmap();
+// if (pixmap.isNull())
+// return;
+
+// QSGImageNode *pixmapNode = m_context->createImageNode();
+// pixmapNode->setRect(pixmap.rect());
+// pixmapNode->setSourceRect(pixmap.rect());
+// pixmapNode->setOpacity(m_opacity);
+// pixmapNode->setClampToEdge(true);
+// pixmapNode->setLinearFiltering(m_linearFiltering);
+
+// appendChildNode(pixmapNode);
+ } else {
+ if (m_text.isEmpty())
+ return;
+
+ // Implement styling by drawing text several times at slight shifts. shiftForStyle
+ // contains the sequence of shifted positions at which to draw the text. All except
+ // the last will be drawn with styleColor.
+ QList<QPointF> shiftForStyle;
+ switch (m_textStyle) {
+ case OutlineTextStyle:
+ // ### Should be made faster by implementing outline material
+ shiftForStyle << QPointF(-1, 0);
+ shiftForStyle << QPointF(0, -1);
+ shiftForStyle << QPointF(1, 0);
+ shiftForStyle << QPointF(0, 1);
+ break;
+ case SunkenTextStyle:
+ shiftForStyle << QPointF(0, -1);
+ break;
+ case RaisedTextStyle:
+ shiftForStyle << QPointF(0, 1);
+ break;
+ default:
+ break;
+ }
+
+ shiftForStyle << QPointF(0, 0); // Regular position
+ while (!shiftForStyle.isEmpty()) {
+ QPointF shift = shiftForStyle.takeFirst();
+
+ // Use styleColor for all but last shift
+ if (m_richText) {
+ QColor overrideColor = shiftForStyle.isEmpty() ? QColor() : m_styleColor;
+
+ QTextFrame *textFrame = m_textDocument->rootFrame();
+ QPointF p = m_textDocument->documentLayout()->frameBoundingRect(textFrame).topLeft();
+
+ QTextFrame::iterator it = textFrame->begin();
+ while (!it.atEnd()) {
+ addTextBlock(shift + p, it.currentBlock(), overrideColor);
+ ++it;
+ }
+ } else {
+ addTextLayout(shift, m_textLayout, shiftForStyle.isEmpty()
+ ? m_color
+ : m_styleColor);
+ }
+ }
+ }
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgtextnode_p.h b/src/declarative/items/qsgtextnode_p.h
new file mode 100644
index 0000000000..7a49f51dbe
--- /dev/null
+++ b/src/declarative/items/qsgtextnode_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTEXTNODE_P_H
+#define QSGTEXTNODE_P_H
+
+#include <qsgnode.h>
+#include <qsgtext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QTextLayout;
+class QSGGlyphNode;
+class QTextBlock;
+class QColor;
+class QTextDocument;
+class QSGContext;
+class QRawFont;
+
+class QSGTextNode : public QSGTransformNode
+{
+public:
+ QSGTextNode(QSGContext *);
+ ~QSGTextNode();
+
+ static bool isComplexRichText(QTextDocument *);
+
+ void deleteContent();
+ void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color = QColor(),
+ QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
+ void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color = QColor(),
+ QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
+
+private:
+ void addTextBlock(const QPointF &position, QTextDocument *textDocument, const QTextBlock &block,
+ const QColor &overrideColor, QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
+ QSGGlyphNode *addGlyphs(const QPointF &position, const QGlyphRun &glyphs, const QColor &color,
+ QSGText::TextStyle style = QSGText::Normal, const QColor &styleColor = QColor());
+ void addTextDecorations(const QPointF &position, const QRawFont &font, const QColor &color,
+ qreal width, bool hasOverline, bool hasStrikeOut, bool hasUnderline);
+ QSGContext *m_context;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGTEXTNODE_P_H
diff --git a/src/declarative/items/qsgtranslate.cpp b/src/declarative/items/qsgtranslate.cpp
new file mode 100644
index 0000000000..5f7112bd42
--- /dev/null
+++ b/src/declarative/items/qsgtranslate.cpp
@@ -0,0 +1,297 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgtranslate_p.h"
+#include "qsgitem_p.h"
+
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGTranslatePrivate : public QSGTransformPrivate
+{
+public:
+ QSGTranslatePrivate()
+ : x(0), y(0) {}
+
+ qreal x;
+ qreal y;
+};
+
+QSGTranslate::QSGTranslate(QObject *parent)
+: QSGTransform(*new QSGTranslatePrivate, parent)
+{
+}
+
+QSGTranslate::~QSGTranslate()
+{
+}
+
+qreal QSGTranslate::x() const
+{
+ Q_D(const QSGTranslate);
+ return d->x;
+}
+
+void QSGTranslate::setX(qreal x)
+{
+ Q_D(QSGTranslate);
+ if (d->x == x)
+ return;
+ d->x = x;
+ update();
+ emit xChanged();
+}
+
+qreal QSGTranslate::y() const
+{
+ Q_D(const QSGTranslate);
+ return d->y;
+}
+void QSGTranslate::setY(qreal y)
+{
+ Q_D(QSGTranslate);
+ if (d->y == y)
+ return;
+ d->y = y;
+ update();
+ emit yChanged();
+}
+
+void QSGTranslate::applyTo(QMatrix4x4 *matrix) const
+{
+ Q_D(const QSGTranslate);
+ matrix->translate(d->x, d->y, 0);
+}
+
+class QSGScalePrivate : public QSGTransformPrivate
+{
+public:
+ QSGScalePrivate()
+ : xScale(1), yScale(1), zScale(1) {}
+ QVector3D origin;
+ qreal xScale;
+ qreal yScale;
+ qreal zScale;
+};
+
+QSGScale::QSGScale(QObject *parent)
+ : QSGTransform(*new QSGScalePrivate, parent)
+{
+}
+
+QSGScale::~QSGScale()
+{
+}
+
+QVector3D QSGScale::origin() const
+{
+ Q_D(const QSGScale);
+ return d->origin;
+}
+void QSGScale::setOrigin(const QVector3D &point)
+{
+ Q_D(QSGScale);
+ if (d->origin == point)
+ return;
+ d->origin = point;
+ update();
+ emit originChanged();
+}
+
+qreal QSGScale::xScale() const
+{
+ Q_D(const QSGScale);
+ return d->xScale;
+}
+void QSGScale::setXScale(qreal scale)
+{
+ Q_D(QSGScale);
+ if (d->xScale == scale)
+ return;
+ d->xScale = scale;
+ update();
+ emit xScaleChanged();
+ emit scaleChanged();
+}
+
+qreal QSGScale::yScale() const
+{
+ Q_D(const QSGScale);
+ return d->yScale;
+}
+void QSGScale::setYScale(qreal scale)
+{
+ Q_D(QSGScale);
+ if (d->yScale == scale)
+ return;
+ d->yScale = scale;
+ update();
+ emit yScaleChanged();
+ emit scaleChanged();
+}
+
+qreal QSGScale::zScale() const
+{
+ Q_D(const QSGScale);
+ return d->zScale;
+}
+void QSGScale::setZScale(qreal scale)
+{
+ Q_D(QSGScale);
+ if (d->zScale == scale)
+ return;
+ d->zScale = scale;
+ update();
+ emit zScaleChanged();
+ emit scaleChanged();
+}
+
+void QSGScale::applyTo(QMatrix4x4 *matrix) const
+{
+ Q_D(const QSGScale);
+ matrix->translate(d->origin);
+ matrix->scale(d->xScale, d->yScale, d->zScale);
+ matrix->translate(-d->origin);
+}
+
+class QSGRotationPrivate : public QSGTransformPrivate
+{
+public:
+ QSGRotationPrivate()
+ : angle(0), axis(0, 0, 1) {}
+ QVector3D origin;
+ qreal angle;
+ QVector3D axis;
+};
+
+QSGRotation::QSGRotation(QObject *parent)
+ : QSGTransform(*new QSGRotationPrivate, parent)
+{
+}
+
+QSGRotation::~QSGRotation()
+{
+}
+
+QVector3D QSGRotation::origin() const
+{
+ Q_D(const QSGRotation);
+ return d->origin;
+}
+
+void QSGRotation::setOrigin(const QVector3D &point)
+{
+ Q_D(QSGRotation);
+ if (d->origin == point)
+ return;
+ d->origin = point;
+ update();
+ emit originChanged();
+}
+
+qreal QSGRotation::angle() const
+{
+ Q_D(const QSGRotation);
+ return d->angle;
+}
+void QSGRotation::setAngle(qreal angle)
+{
+ Q_D(QSGRotation);
+ if (d->angle == angle)
+ return;
+ d->angle = angle;
+ update();
+ emit angleChanged();
+}
+
+QVector3D QSGRotation::axis() const
+{
+ Q_D(const QSGRotation);
+ return d->axis;
+}
+void QSGRotation::setAxis(const QVector3D &axis)
+{
+ Q_D(QSGRotation);
+ if (d->axis == axis)
+ return;
+ d->axis = axis;
+ update();
+ emit axisChanged();
+}
+
+void QSGRotation::setAxis(Qt::Axis axis)
+{
+ switch (axis)
+ {
+ case Qt::XAxis:
+ setAxis(QVector3D(1, 0, 0));
+ break;
+ case Qt::YAxis:
+ setAxis(QVector3D(0, 1, 0));
+ break;
+ case Qt::ZAxis:
+ setAxis(QVector3D(0, 0, 1));
+ break;
+ }
+}
+
+struct QGraphicsRotation {
+ static inline void projectedRotate(QMatrix4x4 *matrix, qreal angle, qreal x, qreal y, qreal z)
+ {
+ matrix->projectedRotate(angle, x, y, z);
+ }
+};
+
+void QSGRotation::applyTo(QMatrix4x4 *matrix) const
+{
+ Q_D(const QSGRotation);
+
+ if (d->angle == 0. || d->axis.isNull())
+ return;
+
+ matrix->translate(d->origin);
+ QGraphicsRotation::projectedRotate(matrix, d->angle, d->axis.x(), d->axis.y(), d->axis.z());
+ matrix->translate(-d->origin);
+}
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgtranslate_p.h b/src/declarative/items/qsgtranslate_p.h
new file mode 100644
index 0000000000..de05778b1e
--- /dev/null
+++ b/src/declarative/items/qsgtranslate_p.h
@@ -0,0 +1,162 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGTRANSLATE_P_H
+#define QSGTRANSLATE_P_H
+
+#include "qsgitem.h"
+
+#include <QtGui/qmatrix4x4.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGTranslatePrivate;
+class Q_AUTOTEST_EXPORT QSGTranslate : public QSGTransform
+{
+ Q_OBJECT
+
+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
+
+public:
+ QSGTranslate(QObject *parent = 0);
+ ~QSGTranslate();
+
+ qreal x() const;
+ void setX(qreal);
+
+ qreal y() const;
+ void setY(qreal);
+
+ void applyTo(QMatrix4x4 *matrix) const;
+
+Q_SIGNALS:
+ void xChanged();
+ void yChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QSGTranslate)
+ Q_DISABLE_COPY(QSGTranslate)
+};
+
+class QSGScalePrivate;
+class Q_AUTOTEST_EXPORT QSGScale : public QSGTransform
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged)
+ Q_PROPERTY(qreal xScale READ xScale WRITE setXScale NOTIFY xScaleChanged)
+ Q_PROPERTY(qreal yScale READ yScale WRITE setYScale NOTIFY yScaleChanged)
+ Q_PROPERTY(qreal zScale READ zScale WRITE setZScale NOTIFY zScaleChanged)
+public:
+ QSGScale(QObject *parent = 0);
+ ~QSGScale();
+
+ QVector3D origin() const;
+ void setOrigin(const QVector3D &point);
+
+ qreal xScale() const;
+ void setXScale(qreal);
+
+ qreal yScale() const;
+ void setYScale(qreal);
+
+ qreal zScale() const;
+ void setZScale(qreal);
+
+ void applyTo(QMatrix4x4 *matrix) const;
+
+Q_SIGNALS:
+ void originChanged();
+ void xScaleChanged();
+ void yScaleChanged();
+ void zScaleChanged();
+ void scaleChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QSGScale)
+};
+
+class QSGRotationPrivate;
+class Q_AUTOTEST_EXPORT QSGRotation : public QSGTransform
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QVector3D origin READ origin WRITE setOrigin NOTIFY originChanged)
+ Q_PROPERTY(qreal angle READ angle WRITE setAngle NOTIFY angleChanged)
+ Q_PROPERTY(QVector3D axis READ axis WRITE setAxis NOTIFY axisChanged)
+public:
+ QSGRotation(QObject *parent = 0);
+ ~QSGRotation();
+
+ QVector3D origin() const;
+ void setOrigin(const QVector3D &point);
+
+ qreal angle() const;
+ void setAngle(qreal);
+
+ QVector3D axis() const;
+ void setAxis(const QVector3D &axis);
+ void setAxis(Qt::Axis axis);
+
+ void applyTo(QMatrix4x4 *matrix) const;
+
+Q_SIGNALS:
+ void originChanged();
+ void angleChanged();
+ void axisChanged();
+
+private:
+ Q_DECLARE_PRIVATE(QSGRotation)
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGTranslate)
+
+QT_END_HEADER
+
+#endif
diff --git a/src/declarative/items/qsgview.cpp b/src/declarative/items/qsgview.cpp
new file mode 100644
index 0000000000..1169c59a1b
--- /dev/null
+++ b/src/declarative/items/qsgview.cpp
@@ -0,0 +1,466 @@
+// Commit: 55c4d94dfea78951f3371d3697a3cb28539b3012
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgview.h"
+
+#include "qsgcanvas_p.h"
+#include "qsgitem_p.h"
+#include "qsgitemchangelistener_p.h"
+
+#include <private/qdeclarativedebugtrace_p.h>
+
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <private/qdeclarativeengine_p.h>
+#include <QtCore/qbasictimer.h>
+
+// XXX todo - This whole class should probably be merged with QDeclarativeView for
+// maximum seamlessness
+QT_BEGIN_NAMESPACE
+
+DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE)
+
+class QSGViewPrivate : public QSGCanvasPrivate,
+ public QSGItemChangeListener
+{
+ Q_DECLARE_PUBLIC(QSGView)
+public:
+ QSGViewPrivate();
+ ~QSGViewPrivate();
+
+ void execute();
+ void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
+ void initResize();
+ void updateSize();
+ void setRootObject(QObject *);
+
+ void init();
+
+ QSize rootObjectSize() const;
+
+ QPointer<QSGItem> root;
+
+ QUrl source;
+
+ QDeclarativeEngine engine;
+ QDeclarativeComponent *component;
+ QBasicTimer resizetimer;
+
+ QSGView::ResizeMode resizeMode;
+ QSize initialSize;
+ QElapsedTimer frameTimer;
+};
+
+void QSGViewPrivate::init()
+{
+ q_func()->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred);
+ QDeclarativeEnginePrivate::get(&engine)->sgContext = QSGCanvasPrivate::context;
+}
+
+QSGViewPrivate::QSGViewPrivate()
+: root(0), component(0), resizeMode(QSGView::SizeViewToRootObject), initialSize(0,0)
+{
+}
+
+QSGViewPrivate::~QSGViewPrivate()
+{
+ delete root;
+}
+
+void QSGViewPrivate::execute()
+{
+ Q_Q(QSGView);
+ if (root) {
+ delete root;
+ root = 0;
+ }
+ if (component) {
+ delete component;
+ component = 0;
+ }
+ if (!source.isEmpty()) {
+ component = new QDeclarativeComponent(&engine, source, q);
+ if (!component->isLoading()) {
+ q->continueExecute();
+ } else {
+ QObject::connect(component, SIGNAL(statusChanged(QDeclarativeComponent::Status)),
+ q, SLOT(continueExecute()));
+ }
+ }
+}
+
+void QSGViewPrivate::itemGeometryChanged(QSGItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_Q(QSGView);
+ if (resizeItem == root && resizeMode == QSGView::SizeViewToRootObject) {
+ // wait for both width and height to be changed
+ resizetimer.start(0,q);
+ }
+ QSGItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry);
+}
+
+QSGView::QSGView(QWidget *parent, Qt::WindowFlags f)
+: QSGCanvas(*(new QSGViewPrivate), parent, f)
+{
+ d_func()->init();
+}
+
+QSGView::QSGView(const QGLFormat &format, QWidget *parent, Qt::WindowFlags f)
+: QSGCanvas(*(new QSGViewPrivate), format, parent, f)
+{
+ d_func()->init();
+}
+
+QSGView::QSGView(const QUrl &source, QWidget *parent, Qt::WindowFlags f)
+: QSGCanvas(*(new QSGViewPrivate), parent, f)
+{
+ d_func()->init();
+ setSource(source);
+}
+
+QSGView::QSGView(const QUrl &source, const QGLFormat &format, QWidget *parent, Qt::WindowFlags f)
+: QSGCanvas(*(new QSGViewPrivate), format, parent, f)
+{
+ d_func()->init();
+ setSource(source);
+}
+
+QSGView::~QSGView()
+{
+}
+
+void QSGView::setSource(const QUrl& url)
+{
+ Q_D(QSGView);
+ d->source = url;
+ d->execute();
+}
+
+QUrl QSGView::source() const
+{
+ Q_D(const QSGView);
+ return d->source;
+}
+
+QDeclarativeEngine* QSGView::engine() const
+{
+ Q_D(const QSGView);
+ return const_cast<QDeclarativeEngine *>(&d->engine);
+}
+
+QDeclarativeContext* QSGView::rootContext() const
+{
+ Q_D(const QSGView);
+ return d->engine.rootContext();
+}
+
+QSGView::Status QSGView::status() const
+{
+ Q_D(const QSGView);
+ if (!d->component)
+ return QSGView::Null;
+
+ return QSGView::Status(d->component->status());
+}
+
+QList<QDeclarativeError> QSGView::errors() const
+{
+ Q_D(const QSGView);
+ if (d->component)
+ return d->component->errors();
+ return QList<QDeclarativeError>();
+}
+
+void QSGView::setResizeMode(ResizeMode mode)
+{
+ Q_D(QSGView);
+ if (d->resizeMode == mode)
+ return;
+
+ if (d->root) {
+ if (d->resizeMode == SizeViewToRootObject) {
+ QSGItemPrivate *p = QSGItemPrivate::get(d->root);
+ p->removeItemChangeListener(d, QSGItemPrivate::Geometry);
+ }
+ }
+
+ d->resizeMode = mode;
+ if (d->root) {
+ d->initResize();
+ }
+}
+
+void QSGViewPrivate::initResize()
+{
+ if (root) {
+ if (resizeMode == QSGView::SizeViewToRootObject) {
+ QSGItemPrivate *p = QSGItemPrivate::get(root);
+ p->addItemChangeListener(this, QSGItemPrivate::Geometry);
+ }
+ }
+ updateSize();
+}
+
+void QSGViewPrivate::updateSize()
+{
+ Q_Q(QSGView);
+ if (!root)
+ return;
+
+ if (resizeMode == QSGView::SizeViewToRootObject) {
+ QSize newSize = QSize(root->width(), root->height());
+ if (newSize.isValid() && newSize != q->size()) {
+ q->resize(newSize);
+ }
+ } else if (resizeMode == QSGView::SizeRootObjectToView) {
+ if (!qFuzzyCompare(q->width(), root->width()))
+ root->setWidth(q->width());
+ if (!qFuzzyCompare(q->height(), root->height()))
+ root->setHeight(q->height());
+ }
+
+ q->updateGeometry();
+}
+
+QSize QSGViewPrivate::rootObjectSize() const
+{
+ QSize rootObjectSize(0,0);
+ int widthCandidate = -1;
+ int heightCandidate = -1;
+ if (root) {
+ widthCandidate = root->width();
+ heightCandidate = root->height();
+ }
+ if (widthCandidate > 0) {
+ rootObjectSize.setWidth(widthCandidate);
+ }
+ if (heightCandidate > 0) {
+ rootObjectSize.setHeight(heightCandidate);
+ }
+ return rootObjectSize;
+}
+
+QSGView::ResizeMode QSGView::resizeMode() const
+{
+ Q_D(const QSGView);
+ return d->resizeMode;
+}
+
+/*!
+ \internal
+ */
+void QSGView::continueExecute()
+{
+ Q_D(QSGView);
+ disconnect(d->component, SIGNAL(statusChanged(QDeclarativeComponent::Status)), this, SLOT(continueExecute()));
+
+ if (d->component->isError()) {
+ QList<QDeclarativeError> errorList = d->component->errors();
+ foreach (const QDeclarativeError &error, errorList) {
+ qWarning() << error;
+ }
+ emit statusChanged(status());
+ return;
+ }
+
+ QObject *obj = d->component->create();
+
+ if(d->component->isError()) {
+ QList<QDeclarativeError> errorList = d->component->errors();
+ foreach (const QDeclarativeError &error, errorList) {
+ qWarning() << error;
+ }
+ emit statusChanged(status());
+ return;
+ }
+
+ d->setRootObject(obj);
+ emit statusChanged(status());
+}
+
+
+/*!
+ \internal
+*/
+void QSGViewPrivate::setRootObject(QObject *obj)
+{
+ Q_Q(QSGView);
+ if (root == obj)
+ return;
+ if (QSGItem *sgItem = qobject_cast<QSGItem *>(obj)) {
+ root = sgItem;
+ sgItem->setParentItem(q->QSGCanvas::rootItem());
+ } else {
+ qWarning() << "QSGView only supports loading of root objects that derive from QSGItem." << endl
+ << endl
+ << "If your example is using QML 2, (such as qmlscene) and the .qml file you" << endl
+ << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl
+ << endl
+ << "To load files with 'import QtQuick 1.0' with QML 2, specify:" << endl
+ << " QMLSCENE_IMPORT_NAME=quick1" << endl
+ << "on as an environment variable prior to launching the application." << endl
+ << endl
+ << "To load files with 'import Qt 4.7' with QML 2, specify:" << endl
+ << " QMLSCENE_IMPORT_NAME=qt" << endl
+ << "on as an environment variable prior to launching the application." << endl;
+ delete obj;
+ root = 0;
+ }
+
+ if (root) {
+ initialSize = rootObjectSize();
+ if (initialSize != q->size()) {
+ if (!(q->parentWidget() && q->parentWidget()->layout())) {
+ q->resize(initialSize);
+ }
+ }
+ initResize();
+ }
+}
+
+/*!
+ \internal
+ If the \l {QTimerEvent} {timer event} \a e is this
+ view's resize timer, sceneResized() is emitted.
+ */
+void QSGView::timerEvent(QTimerEvent* e)
+{
+ Q_D(QSGView);
+ if (!e || e->timerId() == d->resizetimer.timerId()) {
+ d->updateSize();
+ d->resizetimer.stop();
+ }
+}
+
+/*!
+ \internal
+ Preferred size follows the root object geometry.
+*/
+QSize QSGView::sizeHint() const
+{
+ Q_D(const QSGView);
+ QSize rootObjectSize = d->rootObjectSize();
+ if (rootObjectSize.isEmpty()) {
+ return size();
+ } else {
+ return rootObjectSize;
+ }
+}
+
+QSize QSGView::initialSize() const
+{
+ Q_D(const QSGView);
+ return d->initialSize;
+}
+
+QSGItem *QSGView::rootObject() const
+{
+ Q_D(const QSGView);
+ return d->root;
+}
+
+/*!
+ \internal
+ This function handles the \l {QResizeEvent} {resize event}
+ \a e.
+ */
+void QSGView::resizeEvent(QResizeEvent *e)
+{
+ Q_D(QSGView);
+ if (d->resizeMode == SizeRootObjectToView)
+ d->updateSize();
+
+ QSGCanvas::resizeEvent(e);
+}
+
+/*!
+ \internal
+*/
+void QSGView::paintEvent(QPaintEvent *event)
+{
+ Q_D(QSGView);
+ int time = 0;
+ if (frameRateDebug())
+ time = d->frameTimer.restart();
+
+ QSGCanvas::paintEvent(event);
+
+ if (frameRateDebug())
+ qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time;
+}
+
+void QSGView::keyPressEvent(QKeyEvent *e)
+{
+ QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key);
+
+ QSGCanvas::keyPressEvent(e);
+}
+
+void QSGView::keyReleaseEvent(QKeyEvent *e)
+{
+ QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key);
+
+ QSGCanvas::keyReleaseEvent(e);
+}
+
+void QSGView::mouseMoveEvent(QMouseEvent *e)
+{
+ QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse);
+
+ QSGCanvas::mouseMoveEvent(e);
+}
+
+void QSGView::mousePressEvent(QMouseEvent *e)
+{
+ QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse);
+
+ QSGCanvas::mousePressEvent(e);
+}
+
+void QSGView::mouseReleaseEvent(QMouseEvent *e)
+{
+ QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse);
+
+ QSGCanvas::mouseReleaseEvent(e);
+}
+
+
+QT_END_NAMESPACE
diff --git a/src/declarative/items/qsgview.h b/src/declarative/items/qsgview.h
new file mode 100644
index 0000000000..242017df6b
--- /dev/null
+++ b/src/declarative/items/qsgview.h
@@ -0,0 +1,122 @@
+// Commit: 0b83a2161261be525f01359397ab1c8c34827749
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGVIEW_H
+#define QSGVIEW_H
+
+#include <QtCore/qurl.h>
+#include <qsgcanvas.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativeEngine;
+class QDeclarativeContext;
+class QDeclarativeError;
+class QSGItem;
+
+class QSGViewPrivate;
+class Q_DECLARATIVE_EXPORT QSGView : public QSGCanvas
+{
+ Q_OBJECT
+ Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(QUrl source READ source WRITE setSource DESIGNABLE true)
+ Q_ENUMS(ResizeMode Status)
+public:
+ explicit QSGView(QWidget *parent = 0, Qt::WindowFlags f = 0);
+ explicit QSGView(const QGLFormat &format, QWidget *parent = 0, Qt::WindowFlags f = 0);
+ QSGView(const QUrl &source, QWidget *parent = 0, Qt::WindowFlags f = 0);
+ QSGView(const QUrl &source, const QGLFormat &format, QWidget *parent = 0, Qt::WindowFlags f = 0);
+ virtual ~QSGView();
+
+ QUrl source() const;
+
+ QDeclarativeEngine* engine() const;
+ QDeclarativeContext* rootContext() const;
+
+ QSGItem *rootObject() const;
+
+ enum ResizeMode { SizeViewToRootObject, SizeRootObjectToView };
+ ResizeMode resizeMode() const;
+ void setResizeMode(ResizeMode);
+
+ enum Status { Null, Ready, Loading, Error };
+ Status status() const;
+
+ QList<QDeclarativeError> errors() const;
+
+ QSize sizeHint() const;
+ QSize initialSize() const;
+
+public Q_SLOTS:
+ void setSource(const QUrl&);
+
+Q_SIGNALS:
+ void statusChanged(QSGView::Status);
+
+private Q_SLOTS:
+ void continueExecute();
+
+protected:
+ virtual void resizeEvent(QResizeEvent *);
+ virtual void paintEvent(QPaintEvent *event);
+ virtual void timerEvent(QTimerEvent*);
+
+ virtual void keyPressEvent(QKeyEvent *);
+ virtual void keyReleaseEvent(QKeyEvent *);
+ virtual void mousePressEvent(QMouseEvent *);
+ virtual void mouseReleaseEvent(QMouseEvent *);
+ virtual void mouseMoveEvent(QMouseEvent *);
+private:
+ Q_DISABLE_COPY(QSGView)
+ Q_DECLARE_PRIVATE(QSGView)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSGVIEW_H
diff --git a/src/declarative/items/qsgvisualitemmodel.cpp b/src/declarative/items/qsgvisualitemmodel.cpp
new file mode 100644
index 0000000000..daf67b573a
--- /dev/null
+++ b/src/declarative/items/qsgvisualitemmodel.cpp
@@ -0,0 +1,1254 @@
+// Commit: dcb9148091cbf6872b60407c301d7c92427583a6
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgvisualitemmodel_p.h"
+#include "qsgitem.h"
+
+#include <QtDeclarative/qdeclarativecontext.h>
+#include <QtDeclarative/qdeclarativeengine.h>
+#include <QtDeclarative/qdeclarativeexpression.h>
+#include <QtDeclarative/qdeclarativeinfo.h>
+
+#include <private/qdeclarativecontext_p.h>
+#include <private/qdeclarativepackage_p.h>
+#include <private/qdeclarativeopenmetaobject_p.h>
+#include <private/qdeclarativelistaccessor_p.h>
+#include <private/qdeclarativedata_p.h>
+#include <private/qdeclarativepropertycache_p.h>
+#include <private/qdeclarativeguard_p.h>
+#include <private/qdeclarativeglobal_p.h>
+#include <private/qlistmodelinterface_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+#include <private/qobject_p.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+QHash<QObject*, QSGVisualItemModelAttached*> QSGVisualItemModelAttached::attachedProperties;
+
+
+class QSGVisualItemModelPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QSGVisualItemModel)
+public:
+ QSGVisualItemModelPrivate() : QObjectPrivate() {}
+
+ static void children_append(QDeclarativeListProperty<QSGItem> *prop, QSGItem *item) {
+ QDeclarative_setParent_noEvent(item, prop->object);
+ static_cast<QSGVisualItemModelPrivate *>(prop->data)->children.append(Item(item));
+ static_cast<QSGVisualItemModelPrivate *>(prop->data)->itemAppended();
+ static_cast<QSGVisualItemModelPrivate *>(prop->data)->emitChildrenChanged();
+ }
+
+ static int children_count(QDeclarativeListProperty<QSGItem> *prop) {
+ return static_cast<QSGVisualItemModelPrivate *>(prop->data)->children.count();
+ }
+
+ static QSGItem *children_at(QDeclarativeListProperty<QSGItem> *prop, int index) {
+ return static_cast<QSGVisualItemModelPrivate *>(prop->data)->children.at(index).item;
+ }
+
+ void itemAppended() {
+ Q_Q(QSGVisualItemModel);
+ QSGVisualItemModelAttached *attached = QSGVisualItemModelAttached::properties(children.last().item);
+ attached->setIndex(children.count()-1);
+ emit q->itemsInserted(children.count()-1, 1);
+ emit q->countChanged();
+ }
+
+ void emitChildrenChanged() {
+ Q_Q(QSGVisualItemModel);
+ emit q->childrenChanged();
+ }
+
+ int indexOf(QSGItem *item) const {
+ for (int i = 0; i < children.count(); ++i)
+ if (children.at(i).item == item)
+ return i;
+ return -1;
+ }
+
+ class Item {
+ public:
+ Item(QSGItem *i) : item(i), ref(0) {}
+
+ void addRef() { ++ref; }
+ bool deref() { return --ref == 0; }
+
+ QSGItem *item;
+ int ref;
+ };
+
+ QList<Item> children;
+};
+
+QSGVisualItemModel::QSGVisualItemModel(QObject *parent)
+ : QSGVisualModel(*(new QSGVisualItemModelPrivate), parent)
+{
+}
+
+QDeclarativeListProperty<QSGItem> QSGVisualItemModel::children()
+{
+ Q_D(QSGVisualItemModel);
+ return QDeclarativeListProperty<QSGItem>(this, d, d->children_append,
+ d->children_count, d->children_at);
+}
+
+int QSGVisualItemModel::count() const
+{
+ Q_D(const QSGVisualItemModel);
+ return d->children.count();
+}
+
+bool QSGVisualItemModel::isValid() const
+{
+ return true;
+}
+
+QSGItem *QSGVisualItemModel::item(int index, bool)
+{
+ Q_D(QSGVisualItemModel);
+ QSGVisualItemModelPrivate::Item &item = d->children[index];
+ item.addRef();
+ return item.item;
+}
+
+QSGVisualModel::ReleaseFlags QSGVisualItemModel::release(QSGItem *item)
+{
+ Q_D(QSGVisualItemModel);
+ int idx = d->indexOf(item);
+ if (idx >= 0) {
+ if (d->children[idx].deref()) {
+ // XXX todo - the original did item->scene()->removeItem(). Why?
+ item->setParentItem(0);
+ QDeclarative_setParent_noEvent(item, this);
+ }
+ }
+ return 0;
+}
+
+bool QSGVisualItemModel::completePending() const
+{
+ return false;
+}
+
+void QSGVisualItemModel::completeItem()
+{
+ // Nothing to do
+}
+
+QString QSGVisualItemModel::stringValue(int index, const QString &name)
+{
+ Q_D(QSGVisualItemModel);
+ if (index < 0 || index >= d->children.count())
+ return QString();
+ return QDeclarativeEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString();
+}
+
+int QSGVisualItemModel::indexOf(QSGItem *item, QObject *) const
+{
+ Q_D(const QSGVisualItemModel);
+ return d->indexOf(item);
+}
+
+QSGVisualItemModelAttached *QSGVisualItemModel::qmlAttachedProperties(QObject *obj)
+{
+ return QSGVisualItemModelAttached::properties(obj);
+}
+
+//============================================================================
+
+class VDMDelegateDataType : public QDeclarativeOpenMetaObjectType
+{
+public:
+ VDMDelegateDataType(const QMetaObject *base, QDeclarativeEngine *engine) : QDeclarativeOpenMetaObjectType(base, engine) {}
+
+ void propertyCreated(int, QMetaPropertyBuilder &prop) {
+ prop.setWritable(false);
+ }
+};
+
+class QSGVisualDataModelParts;
+class QSGVisualDataModelData;
+class QSGVisualDataModelPrivate : public QObjectPrivate
+{
+public:
+ QSGVisualDataModelPrivate(QDeclarativeContext *);
+
+ static QSGVisualDataModelPrivate *get(QSGVisualDataModel *m) {
+ return static_cast<QSGVisualDataModelPrivate *>(QObjectPrivate::get(m));
+ }
+
+ QDeclarativeGuard<QListModelInterface> m_listModelInterface;
+ QDeclarativeGuard<QAbstractItemModel> m_abstractItemModel;
+ QDeclarativeGuard<QSGVisualDataModel> m_visualItemModel;
+ QString m_part;
+
+ QDeclarativeComponent *m_delegate;
+ QDeclarativeGuard<QDeclarativeContext> m_context;
+ QList<int> m_roles;
+ QHash<QByteArray,int> m_roleNames;
+ void ensureRoles() {
+ if (m_roleNames.isEmpty()) {
+ if (m_listModelInterface) {
+ m_roles = m_listModelInterface->roles();
+ for (int ii = 0; ii < m_roles.count(); ++ii)
+ m_roleNames.insert(m_listModelInterface->toString(m_roles.at(ii)).toUtf8(), m_roles.at(ii));
+ } else if (m_abstractItemModel) {
+ for (QHash<int,QByteArray>::const_iterator it = m_abstractItemModel->roleNames().begin();
+ it != m_abstractItemModel->roleNames().end(); ++it) {
+ m_roles.append(it.key());
+ m_roleNames.insert(*it, it.key());
+ }
+ if (m_roles.count())
+ m_roleNames.insert("hasModelChildren", -1);
+ } else if (m_listAccessor) {
+ m_roleNames.insert("modelData", 0);
+ if (m_listAccessor->type() == QDeclarativeListAccessor::Instance) {
+ if (QObject *object = m_listAccessor->at(0).value<QObject*>()) {
+ int count = object->metaObject()->propertyCount();
+ for (int ii = 1; ii < count; ++ii) {
+ const QMetaProperty &prop = object->metaObject()->property(ii);
+ m_roleNames.insert(prop.name(), 0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ QHash<int,int> m_roleToPropId;
+ int m_modelDataPropId;
+ void createMetaData() {
+ if (!m_metaDataCreated) {
+ ensureRoles();
+ if (m_roleNames.count()) {
+ QHash<QByteArray, int>::const_iterator it = m_roleNames.begin();
+ while (it != m_roleNames.end()) {
+ int propId = m_delegateDataType->createProperty(it.key()) - m_delegateDataType->propertyOffset();
+ m_roleToPropId.insert(*it, propId);
+ ++it;
+ }
+ // Add modelData property
+ if (m_roles.count() == 1)
+ m_modelDataPropId = m_delegateDataType->createProperty("modelData") - m_delegateDataType->propertyOffset();
+ m_metaDataCreated = true;
+ }
+ }
+ }
+
+ struct ObjectRef {
+ ObjectRef(QObject *object=0) : obj(object), ref(1) {}
+ QObject *obj;
+ int ref;
+ };
+ class Cache : public QHash<int, ObjectRef> {
+ public:
+ QObject *getItem(int index) {
+ QObject *item = 0;
+ QHash<int,ObjectRef>::iterator it = find(index);
+ if (it != end()) {
+ (*it).ref++;
+ item = (*it).obj;
+ }
+ return item;
+ }
+ QObject *item(int index) {
+ QObject *item = 0;
+ QHash<int, ObjectRef>::const_iterator it = find(index);
+ if (it != end())
+ item = (*it).obj;
+ return item;
+ }
+ void insertItem(int index, QObject *obj) {
+ insert(index, ObjectRef(obj));
+ }
+ bool releaseItem(QObject *obj) {
+ QHash<int, ObjectRef>::iterator it = begin();
+ for (; it != end(); ++it) {
+ ObjectRef &objRef = *it;
+ if (objRef.obj == obj) {
+ if (--objRef.ref == 0) {
+ erase(it);
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+ }
+ };
+
+ int modelCount() const {
+ if (m_visualItemModel)
+ return m_visualItemModel->count();
+ if (m_listModelInterface)
+ return m_listModelInterface->count();
+ if (m_abstractItemModel)
+ return m_abstractItemModel->rowCount(m_root);
+ if (m_listAccessor)
+ return m_listAccessor->count();
+ return 0;
+ }
+
+ Cache m_cache;
+ QHash<QObject *, QDeclarativePackage*> m_packaged;
+
+ QSGVisualDataModelParts *m_parts;
+ friend class QSGVisualItemParts;
+
+ VDMDelegateDataType *m_delegateDataType;
+ friend class QSGVisualDataModelData;
+ bool m_metaDataCreated : 1;
+ bool m_metaDataCacheable : 1;
+ bool m_delegateValidated : 1;
+ bool m_completePending : 1;
+
+ QSGVisualDataModelData *data(QObject *item);
+
+ QVariant m_modelVariant;
+ QDeclarativeListAccessor *m_listAccessor;
+
+ QModelIndex m_root;
+ QList<QByteArray> watchedRoles;
+ QList<int> watchedRoleIds;
+};
+
+class QSGVisualDataModelDataMetaObject : public QDeclarativeOpenMetaObject
+{
+public:
+ QSGVisualDataModelDataMetaObject(QObject *parent, QDeclarativeOpenMetaObjectType *type)
+ : QDeclarativeOpenMetaObject(parent, type) {}
+
+ virtual QVariant initialValue(int);
+ virtual int createProperty(const char *, const char *);
+
+private:
+ friend class QSGVisualDataModelData;
+};
+
+class QSGVisualDataModelData : public QObject
+{
+Q_OBJECT
+public:
+ QSGVisualDataModelData(int index, QSGVisualDataModel *model);
+ ~QSGVisualDataModelData();
+
+ Q_PROPERTY(int index READ index NOTIFY indexChanged)
+ int index() const;
+ void setIndex(int index);
+
+ int propForRole(int) const;
+ int modelDataPropertyId() const {
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
+ return model->m_modelDataPropId;
+ }
+
+ void setValue(int, const QVariant &);
+ bool hasValue(int id) const {
+ return m_meta->hasValue(id);
+ }
+
+ void ensureProperties();
+
+Q_SIGNALS:
+ void indexChanged();
+
+private:
+ friend class QSGVisualDataModelDataMetaObject;
+ int m_index;
+ QDeclarativeGuard<QSGVisualDataModel> m_model;
+ QSGVisualDataModelDataMetaObject *m_meta;
+};
+
+int QSGVisualDataModelData::propForRole(int id) const
+{
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
+ QHash<int,int>::const_iterator it = model->m_roleToPropId.find(id);
+ if (it != model->m_roleToPropId.end())
+ return *it;
+
+ return -1;
+}
+
+void QSGVisualDataModelData::setValue(int id, const QVariant &val)
+{
+ m_meta->setValue(id, val);
+}
+
+int QSGVisualDataModelDataMetaObject::createProperty(const char *name, const char *type)
+{
+ QSGVisualDataModelData *data =
+ static_cast<QSGVisualDataModelData *>(object());
+
+ if (!data->m_model)
+ return -1;
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(data->m_model);
+ if (data->m_index < 0 || data->m_index >= model->modelCount())
+ return -1;
+
+ if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) {
+ if (model->m_listAccessor->type() == QDeclarativeListAccessor::ListProperty) {
+ model->ensureRoles();
+ if (qstrcmp(name,"modelData") == 0)
+ return QDeclarativeOpenMetaObject::createProperty(name, type);
+ }
+ }
+ return -1;
+}
+
+QVariant QSGVisualDataModelDataMetaObject::initialValue(int propId)
+{
+ QSGVisualDataModelData *data =
+ static_cast<QSGVisualDataModelData *>(object());
+
+ Q_ASSERT(data->m_model);
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(data->m_model);
+
+ QByteArray propName = name(propId);
+ if ((!model->m_listModelInterface || !model->m_abstractItemModel) && model->m_listAccessor) {
+ if (propName == "modelData") {
+ if (model->m_listAccessor->type() == QDeclarativeListAccessor::Instance) {
+ QObject *object = model->m_listAccessor->at(0).value<QObject*>();
+ return object->metaObject()->property(1).read(object); // the first property after objectName
+ }
+ return model->m_listAccessor->at(data->m_index);
+ } else {
+ // return any property of a single object instance.
+ QObject *object = model->m_listAccessor->at(data->m_index).value<QObject*>();
+ return object->property(propName);
+ }
+ } else if (model->m_listModelInterface) {
+ model->ensureRoles();
+ QHash<QByteArray,int>::const_iterator it = model->m_roleNames.find(propName);
+ if (it != model->m_roleNames.end()) {
+ QVariant value = model->m_listModelInterface->data(data->m_index, *it);
+ return value;
+ } else if (model->m_roles.count() == 1 && propName == "modelData") {
+ //for compatibility with other lists, assign modelData if there is only a single role
+ QVariant value = model->m_listModelInterface->data(data->m_index, model->m_roles.first());
+ return value;
+ }
+ } else if (model->m_abstractItemModel) {
+ model->ensureRoles();
+ QModelIndex index = model->m_abstractItemModel->index(data->m_index, 0, model->m_root);
+ if (propName == "hasModelChildren") {
+ return model->m_abstractItemModel->hasChildren(index);
+ } else {
+ QHash<QByteArray,int>::const_iterator it = model->m_roleNames.find(propName);
+ if (it != model->m_roleNames.end()) {
+ return model->m_abstractItemModel->data(index, *it);
+ } else if (model->m_roles.count() == 1 && propName == "modelData") {
+ //for compatibility with other lists, assign modelData if there is only a single role
+ return model->m_abstractItemModel->data(index, model->m_roles.first());
+ }
+ }
+ }
+ Q_ASSERT(!"Can never be reached");
+ return QVariant();
+}
+
+QSGVisualDataModelData::QSGVisualDataModelData(int index,
+ QSGVisualDataModel *model)
+: m_index(index), m_model(model),
+m_meta(new QSGVisualDataModelDataMetaObject(this, QSGVisualDataModelPrivate::get(model)->m_delegateDataType))
+{
+ ensureProperties();
+}
+
+QSGVisualDataModelData::~QSGVisualDataModelData()
+{
+}
+
+void QSGVisualDataModelData::ensureProperties()
+{
+ QSGVisualDataModelPrivate *modelPriv = QSGVisualDataModelPrivate::get(m_model);
+ if (modelPriv->m_metaDataCacheable) {
+ if (!modelPriv->m_metaDataCreated)
+ modelPriv->createMetaData();
+ if (modelPriv->m_metaDataCreated)
+ m_meta->setCached(true);
+ }
+}
+
+int QSGVisualDataModelData::index() const
+{
+ return m_index;
+}
+
+// This is internal only - it should not be set from qml
+void QSGVisualDataModelData::setIndex(int index)
+{
+ m_index = index;
+ emit indexChanged();
+}
+
+//---------------------------------------------------------------------------
+
+class QSGVisualDataModelPartsMetaObject : public QDeclarativeOpenMetaObject
+{
+public:
+ QSGVisualDataModelPartsMetaObject(QObject *parent)
+ : QDeclarativeOpenMetaObject(parent) {}
+
+ virtual void propertyCreated(int, QMetaPropertyBuilder &);
+ virtual QVariant initialValue(int);
+};
+
+class QSGVisualDataModelParts : public QObject
+{
+Q_OBJECT
+public:
+ QSGVisualDataModelParts(QSGVisualDataModel *parent);
+
+private:
+ friend class QSGVisualDataModelPartsMetaObject;
+ QSGVisualDataModel *model;
+};
+
+void QSGVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop)
+{
+ prop.setWritable(false);
+}
+
+QVariant QSGVisualDataModelPartsMetaObject::initialValue(int id)
+{
+ QSGVisualDataModel *m = new QSGVisualDataModel;
+ m->setParent(object());
+ m->setPart(QString::fromUtf8(name(id)));
+ m->setModel(QVariant::fromValue(static_cast<QSGVisualDataModelParts *>(object())->model));
+
+ QVariant var = QVariant::fromValue((QObject *)m);
+ return var;
+}
+
+QSGVisualDataModelParts::QSGVisualDataModelParts(QSGVisualDataModel *parent)
+: QObject(parent), model(parent)
+{
+ new QSGVisualDataModelPartsMetaObject(this);
+}
+
+QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt)
+: m_listModelInterface(0), m_abstractItemModel(0), m_visualItemModel(0), m_delegate(0)
+, m_context(ctxt), m_modelDataPropId(-1), m_parts(0), m_delegateDataType(0), m_metaDataCreated(false)
+, m_metaDataCacheable(false), m_delegateValidated(false), m_completePending(false), m_listAccessor(0)
+{
+}
+
+QSGVisualDataModelData *QSGVisualDataModelPrivate::data(QObject *item)
+{
+ QSGVisualDataModelData *dataItem =
+ item->findChild<QSGVisualDataModelData *>();
+ Q_ASSERT(dataItem);
+ return dataItem;
+}
+
+//---------------------------------------------------------------------------
+
+QSGVisualDataModel::QSGVisualDataModel()
+: QSGVisualModel(*(new QSGVisualDataModelPrivate(0)))
+{
+}
+
+QSGVisualDataModel::QSGVisualDataModel(QDeclarativeContext *ctxt, QObject *parent)
+: QSGVisualModel(*(new QSGVisualDataModelPrivate(ctxt)), parent)
+{
+}
+
+QSGVisualDataModel::~QSGVisualDataModel()
+{
+ Q_D(QSGVisualDataModel);
+ if (d->m_listAccessor)
+ delete d->m_listAccessor;
+ if (d->m_delegateDataType)
+ d->m_delegateDataType->release();
+}
+
+QVariant QSGVisualDataModel::model() const
+{
+ Q_D(const QSGVisualDataModel);
+ return d->m_modelVariant;
+}
+
+void QSGVisualDataModel::setModel(const QVariant &model)
+{
+ Q_D(QSGVisualDataModel);
+ delete d->m_listAccessor;
+ d->m_listAccessor = 0;
+ d->m_modelVariant = model;
+ if (d->m_listModelInterface) {
+ // Assume caller has released all items.
+ QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)),
+ this, SLOT(_q_itemsChanged(int,int,QList<int>)));
+ QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)),
+ this, SLOT(_q_itemsInserted(int,int)));
+ QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)),
+ this, SLOT(_q_itemsRemoved(int,int)));
+ QObject::disconnect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)),
+ this, SLOT(_q_itemsMoved(int,int,int)));
+ d->m_listModelInterface = 0;
+ } else if (d->m_abstractItemModel) {
+ QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ QObject::disconnect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ QObject::disconnect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ QObject::disconnect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset()));
+ QObject::disconnect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged()));
+ d->m_abstractItemModel = 0;
+ } else if (d->m_visualItemModel) {
+ QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)),
+ this, SIGNAL(itemsInserted(int,int)));
+ QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)),
+ this, SIGNAL(itemsRemoved(int,int)));
+ QObject::disconnect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)),
+ this, SIGNAL(itemsMoved(int,int,int)));
+ QObject::disconnect(d->m_visualItemModel, SIGNAL(createdPackage(int,QDeclarativePackage*)),
+ this, SLOT(_q_createdPackage(int,QDeclarativePackage*)));
+ QObject::disconnect(d->m_visualItemModel, SIGNAL(destroyingPackage(QDeclarativePackage*)),
+ this, SLOT(_q_destroyingPackage(QDeclarativePackage*)));
+ d->m_visualItemModel = 0;
+ }
+
+ d->m_roles.clear();
+ d->m_roleNames.clear();
+ if (d->m_delegateDataType)
+ d->m_delegateDataType->release();
+ d->m_metaDataCreated = 0;
+ d->m_metaDataCacheable = false;
+ d->m_delegateDataType = new VDMDelegateDataType(&QSGVisualDataModelData::staticMetaObject, d->m_context?d->m_context->engine():qmlEngine(this));
+
+ QObject *object = qvariant_cast<QObject *>(model);
+ if (object && (d->m_listModelInterface = qobject_cast<QListModelInterface *>(object))) {
+ QObject::connect(d->m_listModelInterface, SIGNAL(itemsChanged(int,int,QList<int>)),
+ this, SLOT(_q_itemsChanged(int,int,QList<int>)));
+ QObject::connect(d->m_listModelInterface, SIGNAL(itemsInserted(int,int)),
+ this, SLOT(_q_itemsInserted(int,int)));
+ QObject::connect(d->m_listModelInterface, SIGNAL(itemsRemoved(int,int)),
+ this, SLOT(_q_itemsRemoved(int,int)));
+ QObject::connect(d->m_listModelInterface, SIGNAL(itemsMoved(int,int,int)),
+ this, SLOT(_q_itemsMoved(int,int,int)));
+ d->m_metaDataCacheable = true;
+ if (d->m_delegate && d->m_listModelInterface->count())
+ emit itemsInserted(0, d->m_listModelInterface->count());
+ return;
+ } else if (object && (d->m_abstractItemModel = qobject_cast<QAbstractItemModel *>(object))) {
+ QObject::connect(d->m_abstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ QObject::connect(d->m_abstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ QObject::connect(d->m_abstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
+ this, SLOT(_q_dataChanged(QModelIndex,QModelIndex)));
+ QObject::connect(d->m_abstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ this, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ QObject::connect(d->m_abstractItemModel, SIGNAL(modelReset()), this, SLOT(_q_modelReset()));
+ QObject::connect(d->m_abstractItemModel, SIGNAL(layoutChanged()), this, SLOT(_q_layoutChanged()));
+ d->m_metaDataCacheable = true;
+ if (d->m_abstractItemModel->canFetchMore(d->m_root))
+ d->m_abstractItemModel->fetchMore(d->m_root);
+ return;
+ }
+ if ((d->m_visualItemModel = qvariant_cast<QSGVisualDataModel *>(model))) {
+ QObject::connect(d->m_visualItemModel, SIGNAL(countChanged()),
+ this, SIGNAL(countChanged()));
+ QObject::connect(d->m_visualItemModel, SIGNAL(itemsInserted(int,int)),
+ this, SIGNAL(itemsInserted(int,int)));
+ QObject::connect(d->m_visualItemModel, SIGNAL(itemsRemoved(int,int)),
+ this, SIGNAL(itemsRemoved(int,int)));
+ QObject::connect(d->m_visualItemModel, SIGNAL(itemsMoved(int,int,int)),
+ this, SIGNAL(itemsMoved(int,int,int)));
+ QObject::connect(d->m_visualItemModel, SIGNAL(createdPackage(int,QDeclarativePackage*)),
+ this, SLOT(_q_createdPackage(int,QDeclarativePackage*)));
+ QObject::connect(d->m_visualItemModel, SIGNAL(destroyingPackage(QDeclarativePackage*)),
+ this, SLOT(_q_destroyingPackage(QDeclarativePackage*)));
+ return;
+ }
+ d->m_listAccessor = new QDeclarativeListAccessor;
+ d->m_listAccessor->setList(model, d->m_context?d->m_context->engine():qmlEngine(this));
+ if (d->m_listAccessor->type() != QDeclarativeListAccessor::ListProperty)
+ d->m_metaDataCacheable = true;
+ if (d->m_delegate && d->modelCount()) {
+ emit itemsInserted(0, d->modelCount());
+ emit countChanged();
+ }
+}
+
+QDeclarativeComponent *QSGVisualDataModel::delegate() const
+{
+ Q_D(const QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->delegate();
+ return d->m_delegate;
+}
+
+void QSGVisualDataModel::setDelegate(QDeclarativeComponent *delegate)
+{
+ Q_D(QSGVisualDataModel);
+ bool wasValid = d->m_delegate != 0;
+ d->m_delegate = delegate;
+ d->m_delegateValidated = false;
+ if (!wasValid && d->modelCount() && d->m_delegate) {
+ emit itemsInserted(0, d->modelCount());
+ emit countChanged();
+ }
+ if (wasValid && !d->m_delegate && d->modelCount()) {
+ emit itemsRemoved(0, d->modelCount());
+ emit countChanged();
+ }
+}
+
+QVariant QSGVisualDataModel::rootIndex() const
+{
+ Q_D(const QSGVisualDataModel);
+ return QVariant::fromValue(d->m_root);
+}
+
+void QSGVisualDataModel::setRootIndex(const QVariant &root)
+{
+ Q_D(QSGVisualDataModel);
+ QModelIndex modelIndex = qvariant_cast<QModelIndex>(root);
+ if (d->m_root != modelIndex) {
+ int oldCount = d->modelCount();
+ d->m_root = modelIndex;
+ if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(modelIndex))
+ d->m_abstractItemModel->fetchMore(modelIndex);
+ int newCount = d->modelCount();
+ if (d->m_delegate && oldCount)
+ emit itemsRemoved(0, oldCount);
+ if (d->m_delegate && newCount)
+ emit itemsInserted(0, newCount);
+ if (newCount != oldCount)
+ emit countChanged();
+ emit rootIndexChanged();
+ }
+}
+
+QVariant QSGVisualDataModel::modelIndex(int idx) const
+{
+ Q_D(const QSGVisualDataModel);
+ if (d->m_abstractItemModel)
+ return QVariant::fromValue(d->m_abstractItemModel->index(idx, 0, d->m_root));
+ return QVariant::fromValue(QModelIndex());
+}
+
+QVariant QSGVisualDataModel::parentModelIndex() const
+{
+ Q_D(const QSGVisualDataModel);
+ if (d->m_abstractItemModel)
+ return QVariant::fromValue(d->m_abstractItemModel->parent(d->m_root));
+ return QVariant::fromValue(QModelIndex());
+}
+
+QString QSGVisualDataModel::part() const
+{
+ Q_D(const QSGVisualDataModel);
+ return d->m_part;
+}
+
+void QSGVisualDataModel::setPart(const QString &part)
+{
+ Q_D(QSGVisualDataModel);
+ d->m_part = part;
+}
+
+int QSGVisualDataModel::count() const
+{
+ Q_D(const QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->count();
+ if (!d->m_delegate)
+ return 0;
+ return d->modelCount();
+}
+
+QSGItem *QSGVisualDataModel::item(int index, bool complete)
+{
+ Q_D(QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->item(index, d->m_part.toUtf8(), complete);
+ return item(index, QByteArray(), complete);
+}
+
+/*
+ Returns ReleaseStatus flags.
+*/
+QSGVisualDataModel::ReleaseFlags QSGVisualDataModel::release(QSGItem *item)
+{
+ Q_D(QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->release(item);
+
+ ReleaseFlags stat = 0;
+ QObject *obj = item;
+ bool inPackage = false;
+
+ QHash<QObject*,QDeclarativePackage*>::iterator it = d->m_packaged.find(item);
+ if (it != d->m_packaged.end()) {
+ QDeclarativePackage *package = *it;
+ d->m_packaged.erase(it);
+ if (d->m_packaged.contains(item))
+ stat |= Referenced;
+ inPackage = true;
+ obj = package; // fall through and delete
+ }
+
+ if (d->m_cache.releaseItem(obj)) {
+ // Remove any bindings to avoid warnings due to parent change.
+ QObjectPrivate *p = QObjectPrivate::get(obj);
+ Q_ASSERT(p->declarativeData);
+ QDeclarativeData *d = static_cast<QDeclarativeData*>(p->declarativeData);
+ if (d->ownContext && d->context)
+ d->context->clearContext();
+
+ if (inPackage) {
+ emit destroyingPackage(qobject_cast<QDeclarativePackage*>(obj));
+ } else {
+ // XXX todo - the original did item->scene()->removeItem(). Why?
+ item->setParentItem(0);
+ }
+ stat |= Destroyed;
+ obj->deleteLater();
+ } else if (!inPackage) {
+ stat |= Referenced;
+ }
+
+ return stat;
+}
+
+QObject *QSGVisualDataModel::parts()
+{
+ Q_D(QSGVisualDataModel);
+ if (!d->m_parts)
+ d->m_parts = new QSGVisualDataModelParts(this);
+ return d->m_parts;
+}
+
+QSGItem *QSGVisualDataModel::item(int index, const QByteArray &viewId, bool complete)
+{
+ Q_D(QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->item(index, viewId, complete);
+
+ if (d->modelCount() <= 0 || !d->m_delegate)
+ return 0;
+ QObject *nobj = d->m_cache.getItem(index);
+ bool needComplete = false;
+ if (!nobj) {
+ QDeclarativeContext *ccontext = d->m_context;
+ if (!ccontext) ccontext = qmlContext(this);
+ QDeclarativeContext *ctxt = new QDeclarativeContext(ccontext);
+ QSGVisualDataModelData *data = new QSGVisualDataModelData(index, this);
+ if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor
+ && d->m_listAccessor->type() == QDeclarativeListAccessor::ListProperty) {
+ ctxt->setContextObject(d->m_listAccessor->at(index).value<QObject*>());
+ ctxt = new QDeclarativeContext(ctxt, ctxt);
+ }
+ ctxt->setContextProperty(QLatin1String("model"), data);
+ ctxt->setContextObject(data);
+ d->m_completePending = false;
+ nobj = d->m_delegate->beginCreate(ctxt);
+ if (complete) {
+ d->m_delegate->completeCreate();
+ } else {
+ d->m_completePending = true;
+ needComplete = true;
+ }
+ if (nobj) {
+ QDeclarative_setParent_noEvent(ctxt, nobj);
+ QDeclarative_setParent_noEvent(data, nobj);
+ d->m_cache.insertItem(index, nobj);
+ if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(nobj))
+ emit createdPackage(index, package);
+ } else {
+ delete data;
+ delete ctxt;
+ qmlInfo(this, d->m_delegate->errors()) << "Error creating delegate";
+ }
+ }
+ QSGItem *item = qobject_cast<QSGItem *>(nobj);
+ if (!item) {
+ QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(nobj);
+ if (package) {
+ QObject *o = package->part(QString::fromUtf8(viewId));
+ item = qobject_cast<QSGItem *>(o);
+ if (item)
+ d->m_packaged.insertMulti(item, package);
+ }
+ }
+ if (!item) {
+ if (needComplete)
+ d->m_delegate->completeCreate();
+ d->m_cache.releaseItem(nobj);
+ if (!d->m_delegateValidated) {
+ qmlInfo(d->m_delegate) << QSGVisualDataModel::tr("Delegate component must be Item type.");
+ d->m_delegateValidated = true;
+ }
+ }
+ if (d->modelCount()-1 == index && d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root))
+ d->m_abstractItemModel->fetchMore(d->m_root);
+
+ return item;
+}
+
+bool QSGVisualDataModel::completePending() const
+{
+ Q_D(const QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->completePending();
+ return d->m_completePending;
+}
+
+void QSGVisualDataModel::completeItem()
+{
+ Q_D(QSGVisualDataModel);
+ if (d->m_visualItemModel) {
+ d->m_visualItemModel->completeItem();
+ return;
+ }
+
+ d->m_delegate->completeCreate();
+ d->m_completePending = false;
+}
+
+QString QSGVisualDataModel::stringValue(int index, const QString &name)
+{
+ Q_D(QSGVisualDataModel);
+ if (d->m_visualItemModel)
+ return d->m_visualItemModel->stringValue(index, name);
+
+ if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor) {
+ if (QObject *object = d->m_listAccessor->at(index).value<QObject*>())
+ return object->property(name.toUtf8()).toString();
+ }
+
+ if ((!d->m_listModelInterface && !d->m_abstractItemModel) || !d->m_delegate)
+ return QString();
+
+ QString val;
+ QObject *data = 0;
+ bool tempData = false;
+
+ if (QObject *nobj = d->m_cache.item(index))
+ data = d->data(nobj);
+ if (!data) {
+ data = new QSGVisualDataModelData(index, this);
+ tempData = true;
+ }
+
+ QDeclarativeData *ddata = QDeclarativeData::get(data);
+ if (ddata && ddata->propertyCache) {
+ QDeclarativePropertyCache::Data *prop = ddata->propertyCache->property(name);
+ if (prop) {
+ if (prop->propType == QVariant::String) {
+ void *args[] = { &val, 0 };
+ QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args);
+ } else if (prop->propType == qMetaTypeId<QVariant>()) {
+ QVariant v;
+ void *args[] = { &v, 0 };
+ QMetaObject::metacall(data, QMetaObject::ReadProperty, prop->coreIndex, args);
+ val = v.toString();
+ }
+ } else {
+ val = data->property(name.toUtf8()).toString();
+ }
+ } else {
+ val = data->property(name.toUtf8()).toString();
+ }
+
+ if (tempData)
+ delete data;
+
+ return val;
+}
+
+int QSGVisualDataModel::indexOf(QSGItem *item, QObject *) const
+{
+ QVariant val = QDeclarativeEngine::contextForObject(item)->contextProperty(QLatin1String("index"));
+ return val.toInt();
+ return -1;
+}
+
+void QSGVisualDataModel::setWatchedRoles(QList<QByteArray> roles)
+{
+ Q_D(QSGVisualDataModel);
+ d->watchedRoles = roles;
+ d->watchedRoleIds.clear();
+}
+
+void QSGVisualDataModel::_q_itemsChanged(int index, int count,
+ const QList<int> &roles)
+{
+ Q_D(QSGVisualDataModel);
+ bool changed = false;
+ if (!d->watchedRoles.isEmpty() && d->watchedRoleIds.isEmpty()) {
+ foreach (QByteArray r, d->watchedRoles) {
+ if (d->m_roleNames.contains(r))
+ d->watchedRoleIds << d->m_roleNames.value(r);
+ }
+ }
+
+ for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::ConstIterator iter = d->m_cache.begin();
+ iter != d->m_cache.end(); ++iter) {
+ const int idx = iter.key();
+
+ if (idx >= index && idx < index+count) {
+ QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
+ QSGVisualDataModelData *data = d->data(objRef.obj);
+ for (int roleIdx = 0; roleIdx < roles.count(); ++roleIdx) {
+ int role = roles.at(roleIdx);
+ if (!changed && !d->watchedRoleIds.isEmpty() && d->watchedRoleIds.contains(role))
+ changed = true;
+ int propId = data->propForRole(role);
+ if (propId != -1) {
+ if (data->hasValue(propId)) {
+ if (d->m_listModelInterface) {
+ data->setValue(propId, d->m_listModelInterface->data(idx, role));
+ } else if (d->m_abstractItemModel) {
+ QModelIndex index = d->m_abstractItemModel->index(idx, 0, d->m_root);
+ data->setValue(propId, d->m_abstractItemModel->data(index, role));
+ }
+ }
+ } else {
+ QString roleName;
+ if (d->m_listModelInterface)
+ roleName = d->m_listModelInterface->toString(role);
+ else if (d->m_abstractItemModel)
+ roleName = QString::fromUtf8(d->m_abstractItemModel->roleNames().value(role));
+ qmlInfo(this) << "Changing role not present in item: " << roleName;
+ }
+ }
+ if (d->m_roles.count() == 1) {
+ // Handle the modelData role we add if there is just one role.
+ int propId = data->modelDataPropertyId();
+ if (data->hasValue(propId)) {
+ int role = d->m_roles.at(0);
+ if (d->m_listModelInterface) {
+ data->setValue(propId, d->m_listModelInterface->data(idx, role));
+ } else if (d->m_abstractItemModel) {
+ QModelIndex index = d->m_abstractItemModel->index(idx, 0, d->m_root);
+ data->setValue(propId, d->m_abstractItemModel->data(index, role));
+ }
+ }
+ }
+ }
+ }
+ if (changed)
+ emit itemsChanged(index, count);
+}
+
+void QSGVisualDataModel::_q_itemsInserted(int index, int count)
+{
+ Q_D(QSGVisualDataModel);
+ if (!count)
+ return;
+ // XXX - highly inefficient
+ QHash<int,QSGVisualDataModelPrivate::ObjectRef> items;
+ for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
+ iter != d->m_cache.end(); ) {
+
+ if (iter.key() >= index) {
+ QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
+ int index = iter.key() + count;
+ iter = d->m_cache.erase(iter);
+
+ items.insert(index, objRef);
+
+ QSGVisualDataModelData *data = d->data(objRef.obj);
+ data->setIndex(index);
+ } else {
+ ++iter;
+ }
+ }
+ d->m_cache.unite(items);
+
+ emit itemsInserted(index, count);
+ emit countChanged();
+}
+
+void QSGVisualDataModel::_q_itemsRemoved(int index, int count)
+{
+ Q_D(QSGVisualDataModel);
+ if (!count)
+ return;
+ // XXX - highly inefficient
+ QHash<int, QSGVisualDataModelPrivate::ObjectRef> items;
+ for (QHash<int, QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
+ iter != d->m_cache.end(); ) {
+ if (iter.key() >= index && iter.key() < index + count) {
+ QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
+ iter = d->m_cache.erase(iter);
+ items.insertMulti(-1, objRef); //XXX perhaps better to maintain separately
+ QSGVisualDataModelData *data = d->data(objRef.obj);
+ data->setIndex(-1);
+ } else if (iter.key() >= index + count) {
+ QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
+ int index = iter.key() - count;
+ iter = d->m_cache.erase(iter);
+ items.insert(index, objRef);
+ QSGVisualDataModelData *data = d->data(objRef.obj);
+ data->setIndex(index);
+ } else {
+ ++iter;
+ }
+ }
+
+ d->m_cache.unite(items);
+ emit itemsRemoved(index, count);
+ emit countChanged();
+}
+
+void QSGVisualDataModel::_q_itemsMoved(int from, int to, int count)
+{
+ Q_D(QSGVisualDataModel);
+ // XXX - highly inefficient
+ QHash<int,QSGVisualDataModelPrivate::ObjectRef> items;
+ for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
+ iter != d->m_cache.end(); ) {
+
+ if (iter.key() >= from && iter.key() < from + count) {
+ QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
+ int index = iter.key() - from + to;
+ iter = d->m_cache.erase(iter);
+
+ items.insert(index, objRef);
+
+ QSGVisualDataModelData *data = d->data(objRef.obj);
+ data->setIndex(index);
+ } else {
+ ++iter;
+ }
+ }
+ for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
+ iter != d->m_cache.end(); ) {
+
+ int diff = from > to ? count : -count;
+ if (iter.key() >= qMin(from,to) && iter.key() < qMax(from+count,to+count)) {
+ QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
+ int index = iter.key() + diff;
+ iter = d->m_cache.erase(iter);
+
+ items.insert(index, objRef);
+
+ QSGVisualDataModelData *data = d->data(objRef.obj);
+ data->setIndex(index);
+ } else {
+ ++iter;
+ }
+ }
+ d->m_cache.unite(items);
+
+ emit itemsMoved(from, to, count);
+}
+
+void QSGVisualDataModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end)
+{
+ Q_D(QSGVisualDataModel);
+ if (parent == d->m_root)
+ _q_itemsInserted(begin, end - begin + 1);
+}
+
+void QSGVisualDataModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end)
+{
+ Q_D(QSGVisualDataModel);
+ if (parent == d->m_root)
+ _q_itemsRemoved(begin, end - begin + 1);
+}
+
+void QSGVisualDataModel::_q_rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow)
+{
+ Q_D(QSGVisualDataModel);
+ const int count = sourceEnd - sourceStart + 1;
+ if (destinationParent == d->m_root && sourceParent == d->m_root) {
+ _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow-1, count);
+ } else if (sourceParent == d->m_root) {
+ _q_itemsRemoved(sourceStart, count);
+ } else if (destinationParent == d->m_root) {
+ _q_itemsInserted(destinationRow, count);
+ }
+}
+
+void QSGVisualDataModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end)
+{
+ Q_D(QSGVisualDataModel);
+ if (begin.parent() == d->m_root)
+ _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles);
+}
+
+void QSGVisualDataModel::_q_layoutChanged()
+{
+ Q_D(QSGVisualDataModel);
+ _q_itemsChanged(0, count(), d->m_roles);
+}
+
+void QSGVisualDataModel::_q_modelReset()
+{
+ Q_D(QSGVisualDataModel);
+ d->m_root = QModelIndex();
+ emit modelReset();
+ emit rootIndexChanged();
+ if (d->m_abstractItemModel && d->m_abstractItemModel->canFetchMore(d->m_root))
+ d->m_abstractItemModel->fetchMore(d->m_root);
+}
+
+void QSGVisualDataModel::_q_createdPackage(int index, QDeclarativePackage *package)
+{
+ Q_D(QSGVisualDataModel);
+ emit createdItem(index, qobject_cast<QSGItem*>(package->part(d->m_part)));
+}
+
+void QSGVisualDataModel::_q_destroyingPackage(QDeclarativePackage *package)
+{
+ Q_D(QSGVisualDataModel);
+ emit destroyingItem(qobject_cast<QSGItem*>(package->part(d->m_part)));
+}
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QListModelInterface)
+
+#include <qsgvisualitemmodel.moc>
diff --git a/src/declarative/items/qsgvisualitemmodel_p.h b/src/declarative/items/qsgvisualitemmodel_p.h
new file mode 100644
index 0000000000..62f89905ac
--- /dev/null
+++ b/src/declarative/items/qsgvisualitemmodel_p.h
@@ -0,0 +1,257 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGVISUALITEMMODEL_P_H
+#define QSGVISUALITEMMODEL_P_H
+
+#include <QtDeclarative/qdeclarative.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qabstractitemmodel.h>
+
+QT_BEGIN_HEADER
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGItem;
+class QDeclarativeComponent;
+class QDeclarativePackage;
+class QSGVisualDataModelPrivate;
+
+class Q_DECLARATIVE_EXPORT QSGVisualModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+
+public:
+ virtual ~QSGVisualModel() {}
+
+ enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 };
+ Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag)
+
+ virtual int count() const = 0;
+ virtual bool isValid() const = 0;
+ virtual QSGItem *item(int index, bool complete=true) = 0;
+ virtual ReleaseFlags release(QSGItem *item) = 0;
+ virtual bool completePending() const = 0;
+ virtual void completeItem() = 0;
+ virtual QString stringValue(int, const QString &) = 0;
+ virtual void setWatchedRoles(QList<QByteArray> roles) = 0;
+
+ virtual int indexOf(QSGItem *item, QObject *objectContext) const = 0;
+
+Q_SIGNALS:
+ void countChanged();
+ void itemsInserted(int index, int count);
+ void itemsRemoved(int index, int count);
+ void itemsMoved(int from, int to, int count);
+ void itemsChanged(int index, int count);
+ void modelReset();
+ void createdItem(int index, QSGItem *item);
+ void destroyingItem(QSGItem *item);
+
+protected:
+ QSGVisualModel(QObjectPrivate &dd, QObject *parent = 0)
+ : QObject(dd, parent) {}
+
+private:
+ Q_DISABLE_COPY(QSGVisualModel)
+};
+
+class QSGVisualItemModelAttached;
+class QSGVisualItemModelPrivate;
+class Q_DECLARATIVE_EXPORT QSGVisualItemModel : public QSGVisualModel
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGVisualItemModel)
+
+ Q_PROPERTY(QDeclarativeListProperty<QSGItem> children READ children NOTIFY childrenChanged DESIGNABLE false)
+ Q_CLASSINFO("DefaultProperty", "children")
+
+public:
+ QSGVisualItemModel(QObject *parent=0);
+ virtual ~QSGVisualItemModel() {}
+
+ virtual int count() const;
+ virtual bool isValid() const;
+ virtual QSGItem *item(int index, bool complete=true);
+ virtual ReleaseFlags release(QSGItem *item);
+ virtual bool completePending() const;
+ virtual void completeItem();
+ virtual QString stringValue(int index, const QString &role);
+ virtual void setWatchedRoles(QList<QByteArray>) {}
+
+ virtual int indexOf(QSGItem *item, QObject *objectContext) const;
+
+ QDeclarativeListProperty<QSGItem> children();
+
+ static QSGVisualItemModelAttached *qmlAttachedProperties(QObject *obj);
+
+Q_SIGNALS:
+ void childrenChanged();
+
+private:
+ Q_DISABLE_COPY(QSGVisualItemModel)
+};
+
+
+class Q_DECLARATIVE_EXPORT QSGVisualDataModel : public QSGVisualModel
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QSGVisualDataModel)
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel)
+ Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate)
+ Q_PROPERTY(QString part READ part WRITE setPart)
+ Q_PROPERTY(QObject *parts READ parts CONSTANT)
+ Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+public:
+ QSGVisualDataModel();
+ QSGVisualDataModel(QDeclarativeContext *, QObject *parent=0);
+ virtual ~QSGVisualDataModel();
+
+ QVariant model() const;
+ void setModel(const QVariant &);
+
+ QDeclarativeComponent *delegate() const;
+ void setDelegate(QDeclarativeComponent *);
+
+ QVariant rootIndex() const;
+ void setRootIndex(const QVariant &root);
+
+ Q_INVOKABLE QVariant modelIndex(int idx) const;
+ Q_INVOKABLE QVariant parentModelIndex() const;
+
+ QString part() const;
+ void setPart(const QString &);
+
+ int count() const;
+ bool isValid() const { return delegate() != 0; }
+ QSGItem *item(int index, bool complete=true);
+ QSGItem *item(int index, const QByteArray &, bool complete=true);
+ ReleaseFlags release(QSGItem *item);
+ bool completePending() const;
+ void completeItem();
+ virtual QString stringValue(int index, const QString &role);
+ virtual void setWatchedRoles(QList<QByteArray> roles);
+
+ int indexOf(QSGItem *item, QObject *objectContext) const;
+
+ QObject *parts();
+
+Q_SIGNALS:
+ void createdPackage(int index, QDeclarativePackage *package);
+ void destroyingPackage(QDeclarativePackage *package);
+ void rootIndexChanged();
+
+private Q_SLOTS:
+ void _q_itemsChanged(int, int, const QList<int> &);
+ void _q_itemsInserted(int index, int count);
+ void _q_itemsRemoved(int index, int count);
+ void _q_itemsMoved(int from, int to, int count);
+ void _q_rowsInserted(const QModelIndex &,int,int);
+ void _q_rowsRemoved(const QModelIndex &,int,int);
+ void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
+ void _q_dataChanged(const QModelIndex&,const QModelIndex&);
+ void _q_layoutChanged();
+ void _q_modelReset();
+ void _q_createdPackage(int index, QDeclarativePackage *package);
+ void _q_destroyingPackage(QDeclarativePackage *package);
+
+private:
+ Q_DISABLE_COPY(QSGVisualDataModel)
+};
+
+class QSGVisualItemModelAttached : public QObject
+{
+ Q_OBJECT
+
+public:
+ QSGVisualItemModelAttached(QObject *parent)
+ : QObject(parent), m_index(0) {}
+ ~QSGVisualItemModelAttached() {
+ attachedProperties.remove(parent());
+ }
+
+ Q_PROPERTY(int index READ index NOTIFY indexChanged)
+ int index() const { return m_index; }
+ void setIndex(int idx) {
+ if (m_index != idx) {
+ m_index = idx;
+ emit indexChanged();
+ }
+ }
+
+ static QSGVisualItemModelAttached *properties(QObject *obj) {
+ QSGVisualItemModelAttached *rv = attachedProperties.value(obj);
+ if (!rv) {
+ rv = new QSGVisualItemModelAttached(obj);
+ attachedProperties.insert(obj, rv);
+ }
+ return rv;
+ }
+
+Q_SIGNALS:
+ void indexChanged();
+
+public:
+ int m_index;
+
+ static QHash<QObject*, QSGVisualItemModelAttached*> attachedProperties;
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QSGVisualModel)
+QML_DECLARE_TYPE(QSGVisualItemModel)
+QML_DECLARE_TYPEINFO(QSGVisualItemModel, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QSGVisualDataModel)
+
+QT_END_HEADER
+
+#endif // QSGVISUALITEMMODEL_P_H
diff --git a/src/declarative/items/syncexcludes b/src/declarative/items/syncexcludes
new file mode 100644
index 0000000000..ab7a374a5b
--- /dev/null
+++ b/src/declarative/items/syncexcludes
@@ -0,0 +1,11 @@
+qdeclarativegraphicswidget.cpp
+qdeclarativegraphicswidget_p.h
+qdeclarativetextlayout_p.h
+qdeclarativetextlayout.cpp
+qdeclarativelayoutitem.cpp
+qdeclarativelayoutitem_p.h
+qdeclarativefocuspanel.cpp
+qdeclarativefocuspanel_p.h
+qdeclarativepath_p.h
+qdeclarativepath_p_p.h
+qdeclarativepath.cpp