diff options
author | Martin Jones <martin.jones@nokia.com> | 2011-07-11 13:47:51 +1000 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2011-07-12 06:38:09 +0200 |
commit | 4442dea01b9d4d45964228ac442166d89f091f9e (patch) | |
tree | 28ae20cb26967765a988b1ee522fc2d64d777671 /src/qtquick1 | |
parent | b119220da60453ecf31898f7a57eda9d3c4e9225 (diff) |
Extract all QtQuick 1 elements into a separate library/plugin.
Change-Id: I41a280de2739ee08202f4be2519e5012870090f2
Reviewed-on: http://codereview.qt.nokia.com/1391
Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'src/qtquick1')
150 files changed, 63228 insertions, 0 deletions
diff --git a/src/qtquick1/graphicsitems/graphicsitems.pri b/src/qtquick1/graphicsitems/graphicsitems.pri new file mode 100644 index 0000000000..9904274023 --- /dev/null +++ b/src/qtquick1/graphicsitems/graphicsitems.pri @@ -0,0 +1,94 @@ +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qdeclarativeitemsmodule_p.h \ + $$PWD/qdeclarativeanchors_p.h \ + $$PWD/qdeclarativeanchors_p_p.h \ + $$PWD/qdeclarativeevents_p_p.h \ + $$PWD/qdeclarativeflickable_p.h \ + $$PWD/qdeclarativeflickable_p_p.h \ + $$PWD/qdeclarativeflipable_p.h \ + $$PWD/qdeclarativegridview_p.h \ + $$PWD/qdeclarativeimage_p.h \ + $$PWD/qdeclarativeimagebase_p.h \ + $$PWD/qdeclarativeborderimage_p.h \ + $$PWD/qdeclarativepainteditem_p.h \ + $$PWD/qdeclarativepainteditem_p_p.h \ + $$PWD/qdeclarativeimage_p_p.h \ + $$PWD/qdeclarativeborderimage_p_p.h \ + $$PWD/qdeclarativeimagebase_p_p.h \ + $$PWD/qdeclarativeanimatedimage_p.h \ + $$PWD/qdeclarativeanimatedimage_p_p.h \ + $$PWD/qdeclarativeitem.h \ + $$PWD/qdeclarativeitem_p.h \ + $$PWD/qdeclarativefocuspanel_p.h \ + $$PWD/qdeclarativefocusscope_p.h \ + $$PWD/qdeclarativepositioners_p.h \ + $$PWD/qdeclarativepositioners_p_p.h \ + $$PWD/qdeclarativeloader_p.h \ + $$PWD/qdeclarativeloader_p_p.h \ + $$PWD/qdeclarativemousearea_p.h \ + $$PWD/qdeclarativemousearea_p_p.h \ + $$PWD/qdeclarativepath_p.h \ + $$PWD/qdeclarativepath_p_p.h \ + $$PWD/qdeclarativepathview_p.h \ + $$PWD/qdeclarativepathview_p_p.h \ + $$PWD/qdeclarativerectangle_p.h \ + $$PWD/qdeclarativerectangle_p_p.h \ + $$PWD/qdeclarativerepeater_p.h \ + $$PWD/qdeclarativerepeater_p_p.h \ + $$PWD/qdeclarativescalegrid_p_p.h \ + $$PWD/qdeclarativetranslate_p.h \ + $$PWD/qdeclarativetextinput_p.h \ + $$PWD/qdeclarativetextinput_p_p.h \ + $$PWD/qdeclarativetextedit_p.h \ + $$PWD/qdeclarativetextedit_p_p.h \ + $$PWD/qdeclarativetext_p.h \ + $$PWD/qdeclarativetext_p_p.h \ + $$PWD/qdeclarativevisualitemmodel_p.h \ + $$PWD/qdeclarativelistview_p.h \ + $$PWD/qdeclarativelayoutitem_p.h \ + $$PWD/qdeclarativeitemchangelistener_p.h \ + $$PWD/qdeclarativegraphicswidget_p.h \ + $$PWD/qdeclarativetextlayout_p.h \ + $$PWD/qdeclarativepincharea_p.h \ + $$PWD/qdeclarativepincharea_p_p.h \ + $$PWD/qdeclarativeimplicitsizeitem_p.h \ + $$PWD/qdeclarativeimplicitsizeitem_p_p.h + + +SOURCES += \ + $$PWD/qdeclarativeitemsmodule.cpp \ + $$PWD/qdeclarativeanchors.cpp \ + $$PWD/qdeclarativeevents.cpp \ + $$PWD/qdeclarativeflickable.cpp \ + $$PWD/qdeclarativeflipable.cpp \ + $$PWD/qdeclarativegridview.cpp \ + $$PWD/qdeclarativeimage.cpp \ + $$PWD/qdeclarativeborderimage.cpp \ + $$PWD/qdeclarativeimagebase.cpp \ + $$PWD/qdeclarativeanimatedimage.cpp \ + $$PWD/qdeclarativepainteditem.cpp \ + $$PWD/qdeclarativeitem.cpp \ + $$PWD/qdeclarativefocuspanel.cpp \ + $$PWD/qdeclarativefocusscope.cpp \ + $$PWD/qdeclarativepositioners.cpp \ + $$PWD/qdeclarativeloader.cpp \ + $$PWD/qdeclarativemousearea.cpp \ + $$PWD/qdeclarativepath.cpp \ + $$PWD/qdeclarativepathview.cpp \ + $$PWD/qdeclarativerectangle.cpp \ + $$PWD/qdeclarativerepeater.cpp \ + $$PWD/qdeclarativescalegrid.cpp \ + $$PWD/qdeclarativetranslate.cpp \ + $$PWD/qdeclarativetextinput.cpp \ + $$PWD/qdeclarativetext.cpp \ + $$PWD/qdeclarativetextedit.cpp \ + $$PWD/qdeclarativevisualitemmodel.cpp \ + $$PWD/qdeclarativelistview.cpp \ + $$PWD/qdeclarativelayoutitem.cpp \ + $$PWD/qdeclarativegraphicswidget.cpp \ + $$PWD/qdeclarativetextlayout.cpp \ + $$PWD/qdeclarativepincharea.cpp \ + $$PWD/qdeclarativeimplicitsizeitem.cpp + diff --git a/src/qtquick1/graphicsitems/qdeclarativeanchors.cpp b/src/qtquick1/graphicsitems/qdeclarativeanchors.cpp new file mode 100644 index 0000000000..2f074a4f04 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeanchors.cpp @@ -0,0 +1,1169 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeanchors_p_p.h" + +#include "QtQuick1/qdeclarativeitem.h" +#include "QtQuick1/private/qdeclarativeitem_p.h" + +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QDebug> + +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(QGraphicsItem *i) +{ + QGraphicsItemPrivate *item = QGraphicsItemPrivate::get(i); + + qreal width = item->width(); + int iw = width; + if (iw % 2) + return (width + 1) / 2; + else + return width / 2; +} + +static qreal vcenter(QGraphicsItem *i) +{ + QGraphicsItemPrivate *item = QGraphicsItemPrivate::get(i); + + 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(QGraphicsObject *item, QDeclarative1AnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(item); + switch(anchorLine) { + case QDeclarative1AnchorLine::Left: + ret = item->x(); + break; + case QDeclarative1AnchorLine::Right: + ret = item->x() + d->width(); + break; + case QDeclarative1AnchorLine::Top: + ret = item->y(); + break; + case QDeclarative1AnchorLine::Bottom: + ret = item->y() + d->height(); + break; + case QDeclarative1AnchorLine::HCenter: + ret = item->x() + hcenter(item); + break; + case QDeclarative1AnchorLine::VCenter: + ret = item->y() + vcenter(item); + break; + case QDeclarative1AnchorLine::Baseline: + if (d->isDeclarativeItem) + ret = item->y() + static_cast<QDeclarativeItem*>(item)->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +//position when origin is 0,0 +static qreal adjustedPosition(QGraphicsObject *item, QDeclarative1AnchorLine::AnchorLine anchorLine) +{ + qreal ret = 0.0; + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(item); + switch(anchorLine) { + case QDeclarative1AnchorLine::Left: + ret = 0.0; + break; + case QDeclarative1AnchorLine::Right: + ret = d->width(); + break; + case QDeclarative1AnchorLine::Top: + ret = 0.0; + break; + case QDeclarative1AnchorLine::Bottom: + ret = d->height(); + break; + case QDeclarative1AnchorLine::HCenter: + ret = hcenter(item); + break; + case QDeclarative1AnchorLine::VCenter: + ret = vcenter(item); + break; + case QDeclarative1AnchorLine::Baseline: + if (d->isDeclarativeItem) + ret = static_cast<QDeclarativeItem*>(item)->baselineOffset(); + break; + default: + break; + } + + return ret; +} + +QDeclarative1Anchors::QDeclarative1Anchors(QObject *parent) + : QObject(*new QDeclarative1AnchorsPrivate(0), parent) +{ + qFatal("QDeclarative1Anchors::QDeclarative1Anchors(QObject*) called"); +} + +QDeclarative1Anchors::QDeclarative1Anchors(QGraphicsObject *item, QObject *parent) + : QObject(*new QDeclarative1AnchorsPrivate(item), parent) +{ +} + +QDeclarative1Anchors::~QDeclarative1Anchors() +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1AnchorsPrivate::fillChanged() +{ + Q_Q(QDeclarative1Anchors); + 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)); + } + QGraphicsItemPrivate *fillPrivate = QGraphicsItemPrivate::get(fill); + setItemSize(QSizeF(fillPrivate->width()-leftMargin-rightMargin, fillPrivate->height()-topMargin-bottomMargin)); + + --updatingFill; + } else { + // ### Make this certain :) + qmlInfo(item) << QDeclarative1Anchors::tr("Possible anchor loop detected on fill."); + } + +} + +void QDeclarative1AnchorsPrivate::centerInChanged() +{ + Q_Q(QDeclarative1Anchors); + 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) << QDeclarative1Anchors::tr("Possible anchor loop detected on centerIn."); + } +} + +void QDeclarative1AnchorsPrivate::clearItem(QGraphicsObject *item) +{ + if (!item) + return; + if (fill == item) + fill = 0; + if (centerIn == item) + centerIn = 0; + if (left.item == item) { + left.item = 0; + usedAnchors &= ~QDeclarative1Anchors::LeftAnchor; + } + if (right.item == item) { + right.item = 0; + usedAnchors &= ~QDeclarative1Anchors::RightAnchor; + } + if (top.item == item) { + top.item = 0; + usedAnchors &= ~QDeclarative1Anchors::TopAnchor; + } + if (bottom.item == item) { + bottom.item = 0; + usedAnchors &= ~QDeclarative1Anchors::BottomAnchor; + } + if (vCenter.item == item) { + vCenter.item = 0; + usedAnchors &= ~QDeclarative1Anchors::VCenterAnchor; + } + if (hCenter.item == item) { + hCenter.item = 0; + usedAnchors &= ~QDeclarative1Anchors::HCenterAnchor; + } + if (baseline.item == item) { + baseline.item = 0; + usedAnchors &= ~QDeclarative1Anchors::BaselineAnchor; + } +} + +void QDeclarative1AnchorsPrivate::addDepend(QGraphicsObject *item) +{ + if (!item) + return; + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(item); + if (itemPrivate->isDeclarativeItem) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(item)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } else if(itemPrivate->isWidget) { + Q_Q(QDeclarative1Anchors); + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + QObject::connect(widget, SIGNAL(destroyed(QObject*)), q, SLOT(_q_widgetDestroyed(QObject*))); + QObject::connect(widget, SIGNAL(geometryChanged()), q, SLOT(_q_widgetGeometryChanged())); + } +} + +void QDeclarative1AnchorsPrivate::remDepend(QGraphicsObject *item) +{ + if (!item) + return; + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(item); + if (itemPrivate->isDeclarativeItem) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(itemPrivate); + p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } else if(itemPrivate->isWidget) { + Q_Q(QDeclarative1Anchors); + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + QObject::disconnect(widget, SIGNAL(destroyed(QObject*)), q, SLOT(_q_widgetDestroyed(QObject*))); + QObject::disconnect(widget, SIGNAL(geometryChanged()), q, SLOT(_q_widgetGeometryChanged())); + } +} + +bool QDeclarative1AnchorsPrivate::isItemComplete() const +{ + return componentComplete; +} + +void QDeclarative1Anchors::classBegin() +{ + Q_D(QDeclarative1Anchors); + d->componentComplete = false; +} + +void QDeclarative1Anchors::componentComplete() +{ + Q_D(QDeclarative1Anchors); + d->componentComplete = true; +} + +bool QDeclarative1Anchors::mirrored() +{ + Q_D(QDeclarative1Anchors); + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(d->item); + return itemPrivate->isDeclarativeItem ? static_cast<QDeclarativeItemPrivate *>(itemPrivate)->effectiveLayoutMirror : false; +} + +void QDeclarative1AnchorsPrivate::setItemHeight(qreal v) +{ + updatingMe = true; + QGraphicsItemPrivate::get(item)->setHeight(v); + updatingMe = false; +} + +void QDeclarative1AnchorsPrivate::setItemWidth(qreal v) +{ + updatingMe = true; + QGraphicsItemPrivate::get(item)->setWidth(v); + updatingMe = false; +} + +void QDeclarative1AnchorsPrivate::setItemX(qreal v) +{ + updatingMe = true; + item->setX(v); + updatingMe = false; +} + +void QDeclarative1AnchorsPrivate::setItemY(qreal v) +{ + updatingMe = true; + item->setY(v); + updatingMe = false; +} + +void QDeclarative1AnchorsPrivate::setItemPos(const QPointF &v) +{ + updatingMe = true; + item->setPos(v); + updatingMe = false; +} + +void QDeclarative1AnchorsPrivate::setItemSize(const QSizeF &v) +{ + updatingMe = true; + if(QGraphicsItemPrivate::get(item)->isWidget) + static_cast<QGraphicsWidget *>(item)->resize(v); + else if (QGraphicsItemPrivate::get(item)->isDeclarativeItem) + static_cast<QDeclarativeItem *>(item)->setSize(v); + updatingMe = false; +} + +void QDeclarative1AnchorsPrivate::updateMe() +{ + if (updatingMe) { + updatingMe = false; + return; + } + + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QDeclarative1AnchorsPrivate::updateOnComplete() +{ + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QDeclarative1AnchorsPrivate::_q_widgetDestroyed(QObject *obj) +{ + clearItem(qobject_cast<QGraphicsObject*>(obj)); +} + +void QDeclarative1AnchorsPrivate::_q_widgetGeometryChanged() +{ + fillChanged(); + centerInChanged(); + updateHorizontalAnchors(); + updateVerticalAnchors(); +} + +void QDeclarative1AnchorsPrivate::itemGeometryChanged(QDeclarativeItem *, 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(); +} + +QGraphicsObject *QDeclarative1Anchors::fill() const +{ + Q_D(const QDeclarative1Anchors); + return d->fill; +} + +void QDeclarative1Anchors::setFill(QGraphicsObject *f) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetFill() +{ + setFill(0); +} + +QGraphicsObject *QDeclarative1Anchors::centerIn() const +{ + Q_D(const QDeclarative1Anchors); + return d->centerIn; +} + +void QDeclarative1Anchors::setCenterIn(QGraphicsObject* c) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetCenterIn() +{ + setCenterIn(0); +} + +bool QDeclarative1AnchorsPrivate::calcStretch(const QDeclarative1AnchorLine &edge1, + const QDeclarative1AnchorLine &edge2, + qreal offset1, + qreal offset2, + QDeclarative1AnchorLine::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->parentObject(), line) + + position(edge1.item, edge1.anchorLine) + offset1); + } else if (edge2IsSibling && edge1IsParent) { + stretch = (position(item->parentObject(), line) + position(edge2.item, edge2.anchorLine) + offset2) + - (position(edge1.item, edge1.anchorLine) + offset1); + } else + invalid = true; + + return invalid; +} + +void QDeclarative1AnchorsPrivate::updateVerticalAnchors() +{ + if (fill || centerIn || !isItemComplete()) + return; + + if (updatingVerticalAnchor < 2) { + ++updatingVerticalAnchor; + QGraphicsItemPrivate *itemPrivate = QGraphicsItemPrivate::get(item); + if (usedAnchors & QDeclarative1Anchors::TopAnchor) { + //Handle stretching + bool invalid = true; + qreal height = 0.0; + if (usedAnchors & QDeclarative1Anchors::BottomAnchor) { + invalid = calcStretch(top, bottom, topMargin, -bottomMargin, QDeclarative1AnchorLine::Top, height); + } else if (usedAnchors & QDeclarative1Anchors::VCenterAnchor) { + invalid = calcStretch(top, vCenter, topMargin, vCenterOffset, QDeclarative1AnchorLine::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 & QDeclarative1Anchors::BottomAnchor) { + //Handle stretching (top + bottom case is handled above) + if (usedAnchors & QDeclarative1Anchors::VCenterAnchor) { + qreal height = 0.0; + bool invalid = calcStretch(vCenter, bottom, vCenterOffset, -bottomMargin, + QDeclarative1AnchorLine::Top, height); + if (!invalid) + setItemHeight(height*2); + } + + //Handle bottom + if (bottom.item == item->parentItem()) { + setItemY(adjustedPosition(bottom.item, bottom.anchorLine) - itemPrivate->height() - bottomMargin); + } else if (bottom.item->parentItem() == item->parentItem()) { + setItemY(position(bottom.item, bottom.anchorLine) - itemPrivate->height() - bottomMargin); + } + } else if (usedAnchors & QDeclarative1Anchors::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 & QDeclarative1Anchors::BaselineAnchor) { + //Handle baseline + if (baseline.item == item->parentItem()) { + if (itemPrivate->isDeclarativeItem) + setItemY(adjustedPosition(baseline.item, baseline.anchorLine) + - static_cast<QDeclarativeItem *>(item)->baselineOffset() + baselineOffset); + } else if (baseline.item->parentItem() == item->parentItem()) { + if (itemPrivate->isDeclarativeItem) + setItemY(position(baseline.item, baseline.anchorLine) + - static_cast<QDeclarativeItem *>(item)->baselineOffset() + baselineOffset); + } + } + --updatingVerticalAnchor; + } else { + // ### Make this certain :) + qmlInfo(item) << QDeclarative1Anchors::tr("Possible anchor loop detected on vertical anchor."); + } +} + +inline QDeclarative1AnchorLine::AnchorLine reverseAnchorLine(QDeclarative1AnchorLine::AnchorLine anchorLine) { + if (anchorLine == QDeclarative1AnchorLine::Left) { + return QDeclarative1AnchorLine::Right; + } else if (anchorLine == QDeclarative1AnchorLine::Right) { + return QDeclarative1AnchorLine::Left; + } else { + return anchorLine; + } +} + +void QDeclarative1AnchorsPrivate::updateHorizontalAnchors() +{ + Q_Q(QDeclarative1Anchors); + if (fill || centerIn || !isItemComplete()) + return; + + if (updatingHorizontalAnchor < 3) { + ++updatingHorizontalAnchor; + qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset; + QDeclarative1AnchorLine effectiveLeft, effectiveRight, effectiveHorizontalCenter; + QDeclarative1Anchors::Anchor effectiveLeftAnchor, effectiveRightAnchor; + if (q->mirrored()) { + effectiveLeftAnchor = QDeclarative1Anchors::RightAnchor; + effectiveRightAnchor = QDeclarative1Anchors::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 = QDeclarative1Anchors::LeftAnchor; + effectiveRightAnchor = QDeclarative1Anchors::RightAnchor; + effectiveLeft = left; + effectiveRight = right; + effectiveHorizontalCenter = hCenter; + effectiveLeftMargin = leftMargin; + effectiveRightMargin = rightMargin; + effectiveHorizontalCenterOffset = hCenterOffset; + } + + QGraphicsItemPrivate *itemPrivate = QGraphicsItemPrivate::get(item); + if (usedAnchors & effectiveLeftAnchor) { + //Handle stretching + bool invalid = true; + qreal width = 0.0; + if (usedAnchors & effectiveRightAnchor) { + invalid = calcStretch(effectiveLeft, effectiveRight, effectiveLeftMargin, -effectiveRightMargin, QDeclarative1AnchorLine::Left, width); + } else if (usedAnchors & QDeclarative1Anchors::HCenterAnchor) { + invalid = calcStretch(effectiveLeft, effectiveHorizontalCenter, effectiveLeftMargin, effectiveHorizontalCenterOffset, QDeclarative1AnchorLine::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 & QDeclarative1Anchors::HCenterAnchor) { + qreal width = 0.0; + bool invalid = calcStretch(effectiveHorizontalCenter, effectiveRight, effectiveHorizontalCenterOffset, -effectiveRightMargin, + QDeclarative1AnchorLine::Left, width); + if (!invalid) + setItemWidth(width*2); + } + + //Handle right + if (effectiveRight.item == item->parentItem()) { + setItemX(adjustedPosition(effectiveRight.item, effectiveRight.anchorLine) - itemPrivate->width() - effectiveRightMargin); + } else if (effectiveRight.item->parentItem() == item->parentItem()) { + setItemX(position(effectiveRight.item, effectiveRight.anchorLine) - itemPrivate->width() - effectiveRightMargin); + } + } else if (usedAnchors & QDeclarative1Anchors::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) << QDeclarative1Anchors::tr("Possible anchor loop detected on horizontal anchor."); + } +} + +QDeclarative1AnchorLine QDeclarative1Anchors::top() const +{ + Q_D(const QDeclarative1Anchors); + return d->top; +} + +void QDeclarative1Anchors::setTop(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetTop() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~TopAnchor; + d->remDepend(d->top.item); + d->top = QDeclarative1AnchorLine(); + emit topChanged(); + d->updateVerticalAnchors(); +} + +QDeclarative1AnchorLine QDeclarative1Anchors::bottom() const +{ + Q_D(const QDeclarative1Anchors); + return d->bottom; +} + +void QDeclarative1Anchors::setBottom(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetBottom() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~BottomAnchor; + d->remDepend(d->bottom.item); + d->bottom = QDeclarative1AnchorLine(); + emit bottomChanged(); + d->updateVerticalAnchors(); +} + +QDeclarative1AnchorLine QDeclarative1Anchors::verticalCenter() const +{ + Q_D(const QDeclarative1Anchors); + return d->vCenter; +} + +void QDeclarative1Anchors::setVerticalCenter(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetVerticalCenter() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~VCenterAnchor; + d->remDepend(d->vCenter.item); + d->vCenter = QDeclarative1AnchorLine(); + emit verticalCenterChanged(); + d->updateVerticalAnchors(); +} + +QDeclarative1AnchorLine QDeclarative1Anchors::baseline() const +{ + Q_D(const QDeclarative1Anchors); + return d->baseline; +} + +void QDeclarative1Anchors::setBaseline(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetBaseline() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~BaselineAnchor; + d->remDepend(d->baseline.item); + d->baseline = QDeclarative1AnchorLine(); + emit baselineChanged(); + d->updateVerticalAnchors(); +} + +QDeclarative1AnchorLine QDeclarative1Anchors::left() const +{ + Q_D(const QDeclarative1Anchors); + return d->left; +} + +void QDeclarative1Anchors::setLeft(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetLeft() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~LeftAnchor; + d->remDepend(d->left.item); + d->left = QDeclarative1AnchorLine(); + emit leftChanged(); + d->updateHorizontalAnchors(); +} + +QDeclarative1AnchorLine QDeclarative1Anchors::right() const +{ + Q_D(const QDeclarative1Anchors); + return d->right; +} + +void QDeclarative1Anchors::setRight(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetRight() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~RightAnchor; + d->remDepend(d->right.item); + d->right = QDeclarative1AnchorLine(); + emit rightChanged(); + d->updateHorizontalAnchors(); +} + +QDeclarative1AnchorLine QDeclarative1Anchors::horizontalCenter() const +{ + Q_D(const QDeclarative1Anchors); + return d->hCenter; +} + +void QDeclarative1Anchors::setHorizontalCenter(const QDeclarative1AnchorLine &edge) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::resetHorizontalCenter() +{ + Q_D(QDeclarative1Anchors); + d->usedAnchors &= ~HCenterAnchor; + d->remDepend(d->hCenter.item); + d->hCenter = QDeclarative1AnchorLine(); + emit horizontalCenterChanged(); + d->updateHorizontalAnchors(); +} + +qreal QDeclarative1Anchors::leftMargin() const +{ + Q_D(const QDeclarative1Anchors); + return d->leftMargin; +} + +void QDeclarative1Anchors::setLeftMargin(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->leftMargin == offset) + return; + d->leftMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateHorizontalAnchors(); + emit leftMarginChanged(); +} + +qreal QDeclarative1Anchors::rightMargin() const +{ + Q_D(const QDeclarative1Anchors); + return d->rightMargin; +} + +void QDeclarative1Anchors::setRightMargin(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->rightMargin == offset) + return; + d->rightMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateHorizontalAnchors(); + emit rightMarginChanged(); +} + +qreal QDeclarative1Anchors::margins() const +{ + Q_D(const QDeclarative1Anchors); + return d->margins; +} + +void QDeclarative1Anchors::setMargins(qreal offset) +{ + Q_D(QDeclarative1Anchors); + 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 QDeclarative1Anchors::horizontalCenterOffset() const +{ + Q_D(const QDeclarative1Anchors); + return d->hCenterOffset; +} + +void QDeclarative1Anchors::setHorizontalCenterOffset(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->hCenterOffset == offset) + return; + d->hCenterOffset = offset; + if(d->centerIn) + d->centerInChanged(); + else + d->updateHorizontalAnchors(); + emit horizontalCenterOffsetChanged(); +} + +qreal QDeclarative1Anchors::topMargin() const +{ + Q_D(const QDeclarative1Anchors); + return d->topMargin; +} + +void QDeclarative1Anchors::setTopMargin(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->topMargin == offset) + return; + d->topMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateVerticalAnchors(); + emit topMarginChanged(); +} + +qreal QDeclarative1Anchors::bottomMargin() const +{ + Q_D(const QDeclarative1Anchors); + return d->bottomMargin; +} + +void QDeclarative1Anchors::setBottomMargin(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->bottomMargin == offset) + return; + d->bottomMargin = offset; + if(d->fill) + d->fillChanged(); + else + d->updateVerticalAnchors(); + emit bottomMarginChanged(); +} + +qreal QDeclarative1Anchors::verticalCenterOffset() const +{ + Q_D(const QDeclarative1Anchors); + return d->vCenterOffset; +} + +void QDeclarative1Anchors::setVerticalCenterOffset(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->vCenterOffset == offset) + return; + d->vCenterOffset = offset; + if(d->centerIn) + d->centerInChanged(); + else + d->updateVerticalAnchors(); + emit verticalCenterOffsetChanged(); +} + +qreal QDeclarative1Anchors::baselineOffset() const +{ + Q_D(const QDeclarative1Anchors); + return d->baselineOffset; +} + +void QDeclarative1Anchors::setBaselineOffset(qreal offset) +{ + Q_D(QDeclarative1Anchors); + if (d->baselineOffset == offset) + return; + d->baselineOffset = offset; + d->updateVerticalAnchors(); + emit baselineOffsetChanged(); +} + +QDeclarative1Anchors::Anchors QDeclarative1Anchors::usedAnchors() const +{ + Q_D(const QDeclarative1Anchors); + return d->usedAnchors; +} + +bool QDeclarative1AnchorsPrivate::checkHValid() const +{ + if (usedAnchors & QDeclarative1Anchors::LeftAnchor && + usedAnchors & QDeclarative1Anchors::RightAnchor && + usedAnchors & QDeclarative1Anchors::HCenterAnchor) { + qmlInfo(item) << QDeclarative1Anchors::tr("Cannot specify left, right, and hcenter anchors."); + return false; + } + + return true; +} + +bool QDeclarative1AnchorsPrivate::checkHAnchorValid(QDeclarative1AnchorLine anchor) const +{ + if (!anchor.item) { + qmlInfo(item) << QDeclarative1Anchors::tr("Cannot anchor to a null item."); + return false; + } else if (anchor.anchorLine & QDeclarative1AnchorLine::Vertical_Mask) { + qmlInfo(item) << QDeclarative1Anchors::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) << QDeclarative1Anchors::tr("Cannot anchor to an item that isn't a parent or sibling."); + return false; + } else if (anchor.item == item) { + qmlInfo(item) << QDeclarative1Anchors::tr("Cannot anchor item to self."); + return false; + } + + return true; +} + +bool QDeclarative1AnchorsPrivate::checkVValid() const +{ + if (usedAnchors & QDeclarative1Anchors::TopAnchor && + usedAnchors & QDeclarative1Anchors::BottomAnchor && + usedAnchors & QDeclarative1Anchors::VCenterAnchor) { + qmlInfo(item) << QDeclarative1Anchors::tr("Cannot specify top, bottom, and vcenter anchors."); + return false; + } else if (usedAnchors & QDeclarative1Anchors::BaselineAnchor && + (usedAnchors & QDeclarative1Anchors::TopAnchor || + usedAnchors & QDeclarative1Anchors::BottomAnchor || + usedAnchors & QDeclarative1Anchors::VCenterAnchor)) { + qmlInfo(item) << QDeclarative1Anchors::tr("Baseline anchor cannot be used in conjunction with top, bottom, or vcenter anchors."); + return false; + } + + return true; +} + +bool QDeclarative1AnchorsPrivate::checkVAnchorValid(QDeclarative1AnchorLine anchor) const +{ + if (!anchor.item) { + qmlInfo(item) << QDeclarative1Anchors::tr("Cannot anchor to a null item."); + return false; + } else if (anchor.anchorLine & QDeclarative1AnchorLine::Horizontal_Mask) { + qmlInfo(item) << QDeclarative1Anchors::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) << QDeclarative1Anchors::tr("Cannot anchor to an item that isn't a parent or sibling."); + return false; + } else if (anchor.item == item){ + qmlInfo(item) << QDeclarative1Anchors::tr("Cannot anchor item to self."); + return false; + } + + return true; +} + + + +QT_END_NAMESPACE + +#include <moc_qdeclarativeanchors_p.cpp> + diff --git a/src/qtquick1/graphicsitems/qdeclarativeanchors_p.h b/src/qtquick1/graphicsitems/qdeclarativeanchors_p.h new file mode 100644 index 0000000000..96a5f27f1e --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeanchors_p.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANCHORS_H +#define QDECLARATIVEANCHORS_H + +#include "qdeclarativeitem.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/QObject> + +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1AnchorsPrivate; +class QDeclarative1AnchorLine; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Anchors : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarative1AnchorLine left READ left WRITE setLeft RESET resetLeft NOTIFY leftChanged) + Q_PROPERTY(QDeclarative1AnchorLine right READ right WRITE setRight RESET resetRight NOTIFY rightChanged) + Q_PROPERTY(QDeclarative1AnchorLine horizontalCenter READ horizontalCenter WRITE setHorizontalCenter RESET resetHorizontalCenter NOTIFY horizontalCenterChanged) + Q_PROPERTY(QDeclarative1AnchorLine top READ top WRITE setTop RESET resetTop NOTIFY topChanged) + Q_PROPERTY(QDeclarative1AnchorLine bottom READ bottom WRITE setBottom RESET resetBottom NOTIFY bottomChanged) + Q_PROPERTY(QDeclarative1AnchorLine verticalCenter READ verticalCenter WRITE setVerticalCenter RESET resetVerticalCenter NOTIFY verticalCenterChanged) + Q_PROPERTY(QDeclarative1AnchorLine 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(QGraphicsObject *fill READ fill WRITE setFill RESET resetFill NOTIFY fillChanged) + Q_PROPERTY(QGraphicsObject *centerIn READ centerIn WRITE setCenterIn RESET resetCenterIn NOTIFY centerInChanged) + Q_PROPERTY(bool mirrored READ mirrored NOTIFY mirroredChanged REVISION 1) + +public: + QDeclarative1Anchors(QObject *parent=0); + QDeclarative1Anchors(QGraphicsObject *item, QObject *parent=0); + virtual ~QDeclarative1Anchors(); + + 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) + + QDeclarative1AnchorLine left() const; + void setLeft(const QDeclarative1AnchorLine &edge); + void resetLeft(); + + QDeclarative1AnchorLine right() const; + void setRight(const QDeclarative1AnchorLine &edge); + void resetRight(); + + QDeclarative1AnchorLine horizontalCenter() const; + void setHorizontalCenter(const QDeclarative1AnchorLine &edge); + void resetHorizontalCenter(); + + QDeclarative1AnchorLine top() const; + void setTop(const QDeclarative1AnchorLine &edge); + void resetTop(); + + QDeclarative1AnchorLine bottom() const; + void setBottom(const QDeclarative1AnchorLine &edge); + void resetBottom(); + + QDeclarative1AnchorLine verticalCenter() const; + void setVerticalCenter(const QDeclarative1AnchorLine &edge); + void resetVerticalCenter(); + + QDeclarative1AnchorLine baseline() const; + void setBaseline(const QDeclarative1AnchorLine &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); + + QGraphicsObject *fill() const; + void setFill(QGraphicsObject *); + void resetFill(); + + QGraphicsObject *centerIn() const; + void setCenterIn(QGraphicsObject *); + void resetCenterIn(); + + Anchors usedAnchors() const; + + void classBegin(); + void componentComplete(); + + bool mirrored(); + +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(); + Q_REVISION(1) void mirroredChanged(); + +private: + friend class QDeclarativeItem; + friend class QDeclarativeItemPrivate; + friend class QDeclarative1GraphicsWidget; + Q_DISABLE_COPY(QDeclarative1Anchors) + Q_DECLARE_PRIVATE(QDeclarative1Anchors) + Q_PRIVATE_SLOT(d_func(), void _q_widgetGeometryChanged()) + Q_PRIVATE_SLOT(d_func(), void _q_widgetDestroyed(QObject *obj)) +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarative1Anchors::Anchors) + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Anchors) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeanchors_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeanchors_p_p.h new file mode 100644 index 0000000000..225f112636 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeanchors_p_p.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANCHORS_P_H +#define QDECLARATIVEANCHORS_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 "QtQuick1/private/qdeclarativeanchors_p.h" +#include "QtQuick1/private/qdeclarativeitemchangelistener_p.h" +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +// NOTE: if you change this then also update the copy in qdeclarativev4compiler_p_p.h +class QDeclarative1AnchorLine +{ +public: + QDeclarative1AnchorLine() : 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 + }; + + QGraphicsObject *item; + AnchorLine anchorLine; +}; + +inline bool operator==(const QDeclarative1AnchorLine& a, const QDeclarative1AnchorLine& b) +{ + return a.item == b.item && a.anchorLine == b.anchorLine; +} + +class QDeclarative1AnchorsPrivate : public QObjectPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarative1Anchors) +public: + QDeclarative1AnchorsPrivate(QGraphicsObject *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(QGraphicsObject *); + + void addDepend(QGraphicsObject *); + void remDepend(QGraphicsObject *); + 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(); + + // QDeclarativeItemGeometryListener interface + void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); + void _q_widgetDestroyed(QObject *); + void _q_widgetGeometryChanged(); + QDeclarative1AnchorsPrivate *anchorPrivate() { return this; } + + bool checkHValid() const; + bool checkVValid() const; + bool checkHAnchorValid(QDeclarative1AnchorLine anchor) const; + bool checkVAnchorValid(QDeclarative1AnchorLine anchor) const; + bool calcStretch(const QDeclarative1AnchorLine &edge1, const QDeclarative1AnchorLine &edge2, qreal offset1, qreal offset2, QDeclarative1AnchorLine::AnchorLine line, qreal &stretch); + + bool isMirrored() const; + void updateHorizontalAnchors(); + void updateVerticalAnchors(); + void fillChanged(); + void centerInChanged(); + + QGraphicsObject *item; + QDeclarative1Anchors::Anchors usedAnchors; + + QGraphicsObject *fill; + QGraphicsObject *centerIn; + + QDeclarative1AnchorLine left; + QDeclarative1AnchorLine right; + QDeclarative1AnchorLine top; + QDeclarative1AnchorLine bottom; + QDeclarative1AnchorLine vCenter; + QDeclarative1AnchorLine hCenter; + QDeclarative1AnchorLine baseline; + + qreal leftMargin; + qreal rightMargin; + qreal topMargin; + qreal bottomMargin; + qreal margins; + qreal vCenterOffset; + qreal hCenterOffset; + qreal baselineOffset; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarative1AnchorLine) + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeanimatedimage.cpp b/src/qtquick1/graphicsitems/qdeclarativeanimatedimage.cpp new file mode 100644 index 0000000000..5d7ad80bbb --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeanimatedimage.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeanimatedimage_p.h" +#include "QtQuick1/private/qdeclarativeanimatedimage_p_p.h" + +#ifndef QT_NO_MOVIE + +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +#include <QMovie> +#include <QNetworkRequest> +#include <QNetworkReply> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass AnimatedImage QDeclarative1AnimatedImage + \inherits Image + \since 4.7 + \ingroup basic-visual-elements + + The AnimatedImage element extends the features of the \l Image element, providing + a way to play animations stored as images containing a series of frames, + such as those stored in GIF files. + + Information about the current frame and totla length of the animation can be + obtained using the \l currentFrame and \l frameCount properties. You can + start, pause and stop the animation by changing the values of the \l playing + and \l paused properties. + + The full list of supported formats can be determined with QMovie::supportedFormats(). + + \section1 Example Usage + + \beginfloatleft + \image animatedimageitem.gif + \endfloat + + The following QML shows how to display an animated image and obtain information + about its state, such as the current frame and total number of frames. + The result is an animated image with a simple progress indicator underneath it. + + \clearfloat + \snippet doc/src/snippets/declarative/animatedimage.qml document + + \sa BorderImage, Image +*/ + +/*! + \qmlproperty url AnimatedImage::source + + This property holds the URL that refers to the source image. + + AnimatedImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool AnimatedImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! + \qmlproperty bool AnimatedImage::cache + \since Quick 1.1 + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool AnimatedImage::mirror + \since Quick 1.1 + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +QDeclarative1AnimatedImage::QDeclarative1AnimatedImage(QDeclarativeItem *parent) + : QDeclarative1Image(*(new QDeclarative1AnimatedImagePrivate), parent) +{ +} + +QDeclarative1AnimatedImage::~QDeclarative1AnimatedImage() +{ + Q_D(QDeclarative1AnimatedImage); + delete d->_movie; +} + +/*! + \qmlproperty bool AnimatedImage::paused + This property holds whether the animated image is paused. + + By default, this property is false. Set it to true when you want to pause + the animation. +*/ +bool QDeclarative1AnimatedImage::isPaused() const +{ + Q_D(const QDeclarative1AnimatedImage); + if(!d->_movie) + return false; + return d->_movie->state()==QMovie::Paused; +} + +void QDeclarative1AnimatedImage::setPaused(bool pause) +{ + Q_D(QDeclarative1AnimatedImage); + if(pause == d->paused) + return; + d->paused = pause; + if(!d->_movie) + return; + d->_movie->setPaused(pause); +} +/*! + \qmlproperty bool AnimatedImage::playing + This property holds whether the animated image is playing. + + By default, this property is true, meaning that the animation + will start playing immediately. +*/ +bool QDeclarative1AnimatedImage::isPlaying() const +{ + Q_D(const QDeclarative1AnimatedImage); + if (!d->_movie) + return false; + return d->_movie->state()!=QMovie::NotRunning; +} + +void QDeclarative1AnimatedImage::setPlaying(bool play) +{ + Q_D(QDeclarative1AnimatedImage); + if(play == d->playing) + return; + d->playing = play; + if (!d->_movie) + return; + if (play) + d->_movie->start(); + else + d->_movie->stop(); +} + +/*! + \qmlproperty int AnimatedImage::currentFrame + \qmlproperty int AnimatedImage::frameCount + + currentFrame is the frame that is currently visible. By monitoring this property + for changes, you can animate other items at the same time as the image. + + frameCount is the number of frames in the animation. For some animation formats, + frameCount is unknown and has a value of zero. +*/ +int QDeclarative1AnimatedImage::currentFrame() const +{ + Q_D(const QDeclarative1AnimatedImage); + if (!d->_movie) + return d->preset_currentframe; + return d->_movie->currentFrameNumber(); +} + +void QDeclarative1AnimatedImage::setCurrentFrame(int frame) +{ + Q_D(QDeclarative1AnimatedImage); + if (!d->_movie) { + d->preset_currentframe = frame; + return; + } + d->_movie->jumpToFrame(frame); +} + +int QDeclarative1AnimatedImage::frameCount() const +{ + Q_D(const QDeclarative1AnimatedImage); + if (!d->_movie) + return 0; + return d->_movie->frameCount(); +} + +void QDeclarative1AnimatedImage::setSource(const QUrl &url) +{ + Q_D(QDeclarative1AnimatedImage); + 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 QDeclarative1AnimatedImage::load() +{ + Q_D(QDeclarative1AnimatedImage); + + QDeclarative1ImageBase::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 QDeclarative1AnimatedImage::movieRequestFinished() +{ + Q_D(QDeclarative1AnimatedImage); + + 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 QDeclarative1AnimatedImage::movieUpdate() +{ + Q_D(QDeclarative1AnimatedImage); + d->setPixmap(d->_movie->currentPixmap()); + emit frameChanged(); +} + +void QDeclarative1AnimatedImage::playingStatusChanged() +{ + Q_D(QDeclarative1AnimatedImage); + 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 QDeclarative1AnimatedImage::componentComplete() +{ + Q_D(QDeclarative1AnimatedImage); + QDeclarativeItem::componentComplete(); // NOT QDeclarative1Image + 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/qtquick1/graphicsitems/qdeclarativeanimatedimage_p.h b/src/qtquick1/graphicsitems/qdeclarativeanimatedimage_p.h new file mode 100644 index 0000000000..e4f3939c01 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeanimatedimage_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATEDIMAGE_H +#define QDECLARATIVEANIMATEDIMAGE_H + +#include "private/qdeclarativeimage_p.h" + +#ifndef QT_NO_MOVIE + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QMovie; + +QT_MODULE(Declarative) + +class QDeclarative1AnimatedImagePrivate; + +class Q_AUTOTEST_EXPORT QDeclarative1AnimatedImage : public QDeclarative1Image +{ + 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: + QDeclarative1AnimatedImage(QDeclarativeItem *parent=0); + ~QDeclarative1AnimatedImage(); + + 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 QDeclarative1Image'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(QDeclarative1AnimatedImage) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1AnimatedImage) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1AnimatedImage) + +QT_END_HEADER + +#endif // QT_NO_MOVIE + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeanimatedimage_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeanimatedimage_p_p.h new file mode 100644 index 0000000000..bf6c2f9460 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeanimatedimage_p_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATEDIMAGE_P_H +#define QDECLARATIVEANIMATEDIMAGE_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/qdeclarativeimage_p_p.h" + +#ifndef QT_NO_MOVIE + +QT_BEGIN_NAMESPACE + +class QMovie; +class QNetworkReply; + +class QDeclarative1AnimatedImagePrivate : public QDeclarative1ImagePrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1AnimatedImage) + +public: + QDeclarative1AnimatedImagePrivate() + : 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 // QDECLARATIVEANIMATEDIMAGE_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeborderimage.cpp b/src/qtquick1/graphicsitems/qdeclarativeborderimage.cpp new file mode 100644 index 0000000000..4588d14753 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeborderimage.cpp @@ -0,0 +1,621 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeborderimage_p.h" +#include "QtQuick1/private/qdeclarativeborderimage_p_p.h" + +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QFile> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass BorderImage QDeclarative1BorderImage + \brief The BorderImage element provides an image that can be used as a border. + \inherits Item + \since 4.7 + \ingroup qml-basic-visual-elements + + The BorderImage element is used to create borders out of images by scaling or tiling + parts of each image. + + A BorderImage element breaks a source image, specified using the \l url property, + into 9 regions, as shown below: + + \image declarative-scalegrid.png + + When the image is scaled, regions of the source image are scaled or tiled to + create the displayed border image in the following way: + + \list + \i The corners (regions 1, 3, 7, and 9) are not scaled at all. + \i Regions 2 and 8 are scaled according to + \l{BorderImage::horizontalTileMode}{horizontalTileMode}. + \i Regions 4 and 6 are scaled according to + \l{BorderImage::verticalTileMode}{verticalTileMode}. + \i The middle (region 5) is scaled according to both + \l{BorderImage::horizontalTileMode}{horizontalTileMode} and + \l{BorderImage::verticalTileMode}{verticalTileMode}. + \endlist + + The regions of the image are defined using the \l border property group, which + describes the distance from each edge of the source image to use as a border. + + \section1 Example Usage + + The following examples show the effects of the different modes on an image. + Guide lines are overlaid onto the image to show the different regions of the + image as described above. + + \beginfloatleft + \image qml-borderimage-normal-image.png + \endfloat + + An unscaled image is displayed using an Image element. The \l border property is + used to determine the parts of the image that will lie inside the unscaled corner + areas and the parts that will be stretched horizontally and vertically. + + \snippet doc/src/snippets/declarative/borderimage/normal-image.qml normal image + + \clearfloat + \beginfloatleft + \image qml-borderimage-scaled.png + \endfloat + + A BorderImage element is used to display the image, and it is given a size that is + larger than the original image. Since the \l horizontalTileMode property is set to + \l{BorderImage::horizontalTileMode}{BorderImage.Stretch}, the parts of image in + regions 2 and 8 are stretched horizontally. Since the \l verticalTileMode property + is set to \l{BorderImage::verticalTileMode}{BorderImage.Stretch}, the parts of image + in regions 4 and 6 are stretched vertically. + + \snippet doc/src/snippets/declarative/borderimage/borderimage-scaled.qml scaled border image + + \clearfloat + \beginfloatleft + \image qml-borderimage-tiled.png + \endfloat + + Again, a large BorderImage element is used to display the image. With the + \l horizontalTileMode property set to \l{BorderImage::horizontalTileMode}{BorderImage.Repeat}, + the parts of image in regions 2 and 8 are tiled so that they fill the space at the + top and bottom of the element. Similarly, the \l verticalTileMode property is set to + \l{BorderImage::verticalTileMode}{BorderImage.Repeat}, the parts of image in regions + 4 and 6 are tiled so that they fill the space at the left and right of the element. + + \snippet doc/src/snippets/declarative/borderimage/borderimage-tiled.qml tiled border image + + \clearfloat + In some situations, the width of regions 2 and 8 may not be an exact multiple of the width + of the corresponding regions in the source image. Similarly, the height of regions 4 and 6 + may not be an exact multiple of the height of the corresponding regions. It can be useful + to use \l{BorderImage::horizontalTileMode}{BorderImage.Round} instead of + \l{BorderImage::horizontalTileMode}{BorderImage.Repeat} in cases like these. + + The \l{declarative/imageelements/borderimage}{BorderImage example} shows how a BorderImage + can be used to simulate a shadow effect on a rectangular item. + + \section1 Quality and Performance + + By default, any scaled regions of the image are rendered without smoothing to improve + rendering speed. Setting the \l smooth property improves rendering quality of scaled + regions, but may slow down rendering. + + The source image may not be loaded instantaneously, depending on its original location. + Loading progress can be monitored with the \l progress property. + + \sa Image, AnimatedImage + */ + +/*! + \qmlproperty bool BorderImage::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ +QDeclarative1BorderImage::QDeclarative1BorderImage(QDeclarativeItem *parent) + : QDeclarative1ImageBase(*(new QDeclarative1BorderImagePrivate), parent) +{ +} + +QDeclarative1BorderImage::~QDeclarative1BorderImage() +{ + Q_D(QDeclarative1BorderImage); + if (d->sciReply) + d->sciReply->deleteLater(); +} +/*! + \qmlproperty enumeration BorderImage::status + + This property describes the status of image loading. It can be one of: + + \list + \o BorderImage.Null - no image has been set + \o BorderImage.Ready - the image has been loaded + \o BorderImage.Loading - the image is currently being loaded + \o BorderImage.Error - an error occurred while loading the image + \endlist + + \sa progress +*/ + +/*! + \qmlproperty real BorderImage::progress + + This property holds the progress of image loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ + +/*! + \qmlproperty bool BorderImage::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the image is displayed at its natural size, this property has no visual or + performance effect. + + By default, this property is set to false. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and enable it at the conclusion. +*/ + +/*! + \qmlproperty bool BorderImage::cache + \since Quick 1.1 + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool BorderImage::mirror + \since Quick 1.1 + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + +/*! + \qmlproperty url BorderImage::source + + This property holds the URL that refers to the source image. + + BorderImage can handle any image format supported by Qt, loaded from any + URL scheme supported by Qt. + + This property can also be used to refer to .sci files, which are + written in a QML-specific, text-based format that specifies the + borders, the image file and the tile rules for a given border image. + + The following .sci file sets the borders to 10 on each side for the + image \c picture.png: + + \code + border.left: 10 + border.top: 10 + border.bottom: 10 + border.right: 10 + source: "picture.png" + \endcode + + The URL may be absolute, or relative to the URL of the component. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty QSize BorderImage::sourceSize + + This property holds the actual width and height of the loaded image. + + In BorderImage, this property is read-only. + + \sa Image::sourceSize +*/ +void QDeclarative1BorderImage::setSource(const QUrl &url) +{ + Q_D(QDeclarative1BorderImage); + //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 QDeclarative1BorderImage::load() +{ + Q_D(QDeclarative1BorderImage); + 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(QDeclarative1GridScaledImage(&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 = + QDeclarative1BorderImage::staticMetaObject.indexOfSlot("sciRequestFinished()"); + } + + QMetaObject::connect(d->sciReply, sciReplyFinished, this, + thisSciRequestFinished, Qt::DirectConnection); + } + } else { + + QDeclarative1Pixmap::Options options; + if (d->async) + options |= QDeclarative1Pixmap::Asynchronous; + if (d->cache) + options |= QDeclarative1Pixmap::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); + requestFinished(); + update(); + } + } + } + + emit statusChanged(d->status); +} + +/*! + \qmlproperty int BorderImage::border.left + \qmlproperty int BorderImage::border.right + \qmlproperty int BorderImage::border.top + \qmlproperty int BorderImage::border.bottom + + The 4 border lines (2 horizontal and 2 vertical) break the image into 9 sections, + as shown below: + + \image declarative-scalegrid.png + + Each border line (left, right, top, and bottom) specifies an offset in pixels + from the respective edge of the source image. By default, each border line has + a value of 0. + + For example, the following definition sets the bottom line 10 pixels up from + the bottom of the image: + + \qml + BorderImage { + border.bottom: 10 + // ... + } + \endqml + + The border lines can also be specified using a + \l {BorderImage::source}{.sci file}. +*/ + +QDeclarative1ScaleGrid *QDeclarative1BorderImage::border() +{ + Q_D(QDeclarative1BorderImage); + return d->getScaleGrid(); +} + +/*! + \qmlproperty enumeration BorderImage::horizontalTileMode + \qmlproperty enumeration BorderImage::verticalTileMode + + This property describes how to repeat or stretch the middle parts of the border image. + + \list + \o BorderImage.Stretch - Scales the image to fit to the available area. + \o BorderImage.Repeat - Tile the image until there is no more space. May crop the last image. + \o BorderImage.Round - Like Repeat, but scales the images down to ensure that the last image is not cropped. + \endlist + + The default tile mode for each property is BorderImage.Stretch. +*/ +QDeclarative1BorderImage::TileMode QDeclarative1BorderImage::horizontalTileMode() const +{ + Q_D(const QDeclarative1BorderImage); + return d->horizontalTileMode; +} + +void QDeclarative1BorderImage::setHorizontalTileMode(TileMode t) +{ + Q_D(QDeclarative1BorderImage); + if (t != d->horizontalTileMode) { + d->horizontalTileMode = t; + emit horizontalTileModeChanged(); + update(); + } +} + +QDeclarative1BorderImage::TileMode QDeclarative1BorderImage::verticalTileMode() const +{ + Q_D(const QDeclarative1BorderImage); + return d->verticalTileMode; +} + +void QDeclarative1BorderImage::setVerticalTileMode(TileMode t) +{ + Q_D(QDeclarative1BorderImage); + if (t != d->verticalTileMode) { + d->verticalTileMode = t; + emit verticalTileModeChanged(); + update(); + } +} + +void QDeclarative1BorderImage::setGridScaledImage(const QDeclarative1GridScaledImage& sci) +{ + Q_D(QDeclarative1BorderImage); + if (!sci.isValid()) { + d->status = Error; + emit statusChanged(d->status); + } else { + QDeclarative1ScaleGrid *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())); + + QDeclarative1Pixmap::Options options; + if (d->async) + options |= QDeclarative1Pixmap::Asynchronous; + if (d->cache) + options |= QDeclarative1Pixmap::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 = + QDeclarative1BorderImage::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QDeclarative1BorderImage::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 QDeclarative1BorderImage::requestFinished() +{ + Q_D(QDeclarative1BorderImage); + + 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 QDeclarative1BorderImage::sciRequestFinished() +{ + Q_D(QDeclarative1BorderImage); + + 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 { + QDeclarative1GridScaledImage sci(d->sciReply); + d->sciReply->deleteLater(); + d->sciReply = 0; + setGridScaledImage(sci); + } +} + +void QDeclarative1BorderImage::doUpdate() +{ + update(); +} + +void QDeclarative1BorderImage::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarative1BorderImage); + if (d->pix.isNull() || d->width() <= 0.0 || d->height() <= 0.0) + return; + + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); + QTransform oldTransform; + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + if (d->mirror) { + oldTransform = p->transform(); + QTransform mirror; + mirror.translate(d->width(), 0).scale(-1, 1.0); + p->setWorldTransform(mirror * oldTransform); + } + + const QDeclarative1ScaleGrid *border = d->getScaleGrid(); + int left = border->left(); + int right = border->right(); + qreal borderWidth = left + right; + if (borderWidth > 0.0 && d->width() < borderWidth) { + qreal diff = borderWidth - d->width() - 1; + left -= qRound(diff * qreal(left) / borderWidth); + right -= qRound(diff * qreal(right) / borderWidth); + } + int top = border->top(); + int bottom = border->bottom(); + qreal borderHeight = top + bottom; + if (borderHeight > 0.0 && d->height() < borderHeight) { + qreal diff = borderHeight - d->height() - 1; + top -= qRound(diff * qreal(top) / borderHeight); + bottom -= qRound(diff * qreal(bottom) / borderHeight); + } + QMargins margins(left, top, right, bottom); + QTileRules rules((Qt::TileRule)d->horizontalTileMode, (Qt::TileRule)d->verticalTileMode); + qDrawBorderPixmap(p, QRect(0, 0, (int)d->width(), (int)d->height()), margins, d->pix, d->pix.rect(), margins, rules); + if (d->smooth) { + p->setRenderHint(QPainter::Antialiasing, oldAA); + p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + if (d->mirror) + p->setWorldTransform(oldTransform); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeborderimage_p.h b/src/qtquick1/graphicsitems/qdeclarativeborderimage_p.h new file mode 100644 index 0000000000..f2349b26c6 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeborderimage_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBORDERIMAGE_H +#define QDECLARATIVEBORDERIMAGE_H + +#include "private/qdeclarativeimagebase_p.h" + +#include <QtNetwork/qnetworkreply.h> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1ScaleGrid; +class QDeclarative1GridScaledImage; +class QDeclarative1BorderImagePrivate; +class Q_AUTOTEST_EXPORT QDeclarative1BorderImage : public QDeclarative1ImageBase +{ + Q_OBJECT + Q_ENUMS(TileMode) + + Q_PROPERTY(QDeclarative1ScaleGrid *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: + QDeclarative1BorderImage(QDeclarativeItem *parent=0); + ~QDeclarative1BorderImage(); + + QDeclarative1ScaleGrid *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 paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + void setSource(const QUrl &url); + +Q_SIGNALS: + void horizontalTileModeChanged(); + void verticalTileModeChanged(); + void sourceSizeChanged(); + +protected: + virtual void load(); + +private: + void setGridScaledImage(const QDeclarative1GridScaledImage& sci); + +private Q_SLOTS: + void doUpdate(); + void requestFinished(); + void sciRequestFinished(); + +private: + Q_DISABLE_COPY(QDeclarative1BorderImage) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1BorderImage) +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QDeclarative1BorderImage) +QT_END_HEADER + +#endif // QDECLARATIVEBORDERIMAGE_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeborderimage_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeborderimage_p_p.h new file mode 100644 index 0000000000..0d1c30ee2a --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeborderimage_p_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBORDERIMAGE_P_H +#define QDECLARATIVEBORDERIMAGE_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/qdeclarativeimagebase_p_p.h" +#include "private/qdeclarativescalegrid_p_p.h" + +QT_BEGIN_NAMESPACE + +class QNetworkReply; + +class QDeclarative1BorderImagePrivate : public QDeclarative1ImageBasePrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1BorderImage) + +public: + QDeclarative1BorderImagePrivate() + : border(0), sciReply(0), + horizontalTileMode(QDeclarative1BorderImage::Stretch), + verticalTileMode(QDeclarative1BorderImage::Stretch), + redirectCount(0) + { + } + + ~QDeclarative1BorderImagePrivate() + { + } + + + QDeclarative1ScaleGrid *getScaleGrid() + { + Q_Q(QDeclarative1BorderImage); + if (!border) { + border = new QDeclarative1ScaleGrid(q); + static int borderChangedSignalIdx = -1; + static int doUpdateSlotIdx = -1; + if (borderChangedSignalIdx < 0) + borderChangedSignalIdx = QDeclarative1ScaleGrid::staticMetaObject.indexOfSignal("borderChanged()"); + if (doUpdateSlotIdx < 0) + doUpdateSlotIdx = QDeclarative1BorderImage::staticMetaObject.indexOfSlot("doUpdate()"); + QMetaObject::connect(border, borderChangedSignalIdx, q, doUpdateSlotIdx); + } + return border; + } + + QDeclarative1ScaleGrid *border; + QUrl sciurl; + QNetworkReply *sciReply; + QDeclarative1BorderImage::TileMode horizontalTileMode; + QDeclarative1BorderImage::TileMode verticalTileMode; + int redirectCount; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEBORDERIMAGE_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeevents.cpp b/src/qtquick1/graphicsitems/qdeclarativeevents.cpp new file mode 100644 index 0000000000..227ad6d662 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeevents.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeevents_p_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + \qmlclass KeyEvent QDeclarative1KeyEvent + \since 4.7 + \ingroup qml-event-elements + + \brief The KeyEvent object provides information about a key event. + + For example, the following changes the Item's state property when the Enter + key is pressed: + \qml +Item { + focus: true + Keys.onPressed: { if (event.key == Qt.Key_Enter) state = 'ShowDetails'; } +} + \endqml +*/ + +/*! + \qmlproperty int KeyEvent::key + + This property holds the code of the key that was pressed or released. + + See \l {Qt::Key}{Qt.Key} for the list of keyboard codes. These codes are + independent of the underlying window system. Note that this + function does not distinguish between capital and non-capital + letters, use the text() function (returning the Unicode text the + key generated) for this purpose. + + A value of either 0 or \l {Qt::Key_unknown}{Qt.Key_Unknown} means that the event is not + the result of a known key; for example, it may be the result of + a compose sequence, a keyboard macro, or due to key event + compression. +*/ + +/*! + \qmlproperty string KeyEvent::text + + This property holds the Unicode text that the key generated. + The text returned can be an empty string in cases where modifier keys, + such as Shift, Control, Alt, and Meta, are being pressed or released. + In such cases \c key will contain a valid value +*/ + +/*! + \qmlproperty bool KeyEvent::isAutoRepeat + + This property holds whether this event comes from an auto-repeating key. +*/ + +/*! + \qmlproperty int KeyEvent::count + + This property holds the number of keys involved in this event. If \l KeyEvent::text + is not empty, this is simply the length of the string. +*/ + +/*! + \qmlproperty bool KeyEvent::accepted + + Setting \a accepted to true prevents the key event from being + propagated to the item's parent. + + Generally, if the item acts on the key event then it should be accepted + so that ancestor items do not also respond to the same event. +*/ + +/*! + \qmlproperty int KeyEvent::modifiers + + This property holds the keyboard modifier flags that existed immediately + before the event occurred. + + It contains a bitwise combination of: + \list + \o Qt.NoModifier - No modifier key is pressed. + \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. + \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. + \o Qt.AltModifier - An Alt key on the keyboard is pressed. + \o Qt.MetaModifier - A Meta key on the keyboard is pressed. + \o Qt.KeypadModifier - A keypad button is pressed. + \endlist + + For example, to react to a Shift key + Enter key combination: + \qml + Item { + focus: true + Keys.onPressed: { + if ((event.key == Qt.Key_Enter) && (event.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + \endqml +*/ + + +/*! + \qmlclass MouseEvent QDeclarative1MouseEvent + \since 4.7 + \ingroup qml-event-elements + + \brief The MouseEvent object provides information about a mouse event. + + The position of the mouse can be found via the \l x and \l y properties. + The button that caused the event is available via the \l button property. + + \sa MouseArea +*/ + +/*! + \internal + \class QDeclarative1MouseEvent +*/ + +/*! + \qmlproperty int MouseEvent::x + \qmlproperty int MouseEvent::y + + These properties hold the coordinates of the position supplied by the mouse event. +*/ + + +/*! + \qmlproperty bool MouseEvent::accepted + + Setting \a accepted to true prevents the mouse event from being + propagated to items below this item. + + Generally, if the item acts on the mouse event then it should be accepted + so that items lower in the stacking order do not also respond to the same event. +*/ + +/*! + \qmlproperty enumeration MouseEvent::button + + This property holds the button that caused the event. It can be one of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist +*/ + +/*! + \qmlproperty bool MouseEvent::wasHeld + + This property is true if the mouse button has been held pressed longer the + threshold (800ms). +*/ + +/*! + \qmlproperty int MouseEvent::buttons + + This property holds the mouse buttons pressed when the event was generated. + For mouse move events, this is all buttons that are pressed down. For mouse + press and double click events this includes the button that caused the event. + For mouse release events this excludes the button that caused the event. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist +*/ + +/*! + \qmlproperty int MouseEvent::modifiers + + This property holds the keyboard modifier flags that existed immediately + before the event occurred. + + It contains a bitwise combination of: + \list + \o Qt.NoModifier - No modifier key is pressed. + \o Qt.ShiftModifier - A Shift key on the keyboard is pressed. + \o Qt.ControlModifier - A Ctrl key on the keyboard is pressed. + \o Qt.AltModifier - An Alt key on the keyboard is pressed. + \o Qt.MetaModifier - A Meta key on the keyboard is pressed. + \o Qt.KeypadModifier - A keypad button is pressed. + \endlist + + For example, to react to a Shift key + Left mouse button click: + \qml + MouseArea { + onClicked: { + if ((mouse.button == Qt.LeftButton) && (mouse.modifiers & Qt.ShiftModifier)) + doSomething(); + } + } + \endqml +*/ + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeevents_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeevents_p_p.h new file mode 100644 index 0000000000..bda4814a52 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeevents_p_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEEVENTS_P_H +#define QDECLARATIVEEVENTS_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 <QtDeclarative/qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +class QDeclarative1KeyEvent : 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: + QDeclarative1KeyEvent(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); } + QDeclarative1KeyEvent(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 QDeclarative1MouseEvent : 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: + QDeclarative1MouseEvent(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(QDeclarative1KeyEvent) +QML_DECLARE_TYPE(QDeclarative1MouseEvent) + +#endif // QDECLARATIVEEVENTS_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeflickable.cpp b/src/qtquick1/graphicsitems/qdeclarativeflickable.cpp new file mode 100644 index 0000000000..37a784f480 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeflickable.cpp @@ -0,0 +1,1803 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeflickable_p.h" +#include "QtQuick1/private/qdeclarativeflickable_p_p.h" +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QGraphicsSceneMouseEvent> +#include <QPointer> +#include <QTimer> +#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 1750 +#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; + +QDeclarative1FlickableVisibleArea::QDeclarative1FlickableVisibleArea(QDeclarative1Flickable *parent) + : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.) + , m_yPosition(0.), m_heightRatio(0.) +{ +} + +qreal QDeclarative1FlickableVisibleArea::widthRatio() const +{ + return m_widthRatio; +} + +qreal QDeclarative1FlickableVisibleArea::xPosition() const +{ + return m_xPosition; +} + +qreal QDeclarative1FlickableVisibleArea::heightRatio() const +{ + return m_heightRatio; +} + +qreal QDeclarative1FlickableVisibleArea::yPosition() const +{ + return m_yPosition; +} + +void QDeclarative1FlickableVisibleArea::updateVisible() +{ + QDeclarative1FlickablePrivate *p = static_cast<QDeclarative1FlickablePrivate *>(QGraphicsItemPrivate::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); +} + + +QDeclarative1FlickablePrivate::QDeclarative1FlickablePrivate() + : contentItem(new QDeclarativeItem) + , hData(this, &QDeclarative1FlickablePrivate::setRoundedViewportX) + , vData(this, &QDeclarative1FlickablePrivate::setRoundedViewportY) + , 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(QDeclarative1Flickable::AutoFlickDirection) + , boundsBehavior(QDeclarative1Flickable::DragAndOvershootBounds) +{ +} + +void QDeclarative1FlickablePrivate::init() +{ + Q_Q(QDeclarative1Flickable); + 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 = QDeclarative1TimeLine::staticMetaObject.indexOfSignal("updated()"); + timelineCompletedIdx = QDeclarative1TimeLine::staticMetaObject.indexOfSignal("completed()"); + flickableTickedIdx = QDeclarative1Flickable::staticMetaObject.indexOfSlot("ticked()"); + flickableMovementEndingIdx = QDeclarative1Flickable::staticMetaObject.indexOfSlot("movementEnding()"); + } + QMetaObject::connect(&timeline, timelineUpdatedIdx, + q, flickableTickedIdx, Qt::DirectConnection); + QMetaObject::connect(&timeline, timelineCompletedIdx, + q, flickableMovementEndingIdx, Qt::DirectConnection); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildEvents(true); + QDeclarativeItemPrivate *viewportPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(contentItem)); + viewportPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + lastPosTime.invalidate(); +} + +/* + Returns the amount to overshoot by given a view size. + Will be up to the lesser of 1/3 of the view size or QML_FLICK_OVERSHOOT +*/ +qreal QDeclarative1FlickablePrivate::overShootDistance(qreal size) +{ + if (maxVelocity <= 0) + return 0.0; + + return qMin(qreal(QML_FLICK_OVERSHOOT), size/3); +} + +void QDeclarative1FlickablePrivate::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 QDeclarative1FlickablePrivate::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 QDeclarative1FlickablePrivate::itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeom, const QRectF &oldGeom) +{ + Q_Q(QDeclarative1Flickable); + if (item == contentItem) { + if (newGeom.x() != oldGeom.x()) + emit q->contentXChanged(); + if (newGeom.y() != oldGeom.y()) + emit q->contentYChanged(); + } +} + +void QDeclarative1FlickablePrivate::flickX(qreal velocity) +{ + Q_Q(QDeclarative1Flickable); + flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity); +} + +void QDeclarative1FlickablePrivate::flickY(qreal velocity) +{ + Q_Q(QDeclarative1Flickable); + flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity); +} + +void QDeclarative1FlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal, + QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarative1Flickable); + 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 == QDeclarative1Flickable::DragAndOvershootBounds) + timeline.accel(data.move, v, deceleration); + else + timeline.accel(data.move, v, deceleration, maxDistance); + timeline.callback(QDeclarative1TimeLineCallback(&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 QDeclarative1FlickablePrivate::fixupY_callback(void *data) +{ + ((QDeclarative1FlickablePrivate *)data)->fixupY(); +} + +void QDeclarative1FlickablePrivate::fixupX_callback(void *data) +{ + ((QDeclarative1FlickablePrivate *)data)->fixupX(); +} + +void QDeclarative1FlickablePrivate::fixupX() +{ + Q_Q(QDeclarative1Flickable); + fixup(hData, q->minXExtent(), q->maxXExtent()); +} + +void QDeclarative1FlickablePrivate::fixupY() +{ + Q_Q(QDeclarative1Flickable); + fixup(vData, q->minYExtent(), q->maxYExtent()); +} + +void QDeclarative1FlickablePrivate::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 QDeclarative1FlickablePrivate::updateBeginningEnd() +{ + Q_Q(QDeclarative1Flickable); + 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(); +} + +/*! + \qmlclass Flickable QDeclarative1Flickable + \since 4.7 + \ingroup qml-basic-interaction-elements + + \brief The Flickable item provides a surface that can be "flicked". + \inherits Item + + The Flickable item places its children on a surface that can be dragged + and flicked, causing the view onto the child items to scroll. This + behavior forms the basis of Items that are designed to show large numbers + of child items, such as \l ListView and \l GridView. + + In traditional user interfaces, views can be scrolled using standard + controls, such as scroll bars and arrow buttons. In some situations, it + is also possible to drag the view directly by pressing and holding a + mouse button while moving the cursor. In touch-based user interfaces, + this dragging action is often complemented with a flicking action, where + scrolling continues after the user has stopped touching the view. + + Flickable does not automatically clip its contents. If it is not used as + a full-screen item, you should consider setting the \l{Item::}{clip} property + to true. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage flickable.gif + \enddiv + + The following example shows a small view onto a large image in which the + user can drag or flick the image in order to view different parts of it. + + \snippet doc/src/snippets/declarative/flickable.qml document + + \clearfloat + + Items declared as children of a Flickable are automatically parented to the + Flickable's \l contentItem. This should be taken into account when + operating on the children of the Flickable; it is usually the children of + \c contentItem that are relevant. For example, the bound of Items added + to the Flickable will be available by \c contentItem.childrenRect + + \section1 Limitations + + \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by + \c id. Use \c parent instead. +*/ + +/*! + \qmlsignal Flickable::onMovementStarted() + + This handler is called when the view begins moving due to user + interaction. +*/ + +/*! + \qmlsignal Flickable::onMovementEnded() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - i.e. a mouse or touch release. +*/ + +/*! + \qmlsignal Flickable::onFlickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal Flickable::onFlickEnded() + + This handler is called when the view stops moving due to a flick. +*/ + +/*! + \qmlproperty real Flickable::visibleArea.xPosition + \qmlproperty real Flickable::visibleArea.widthRatio + \qmlproperty real Flickable::visibleArea.yPosition + \qmlproperty real Flickable::visibleArea.heightRatio + + These properties describe the position and size of the currently viewed area. + The size is defined as the percentage of the full view currently visible, + scaled to 0.0 - 1.0. The page position is usually in the range 0.0 (beginning) to + 1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio. + However, it is possible for the contents to be dragged outside of the normal + range, resulting in the page positions also being outside the normal range. + + These properties are typically used to draw a scrollbar. For example: + + \snippet doc/src/snippets/declarative/flickableScrollbar.qml 0 + \dots 8 + \snippet doc/src/snippets/declarative/flickableScrollbar.qml 1 + + \sa {declarative/ui-components/scrollbar}{scrollbar example} +*/ + +QDeclarative1Flickable::QDeclarative1Flickable(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1FlickablePrivate), parent) +{ + Q_D(QDeclarative1Flickable); + d->init(); +} + +QDeclarative1Flickable::QDeclarative1Flickable(QDeclarative1FlickablePrivate &dd, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ + Q_D(QDeclarative1Flickable); + d->init(); +} + +QDeclarative1Flickable::~QDeclarative1Flickable() +{ +} + +/*! + \qmlproperty real Flickable::contentX + \qmlproperty real Flickable::contentY + + These properties hold the surface coordinate currently at the top-left + corner of the Flickable. For example, if you flick an image up 100 pixels, + \c contentY will be 100. +*/ +qreal QDeclarative1Flickable::contentX() const +{ + Q_D(const QDeclarative1Flickable); + return -d->contentItem->x(); +} + +void QDeclarative1Flickable::setContentX(qreal pos) +{ + Q_D(QDeclarative1Flickable); + 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 QDeclarative1Flickable::contentY() const +{ + Q_D(const QDeclarative1Flickable); + return -d->contentItem->y(); +} + +void QDeclarative1Flickable::setContentY(qreal pos) +{ + Q_D(QDeclarative1Flickable); + d->timeline.reset(d->vData.move); + d->vTime = d->timeline.time(); + movementYEnding(); + if (-pos != d->vData.move.value()) { + d->vData.move.setValue(-pos); + viewportMoved(); + } +} + +/*! + \qmlproperty bool Flickable::interactive + + This property describes whether the user can interact with the Flickable. + A user cannot drag or flick a Flickable that is not interactive. + + By default, this property is true. + + This property is useful for temporarily disabling flicking. This allows + special interaction with Flickable's children; for example, you might want + to freeze a flickable map while scrolling through a pop-up dialog that + is a child of the Flickable. +*/ +bool QDeclarative1Flickable::isInteractive() const +{ + Q_D(const QDeclarative1Flickable); + return d->interactive; +} + +void QDeclarative1Flickable::setInteractive(bool interactive) +{ + Q_D(QDeclarative1Flickable); + 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(); + } +} + +/*! + \qmlproperty real Flickable::horizontalVelocity + \qmlproperty real Flickable::verticalVelocity + + The instantaneous velocity of movement along the x and y axes, in pixels/sec. + + The reported velocity is smoothed to avoid erratic output. +*/ +qreal QDeclarative1Flickable::horizontalVelocity() const +{ + Q_D(const QDeclarative1Flickable); + return d->hData.smoothVelocity.value(); +} + +qreal QDeclarative1Flickable::verticalVelocity() const +{ + Q_D(const QDeclarative1Flickable); + return d->vData.smoothVelocity.value(); +} + +/*! + \qmlproperty bool Flickable::atXBeginning + \qmlproperty bool Flickable::atXEnd + \qmlproperty bool Flickable::atYBeginning + \qmlproperty bool Flickable::atYEnd + + These properties are true if the flickable view is positioned at the beginning, + or end respecively. +*/ +bool QDeclarative1Flickable::isAtXEnd() const +{ + Q_D(const QDeclarative1Flickable); + return d->hData.atEnd; +} + +bool QDeclarative1Flickable::isAtXBeginning() const +{ + Q_D(const QDeclarative1Flickable); + return d->hData.atBeginning; +} + +bool QDeclarative1Flickable::isAtYEnd() const +{ + Q_D(const QDeclarative1Flickable); + return d->vData.atEnd; +} + +bool QDeclarative1Flickable::isAtYBeginning() const +{ + Q_D(const QDeclarative1Flickable); + return d->vData.atBeginning; +} + +void QDeclarative1Flickable::ticked() +{ + viewportMoved(); +} + +/*! + \qmlproperty Item Flickable::contentItem + + The internal item that contains the Items to be moved in the Flickable. + + Items declared as children of a Flickable are automatically parented to the Flickable's contentItem. + + Items created dynamically need to be explicitly parented to the \e contentItem: + \code + Flickable { + id: myFlickable + function addItem(file) { + var component = Qt.createComponent(file) + component.createObject(myFlickable.contentItem); + } + } + \endcode +*/ +QDeclarativeItem *QDeclarative1Flickable::contentItem() +{ + Q_D(QDeclarative1Flickable); + return d->contentItem; +} + +QDeclarative1FlickableVisibleArea *QDeclarative1Flickable::visibleArea() +{ + Q_D(QDeclarative1Flickable); + if (!d->visibleArea) + d->visibleArea = new QDeclarative1FlickableVisibleArea(this); + return d->visibleArea; +} + +/*! + \qmlproperty enumeration Flickable::flickableDirection + + This property determines which directions the view can be flicked. + + \list + \o Flickable.AutoFlickDirection (default) - allows flicking vertically if the + \e contentHeight is not equal to the \e height of the Flickable. + Allows flicking horizontally if the \e contentWidth is not equal + to the \e width of the Flickable. + \o Flickable.HorizontalFlick - allows flicking horizontally. + \o Flickable.VerticalFlick - allows flicking vertically. + \o Flickable.HorizontalAndVerticalFlick - allows flicking in both directions. + \endlist +*/ +QDeclarative1Flickable::FlickableDirection QDeclarative1Flickable::flickableDirection() const +{ + Q_D(const QDeclarative1Flickable); + return d->flickableDirection; +} + +void QDeclarative1Flickable::setFlickableDirection(FlickableDirection direction) +{ + Q_D(QDeclarative1Flickable); + if (direction != d->flickableDirection) { + d->flickableDirection = direction; + emit flickableDirectionChanged(); + } +} + +void QDeclarative1FlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarative1Flickable); + 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(); + QDeclarativeItemPrivate::start(lastPosTime); + pressPos = event->pos(); + hData.pressPos = hData.move.value(); + vData.pressPos = vData.move.value(); + flickingHorizontally = false; + flickingVertically = false; + QDeclarativeItemPrivate::start(pressTime); + QDeclarativeItemPrivate::start(velocityTime); +} + +void QDeclarative1FlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarative1Flickable); + 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() || QDeclarativeItemPrivate::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 == QDeclarative1Flickable::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() || QDeclarativeItemPrivate::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 == QDeclarative1Flickable::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(QDeclarativeItemPrivate::elapsed(lastPosTime)) / 1000.; + if (elapsed <= 0) + return; + QDeclarativeItemPrivate::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 QDeclarative1FlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarative1Flickable); + 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 (QDeclarativeItemPrivate::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 QDeclarative1Flickable::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1Flickable); + if (d->interactive) { + if (!d->pressed) + d->handleMousePressEvent(event); + event->accept(); + } else { + QDeclarativeItem::mousePressEvent(event); + } +} + +void QDeclarative1Flickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1Flickable); + if (d->interactive) { + d->handleMouseMoveEvent(event); + event->accept(); + } else { + QDeclarativeItem::mouseMoveEvent(event); + } +} + +void QDeclarative1Flickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1Flickable); + if (d->interactive) { + d->clearDelayedPress(); + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QDeclarativeItem::mouseReleaseEvent(event); + } +} + +void QDeclarative1Flickable::wheelEvent(QGraphicsSceneWheelEvent *event) +{ + Q_D(QDeclarative1Flickable); + if (!d->interactive) { + QDeclarativeItem::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 { + QDeclarativeItem::wheelEvent(event); + } +} + +bool QDeclarative1FlickablePrivate::isOutermostPressDelay() const +{ + Q_Q(const QDeclarative1Flickable); + QDeclarativeItem *item = q->parentItem(); + while (item) { + QDeclarative1Flickable *flick = qobject_cast<QDeclarative1Flickable*>(item); + if (flick && flick->pressDelay() > 0 && flick->isInteractive()) + return false; + item = item->parentItem(); + } + + return true; +} + +void QDeclarative1FlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarative1Flickable); + if (!q->scene() || pressDelay <= 0) + return; + if (!isOutermostPressDelay()) + return; + delayedPressTarget = q->scene()->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 QDeclarative1FlickablePrivate::clearDelayedPress() +{ + if (delayedPressEvent) { + delayedPressTimer.stop(); + delete delayedPressEvent; + delayedPressEvent = 0; + } +} + +void QDeclarative1FlickablePrivate::setRoundedViewportX(qreal x) +{ + contentItem->setX(qRound(x)); +} + +void QDeclarative1FlickablePrivate::setRoundedViewportY(qreal y) +{ + contentItem->setY(qRound(y)); +} + +void QDeclarative1Flickable::timerEvent(QTimerEvent *event) +{ + Q_D(QDeclarative1Flickable); + if (event->timerId() == d->delayedPressTimer.timerId()) { + d->delayedPressTimer.stop(); + if (d->delayedPressEvent) { + QDeclarativeItem *grabber = scene() ? qobject_cast<QDeclarativeItem*>(scene()->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 (scene()->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QApplication::postEvent(scene(), d->delayedPressEvent); + } else { + delete d->delayedPressEvent; + } + d->delayedPressEvent = 0; + } + } +} + +qreal QDeclarative1Flickable::minYExtent() const +{ + return 0.0; +} + +qreal QDeclarative1Flickable::minXExtent() const +{ + return 0.0; +} + +/* returns -ve */ +qreal QDeclarative1Flickable::maxXExtent() const +{ + return width() - vWidth(); +} +/* returns -ve */ +qreal QDeclarative1Flickable::maxYExtent() const +{ + return height() - vHeight(); +} + +void QDeclarative1Flickable::viewportMoved() +{ + Q_D(QDeclarative1Flickable); + + qreal prevX = d->lastFlickablePosition.x(); + qreal prevY = d->lastFlickablePosition.y(); + d->velocityTimeline.clear(); + if (d->pressed || d->calcVelocity) { + int elapsed = QDeclarativeItemPrivate::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(QDeclarative1TimeLineCallback(&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(QDeclarative1TimeLineCallback(&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 QDeclarative1Flickable::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarative1Flickable); + QDeclarativeItem::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 = QDeclarative1FlickablePrivate::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 = QDeclarative1FlickablePrivate::Immediate; + d->fixupY(); + } + } + + if (changed) + d->updateBeginningEnd(); +} + +void QDeclarative1Flickable::cancelFlick() +{ + Q_D(QDeclarative1Flickable); + d->timeline.reset(d->hData.move); + d->timeline.reset(d->vData.move); + movementEnding(); +} + +void QDeclarative1FlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o) +{ + QGraphicsObject *i = qobject_cast<QGraphicsObject *>(o); + if (i) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(i); + if (static_cast<QDeclarativeItemPrivate*>(d)->componentComplete) { + i->setParentItem(static_cast<QDeclarative1FlickablePrivate*>(prop->data)->contentItem); + } else { + d->setParentItemHelper(static_cast<QDeclarative1FlickablePrivate*>(prop->data)->contentItem, 0, 0); + } + } else { + o->setParent(prop->object); + } +} + +int QDeclarative1FlickablePrivate::data_count(QDeclarativeListProperty<QObject> *property) +{ + QDeclarativeItem *contentItem= static_cast<QDeclarative1FlickablePrivate*>(property->data)->contentItem; + return contentItem->childItems().count() + contentItem->children().count(); +} + +QObject *QDeclarative1FlickablePrivate::data_at(QDeclarativeListProperty<QObject> *property, int index) +{ + QDeclarativeItem *contentItem = static_cast<QDeclarative1FlickablePrivate*>(property->data)->contentItem; + + int childItemCount = contentItem->childItems().count(); + + if (index < 0) + return 0; + + if (index < childItemCount) { + return contentItem->childItems().at(index)->toGraphicsObject(); + } else { + return contentItem->children().at(index - childItemCount); + } + + return 0; +} + +void QDeclarative1FlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *property) +{ + QDeclarativeItem *contentItem = static_cast<QDeclarative1FlickablePrivate*>(property->data)->contentItem; + + const QList<QGraphicsItem*> graphicsItems = contentItem->childItems(); + for (int i = 0; i < graphicsItems.count(); i++) + contentItem->scene()->removeItem(graphicsItems[i]); + + const QList<QObject*> objects = contentItem->children(); + for (int i = 0; i < objects.count(); i++) + objects[i]->setParent(0); +} + +QDeclarativeListProperty<QObject> QDeclarative1Flickable::flickableData() +{ + Q_D(QDeclarative1Flickable); + return QDeclarativeListProperty<QObject>(this, (void *)d, QDeclarative1FlickablePrivate::data_append, + QDeclarative1FlickablePrivate::data_count, + QDeclarative1FlickablePrivate::data_at, + QDeclarative1FlickablePrivate::data_clear); +} + +QDeclarativeListProperty<QGraphicsObject> QDeclarative1Flickable::flickableChildren() +{ + Q_D(QDeclarative1Flickable); + return QGraphicsItemPrivate::get(d->contentItem)->childrenList(); +} + +/*! + \qmlproperty enumeration Flickable::boundsBehavior + This property holds whether the surface may be dragged + beyond the Fickable's boundaries, or overshoot the + Flickable's boundaries when flicked. + + This enables the feeling that the edges of the view are soft, + rather than a hard physical boundary. + + The \c boundsBehavior can be one of: + + \list + \o Flickable.StopAtBounds - the contents can not be dragged beyond the boundary + of the flickable, and flicks will not overshoot. + \o Flickable.DragOverBounds - the contents can be dragged beyond the boundary + of the Flickable, but flicks will not overshoot. + \o Flickable.DragAndOvershootBounds (default) - the contents can be dragged + beyond the boundary of the Flickable, and can overshoot the + boundary when flicked. + \endlist +*/ +QDeclarative1Flickable::BoundsBehavior QDeclarative1Flickable::boundsBehavior() const +{ + Q_D(const QDeclarative1Flickable); + return d->boundsBehavior; +} + +void QDeclarative1Flickable::setBoundsBehavior(BoundsBehavior b) +{ + Q_D(QDeclarative1Flickable); + if (b == d->boundsBehavior) + return; + d->boundsBehavior = b; + emit boundsBehaviorChanged(); +} + +/*! + \qmlproperty real Flickable::contentWidth + \qmlproperty real Flickable::contentHeight + + The dimensions of the content (the surface controlled by Flickable). + This should typically be set to the combined size of the items placed in the + Flickable. + + The following snippet shows how these properties are used to display + an image that is larger than the Flickable item itself: + + \snippet doc/src/snippets/declarative/flickable.qml document + + In some cases, the the content dimensions can be automatically set + using the \l {Item::childrenRect.width}{childrenRect.width} + and \l {Item::childrenRect.height}{childrenRect.height} properties. +*/ +qreal QDeclarative1Flickable::contentWidth() const +{ + Q_D(const QDeclarative1Flickable); + return d->hData.viewSize; +} + +void QDeclarative1Flickable::setContentWidth(qreal w) +{ + Q_D(QDeclarative1Flickable); + 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 = QDeclarative1FlickablePrivate::Immediate; + d->fixupX(); + } else if (!d->pressed && d->hData.fixingUp) { + d->fixupMode = QDeclarative1FlickablePrivate::ExtentChanged; + d->fixupX(); + } + emit contentWidthChanged(); + d->updateBeginningEnd(); +} + +qreal QDeclarative1Flickable::contentHeight() const +{ + Q_D(const QDeclarative1Flickable); + return d->vData.viewSize; +} + +void QDeclarative1Flickable::setContentHeight(qreal h) +{ + Q_D(QDeclarative1Flickable); + 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 = QDeclarative1FlickablePrivate::Immediate; + d->fixupY(); + } else if (!d->pressed && d->vData.fixingUp) { + d->fixupMode = QDeclarative1FlickablePrivate::ExtentChanged; + d->fixupY(); + } + emit contentHeightChanged(); + d->updateBeginningEnd(); +} + +/*! + \qmlmethod Flickable::resizeContent(real width, real height, QPointF center) + \preliminary + \since Quick 1.1 + + Resizes the content to \a width x \a height about \a center. + + This does not scale the contents of the Flickable - it only resizes the \l contentWidth + and \l contentHeight. + + Resizing the content may result in the content being positioned outside + the bounds of the Flickable. Calling \l returnToBounds() will + move the content back within legal bounds. +*/ +void QDeclarative1Flickable::resizeContent(qreal w, qreal h, QPointF center) +{ + Q_D(QDeclarative1Flickable); + 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(); +} + +/*! + \qmlmethod Flickable::returnToBounds() + \preliminary + \since Quick 1.1 + + Ensures the content is within legal bounds. + + This may be called to ensure that the content is within legal bounds + after manually positioning the content. +*/ +void QDeclarative1Flickable::returnToBounds() +{ + Q_D(QDeclarative1Flickable); + d->fixupX(); + d->fixupY(); +} + +qreal QDeclarative1Flickable::vWidth() const +{ + Q_D(const QDeclarative1Flickable); + if (d->hData.viewSize < 0) + return width(); + else + return d->hData.viewSize; +} + +qreal QDeclarative1Flickable::vHeight() const +{ + Q_D(const QDeclarative1Flickable); + if (d->vData.viewSize < 0) + return height(); + else + return d->vData.viewSize; +} + +bool QDeclarative1Flickable::xflick() const +{ + Q_D(const QDeclarative1Flickable); + if (d->flickableDirection == QDeclarative1Flickable::AutoFlickDirection) + return vWidth() != width(); + return d->flickableDirection & QDeclarative1Flickable::HorizontalFlick; +} + +bool QDeclarative1Flickable::yflick() const +{ + Q_D(const QDeclarative1Flickable); + if (d->flickableDirection == QDeclarative1Flickable::AutoFlickDirection) + return vHeight() != height(); + return d->flickableDirection & QDeclarative1Flickable::VerticalFlick; +} + +bool QDeclarative1Flickable::sceneEvent(QEvent *event) +{ + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + Q_D(QDeclarative1Flickable); + 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); + } + } + return rv; +} + +bool QDeclarative1Flickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1Flickable); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()) : 0; + QGraphicsItem *grabberItem = s ? s->mouseGrabberItem() : 0; + bool disabledItem = grabberItem && !grabberItem->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 (s->mouseGrabberItem() == d->delayedPressTarget) + d->delayedPressTarget->ungrabMouse(); + //Use the event handler that will take care of finding the proper item to propagate the event + QApplication::sendEvent(scene(), d->delayedPressEvent); + d->clearDelayedPress(); + // We send the release + scene()->sendEvent(s->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<QDeclarativeItem*>(s->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 QDeclarative1Flickable::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarative1Flickable); + if (!isVisible() || !d->interactive) + return QDeclarativeItem::sceneEventFilter(i, e); + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e)); + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +/*! + \qmlproperty real Flickable::maximumFlickVelocity + This property holds the maximum velocity that the user can flick the view in pixels/second. + + The default value is platform dependent. +*/ +qreal QDeclarative1Flickable::maximumFlickVelocity() const +{ + Q_D(const QDeclarative1Flickable); + return d->maxVelocity; +} + +void QDeclarative1Flickable::setMaximumFlickVelocity(qreal v) +{ + Q_D(QDeclarative1Flickable); + if (v == d->maxVelocity) + return; + d->maxVelocity = v; + emit maximumFlickVelocityChanged(); +} + +/*! + \qmlproperty real Flickable::flickDeceleration + This property holds the rate at which a flick will decelerate. + + The default value is platform dependent. +*/ +qreal QDeclarative1Flickable::flickDeceleration() const +{ + Q_D(const QDeclarative1Flickable); + return d->deceleration; +} + +void QDeclarative1Flickable::setFlickDeceleration(qreal deceleration) +{ + Q_D(QDeclarative1Flickable); + if (deceleration == d->deceleration) + return; + d->deceleration = deceleration; + emit flickDecelerationChanged(); +} + +bool QDeclarative1Flickable::isFlicking() const +{ + Q_D(const QDeclarative1Flickable); + return d->flickingHorizontally || d->flickingVertically; +} + +/*! + \qmlproperty bool Flickable::flicking + \qmlproperty bool Flickable::flickingHorizontally + \qmlproperty bool Flickable::flickingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user flicking the view. +*/ +bool QDeclarative1Flickable::isFlickingHorizontally() const +{ + Q_D(const QDeclarative1Flickable); + return d->flickingHorizontally; +} + +bool QDeclarative1Flickable::isFlickingVertically() const +{ + Q_D(const QDeclarative1Flickable); + return d->flickingVertically; +} + +/*! + \qmlproperty int Flickable::pressDelay + + This property holds the time to delay (ms) delivering a press to + children of the Flickable. This can be useful where reacting + to a press before a flicking action has undesirable effects. + + If the flickable is dragged/flicked before the delay times out + the press event will not be delivered. If the button is released + within the timeout, both the press and release will be delivered. + + Note that for nested Flickables with pressDelay set, the pressDelay of + inner Flickables is overridden by the outermost Flickable. +*/ +int QDeclarative1Flickable::pressDelay() const +{ + Q_D(const QDeclarative1Flickable); + return d->pressDelay; +} + +void QDeclarative1Flickable::setPressDelay(int delay) +{ + Q_D(QDeclarative1Flickable); + if (d->pressDelay == delay) + return; + d->pressDelay = delay; + emit pressDelayChanged(); +} + + +bool QDeclarative1Flickable::isMoving() const +{ + Q_D(const QDeclarative1Flickable); + return d->movingHorizontally || d->movingVertically; +} + +/*! + \qmlproperty bool Flickable::moving + \qmlproperty bool Flickable::movingHorizontally + \qmlproperty bool Flickable::movingVertically + + These properties describe whether the view is currently moving horizontally, + vertically or in either direction, due to the user either dragging or + flicking the view. +*/ +bool QDeclarative1Flickable::isMovingHorizontally() const +{ + Q_D(const QDeclarative1Flickable); + return d->movingHorizontally; +} + +bool QDeclarative1Flickable::isMovingVertically() const +{ + Q_D(const QDeclarative1Flickable); + return d->movingVertically; +} + +void QDeclarative1Flickable::movementStarting() +{ + Q_D(QDeclarative1Flickable); + 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 QDeclarative1Flickable::movementEnding() +{ + Q_D(QDeclarative1Flickable); + movementXEnding(); + movementYEnding(); + d->hData.smoothVelocity.setValue(0); + d->vData.smoothVelocity.setValue(0); +} + +void QDeclarative1Flickable::movementXEnding() +{ + Q_D(QDeclarative1Flickable); + 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 QDeclarative1Flickable::movementYEnding() +{ + Q_D(QDeclarative1Flickable); + 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 QDeclarative1FlickablePrivate::updateVelocity() +{ + Q_Q(QDeclarative1Flickable); + emit q->horizontalVelocityChanged(); + emit q->verticalVelocityChanged(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeflickable_p.h b/src/qtquick1/graphicsitems/qdeclarativeflickable_p.h new file mode 100644 index 0000000000..63428840a0 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeflickable_p.h @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFLICKABLE_H +#define QDECLARATIVEFLICKABLE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1FlickablePrivate; +class QDeclarative1FlickableVisibleArea; +class Q_AUTOTEST_EXPORT QDeclarative1Flickable : public QDeclarativeItem +{ + 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(QDeclarativeItem *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(QDeclarative1FlickableVisibleArea *visibleArea READ visibleArea CONSTANT) + + Q_PROPERTY(QDeclarativeListProperty<QObject> flickableData READ flickableData) + Q_PROPERTY(QDeclarativeListProperty<QGraphicsObject> flickableChildren READ flickableChildren) + Q_CLASSINFO("DefaultProperty", "flickableData") + + Q_ENUMS(FlickableDirection) + Q_ENUMS(BoundsBehavior) + +public: + QDeclarative1Flickable(QDeclarativeItem *parent=0); + ~QDeclarative1Flickable(); + + QDeclarativeListProperty<QObject> flickableData(); + QDeclarativeListProperty<QGraphicsObject> 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; + + QDeclarativeItem *contentItem(); + + enum FlickableDirection { AutoFlickDirection=0x00, HorizontalFlick=0x01, VerticalFlick=0x02, HorizontalAndVerticalFlick=0x03 }; + FlickableDirection flickableDirection() const; + void setFlickableDirection(FlickableDirection); + + Q_INVOKABLE Q_REVISION(1) void resizeContent(qreal w, qreal h, QPointF center); + Q_INVOKABLE Q_REVISION(1) 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 sceneEventFilter(QGraphicsItem *, QEvent *); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void wheelEvent(QGraphicsSceneWheelEvent *event); + void timerEvent(QTimerEvent *event); + + QDeclarative1FlickableVisibleArea *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); + bool sceneEvent(QEvent *event); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + + bool xflick() const; + bool yflick() const; + void cancelFlick(); + +protected: + QDeclarative1Flickable(QDeclarative1FlickablePrivate &dd, QDeclarativeItem *parent); + +private: + Q_DISABLE_COPY(QDeclarative1Flickable) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Flickable) + friend class QDeclarative1FlickableVisibleArea; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Flickable) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeflickable_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeflickable_p_p.h new file mode 100644 index 0000000000..5bbeb27c9e --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeflickable_p_p.h @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFLICKABLE_P_H +#define QDECLARATIVEFLICKABLE_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/qdeclarativeflickable_p.h" + +#include "private/qdeclarativeitem_p.h" +#include "private/qdeclarativeitemchangelistener_p.h" + +#include <QtDeclarative/qdeclarative.h> +#include <QtQuick1/private/qdeclarativetimeline_p_p.h> +#include <QtQuick1/private/qdeclarativeanimation_p_p.h> + +#include <qdatetime.h> + +QT_BEGIN_NAMESPACE + +// Really slow flicks can be annoying. +const qreal MinimumFlickVelocity = 75.0; + +class QDeclarative1FlickableVisibleArea; +class QDeclarative1FlickablePrivate : public QDeclarativeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarative1Flickable) + +public: + QDeclarative1FlickablePrivate(); + void init(); + + struct Velocity : public QDeclarative1TimeLineValue + { + Velocity(QDeclarative1FlickablePrivate *p) + : parent(p) {} + virtual void setValue(qreal v) { + if (v != value()) { + QDeclarative1TimeLineValue::setValue(v); + parent->updateVelocity(); + } + } + QDeclarative1FlickablePrivate *parent; + }; + + struct AxisData { + AxisData(QDeclarative1FlickablePrivate *fp, void (QDeclarative1FlickablePrivate::*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(); + + QDeclarative1TimeLineValueProxy<QDeclarative1FlickablePrivate> move; + qreal viewSize; + qreal pressPos; + qreal dragStartOffset; + qreal dragMinBound; + qreal dragMaxBound; + qreal velocity; + qreal flickTarget; + QDeclarative1FlickablePrivate::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, + QDeclarative1TimeLineCallback::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 setRoundedViewportX(qreal x); + void setRoundedViewportY(qreal y); + + qreal overShootDistance(qreal size); + + void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &); + +public: + QDeclarativeItem *contentItem; + + AxisData hData; + AxisData vData; + + QDeclarative1TimeLine 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; + QGraphicsItem *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; + QDeclarative1TimeLine velocityTimeline; + QDeclarative1FlickableVisibleArea *visibleArea; + QDeclarative1Flickable::FlickableDirection flickableDirection; + QDeclarative1Flickable::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 QDeclarative1FlickableVisibleArea : 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: + QDeclarative1FlickableVisibleArea(QDeclarative1Flickable *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: + QDeclarative1Flickable *flickable; + qreal m_xPosition; + qreal m_widthRatio; + qreal m_yPosition; + qreal m_heightRatio; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1FlickableVisibleArea) + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeflipable.cpp b/src/qtquick1/graphicsitems/qdeclarativeflipable.cpp new file mode 100644 index 0000000000..d25f33a129 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeflipable.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeflipable_p.h" + +#include "QtQuick1/private/qdeclarativeitem_p.h" +#include "QtDeclarative/private/qdeclarativeguard_p.h" + +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QtGui/qgraphicstransform.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1FlipablePrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Flipable) +public: + QDeclarative1FlipablePrivate() : current(QDeclarative1Flipable::Front), front(0), back(0) {} + + void updateSceneTransformFromParent(); + void setBackTransform(); + + QDeclarative1Flipable::Side current; + QDeclarativeGuard<QGraphicsObject> front; + QDeclarativeGuard<QGraphicsObject> back; + + bool wantBackXFlipped; + bool wantBackYFlipped; +}; + +/*! + \qmlclass Flipable QDeclarative1Flipable + \since 4.7 + \ingroup qml-basic-interaction-elements + \brief The Flipable item provides a surface that can be flipped. + \inherits Item + + Flipable is an item that can be visibly "flipped" between its front and + back sides, like a card. It is used together with \l Rotation, \l State + and \l Transition elements to produce a flipping effect. + + The \l front and \l back properties are used to hold the items that are + shown respectively on the front and back sides of the flipable item. + + \section1 Example Usage + + The following example shows a Flipable item that flips whenever it is + clicked, rotating about the y-axis. + + This flipable item has a \c flipped boolean property that is toggled + whenever the MouseArea within the flipable is clicked. When + \c flipped is true, the item changes to the "back" state; in this + state, the \c angle of the \l Rotation item is changed to 180 + degrees to produce the flipping effect. When \c flipped is false, the + item reverts to the default state, in which the \c angle value is 0. + + \snippet doc/src/snippets/declarative/flipable/flipable.qml 0 + + \image flipable.gif + + The \l Transition creates the animation that changes the angle over + four seconds. When the item changes between its "back" and + default states, the NumberAnimation animates the angle between + its old and new values. + + See \l {QML States} for details on state changes and the default + state, and \l {QML Animation and Transitions} for more information on how + animations work within transitions. + + \sa {declarative/ui-components/flipable}{Flipable example} +*/ + +QDeclarative1Flipable::QDeclarative1Flipable(QDeclarativeItem *parent) +: QDeclarativeItem(*(new QDeclarative1FlipablePrivate), parent) +{ +} + +QDeclarative1Flipable::~QDeclarative1Flipable() +{ +} + +/*! + \qmlproperty Item Flipable::front + \qmlproperty Item Flipable::back + + The front and back sides of the flipable. +*/ + +QGraphicsObject *QDeclarative1Flipable::front() +{ + Q_D(const QDeclarative1Flipable); + return d->front; +} + +void QDeclarative1Flipable::setFront(QGraphicsObject *front) +{ + Q_D(QDeclarative1Flipable); + 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(); +} + +QGraphicsObject *QDeclarative1Flipable::back() +{ + Q_D(const QDeclarative1Flipable); + return d->back; +} + +void QDeclarative1Flipable::setBack(QGraphicsObject *back) +{ + Q_D(QDeclarative1Flipable); + if (d->back) { + qmlInfo(this) << tr("back is a write-once property"); + return; + } + d->back = back; + d->back->setParentItem(this); + 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 QDeclarative1Flipable::retransformBack() +{ + Q_D(QDeclarative1Flipable); + if (d->current == QDeclarative1Flipable::Back && d->back) + d->setBackTransform(); +} + +/*! + \qmlproperty enumeration Flipable::side + + The side of the Flipable currently visible. Possible values are \c + Flipable.Front and \c Flipable.Back. +*/ +QDeclarative1Flipable::Side QDeclarative1Flipable::side() const +{ + Q_D(const QDeclarative1Flipable); + if (d->dirtySceneTransform) + const_cast<QDeclarative1FlipablePrivate *>(d)->ensureSceneTransform(); + + return d->current; +} + +// determination on the currently visible side of the flipable +// has to be done on the complete scene transform to give +// correct results. +void QDeclarative1FlipablePrivate::updateSceneTransformFromParent() +{ + Q_Q(QDeclarative1Flipable); + + QDeclarativeItemPrivate::updateSceneTransformFromParent(); + 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); + p1 = q->mapToParent(p1); + p2 = q->mapToParent(p2); + p3 = q->mapToParent(p3); + + qreal cross = (scenep1.x() - scenep2.x()) * (scenep3.y() - scenep2.y()) - + (scenep1.y() - scenep2.y()) * (scenep3.x() - scenep2.x()); + + wantBackYFlipped = p1.x() >= p2.x(); + wantBackXFlipped = p2.y() >= p3.y(); + + QDeclarative1Flipable::Side newSide; + if (cross > 0) { + newSide = QDeclarative1Flipable::Back; + } else { + newSide = QDeclarative1Flipable::Front; + } + + if (newSide != current) { + current = newSide; + if (current == QDeclarative1Flipable::Back && back) + setBackTransform(); + if (front) + front->setOpacity((current==QDeclarative1Flipable::Front)?1.:0.); + if (back) + back->setOpacity((current==QDeclarative1Flipable::Back)?1.:0.); + emit q->sideChanged(); + } +} + +/* Depends on the width/height of the back item, and so needs reevaulating + if those change. +*/ +void QDeclarative1FlipablePrivate::setBackTransform() +{ + QTransform mat; + QGraphicsItemPrivate *dBack = QGraphicsItemPrivate::get(back); + mat.translate(dBack->width()/2,dBack->height()/2); + if (dBack->width() && wantBackYFlipped) + mat.rotate(180, Qt::YAxis); + if (dBack->height() && wantBackXFlipped) + mat.rotate(180, Qt::XAxis); + mat.translate(-dBack->width()/2,-dBack->height()/2); + back->setTransform(mat); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeflipable_p.h b/src/qtquick1/graphicsitems/qdeclarativeflipable_p.h new file mode 100644 index 0000000000..f4cdb86126 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeflipable_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFLIPABLE_H +#define QDECLARATIVEFLIPABLE_H + +#include "qdeclarativeitem.h" + +#include <QtCore/QObject> +#include <QtGui/QTransform> +#include <QtGui/qvector3d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1FlipablePrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Flipable : public QDeclarativeItem +{ + Q_OBJECT + + Q_ENUMS(Side) + Q_PROPERTY(QGraphicsObject *front READ front WRITE setFront NOTIFY frontChanged) + Q_PROPERTY(QGraphicsObject *back READ back WRITE setBack NOTIFY backChanged) + Q_PROPERTY(Side side READ side NOTIFY sideChanged) + //### flipAxis + //### flipRotation +public: + QDeclarative1Flipable(QDeclarativeItem *parent=0); + ~QDeclarative1Flipable(); + + QGraphicsObject *front(); + void setFront(QGraphicsObject *); + + QGraphicsObject *back(); + void setBack(QGraphicsObject *); + + enum Side { Front, Back }; + Side side() const; + +Q_SIGNALS: + void frontChanged(); + void backChanged(); + void sideChanged(); + +private Q_SLOTS: + void retransformBack(); + +private: + Q_DISABLE_COPY(QDeclarative1Flipable) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Flipable) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Flipable) + +QT_END_HEADER + +#endif // QDECLARATIVEFLIPABLE_H diff --git a/src/qtquick1/graphicsitems/qdeclarativefocuspanel.cpp b/src/qtquick1/graphicsitems/qdeclarativefocuspanel.cpp new file mode 100644 index 0000000000..e50df2ace5 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativefocuspanel.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativefocuspanel_p.h" + +#include "QtQuick1/private/qdeclarativeitem_p.h" + +#include <QtGui/qgraphicsscene.h> +#include <QEvent> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass FocusPanel QDeclarative1FocusPanel + \since 4.7 + \ingroup qml-basic-interaction-elements + + \brief The FocusPanel item explicitly creates a focus panel. + \inherits Item + + Focus panels assist in keyboard focus handling when building QML + applications. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. +*/ + +QDeclarative1FocusPanel::QDeclarative1FocusPanel(QDeclarativeItem *parent) : + QDeclarativeItem(parent) +{ + Q_D(QDeclarativeItem); + d->flags |= QGraphicsItem::ItemIsPanel; +} + +QDeclarative1FocusPanel::~QDeclarative1FocusPanel() +{ +} + +/*! + \qmlproperty bool FocusPanel::active + + Sets whether the item is the active focus panel. +*/ + +bool QDeclarative1FocusPanel::sceneEvent(QEvent *event) +{ + if (event->type() == QEvent::WindowActivate || + event->type() == QEvent::WindowDeactivate) + emit activeChanged(); + return QDeclarativeItem::sceneEvent(event); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativefocuspanel_p.h b/src/qtquick1/graphicsitems/qdeclarativefocuspanel_p.h new file mode 100644 index 0000000000..38eadfdba3 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativefocuspanel_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFOCUSPANEL_H +#define QDECLARATIVEFOCUSPANEL_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarative1FocusPanel : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) +public: + QDeclarative1FocusPanel(QDeclarativeItem *parent=0); + virtual ~QDeclarative1FocusPanel(); + +Q_SIGNALS: + void activeChanged(); + +protected: + bool sceneEvent(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarative1FocusPanel) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1FocusPanel) + +QT_END_HEADER + +#endif // QDECLARATIVEFOCUSPANEL_H diff --git a/src/qtquick1/graphicsitems/qdeclarativefocusscope.cpp b/src/qtquick1/graphicsitems/qdeclarativefocusscope.cpp new file mode 100644 index 0000000000..5d942de9bc --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativefocusscope.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativefocusscope_p.h" + +#include "QtQuick1/private/qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass FocusScope QDeclarative1FocusScope + \since 4.7 + \ingroup qml-basic-interaction-elements + + \brief The FocusScope object explicitly creates a focus scope. + \inherits Item + + Focus scopes assist in keyboard focus handling when building reusable QML + components. All the details are covered in the + \l {qmlfocus}{keyboard focus documentation}. + + \sa {declarative/keyinteraction/focus}{Keyboard focus example} +*/ + +QDeclarative1FocusScope::QDeclarative1FocusScope(QDeclarativeItem *parent) : + QDeclarativeItem(parent) +{ + Q_D(QDeclarativeItem); + d->flags |= QGraphicsItem::ItemIsFocusScope; +} + +QDeclarative1FocusScope::~QDeclarative1FocusScope() +{ +} + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativefocusscope_p.h b/src/qtquick1/graphicsitems/qdeclarativefocusscope_p.h new file mode 100644 index 0000000000..ee31d94347 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativefocusscope_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFOCUSSCOPE_H +#define QDECLARATIVEFOCUSSCOPE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +//### set component root as focusscope +class Q_AUTOTEST_EXPORT QDeclarative1FocusScope : public QDeclarativeItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +public: + QDeclarative1FocusScope(QDeclarativeItem *parent=0); + virtual ~QDeclarative1FocusScope(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1FocusScope) + +QT_END_HEADER + +#endif // QDECLARATIVEFOCUSSCOPE_H diff --git a/src/qtquick1/graphicsitems/qdeclarativegraphicswidget.cpp b/src/qtquick1/graphicsitems/qdeclarativegraphicswidget.cpp new file mode 100644 index 0000000000..8ccf23221a --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativegraphicswidget.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativegraphicswidget_p.h" +#include "QtQuick1/private/qdeclarativeanchors_p.h" +#include "QtQuick1/private/qdeclarativeitem_p.h" +#include "QtQuick1/private/qdeclarativeanchors_p_p.h" + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1GraphicsWidgetPrivate : public QObjectPrivate { + Q_DECLARE_PUBLIC(QDeclarative1GraphicsWidget) +public : + QDeclarative1GraphicsWidgetPrivate() : + _anchors(0), _anchorLines(0) + {} + QDeclarativeItemPrivate::AnchorLines *anchorLines() const; + QDeclarative1Anchors *_anchors; + mutable QDeclarativeItemPrivate::AnchorLines *_anchorLines; +}; + +QDeclarative1GraphicsWidget::QDeclarative1GraphicsWidget(QObject *parent) : + QObject(*new QDeclarative1GraphicsWidgetPrivate, parent) +{ +} +QDeclarative1GraphicsWidget::~QDeclarative1GraphicsWidget() +{ + Q_D(QDeclarative1GraphicsWidget); + delete d->_anchorLines; d->_anchorLines = 0; + delete d->_anchors; d->_anchors = 0; +} + +QDeclarative1Anchors *QDeclarative1GraphicsWidget::anchors() +{ + Q_D(QDeclarative1GraphicsWidget); + if (!d->_anchors) + d->_anchors = new QDeclarative1Anchors(static_cast<QGraphicsObject *>(parent())); + return d->_anchors; +} + +QDeclarativeItemPrivate::AnchorLines *QDeclarative1GraphicsWidgetPrivate::anchorLines() const +{ + Q_Q(const QDeclarative1GraphicsWidget); + if (!_anchorLines) + _anchorLines = new QDeclarativeItemPrivate::AnchorLines(static_cast<QGraphicsObject *>(q->parent())); + return _anchorLines; +} + +QDeclarative1AnchorLine QDeclarative1GraphicsWidget::left() const +{ + Q_D(const QDeclarative1GraphicsWidget); + return d->anchorLines()->left; +} + +QDeclarative1AnchorLine QDeclarative1GraphicsWidget::right() const +{ + Q_D(const QDeclarative1GraphicsWidget); + return d->anchorLines()->right; +} + +QDeclarative1AnchorLine QDeclarative1GraphicsWidget::horizontalCenter() const +{ + Q_D(const QDeclarative1GraphicsWidget); + return d->anchorLines()->hCenter; +} + +QDeclarative1AnchorLine QDeclarative1GraphicsWidget::top() const +{ + Q_D(const QDeclarative1GraphicsWidget); + return d->anchorLines()->top; +} + +QDeclarative1AnchorLine QDeclarative1GraphicsWidget::bottom() const +{ + Q_D(const QDeclarative1GraphicsWidget); + return d->anchorLines()->bottom; +} + +QDeclarative1AnchorLine QDeclarative1GraphicsWidget::verticalCenter() const +{ + Q_D(const QDeclarative1GraphicsWidget); + return d->anchorLines()->vCenter; +} + + + +QT_END_NAMESPACE + +#include <moc_qdeclarativegraphicswidget_p.cpp> diff --git a/src/qtquick1/graphicsitems/qdeclarativegraphicswidget_p.h b/src/qtquick1/graphicsitems/qdeclarativegraphicswidget_p.h new file mode 100644 index 0000000000..2e433d78aa --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativegraphicswidget_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGRAPHICSWIDGET_P_H +#define QDECLARATIVEGRAPHICSWIDGET_P_H + +#include <QObject> +#include <QtDeclarative/qdeclarativecomponent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QGraphicsObject; +class QDeclarative1AnchorLine; +class QDeclarative1Anchors; +class QDeclarative1GraphicsWidgetPrivate; + +// ### TODO can the extension object be the anchor directly? We save one allocation -> awesome. +class QDeclarative1GraphicsWidget : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarative1Anchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL) + Q_PROPERTY(QDeclarative1AnchorLine left READ left CONSTANT FINAL) + Q_PROPERTY(QDeclarative1AnchorLine right READ right CONSTANT FINAL) + Q_PROPERTY(QDeclarative1AnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL) + Q_PROPERTY(QDeclarative1AnchorLine top READ top CONSTANT FINAL) + Q_PROPERTY(QDeclarative1AnchorLine bottom READ bottom CONSTANT FINAL) + Q_PROPERTY(QDeclarative1AnchorLine verticalCenter READ verticalCenter CONSTANT FINAL) + // ### TODO : QGraphicsWidget don't have a baseline concept yet. + //Q_PROPERTY(QDeclarative1AnchorLine baseline READ baseline CONSTANT FINAL) +public: + QDeclarative1GraphicsWidget(QObject *parent = 0); + ~QDeclarative1GraphicsWidget(); + QDeclarative1Anchors *anchors(); + QDeclarative1AnchorLine left() const; + QDeclarative1AnchorLine right() const; + QDeclarative1AnchorLine horizontalCenter() const; + QDeclarative1AnchorLine top() const; + QDeclarative1AnchorLine bottom() const; + QDeclarative1AnchorLine verticalCenter() const; + Q_DISABLE_COPY(QDeclarative1GraphicsWidget) + Q_DECLARE_PRIVATE(QDeclarative1GraphicsWidget) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEGRAPHICSWIDGET_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativegridview.cpp b/src/qtquick1/graphicsitems/qdeclarativegridview.cpp new file mode 100644 index 0000000000..228cd2aba1 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativegridview.cpp @@ -0,0 +1,3133 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativegridview_p.h" + +#include "QtQuick1/private/qdeclarativevisualitemmodel_p.h" +#include "QtQuick1/private/qdeclarativeflickable_p_p.h" + +#include "QtQuick1/private/qdeclarativesmoothedanimation_p_p.h" +#include <QtDeclarative/private/qdeclarativeguard_p.h> + +#include <QKeyEvent> + +#include <qmath.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + + + + +//---------------------------------------------------------------------------- + +class FxGridItem1 +{ +public: + FxGridItem1(QDeclarativeItem *i, QDeclarative1GridView *v) : item(i), view(v) { + attached = static_cast<QDeclarative1GridViewAttached*>(qmlAttachedPropertiesObject<QDeclarative1GridView>(item)); + if (attached) + attached->setView(view); + } + ~FxGridItem1() {} + + qreal rowPos() const { + qreal rowPos = 0; + if (view->flow() == QDeclarative1GridView::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() == QDeclarative1GridView::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() == QDeclarative1GridView::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() == QDeclarative1GridView::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() == QDeclarative1GridView::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()); + } + + QDeclarativeItem *item; + QDeclarative1GridView *view; + QDeclarative1GridViewAttached *attached; + int index; +}; + +//---------------------------------------------------------------------------- + +class QDeclarative1GridViewPrivate : public QDeclarative1FlickablePrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1GridView) + +public: + QDeclarative1GridViewPrivate() + : currentItem(0), layoutDirection(Qt::LeftToRight), flow(QDeclarative1GridView::LeftToRight) + , visibleIndex(0) , currentIndex(-1) + , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeStartValid(false), highlightRangeEndValid(false) + , highlightRange(QDeclarative1GridView::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(QDeclarative1GridView::NoSnap) + , ownModel(false), wrap(false), autoHighlight(true) + , fixCurrentVisibility(false), lazyRelease(false), layoutScheduled(false) + , deferredRelease(false), haveHighlightRange(false), currentIndexCleared(false) {} + + void init(); + void clear(); + FxGridItem1 *createItem(int modelIndex); + void releaseItem(FxGridItem1 *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(); + + FxGridItem1 *visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxGridItem1 *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; + } + + bool isRightToLeftTopToBottom() const { + Q_Q(const QDeclarative1GridView); + return flow == QDeclarative1GridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; + } + + void regenerate() { + Q_Q(QDeclarative1GridView); + if (q->isComponentComplete()) { + clear(); + updateGrid(); + setPosition(0); + q->refill(); + updateCurrent(currentIndex); + } + } + + void mirrorChange() { + Q_Q(QDeclarative1GridView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); + } + + qreal position() const { + Q_Q(const QDeclarative1GridView); + return flow == QDeclarative1GridView::LeftToRight ? q->contentY() : q->contentX(); + } + void setPosition(qreal pos) { + Q_Q(QDeclarative1GridView); + if (flow == QDeclarative1GridView::LeftToRight) { + q->QDeclarative1Flickable::setContentY(pos); + q->QDeclarative1Flickable::setContentX(0); + } else { + if (q->effectiveLayoutDirection() == Qt::LeftToRight) + q->QDeclarative1Flickable::setContentX(pos); + else + q->QDeclarative1Flickable::setContentX(-pos-size()); + q->QDeclarative1Flickable::setContentY(0); + } + } + int size() const { + Q_Q(const QDeclarative1GridView); + return flow == QDeclarative1GridView::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 == QDeclarative1GridView::LeftToRight ? cellHeight : cellWidth; + } + int colSize() const { + return flow == QDeclarative1GridView::LeftToRight ? cellWidth : cellHeight; + } + + qreal colPosAt(int modelIndex) const { + if (FxGridItem1 *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 (FxGridItem1 *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; + } + + FxGridItem1 *firstVisibleItem() const { + const qreal pos = isRightToLeftTopToBottom() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem1 *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) { + FxGridItem1 *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) { + FxGridItem1 *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 QDeclarative1GridView); + 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 == QDeclarative1GridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + minExtent = flow == QDeclarative1GridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + } + if (snapPos > maxExtent) + snapPos = maxExtent; + if (snapPos < minExtent) + snapPos = minExtent; + } + return snapPos; + } + + FxGridItem1 *snapItemAt(qreal pos) { + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem1 *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) { + FxGridItem1 *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 == QDeclarative1GridView::LeftToRight + ? header->item->height() + : header->item->width(); + } + + + virtual void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + Q_Q(const QDeclarative1GridView); + QDeclarative1FlickablePrivate::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, + QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity); + + // for debugging only + void checkVisible() const { + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem1 *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<QDeclarative1VisualModel> model; + QVariant modelVariant; + QList<FxGridItem1*> visibleItems; + QHash<QDeclarativeItem*,int> unrequestedItems; + FxGridItem1 *currentItem; + Qt::LayoutDirection layoutDirection; + QDeclarative1GridView::Flow flow; + int visibleIndex; + int currentIndex; + int cellWidth; + int cellHeight; + int columns; + int requestedIndex; + int itemCount; + qreal highlightRangeStart; + qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; + QDeclarative1GridView::HighlightRangeMode highlightRange; + QDeclarativeComponent *highlightComponent; + FxGridItem1 *highlight; + FxGridItem1 *trackedItem; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + int buffer; + QSmoothedAnimation_1 *highlightXAnimator; + QSmoothedAnimation_1 *highlightYAnimator; + int highlightMoveDuration; + QDeclarativeComponent *footerComponent; + FxGridItem1 *footer; + QDeclarativeComponent *headerComponent; + FxGridItem1 *header; + enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 }; + int bufferMode; + QDeclarative1GridView::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; +}; + +void QDeclarative1GridViewPrivate::init() +{ + Q_Q(QDeclarative1GridView); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlag(QGraphicsItem::ItemIsFocusScope); + q->setFlickableDirection(QDeclarative1Flickable::VerticalFlick); + addItemChangeListener(this, Geometry); +} + +void QDeclarative1GridViewPrivate::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; +} + +FxGridItem1 *QDeclarative1GridViewPrivate::createItem(int modelIndex) +{ + Q_Q(QDeclarative1GridView); + // create object + requestedIndex = modelIndex; + FxGridItem1 *listItem = 0; + if (QDeclarativeItem *item = model->item(modelIndex, false)) { + listItem = new FxGridItem1(item, q); + listItem->index = modelIndex; + if (model->completePending()) { + // complete + listItem->item->setZValue(1); + listItem->item->setParentItem(q->contentItem()); + model->completeItem(); + } else { + listItem->item->setParentItem(q->contentItem()); + } + unrequestedItems.remove(listItem->item); + } + requestedIndex = -1; + return listItem; +} + + +void QDeclarative1GridViewPrivate::releaseItem(FxGridItem1 *item) +{ + Q_Q(QDeclarative1GridView); + 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 QDeclarative1GridViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QDeclarative1GridView); + 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(); + + FxGridItem1 *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 == QDeclarative1GridView::LeftToRight) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(endPosition() - startPosition()); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } + lazyRelease = false; +} + +void QDeclarative1GridViewPrivate::updateGrid() +{ + Q_Q(QDeclarative1GridView); + + columns = (int)qMax((flow == QDeclarative1GridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); + if (isValid()) { + if (flow == QDeclarative1GridView::LeftToRight) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(lastPosition() - originPosition()); + } +} + +void QDeclarative1GridViewPrivate::scheduleLayout() +{ + Q_Q(QDeclarative1GridView); + if (!layoutScheduled) { + layoutScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); + } +} + +void QDeclarative1GridViewPrivate::layout() +{ + Q_Q(QDeclarative1GridView); + 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) { + FxGridItem1 *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 == QDeclarative1GridView::LeftToRight) { + q->setContentHeight(endPosition() - startPosition()); + fixupY(); + } else { + q->setContentWidth(endPosition() - startPosition()); + fixupX(); + } + updateUnrequestedPositions(); +} + +void QDeclarative1GridViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QDeclarative1GridView); + QHash<QDeclarativeItem*,int>::iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QDeclarative1GridViewPrivate::updateUnrequestedPositions() +{ + QHash<QDeclarativeItem*,int>::const_iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { + QDeclarativeItem *item = it.key(); + if (flow == QDeclarative1GridView::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 QDeclarative1GridViewPrivate::updateTrackedItem() +{ + Q_Q(QDeclarative1GridView); + FxGridItem1 *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 QDeclarative1GridViewPrivate::createHighlight() +{ + Q_Q(QDeclarative1GridView); + bool changed = false; + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + if (highlight->item->scene()) + highlight->item->scene()->removeItem(highlight->item); + highlight->item->deleteLater(); + delete highlight; + highlight = 0; + delete highlightXAnimator; + delete highlightYAnimator; + highlightXAnimator = 0; + highlightYAnimator = 0; + changed = true; + } + + if (currentItem) { + QDeclarativeItem *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<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QDeclarativeItem; + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + highlight = new FxGridItem1(item, q); + if (currentItem && autoHighlight) + highlight->setPosition(currentItem->colPos(), currentItem->rowPos()); + highlightXAnimator = new QSmoothedAnimation_1(q); + highlightXAnimator->target = QDeclarativeProperty(highlight->item, QLatin1String("x")); + highlightXAnimator->userDuration = highlightMoveDuration; + highlightYAnimator = new QSmoothedAnimation_1(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 QDeclarative1GridViewPrivate::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 QDeclarative1GridViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QDeclarative1GridView); + 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; + } + + FxGridItem1 *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 QDeclarative1GridViewPrivate::updateFooter() +{ + Q_Q(QDeclarative1GridView); + if (!footer && footerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = footerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + footer = new FxGridItem1(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 == QDeclarative1GridView::LeftToRight ? header->item->height() : header->item->width(); + } + footer->setPosition(colOffset, endPos); + } + } +} + +void QDeclarative1GridViewPrivate::updateHeader() +{ + Q_Q(QDeclarative1GridView); + if (!header && headerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = headerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + header = new FxGridItem1(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 QDeclarative1GridViewPrivate::fixupPosition() +{ + moveReason = Other; + if (flow == QDeclarative1GridView::LeftToRight) + fixupY(); + else + fixupX(); +} + +void QDeclarative1GridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((flow == QDeclarative1GridView::TopToBottom && &data == &vData) + || (flow == QDeclarative1GridView::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 != QDeclarative1GridView::NoSnap) { + qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position(); + FxGridItem1 *topItem = snapItemAt(tempPosition+highlightStart); + FxGridItem1 *bottomItem = snapItemAt(tempPosition+highlightEnd); + qreal pos; + if (topItem && bottomItem && haveHighlightRange && highlightRange == QDeclarative1GridView::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 { + QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent); + return; + } + if (currentItem && haveHighlightRange && highlightRange == QDeclarative1GridView::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 == QDeclarative1GridView::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 { + QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent); + } + data.inOvershoot = false; + fixupMode = Normal; +} + +void QDeclarative1GridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarative1GridView); + data.fixingUp = false; + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QDeclarative1GridView::StrictlyEnforceRange) + && snapMode == QDeclarative1GridView::NoSnap) { + QDeclarative1FlickablePrivate::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 == QDeclarative1GridView::SnapOneRow) { + if (FxGridItem1 *item = firstVisibleItem()) { + maxDistance = qAbs(item->rowPos() + dataValue); + } + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QDeclarative1GridView::NoSnap && highlightRange != QDeclarative1GridView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QDeclarative1GridView::SnapOneRow) { + qreal pos = snapPosAt(-dataValue) + (isRightToLeftTopToBottom() ? 0 : rowSize()); + maxDistance = qAbs(pos + dataValue); + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QDeclarative1GridView::NoSnap && highlightRange != QDeclarative1GridView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + + bool overShoot = boundsBehavior == QDeclarative1Flickable::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 == QDeclarative1GridView::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(QDeclarative1TimeLineCallback(&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); + } +} + + +//---------------------------------------------------------------------------- + +/*! + \qmlclass GridView QDeclarative1GridView + \since 4.7 + \ingroup qml-view-elements + + \inherits Flickable + \brief The GridView item provides a grid view of items provided by a model. + + A GridView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + A GridView has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. Items in a + GridView are laid out horizontally or vertically. Grid views are inherently flickable + as GridView inherits from \l Flickable. + + \section1 Example Usage + + The following example shows the definition of a simple list model defined + in a file called \c ContactModel.qml: + + \snippet doc/src/snippets/declarative/gridview/ContactModel.qml 0 + + \div {class="float-right"} + \inlineimage gridview-simple.png + \enddiv + + This model can be referenced as \c ContactModel in other QML files. See \l{QML Modules} + for more information about creating reusable components like this. + + Another component can display this model data in a GridView, as in the following + example, which creates a \c ContactModel component for its model, and a \l Column element + (containing \l Image and \l Text elements) for its delegate. + + \clearfloat + \snippet doc/src/snippets/declarative/gridview/gridview.qml import + \codeline + \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs simple + + \div {class="float-right"} + \inlineimage gridview-highlight.png + \enddiv + + The view will create a new delegate for each item in the model. Note that the delegate + is able to access the model's \c name and \c portrait data directly. + + An improved grid view is shown below. The delegate is visually improved and is moved + into a separate \c contactDelegate component. + + \clearfloat + \snippet doc/src/snippets/declarative/gridview/gridview.qml classdocs advanced + + The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, + and \c focus is set to \c true to enable keyboard navigation for the grid view. + The grid view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + GridView attaches a number of properties to the root item of the delegate, for example + \c {GridView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c GridView.isCurrentItem, while the child + \c contactInfo object must refer to this property as \c wrapper.GridView.isCurrentItem. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem + + \note Views do not set the \l{Item::}{clip} property automatically. + If the view is not clipped by another item or the screen, it will be necessary + to set this property to true in order to clip the items that are partially or + fully outside the view. + + \sa {declarative/modelviews/gridview}{GridView example} +*/ +QDeclarative1GridView::QDeclarative1GridView(QDeclarativeItem *parent) + : QDeclarative1Flickable(*(new QDeclarative1GridViewPrivate), parent) +{ + Q_D(QDeclarative1GridView); + d->init(); +} + +QDeclarative1GridView::~QDeclarative1GridView() +{ + Q_D(QDeclarative1GridView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + +/*! + \qmlattachedproperty bool GridView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty GridView GridView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml isCurrentItem +*/ + +/*! + \qmlattachedproperty bool GridView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. + + The example below ensures that the animation completes before + the item is removed from the grid. + + \snippet doc/src/snippets/declarative/gridview/gridview.qml delayRemove +*/ + +/*! + \qmlattachedsignal GridView::onAdd() + This attached handler is called immediately after an item is added to the view. +*/ + +/*! + \qmlattachedsignal GridView::onRemove() + This attached handler is called immediately before an item is removed from the view. +*/ + + +/*! + \qmlproperty model GridView::model + This property holds the model providing data for the grid. + + The model provides the set of data that is used to create the items + in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel + or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is + used, it must be a subclass of \l QAbstractItemModel or a simple list. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarative1GridView::model() const +{ + Q_D(const QDeclarative1GridView); + return d->modelVariant; +} + +// For internal use +int QDeclarative1GridView::modelCount() const +{ + Q_D(const QDeclarative1GridView); + return d->model->count(); +} + +void QDeclarative1GridView::setModel(const QVariant &model) +{ + Q_D(QDeclarative1GridView); + 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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + } + d->clear(); + d->modelVariant = model; + QObject *object = qvariant_cast<QObject*>(model); + QDeclarative1VisualModel *vim = 0; + if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this), this); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + dataModel->setModel(model); + } + if (d->model) { + d->bufferMode = QDeclarative1GridViewPrivate::BufferBefore | QDeclarative1GridViewPrivate::BufferAfter; + if (isComponentComplete()) { + refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QDeclarative1GridViewPrivate::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 = QDeclarative1GridViewPrivate::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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + emit countChanged(); + } + emit modelChanged(); +} + +/*! + \qmlproperty Component GridView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + The GridView will layout the items based on the size of the root item + in the delegate. + + \note Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. +*/ +QDeclarativeComponent *QDeclarative1GridView::delegate() const +{ + Q_D(const QDeclarative1GridView); + if (d->model) { + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarative1GridView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarative1GridView); + if (delegate == this->delegate()) + return; + + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(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 = QDeclarative1GridViewPrivate::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 = QDeclarative1GridViewPrivate::Other; + } + if (oldCount != dataModel->count()) + emit countChanged(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int GridView::currentIndex + \qmlproperty Item GridView::currentItem + + The \c currentIndex property holds the index of the current item, and + \c currentItem holds the current item. Setting the currentIndex to -1 + will clear the highlight and set currentItem to null. + + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the GridView so that the current + item becomes visible. + + Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ +int QDeclarative1GridView::currentIndex() const +{ + Q_D(const QDeclarative1GridView); + return d->currentIndex; +} + +void QDeclarative1GridView::setCurrentIndex(int index) +{ + Q_D(QDeclarative1GridView); + if (d->requestedIndex >= 0) // currently creating item + return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + d->moveReason = QDeclarative1GridViewPrivate::SetIndex; + d->updateCurrent(index); + } else { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + +QDeclarativeItem *QDeclarative1GridView::currentItem() +{ + Q_D(QDeclarative1GridView); + if (!d->currentItem) + return 0; + return d->currentItem->item; +} + +/*! + \qmlproperty Item GridView::highlightItem + + This holds the highlight item created from the \l highlight component. + + The highlightItem is managed by the view unless + \l highlightFollowsCurrentItem is set to false. + + \sa highlight, highlightFollowsCurrentItem +*/ +QDeclarativeItem *QDeclarative1GridView::highlightItem() +{ + Q_D(QDeclarative1GridView); + if (!d->highlight) + return 0; + return d->highlight->item; +} + +/*! + \qmlproperty int GridView::count + This property holds the number of items in the view. +*/ +int QDeclarative1GridView::count() const +{ + Q_D(const QDeclarative1GridView); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlproperty Component GridView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component is created for each view. + The geometry of the resulting component instance will be managed by the view + so as to stay with the current item, unless the highlightFollowsCurrentItem property is false. + + \sa highlightItem, highlightFollowsCurrentItem +*/ +QDeclarativeComponent *QDeclarative1GridView::highlight() const +{ + Q_D(const QDeclarative1GridView); + return d->highlightComponent; +} + +void QDeclarative1GridView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QDeclarative1GridView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->updateCurrent(d->currentIndex); + emit highlightChanged(); + } +} + +/*! + \qmlproperty bool GridView::highlightFollowsCurrentItem + This property sets whether the highlight is managed by the view. + + If this property is true (the default value), the highlight is moved smoothly + to follow the current item. Otherwise, the + highlight is not moved by the view, and any movement must be implemented + by the highlight. + + Here is a highlight with its motion defined by a \l {SpringAnimation} item: + + \snippet doc/src/snippets/declarative/gridview/gridview.qml highlightFollowsCurrentItem +*/ +bool QDeclarative1GridView::highlightFollowsCurrentItem() const +{ + Q_D(const QDeclarative1GridView); + return d->autoHighlight; +} + +void QDeclarative1GridView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QDeclarative1GridView); + if (d->autoHighlight != autoHighlight) { + d->autoHighlight = autoHighlight; + if (autoHighlight) { + d->updateHighlight(); + } else if (d->highlightXAnimator) { + d->highlightXAnimator->stop(); + d->highlightYAnimator->stop(); + } + } +} + +/*! + \qmlproperty int GridView::highlightMoveDuration + This property holds the move animation duration of the highlight delegate. + + highlightFollowsCurrentItem must be true for this property + to have effect. + + The default value for the duration is 150ms. + + \sa highlightFollowsCurrentItem +*/ +int QDeclarative1GridView::highlightMoveDuration() const +{ + Q_D(const QDeclarative1GridView); + return d->highlightMoveDuration; +} + +void QDeclarative1GridView::setHighlightMoveDuration(int duration) +{ + Q_D(QDeclarative1GridView); + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + if (d->highlightYAnimator) { + d->highlightXAnimator->userDuration = d->highlightMoveDuration; + d->highlightYAnimator->userDuration = d->highlightMoveDuration; + } + emit highlightMoveDurationChanged(); + } +} + + +/*! + \qmlproperty real GridView::preferredHighlightBegin + \qmlproperty real GridView::preferredHighlightEnd + \qmlproperty enumeration GridView::highlightRangeMode + + These properties define the preferred range of the highlight (for the current item) + within the view. The \c preferredHighlightBegin value must be less than the + \c preferredHighlightEnd value. + + These properties affect the position of the current item when the view is scrolled. + For example, if the currently selected item should stay in the middle of the + view when it is scrolled, set the \c preferredHighlightBegin and + \c preferredHighlightEnd values to the top and bottom coordinates of where the middle + item would be. If the \c currentItem is changed programmatically, the view will + automatically scroll so that the current item is in the middle of the view. + Furthermore, the behavior of the current item index will occur whether or not a + highlight exists. + + Valid values for \c highlightRangeMode are: + + \list + \o GridView.ApplyRange - the view attempts to maintain the highlight within the range. + However, the highlight can move outside of the range at the ends of the view or due + to mouse interaction. + \o GridView.StrictlyEnforceRange - the highlight never moves outside of the range. + The current item changes if a keyboard or mouse action would cause the highlight to move + outside of the range. + \o GridView.NoHighlightRange - this is the default value. + \endlist +*/ +qreal QDeclarative1GridView::preferredHighlightBegin() const +{ + Q_D(const QDeclarative1GridView); + return d->highlightRangeStart; +} + +void QDeclarative1GridView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QDeclarative1GridView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QDeclarative1GridView::resetPreferredHighlightBegin() +{ + Q_D(QDeclarative1GridView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QDeclarative1GridView::preferredHighlightEnd() const +{ + Q_D(const QDeclarative1GridView); + return d->highlightRangeEnd; +} + +void QDeclarative1GridView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QDeclarative1GridView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QDeclarative1GridView::resetPreferredHighlightEnd() +{ + Q_D(QDeclarative1GridView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +QDeclarative1GridView::HighlightRangeMode QDeclarative1GridView::highlightRangeMode() const +{ + Q_D(const QDeclarative1GridView); + return d->highlightRange; +} + +void QDeclarative1GridView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QDeclarative1GridView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +/*! + \qmlproperty enumeration GridView::layoutDirection + This property holds the layout direction of the grid. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items will be laid out starting in the top, left corner. The flow is + dependent on the \l GridView::flow property. + \o Qt.RightToLeft - Items will be laid out starting in the top, right corner. The flow is dependent + on the \l GridView::flow property. + \endlist + + \bold Note: If GridView::flow is set to GridView.LeftToRight, this is not to be confused if + GridView::layoutDirection is set to Qt.RightToLeft. The GridView.LeftToRight flow value simply + indicates that the flow is horizontal. +*/ + +Qt::LayoutDirection QDeclarative1GridView::layoutDirection() const +{ + Q_D(const QDeclarative1GridView); + return d->layoutDirection; +} + +void QDeclarative1GridView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarative1GridView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration GridView::effectiveLayoutDirection + This property holds the effective layout direction of the grid. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid will be mirrored. However, the + property \l {GridView::layoutDirection}{layoutDirection} will remain unchanged. + + \sa GridView::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarative1GridView::effectiveLayoutDirection() const +{ + Q_D(const QDeclarative1GridView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + +/*! + \qmlproperty enumeration GridView::flow + This property holds the flow of the grid. + + Possible values: + + \list + \o GridView.LeftToRight (default) - Items are laid out from left to right, and the view scrolls vertically + \o GridView.TopToBottom - Items are laid out from top to bottom, and the view scrolls horizontally + \endlist +*/ +QDeclarative1GridView::Flow QDeclarative1GridView::flow() const +{ + Q_D(const QDeclarative1GridView); + return d->flow; +} + +void QDeclarative1GridView::setFlow(Flow flow) +{ + Q_D(QDeclarative1GridView); + if (d->flow != flow) { + d->flow = flow; + if (d->flow == LeftToRight) { + setContentWidth(-1); + setFlickableDirection(QDeclarative1Flickable::VerticalFlick); + } else { + setContentHeight(-1); + setFlickableDirection(QDeclarative1Flickable::HorizontalFlick); + } + setContentX(0); + setContentY(0); + d->regenerate(); + emit flowChanged(); + } +} + +/*! + \qmlproperty bool GridView::keyNavigationWraps + This property holds whether the grid wraps key navigation + + If this is true, key navigation that would move the current item selection + past one end of the view instead wraps around and moves the selection to + the other end of the view. + + By default, key navigation is not wrapped. +*/ +bool QDeclarative1GridView::isWrapEnabled() const +{ + Q_D(const QDeclarative1GridView); + return d->wrap; +} + +void QDeclarative1GridView::setWrapEnabled(bool wrap) +{ + Q_D(QDeclarative1GridView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +/*! + \qmlproperty int GridView::cacheBuffer + This property determines whether delegates are retained outside the + visible area of the view. + + If non-zero the view will keep as many delegates + instantiated as will fit within the buffer specified. For example, + if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is + set to 40, then up to 2 delegates above and 2 delegates below the visible + area may be retained. + + Note that cacheBuffer is not a pixel buffer - it only maintains additional + instantiated delegates. + + Setting this value can make scrolling the list smoother at the expense + of additional memory usage. It is not a substitute for creating efficient + delegates; the fewer elements in a delegate, the faster a view may be + scrolled. +*/ +int QDeclarative1GridView::cacheBuffer() const +{ + Q_D(const QDeclarative1GridView); + return d->buffer; +} + +void QDeclarative1GridView::setCacheBuffer(int buffer) +{ + Q_D(QDeclarative1GridView); + if (d->buffer != buffer) { + d->buffer = buffer; + if (isComponentComplete()) + refill(); + emit cacheBufferChanged(); + } +} + +/*! + \qmlproperty int GridView::cellWidth + \qmlproperty int GridView::cellHeight + + These properties holds the width and height of each cell in the grid. + + The default cell size is 100x100. +*/ +int QDeclarative1GridView::cellWidth() const +{ + Q_D(const QDeclarative1GridView); + return d->cellWidth; +} + +void QDeclarative1GridView::setCellWidth(int cellWidth) +{ + Q_D(QDeclarative1GridView); + if (cellWidth != d->cellWidth && cellWidth > 0) { + d->cellWidth = qMax(1, cellWidth); + d->updateGrid(); + emit cellWidthChanged(); + d->layout(); + } +} + +int QDeclarative1GridView::cellHeight() const +{ + Q_D(const QDeclarative1GridView); + return d->cellHeight; +} + +void QDeclarative1GridView::setCellHeight(int cellHeight) +{ + Q_D(QDeclarative1GridView); + if (cellHeight != d->cellHeight && cellHeight > 0) { + d->cellHeight = qMax(1, cellHeight); + d->updateGrid(); + emit cellHeightChanged(); + d->layout(); + } +} +/*! + \qmlproperty enumeration GridView::snapMode + + This property determines how the view scrolling will settle following a drag or flick. + The possible values are: + + \list + \o GridView.NoSnap (default) - the view stops anywhere within the visible area. + \o GridView.SnapToRow - the view settles with a row (or column for \c GridView.TopToBottom flow) + aligned with the start of the view. + \o GridView.SnapOneRow - the view will settle no more than one row (or column for \c GridView.TopToBottom flow) + away from the first visible row at the time the mouse button is released. + This mode is particularly useful for moving one page at a time. + \endlist + +*/ +QDeclarative1GridView::SnapMode QDeclarative1GridView::snapMode() const +{ + Q_D(const QDeclarative1GridView); + return d->snapMode; +} + +void QDeclarative1GridView::setSnapMode(SnapMode mode) +{ + Q_D(QDeclarative1GridView); + if (d->snapMode != mode) { + d->snapMode = mode; + emit snapModeChanged(); + } +} + +/*! + \qmlproperty Component GridView::footer + This property holds the component to use as the footer. + + An instance of the footer component is created for each view. The + footer is positioned at the end of the view, after any items. + + \sa header +*/ +QDeclarativeComponent *QDeclarative1GridView::footer() const +{ + Q_D(const QDeclarative1GridView); + return d->footerComponent; +} + +void QDeclarative1GridView::setFooter(QDeclarativeComponent *footer) +{ + Q_D(QDeclarative1GridView); + if (d->footerComponent != footer) { + if (d->footer) { + if (scene()) + scene()->removeItem(d->footer->item); + d->footer->item->deleteLater(); + delete d->footer; + d->footer = 0; + } + d->footerComponent = footer; + if (isComponentComplete()) { + d->updateFooter(); + d->updateGrid(); + d->fixupPosition(); + } + emit footerChanged(); + } +} + +/*! + \qmlproperty Component GridView::header + This property holds the component to use as the header. + + An instance of the header component is created for each view. The + header is positioned at the beginning of the view, before any items. + + \sa footer +*/ +QDeclarativeComponent *QDeclarative1GridView::header() const +{ + Q_D(const QDeclarative1GridView); + return d->headerComponent; +} + +void QDeclarative1GridView::setHeader(QDeclarativeComponent *header) +{ + Q_D(QDeclarative1GridView); + if (d->headerComponent != header) { + if (d->header) { + if (scene()) + scene()->removeItem(d->header->item); + 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 QDeclarative1GridView::setContentX(qreal pos) +{ + Q_D(QDeclarative1GridView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarative1GridViewPrivate::Other; + QDeclarative1Flickable::setContentX(pos); +} + +void QDeclarative1GridView::setContentY(qreal pos) +{ + Q_D(QDeclarative1GridView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarative1GridViewPrivate::Other; + QDeclarative1Flickable::setContentY(pos); +} + +bool QDeclarative1GridView::event(QEvent *event) +{ + Q_D(QDeclarative1GridView); + if (event->type() == QEvent::User) { + d->layout(); + return true; + } + + return QDeclarative1Flickable::event(event); +} + +void QDeclarative1GridView::viewportMoved() +{ + Q_D(QDeclarative1GridView); + QDeclarative1Flickable::viewportMoved(); + if (!d->itemCount) + return; + d->lazyRelease = true; + if (d->flickingHorizontally || d->flickingVertically) { + if (yflick()) { + if (d->vData.velocity > 0) + d->bufferMode = QDeclarative1GridViewPrivate::BufferBefore; + else if (d->vData.velocity < 0) + d->bufferMode = QDeclarative1GridViewPrivate::BufferAfter; + } + + if (xflick()) { + if (d->hData.velocity > 0) + d->bufferMode = QDeclarative1GridViewPrivate::BufferBefore; + else if (d->hData.velocity < 0) + d->bufferMode = QDeclarative1GridViewPrivate::BufferAfter; + } + } + refill(); + if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically) + d->moveReason = QDeclarative1GridViewPrivate::Mouse; + if (d->moveReason != QDeclarative1GridViewPrivate::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 QDeclarative1GridView::minYExtent() const +{ + Q_D(const QDeclarative1GridView); + if (d->flow == QDeclarative1GridView::TopToBottom) + return QDeclarative1Flickable::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 QDeclarative1GridView::maxYExtent() const +{ + Q_D(const QDeclarative1GridView); + if (d->flow == QDeclarative1GridView::TopToBottom) + return QDeclarative1Flickable::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 QDeclarative1GridView::minXExtent() const +{ + Q_D(const QDeclarative1GridView); + if (d->flow == QDeclarative1GridView::LeftToRight) + return QDeclarative1Flickable::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 QDeclarative1GridView::maxXExtent() const +{ + Q_D(const QDeclarative1GridView); + if (d->flow == QDeclarative1GridView::LeftToRight) + return QDeclarative1Flickable::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 QDeclarative1GridView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarative1GridView); + keyPressPreHandler(event); + if (event->isAccepted()) + return; + if (d->model && d->model->count() && d->interactive) { + d->moveReason = QDeclarative1GridViewPrivate::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 = QDeclarative1GridViewPrivate::Other; + event->ignore(); + QDeclarative1Flickable::keyPressEvent(event); +} + +/*! + \qmlmethod GridView::moveCurrentIndexUp() + + Move the currentIndex up one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1GridView::moveCurrentIndexUp() +{ + Q_D(QDeclarative1GridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (d->flow == QDeclarative1GridView::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); + } + } +} + +/*! + \qmlmethod GridView::moveCurrentIndexDown() + + Move the currentIndex down one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1GridView::moveCurrentIndexDown() +{ + Q_D(QDeclarative1GridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + if (d->flow == QDeclarative1GridView::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); + } + } +} + +/*! + \qmlmethod GridView::moveCurrentIndexLeft() + + Move the currentIndex left one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1GridView::moveCurrentIndexLeft() +{ + Q_D(QDeclarative1GridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QDeclarative1GridView::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 == QDeclarative1GridView::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); + } + } + } +} + +/*! + \qmlmethod GridView::moveCurrentIndexRight() + + Move the currentIndex right one item in the view. + The current index will wrap if keyNavigationWraps is true and it + is currently at the end. This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1GridView::moveCurrentIndexRight() +{ + Q_D(QDeclarative1GridView); + const int count = d->model ? d->model->count() : 0; + if (!count) + return; + + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QDeclarative1GridView::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 == QDeclarative1GridView::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 QDeclarative1GridViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QDeclarative1GridView); + if (!isValid()) + return; + if (mode < QDeclarative1GridView::Beginning || mode > QDeclarative1GridView::Contain) + return; + + int idx = qMax(qMin(index, model->count()-1), 0); + + if (layoutScheduled) + layout(); + qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position(); + FxGridItem1 *item = visibleItem(idx); + qreal maxExtent; + if (flow == QDeclarative1GridView::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<FxGridItem1*> oldVisible = visibleItems; + visibleItems.clear(); + visibleIndex = idx - idx % columns; + if (flow == QDeclarative1GridView::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 QDeclarative1GridView::Beginning: + pos = itemPos; + if (index < 0 && header) { + pos -= flow == QDeclarative1GridView::LeftToRight + ? header->item->height() + : header->item->width(); + } + break; + case QDeclarative1GridView::Center: + pos = itemPos - (size() - rowSize())/2; + break; + case QDeclarative1GridView::End: + pos = itemPos - size() + rowSize(); + if (index >= model->count() && footer) { + pos += flow == QDeclarative1GridView::LeftToRight + ? footer->item->height() + : footer->item->width(); + } + break; + case QDeclarative1GridView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + rowSize(); + else if (item->endRowPos() < pos) + pos = itemPos; + break; + case QDeclarative1GridView::Contain: + if (item->endRowPos() > pos + size()) + pos = itemPos - size() + rowSize(); + if (itemPos < pos) + pos = itemPos; + } + + pos = qMin(pos, maxExtent); + qreal minExtent; + if (flow == QDeclarative1GridView::LeftToRight) + minExtent = -q->minYExtent(); + else + minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent(); + pos = qMax(pos, minExtent); + moveReason = QDeclarative1GridViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + } + fixupPosition(); +} + +/*! + \qmlmethod GridView::positionViewAtIndex(int index, PositionMode mode) + + Positions the view such that the \a index is at the position specified by + \a mode: + + \list + \o GridView.Beginning - position item at the top (or left for \c GridView.TopToBottom flow) of the view. + \o GridView.Center - position item in the center of the view. + \o GridView.End - position item at bottom (or right for horizontal orientation) of the view. + \o GridView.Visible - if any part of the item is visible then take no action, otherwise + bring the item into view. + \o GridView.Contain - ensure the entire item is visible. If the item is larger than + the view the item is positioned at the top (or left for \c GridView.TopToBottom flow) of the view. + \endlist + + If positioning the view at the index would cause empty space to be displayed at + the beginning or end of the view, the view will be positioned at the boundary. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the view does not cause all other items to be repositioned. + The correct way to bring an item into view is with \c positionViewAtIndex. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end: + + \code + Component.onCompleted: positionViewAtIndex(count - 1, GridView.Beginning) + \endcode +*/ +void QDeclarative1GridView::positionViewAtIndex(int index, int mode) +{ + Q_D(QDeclarative1GridView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + +/*! + \qmlmethod GridView::positionViewAtBeginning() + \qmlmethod GridView::positionViewAtEnd() + \since Quick 1.1 + + Positions the view at the beginning or end, taking into account any header or footer. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end on startup: + + \code + Component.onCompleted: positionViewAtEnd() + \endcode +*/ +void QDeclarative1GridView::positionViewAtBeginning() +{ + Q_D(QDeclarative1GridView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QDeclarative1GridView::positionViewAtEnd() +{ + Q_D(QDeclarative1GridView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +/*! + \qmlmethod int GridView::indexAt(int x, int y) + + Returns the index of the visible item containing the point \a x, \a y in content + coordinates. If there is no item at the point specified, or the item is + not visible -1 is returned. + + If the item is outside the visible area, -1 is returned, regardless of + whether an item will exist at that point when scrolled into view. + + \bold Note: methods should only be called after the Component has completed. +*/ +int QDeclarative1GridView::indexAt(qreal x, qreal y) const +{ + Q_D(const QDeclarative1GridView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxGridItem1 *listItem = d->visibleItems.at(i); + if(listItem->contains(x, y)) + return listItem->index; + } + + return -1; +} + +void QDeclarative1GridView::componentComplete() +{ + Q_D(QDeclarative1GridView); + QDeclarative1Flickable::componentComplete(); + d->updateHeader(); + d->updateFooter(); + d->updateGrid(); + if (d->isValid()) { + refill(); + d->moveReason = QDeclarative1GridViewPrivate::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 = QDeclarative1GridViewPrivate::Other; + d->fixupPosition(); + } +} + +void QDeclarative1GridView::trackedPositionChanged() +{ + Q_D(QDeclarative1GridView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QDeclarative1GridViewPrivate::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 QDeclarative1GridView::itemsInserted(int modelIndex, int count) +{ + Q_D(QDeclarative1GridView); + 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) { + FxGridItem1 *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()+d->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) { + FxGridItem1 *listItem = d->visibleItems.at(i); + if (listItem->index != -1 && listItem->index >= modelIndex) + listItem->index += count; + } + + bool addedVisible = false; + QList<FxGridItem1*> 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; + } + FxGridItem1 *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<FxGridItem1*>::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 QDeclarative1GridView::itemsRemoved(int modelIndex, int count) +{ + Q_D(QDeclarative1GridView); + 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<FxGridItem1*>::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxGridItem1 *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(); + update(); + } + } + + emit countChanged(); +} + +void QDeclarative1GridView::destroyRemoved() +{ + Q_D(QDeclarative1GridView); + for (QList<FxGridItem1*>::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxGridItem1 *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 QDeclarative1GridView::itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarative1GridView); + if (!isComponentComplete()) + return; + QHash<int,FxGridItem1*> moved; + + FxGridItem1 *firstItem = d->firstVisibleItem(); + + QList<FxGridItem1*>::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxGridItem1 *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()) { + FxGridItem1 *item = *it; + if (remaining && item->index >= to && item->index < to + count) { + // place items in the target position, reusing any existing items + FxGridItem1 *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 (FxGridItem1 *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(); + FxGridItem1 *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 QDeclarative1GridView::modelReset() +{ + Q_D(QDeclarative1GridView); + d->clear(); + refill(); + d->moveReason = QDeclarative1GridViewPrivate::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 = QDeclarative1GridViewPrivate::Other; + + emit countChanged(); +} + +void QDeclarative1GridView::createdItem(int index, QDeclarativeItem *item) +{ + Q_D(QDeclarative1GridView); + if (d->requestedIndex != index) { + item->setParentItem(this); + d->unrequestedItems.insert(item, index); + if (d->flow == QDeclarative1GridView::LeftToRight) { + item->setPos(QPointF(d->colPosAt(index), d->rowPosAt(index))); + } else { + item->setPos(QPointF(d->rowPosAt(index), d->colPosAt(index))); + } + } +} + +void QDeclarative1GridView::destroyingItem(QDeclarativeItem *item) +{ + Q_D(QDeclarative1GridView); + d->unrequestedItems.remove(item); +} + +void QDeclarative1GridView::animStopped() +{ + Q_D(QDeclarative1GridView); + d->bufferMode = QDeclarative1GridViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QDeclarative1GridView::StrictlyEnforceRange) + d->updateHighlight(); +} + +void QDeclarative1GridView::refill() +{ + Q_D(QDeclarative1GridView); + if (d->isRightToLeftTopToBottom()) + d->refill(-d->position()-d->size()+1, -d->position()); + else + d->refill(d->position(), d->position()+d->size()-1); +} + + +QDeclarative1GridViewAttached *QDeclarative1GridView::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarative1GridViewAttached(obj); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativegridview_p.h b/src/qtquick1/graphicsitems/qdeclarativegridview_p.h new file mode 100644 index 0000000000..c53a34507d --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativegridview_p.h @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGRIDVIEW_H +#define QDECLARATIVEGRIDVIEW_H + +#include "private/qdeclarativeflickable_p.h" +#include "QtDeclarative/private/qdeclarativeguard_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QDeclarative1VisualModel; +class QDeclarative1GridViewAttached; +class QDeclarative1GridViewPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1GridView : public QDeclarative1Flickable +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1GridView) + + 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(QDeclarativeItem *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(QDeclarativeItem *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 REVISION 1) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged REVISION 1) + 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: + QDeclarative1GridView(QDeclarativeItem *parent=0); + ~QDeclarative1GridView(); + + QVariant model() const; + int modelCount() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QDeclarativeItem *currentItem(); + QDeclarativeItem *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 Q_REVISION(1) void positionViewAtBeginning(); + Q_INVOKABLE Q_REVISION(1) void positionViewAtEnd(); + + static QDeclarative1GridViewAttached *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(); + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) void effectiveLayoutDirectionChanged(); + void keyNavigationWrapsChanged(); + void cacheBufferChanged(); + void snapModeChanged(); + void headerChanged(); + void footerChanged(); + +protected: + virtual bool event(QEvent *event); + 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, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + void animStopped(); + +private: + void refill(); +}; + +class QDeclarative1GridViewAttached : public QObject +{ + Q_OBJECT +public: + QDeclarative1GridViewAttached(QObject *parent) + : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {} + ~QDeclarative1GridViewAttached() {} + + Q_PROPERTY(QDeclarative1GridView *view READ view NOTIFY viewChanged) + QDeclarative1GridView *view() { return m_view; } + void setView(QDeclarative1GridView *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<QDeclarative1GridView> m_view; + bool m_isCurrent : 1; + bool m_delayRemove : 1; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1GridView) +QML_DECLARE_TYPEINFO(QDeclarative1GridView, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeimage.cpp b/src/qtquick1/graphicsitems/qdeclarativeimage.cpp new file mode 100644 index 0000000000..c3d0802e8c --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimage.cpp @@ -0,0 +1,588 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeimage_p.h" +#include "QtQuick1/private/qdeclarativeimage_p_p.h" + +#include <QKeyEvent> +#include <QPainter> + +QT_BEGIN_NAMESPACE + + + + +/*! + \qmlclass Image QDeclarative1Image + \since 4.7 + \ingroup qml-basic-visual-elements + \brief The Image element displays an image in a declarative user interface + \inherits Item + + The Image element is used to display images in a declarative user interface. + + The source of the image is specified as a URL using the \l source property. + Images can be supplied in any of the standard image formats supported by Qt, + including bitmap formats such as PNG and JPEG, and vector graphics formats + such as SVG. If you need to display animated images, use the \l AnimatedImage + element. + + If the \l{Item::width}{width} and \l{Item::height}{height} properties are not + specified, the Image element automatically uses the size of the loaded image. + By default, specifying the width and height of the element causes the image + to be scaled to that size. This behavior can be changed by setting the + \l fillMode property, allowing the image to be stretched and tiled instead. + + \section1 Example Usage + + The following example shows the simplest usage of the Image element. + + \snippet doc/src/snippets/declarative/image.qml document + + \beginfloatleft + \image declarative-qtlogo.png + \endfloat + + \clearfloat + + \section1 Performance + + By default, locally available images are loaded immediately, and the user interface + is blocked until loading is complete. If a large image is to be loaded, it may be + preferable to load the image in a low priority thread, by enabling the \l asynchronous + property. + + If the image is obtained from a network rather than a local resource, it is + automatically loaded asynchronously, and the \l progress and \l status properties + are updated as appropriate. + + Images are cached and shared internally, so if several Image elements have the same \l source, + only one copy of the image will be loaded. + + \bold Note: Images are often the greatest user of memory in QML user interfaces. It is recommended + that images which do not form part of the user interface have their + size bounded via the \l sourceSize property. This is especially important for content + that is loaded from external sources or provided by the user. + + \sa {declarative/imageelements/image}{Image example}, QDeclarativeImageProvider +*/ + +QDeclarative1Image::QDeclarative1Image(QDeclarativeItem *parent) + : QDeclarative1ImageBase(*(new QDeclarative1ImagePrivate), parent) +{ +} + +QDeclarative1Image::QDeclarative1Image(QDeclarative1ImagePrivate &dd, QDeclarativeItem *parent) + : QDeclarative1ImageBase(dd, parent) +{ +} + +QDeclarative1Image::~QDeclarative1Image() +{ +} + +QPixmap QDeclarative1Image::pixmap() const +{ + Q_D(const QDeclarative1Image); + return d->pix.pixmap(); +} + +void QDeclarative1Image::setPixmap(const QPixmap &pix) +{ + Q_D(QDeclarative1Image); + if (!d->url.isEmpty()) + return; + d->setPixmap(pix); +} + +void QDeclarative1ImagePrivate::setPixmap(const QPixmap &pixmap) +{ + Q_Q(QDeclarative1Image); + pix.setPixmap(pixmap); + + q->pixmapChange(); + status = pix.isNull() ? QDeclarative1ImageBase::Null : QDeclarative1ImageBase::Ready; + + q->update(); +} + +/*! + \qmlproperty enumeration Image::fillMode + + Set this property to define what happens when the source image has a different size + than the item. + + \list + \o Image.Stretch - the image is scaled to fit + \o Image.PreserveAspectFit - the image is scaled uniformly to fit without cropping + \o Image.PreserveAspectCrop - the image is scaled uniformly to fill, cropping if necessary + \o Image.Tile - the image is duplicated horizontally and vertically + \o Image.TileVertically - the image is stretched horizontally and tiled vertically + \o Image.TileHorizontally - the image is stretched vertically and tiled horizontally + \endlist + + \table + + \row + \o \image declarative-qtlogo-stretch.png + \o Stretch (default) + \qml + Image { + width: 130; height: 100 + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-preserveaspectfit.png + \o PreserveAspectFit + \qml + Image { + width: 130; height: 100 + fillMode: Image.PreserveAspectFit + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-preserveaspectcrop.png + \o PreserveAspectCrop + \qml + Image { + width: 130; height: 100 + fillMode: Image.PreserveAspectCrop + smooth: true + source: "qtlogo.png" + clip: true + } + \endqml + + \row + \o \image declarative-qtlogo-tile.png + \o Tile + \qml + Image { + width: 120; height: 120 + fillMode: Image.Tile + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-tilevertically.png + \o TileVertically + \qml + Image { + width: 120; height: 120 + fillMode: Image.TileVertically + smooth: true + source: "qtlogo.png" + } + \endqml + + \row + \o \image declarative-qtlogo-tilehorizontally.png + \o TileHorizontally + \qml + Image { + width: 120; height: 120 + fillMode: Image.TileHorizontally + smooth: true + source: "qtlogo.png" + } + \endqml + + \endtable + + Note that \c clip is \c false by default which means that the element might + paint outside its bounding rectangle even if the fillMode is set to \c PreserveAspectCrop. + + \sa {declarative/imageelements/image}{Image example} +*/ +QDeclarative1Image::FillMode QDeclarative1Image::fillMode() const +{ + Q_D(const QDeclarative1Image); + return d->fillMode; +} + +void QDeclarative1Image::setFillMode(FillMode mode) +{ + Q_D(QDeclarative1Image); + if (d->fillMode == mode) + return; + d->fillMode = mode; + update(); + updatePaintedGeometry(); + emit fillModeChanged(); +} + +/*! + + \qmlproperty real Image::paintedWidth + \qmlproperty real Image::paintedHeight + + These properties hold the size of the image that is actually painted. + In most cases it is the same as \c width and \c height, but when using a + \c fillMode \c PreserveAspectFit or \c fillMode \c PreserveAspectCrop + \c paintedWidth or \c paintedHeight can be smaller or larger than + \c width and \c height of the Image element. +*/ +qreal QDeclarative1Image::paintedWidth() const +{ + Q_D(const QDeclarative1Image); + return d->paintedWidth; +} + +qreal QDeclarative1Image::paintedHeight() const +{ + Q_D(const QDeclarative1Image); + return d->paintedHeight; +} + +/*! + \qmlproperty enumeration Image::status + + This property holds the status of image loading. It can be one of: + \list + \o Image.Null - no image has been set + \o Image.Ready - the image has been loaded + \o Image.Loading - the image is currently being loaded + \o Image.Error - an error occurred while loading the image + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: image.status == Image.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + Image { + id: image + onStatusChanged: if (image.status == Image.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: image.status == Image.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist + + \sa progress +*/ + +/*! + \qmlproperty real Image::progress + + This property holds the progress of image loading, from 0.0 (nothing loaded) + to 1.0 (finished). + + \sa status +*/ + +/*! + \qmlproperty bool Image::smooth + + Set this property if you want the image to be smoothly filtered when scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the image is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the image is stationary on + the screen. A common pattern when animating an image is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty QSize Image::sourceSize + + This property holds the actual width and height of the loaded image. + + Unlike the \l {Item::}{width} and \l {Item::}{height} properties, which scale + the painting of the image, this property sets the actual number of pixels + stored for the loaded image so that large images do not use more + memory than necessary. For example, this ensures the image in memory is no + larger than 1024x1024 pixels, regardless of the Image's \l {Item::}{width} and + \l {Item::}{height} values: + + \code + Rectangle { + width: ... + height: ... + + Image { + anchors.fill: parent + source: "reallyBigImage.jpg" + sourceSize.width: 1024 + sourceSize.height: 1024 + } + } + \endcode + + If the image's actual size is larger than the sourceSize, the image is scaled down. + If only one dimension of the size is set to greater than 0, the + other dimension is set in proportion to preserve the source image's aspect ratio. + (The \l fillMode is independent of this.) + + If the source is an instrinsically scalable image (eg. SVG), this property + determines the size of the loaded image regardless of intrinsic size. + Avoid changing this property dynamically; rendering an SVG is \e slow compared + to an image. + + If the source is a non-scalable image (eg. JPEG), the loaded image will + be no greater than this property specifies. For some formats (currently only JPEG), + the whole image will never actually be loaded into memory. + + Since QtQuick 1.1 the sourceSize can be cleared to the natural size of the image + by setting sourceSize to \c undefined. + + \note \e {Changing this property dynamically causes the image source to be reloaded, + potentially even from the network, if it is not in the disk cache.} +*/ + +void QDeclarative1Image::updatePaintedGeometry() +{ + Q_D(QDeclarative1Image); + + 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 QDeclarative1Image::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QDeclarative1ImageBase::geometryChanged(newGeometry, oldGeometry); + updatePaintedGeometry(); +} + +QRectF QDeclarative1Image::boundingRect() const +{ + Q_D(const QDeclarative1Image); + return QRectF(0, 0, qMax(d->mWidth, d->paintedWidth), qMax(d->mHeight, d->paintedHeight)); +} + +/*! + \qmlproperty url Image::source + + Image can handle any image format supported by Qt, loaded from any URL scheme supported by Qt. + + The URL may be absolute, or relative to the URL of the component. + + \sa QDeclarativeImageProvider +*/ + +/*! + \qmlproperty bool Image::asynchronous + + Specifies that images on the local filesystem should be loaded + asynchronously in a separate thread. The default value is + false, causing the user interface thread to block while the + image is loaded. Setting \a asynchronous to true is useful where + maintaining a responsive user interface is more desirable + than having images immediately visible. + + Note that this property is only valid for images read from the + local filesystem. Images loaded via a network resource (e.g. HTTP) + are always loaded asynchonously. +*/ + +/*! + \qmlproperty bool Image::cache + \since Quick 1.1 + + Specifies whether the image should be cached. The default value is + true. Setting \a cache to false is useful when dealing with large images, + to make sure that they aren't cached at the expense of small 'ui element' images. +*/ + +/*! + \qmlproperty bool Image::mirror + \since Quick 1.1 + + This property holds whether the image should be horizontally inverted + (effectively displaying a mirrored image). + + The default value is false. +*/ + + +void QDeclarative1Image::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarative1Image); + if (d->pix.pixmap().isNull() ) + return; + + int drawWidth = width(); + int drawHeight = height(); + bool doClip = false; + QTransform transform; + qreal widthScale = width() / qreal(d->pix.width()); + qreal heightScale = height() / qreal(d->pix.height()); + + if (width() != d->pix.width() || height() != d->pix.height()) { + if (d->fillMode >= Tile) { + if (d->fillMode == TileVertically) { + transform.scale(widthScale, 1.0); + drawWidth = d->pix.width(); + } else if (d->fillMode == TileHorizontally) { + transform.scale(1.0, heightScale); + drawHeight = d->pix.height(); + } + } else { + if (d->fillMode == PreserveAspectFit) { + if (widthScale <= heightScale) { + heightScale = widthScale; + transform.translate(0, (height() - heightScale * d->pix.height()) / 2); + } else if(heightScale < widthScale) { + widthScale = heightScale; + transform.translate((width() - widthScale * d->pix.width()) / 2, 0); + } + } else if (d->fillMode == PreserveAspectCrop) { + if (widthScale < heightScale) { + widthScale = heightScale; + transform.translate((width() - widthScale * d->pix.width()) / 2, 0); + } else if(heightScale < widthScale) { + heightScale = widthScale; + transform.translate(0, (height() - heightScale * d->pix.height()) / 2); + } + } + transform.scale(widthScale, heightScale); + drawWidth = d->pix.width(); + drawHeight = d->pix.height(); + doClip = clip(); + } + } + + QTransform oldTransform; + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + if (doClip) { + p->save(); + p->setClipRect(QRectF(0, 0, d->mWidth, d->mHeight), Qt::IntersectClip); + } + if (d->mirror) + transform.translate(drawWidth, 0).scale(-1.0, 1.0); + if (!transform.isIdentity()) { + oldTransform = p->transform(); + p->setWorldTransform(transform * oldTransform); + } + + if (d->fillMode >= Tile) + p->drawTiledPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix); + else + p->drawPixmap(QRectF(0, 0, drawWidth, drawHeight), d->pix, QRectF(0, 0, drawWidth, drawHeight)); + + if (d->smooth) { + p->setRenderHint(QPainter::Antialiasing, oldAA); + p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + if (doClip) + p->restore(); + if (!transform.isIdentity()) + p->setWorldTransform(oldTransform); +} + +void QDeclarative1Image::pixmapChange() +{ + Q_D(QDeclarative1Image); + // 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) + QDeclarative1ImageBase::pixmapChange(); + updatePaintedGeometry(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeimage_p.h b/src/qtquick1/graphicsitems/qdeclarativeimage_p.h new file mode 100644 index 0000000000..615587c384 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimage_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGE_H +#define QDECLARATIVEIMAGE_H + +#include "private/qdeclarativeimagebase_p.h" + +#include <QtNetwork/qnetworkreply.h> + +QT_BEGIN_HEADER +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1ImagePrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Image : public QDeclarative1ImageBase +{ + 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) + +public: + QDeclarative1Image(QDeclarativeItem *parent=0); + ~QDeclarative1Image(); + + enum FillMode { Stretch, PreserveAspectFit, PreserveAspectCrop, Tile, TileVertically, TileHorizontally }; + FillMode fillMode() const; + void setFillMode(FillMode); + + QPixmap pixmap() const; + void setPixmap(const QPixmap &); + + qreal paintedWidth() const; + qreal paintedHeight() const; + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + QRectF boundingRect() const; + +Q_SIGNALS: + void fillModeChanged(); + void paintedGeometryChanged(); + +protected: + QDeclarative1Image(QDeclarative1ImagePrivate &dd, QDeclarativeItem *parent); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + void pixmapChange(); + void updatePaintedGeometry(); + +private: + Q_DISABLE_COPY(QDeclarative1Image) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Image) +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QDeclarative1Image) +QT_END_HEADER + +#endif // QDECLARATIVEIMAGE_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeimage_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeimage_p_p.h new file mode 100644 index 0000000000..19b8642892 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimage_p_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGE_P_H +#define QDECLARATIVEIMAGE_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/qdeclarativeitem_p.h" +#include "private/qdeclarativeimagebase_p_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarative1ImagePrivate : public QDeclarative1ImageBasePrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Image) + +public: + QDeclarative1ImagePrivate() + : fillMode(QDeclarative1Image::Stretch), paintedWidth(0), paintedHeight(0) + { + } + + QDeclarative1Image::FillMode fillMode; + qreal paintedWidth; + qreal paintedHeight; + void setPixmap(const QPixmap &pix); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEIMAGE_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeimagebase.cpp b/src/qtquick1/graphicsitems/qdeclarativeimagebase.cpp new file mode 100644 index 0000000000..a4f2f72d9f --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimagebase.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeimagebase_p.h" +#include "QtQuick1/private/qdeclarativeimagebase_p_p.h" + +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtQuick1/private/qdeclarativepixmapcache_p.h> + +QT_BEGIN_NAMESPACE + + + +QDeclarative1ImageBase::QDeclarative1ImageBase(QDeclarativeItem *parent) + : QDeclarative1ImplicitSizeItem(*(new QDeclarative1ImageBasePrivate), parent) +{ +} + +QDeclarative1ImageBase::QDeclarative1ImageBase(QDeclarative1ImageBasePrivate &dd, QDeclarativeItem *parent) + : QDeclarative1ImplicitSizeItem(dd, parent) +{ +} + +QDeclarative1ImageBase::~QDeclarative1ImageBase() +{ +} + +QDeclarative1ImageBase::Status QDeclarative1ImageBase::status() const +{ + Q_D(const QDeclarative1ImageBase); + return d->status; +} + + +qreal QDeclarative1ImageBase::progress() const +{ + Q_D(const QDeclarative1ImageBase); + return d->progress; +} + + +bool QDeclarative1ImageBase::asynchronous() const +{ + Q_D(const QDeclarative1ImageBase); + return d->async; +} + +void QDeclarative1ImageBase::setAsynchronous(bool async) +{ + Q_D(QDeclarative1ImageBase); + if (d->async != async) { + d->async = async; + emit asynchronousChanged(); + } +} + +QUrl QDeclarative1ImageBase::source() const +{ + Q_D(const QDeclarative1ImageBase); + return d->url; +} + +void QDeclarative1ImageBase::setSource(const QUrl &url) +{ + Q_D(QDeclarative1ImageBase); + //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 QDeclarative1ImageBase::setSourceSize(const QSize& size) +{ + Q_D(QDeclarative1ImageBase); + if (d->sourcesize == size) + return; + + d->sourcesize = size; + d->explicitSourceSize = true; + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +QSize QDeclarative1ImageBase::sourceSize() const +{ + Q_D(const QDeclarative1ImageBase); + + 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 QDeclarative1ImageBase::resetSourceSize() +{ + Q_D(QDeclarative1ImageBase); + if (!d->explicitSourceSize) + return; + d->explicitSourceSize = false; + d->sourcesize = QSize(); + emit sourceSizeChanged(); + if (isComponentComplete()) + load(); +} + +bool QDeclarative1ImageBase::cache() const +{ + Q_D(const QDeclarative1ImageBase); + return d->cache; +} + +void QDeclarative1ImageBase::setCache(bool cache) +{ + Q_D(QDeclarative1ImageBase); + if (d->cache == cache) + return; + + d->cache = cache; + emit cacheChanged(); + if (isComponentComplete()) + load(); +} + +void QDeclarative1ImageBase::setMirror(bool mirror) +{ + Q_D(QDeclarative1ImageBase); + if (mirror == d->mirror) + return; + + d->mirror = mirror; + + if (isComponentComplete()) + update(); + + emit mirrorChanged(); +} + +bool QDeclarative1ImageBase::mirror() const +{ + Q_D(const QDeclarative1ImageBase); + return d->mirror; +} + +void QDeclarative1ImageBase::load() +{ + Q_D(QDeclarative1ImageBase); + + 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 { + QDeclarative1Pixmap::Options options; + if (d->async) + options |= QDeclarative1Pixmap::Asynchronous; + if (d->cache) + options |= QDeclarative1Pixmap::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 = + QDeclarative1ImageBase::staticMetaObject.indexOfSlot("requestProgress(qint64,qint64)"); + thisRequestFinished = + QDeclarative1ImageBase::staticMetaObject.indexOfSlot("requestFinished()"); + } + + d->pix.connectFinished(this, thisRequestFinished); + d->pix.connectDownloadProgress(this, thisRequestProgress); + + } else { + requestFinished(); + } + } +} + +void QDeclarative1ImageBase::requestFinished() +{ + Q_D(QDeclarative1ImageBase); + + QDeclarative1ImageBase::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 QDeclarative1ImageBase::requestProgress(qint64 received, qint64 total) +{ + Q_D(QDeclarative1ImageBase); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QDeclarative1ImageBase::componentComplete() +{ + Q_D(QDeclarative1ImageBase); + QDeclarativeItem::componentComplete(); + if (d->url.isValid()) + load(); +} + +void QDeclarative1ImageBase::pixmapChange() +{ + Q_D(QDeclarative1ImageBase); + setImplicitWidth(d->pix.width()); + setImplicitHeight(d->pix.height()); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeimagebase_p.h b/src/qtquick1/graphicsitems/qdeclarativeimagebase_p.h new file mode 100644 index 0000000000..85af4a5d7f --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimagebase_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEBASE_H +#define QDECLARATIVEIMAGEBASE_H + +#include "qdeclarativeimplicitsizeitem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarative1ImageBasePrivate; +class Q_AUTOTEST_EXPORT QDeclarative1ImageBase : public QDeclarative1ImplicitSizeItem +{ + 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 REVISION 1) + Q_PROPERTY(QSize sourceSize READ sourceSize WRITE setSourceSize RESET resetSourceSize NOTIFY sourceSizeChanged) + Q_PROPERTY(bool mirror READ mirror WRITE setMirror NOTIFY mirrorChanged REVISION 1) + +public: + QDeclarative1ImageBase(QDeclarativeItem *parent=0); + ~QDeclarative1ImageBase(); + 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(QDeclarative1ImageBase::Status); + void progressChanged(qreal progress); + void asynchronousChanged(); + Q_REVISION(1) void cacheChanged(); + Q_REVISION(1) void mirrorChanged(); + +protected: + virtual void load(); + virtual void componentComplete(); + virtual void pixmapChange(); + QDeclarative1ImageBase(QDeclarative1ImageBasePrivate &dd, QDeclarativeItem *parent); + +private Q_SLOTS: + virtual void requestFinished(); + void requestProgress(qint64,qint64); + +private: + Q_DISABLE_COPY(QDeclarative1ImageBase) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1ImageBase) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEIMAGEBASE_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeimagebase_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeimagebase_p_p.h new file mode 100644 index 0000000000..bccf953d1c --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimagebase_p_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEBASE_P_H +#define QDECLARATIVEIMAGEBASE_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/qdeclarativeimplicitsizeitem_p_p.h" +#include "QtQuick1/private/qdeclarativepixmapcache_p.h" + +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QNetworkReply; + +class QDeclarative1ImageBasePrivate : public QDeclarative1ImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ImageBase) + +public: + QDeclarative1ImageBasePrivate() + : status(QDeclarative1ImageBase::Null), + progress(0.0), + explicitSourceSize(false), + async(false), + cache(true), + mirror(false) + { + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; + } + + QDeclarative1Pixmap pix; + QDeclarative1ImageBase::Status status; + QUrl url; + qreal progress; + QSize sourcesize; + bool explicitSourceSize : 1; + bool async : 1; + bool cache : 1; + bool mirror: 1; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem.cpp b/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem.cpp new file mode 100644 index 0000000000..08fdc83170 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeimplicitsizeitem_p.h" +#include "QtQuick1/private/qdeclarativeimplicitsizeitem_p_p.h" + +QT_BEGIN_NAMESPACE + + + +void QDeclarative1ImplicitSizeItemPrivate::implicitWidthChanged() +{ + Q_Q(QDeclarative1ImplicitSizeItem); + emit q->implicitWidthChanged(); +} + +void QDeclarative1ImplicitSizeItemPrivate::implicitHeightChanged() +{ + Q_Q(QDeclarative1ImplicitSizeItem); + emit q->implicitHeightChanged(); +} + +QDeclarative1ImplicitSizeItem::QDeclarative1ImplicitSizeItem(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1ImplicitSizeItemPrivate), parent) +{ +} + +QDeclarative1ImplicitSizeItem::QDeclarative1ImplicitSizeItem(QDeclarative1ImplicitSizeItemPrivate &dd, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ +} + + +void QDeclarative1ImplicitSizePaintedItemPrivate::implicitWidthChanged() +{ + Q_Q(QDeclarative1ImplicitSizePaintedItem); + emit q->implicitWidthChanged(); +} + +void QDeclarative1ImplicitSizePaintedItemPrivate::implicitHeightChanged() +{ + Q_Q(QDeclarative1ImplicitSizePaintedItem); + emit q->implicitHeightChanged(); +} + +QDeclarative1ImplicitSizePaintedItem::QDeclarative1ImplicitSizePaintedItem(QDeclarativeItem *parent) + : QDeclarative1PaintedItem(*(new QDeclarative1ImplicitSizePaintedItemPrivate), parent) +{ +} + +QDeclarative1ImplicitSizePaintedItem::QDeclarative1ImplicitSizePaintedItem(QDeclarative1ImplicitSizePaintedItemPrivate &dd, QDeclarativeItem *parent) + : QDeclarative1PaintedItem(dd, parent) +{ +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem_p.h b/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem_p.h new file mode 100644 index 0000000000..15275acba0 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMPLICITSIZEITEM_H +#define QDECLARATIVEIMPLICITSIZEITEM_H + +#include "qdeclarativepainteditem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarative1ImplicitSizeItemPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1ImplicitSizeItem : public QDeclarativeItem +{ + Q_OBJECT + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged REVISION 1) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged REVISION 1) + +public: + QDeclarative1ImplicitSizeItem(QDeclarativeItem *parent = 0); + +protected: + QDeclarative1ImplicitSizeItem(QDeclarative1ImplicitSizeItemPrivate &dd, QDeclarativeItem *parent); + +Q_SIGNALS: + Q_REVISION(1) void implicitWidthChanged(); + Q_REVISION(1) void implicitHeightChanged(); + +private: + Q_DISABLE_COPY(QDeclarative1ImplicitSizeItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1ImplicitSizeItem) +}; + +class QDeclarative1ImplicitSizePaintedItemPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1ImplicitSizePaintedItem : public QDeclarative1PaintedItem +{ + Q_OBJECT + Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged REVISION 1) + Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged REVISION 1) + +public: + QDeclarative1ImplicitSizePaintedItem(QDeclarativeItem *parent = 0); + +protected: + QDeclarative1ImplicitSizePaintedItem(QDeclarative1ImplicitSizePaintedItemPrivate &dd, QDeclarativeItem *parent); + virtual void drawContents(QPainter *, const QRect &) {}; + +Q_SIGNALS: + Q_REVISION(1) void implicitWidthChanged(); + Q_REVISION(1) void implicitHeightChanged(); + +private: + Q_DISABLE_COPY(QDeclarative1ImplicitSizePaintedItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1ImplicitSizePaintedItem) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEIMPLICITSIZEITEM_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h new file mode 100644 index 0000000000..4802b2754d --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeimplicitsizeitem_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMPLICITSIZEITEM_P_H +#define QDECLARATIVEIMPLICITSIZEITEM_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/qdeclarativeitem_p.h" +#include "private/qdeclarativepainteditem_p_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarative1ImplicitSizeItemPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ImplicitSizeItem) + +public: + QDeclarative1ImplicitSizeItemPrivate() + { + } + + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); +}; + + +class QDeclarative1ImplicitSizePaintedItemPrivate : public QDeclarative1PaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ImplicitSizePaintedItem) + +public: + QDeclarative1ImplicitSizePaintedItemPrivate() + { + } + + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEIMPLICITSIZEITEM_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeitem.cpp b/src/qtquick1/graphicsitems/qdeclarativeitem.cpp new file mode 100644 index 0000000000..46cfe526e4 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeitem.cpp @@ -0,0 +1,3815 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/qdeclarativeitem.h" + +#include "QtQuick1/private/qdeclarativeevents_p_p.h" +#include <QtDeclarative/private/qdeclarativeengine_p.h> +#include <private/qgraphicsitem_p.h> +#include <QtQuick1/private/qdeclarativeitem_p.h> + +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtQuick1/private/qdeclarativeopenmetaobject_p.h> +#include <QtQuick1/private/qdeclarativestate_p.h> +#include <QtQuick1/qdeclarativeview.h> +#include <QtQuick1/private/qdeclarativestategroup_p.h> +#include <QtDeclarative/qdeclarativecomponent.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QDebug> +#include <QPen> +#include <QEvent> +#include <QGraphicsSceneMouseEvent> +#include <QtCore/qnumeric.h> +#include <QtScript/qscriptengine.h> +#include <QtGui/qgraphicstransform.h> + +#include <private/qv8engine_p.h> + +#include <float.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass Transform QGraphicsTransform + \ingroup qml-transform-elements + \since 4.7 + \brief The Transform elements provide a way of building advanced transformations on Items. + + The Transform element is a base type which cannot be instantiated directly. + The following concrete Transform types are available: + + \list + \o \l Rotation + \o \l Scale + \o \l Translate + \endlist + + The Transform elements let you create and control advanced transformations that can be configured + independently using specialized properties. + + You can assign any number of Transform elements to an \l Item. Each Transform is applied in order, + one at a time. +*/ + +/*! + \qmlclass Translate QDeclarative1Translate + \ingroup qml-transform-elements + \since 4.7 + \brief The Translate object provides a way to move an Item without changing its x or y properties. + + The Translate object provides independent control over position in addition to the Item's x and y properties. + + The following example moves the Y axis of the \l Rectangle elements while still allowing the \l Row element + to lay the items out as if they had not been transformed: + \qml + import QtQuick 1.0 + + Row { + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Translate { y: 20 } + } + Rectangle { + width: 100; height: 100 + color: "red" + transform: Translate { y: -20 } + } + } + \endqml + + \image translate.png +*/ + +/*! + \qmlproperty real Translate::x + + The translation along the X axis. +*/ + +/*! + \qmlproperty real Translate::y + + The translation along the Y axis. +*/ + +/*! + \qmlclass Scale QGraphicsScale + \ingroup qml-transform-elements + \since 4.7 + \brief The Scale element provides a way to scale an Item. + + The Scale element gives more control over scaling than using \l Item's \l{Item::scale}{scale} property. Specifically, + it allows a different scale for the x and y axes, and allows the scale to be relative to an + arbitrary point. + + The following example scales the X axis of the Rectangle, relative to its interior point 25, 25: + \qml + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Scale { origin.x: 25; origin.y: 25; xScale: 3} + } + \endqml + + \sa Rotation, Translate +*/ + +/*! + \qmlproperty real Scale::origin.x + \qmlproperty real Scale::origin.y + + The point that the item is scaled from (i.e., the point that stays fixed relative to the parent as + the rest of the item grows). By default the origin is 0, 0. +*/ + +/*! + \qmlproperty real Scale::xScale + + The scaling factor for the X axis. +*/ + +/*! + \qmlproperty real Scale::yScale + + The scaling factor for the Y axis. +*/ + +/*! + \qmlclass Rotation QGraphicsRotation + \ingroup qml-transform-elements + \since 4.7 + \brief The Rotation object provides a way to rotate an Item. + + The Rotation object gives more control over rotation than using \l Item's \l{Item::rotation}{rotation} property. + Specifically, it allows (z axis) rotation to be relative to an arbitrary point. + + The following example rotates a Rectangle around its interior point 25, 25: + \qml + Rectangle { + width: 100; height: 100 + color: "blue" + transform: Rotation { origin.x: 25; origin.y: 25; angle: 45} + } + \endqml + + Rotation also provides a way to specify 3D-like rotations for Items. For these types of + rotations you must specify the axis to rotate around in addition to the origin point. + + The following example shows various 3D-like rotations applied to an \l Image. + \snippet doc/src/snippets/declarative/rotation.qml 0 + + \image axisrotation.png + + \sa {declarative/ui-components/dialcontrol}{Dial Control example}, {declarative/toys/clocks}{Clocks example} +*/ + +/*! + \qmlproperty real Rotation::origin.x + \qmlproperty real Rotation::origin.y + + The origin point of the rotation (i.e., the point that stays fixed relative to the parent as + the rest of the item rotates). By default the origin is 0, 0. +*/ + +/*! + \qmlproperty real Rotation::axis.x + \qmlproperty real Rotation::axis.y + \qmlproperty real Rotation::axis.z + + The axis to rotate around. For simple (2D) rotation around a point, you do not need to specify an axis, + as the default axis is the z axis (\c{ axis { x: 0; y: 0; z: 1 } }). + + For a typical 3D-like rotation you will usually specify both the origin and the axis. + + \image 3d-rotation-axis.png +*/ + +/*! + \qmlproperty real Rotation::angle + + The angle to rotate, in degrees clockwise. +*/ + +QDeclarative1Contents::QDeclarative1Contents(QDeclarativeItem *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))); +} + +QDeclarative1Contents::~QDeclarative1Contents() +{ + QList<QGraphicsItem *> children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast<QDeclarativeItem *>(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + QDeclarativeItemPrivate::get(child)->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + } +} + +QRectF QDeclarative1Contents::rectF() const +{ + return QRectF(m_x, m_y, m_width, m_height); +} + +void QDeclarative1Contents::calcHeight(QDeclarativeItem *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<QGraphicsItem *> children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast<QDeclarativeItem *>(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + 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 QDeclarative1Contents::calcWidth(QDeclarativeItem *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<QGraphicsItem *> children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast<QDeclarativeItem *>(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + 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 QDeclarative1Contents::complete() +{ + QList<QGraphicsItem *> children = m_item->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast<QDeclarativeItem *>(children.at(i)); + if(!child)//### Should this be ignoring non-QDeclarativeItem graphicsobjects? + continue; + QDeclarativeItemPrivate::get(child)->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + //###what about changes to visibility? + } + + calcGeometry(); +} + +void QDeclarative1Contents::itemGeometryChanged(QDeclarativeItem *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 QDeclarative1Contents::itemDestroyed(QDeclarativeItem *item) +{ + if (item) + QDeclarativeItemPrivate::get(item)->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + calcGeometry(); +} + +void QDeclarative1Contents::childRemoved(QDeclarativeItem *item) +{ + if (item) + QDeclarativeItemPrivate::get(item)->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + calcGeometry(); +} + +void QDeclarative1Contents::childAdded(QDeclarativeItem *item) +{ + if (item) + QDeclarativeItemPrivate::get(item)->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry | QDeclarativeItemPrivate::Destroyed); + calcWidth(item); + calcHeight(item); +} + +QDeclarativeItemKeyFilter::QDeclarativeItemKeyFilter(QDeclarativeItem *item) +: m_processPost(false), m_next(0) +{ + QDeclarativeItemPrivate *p = + item?static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(item)):0; + if (p) { + m_next = p->keyHandler; + p->keyHandler = this; + } +} + +QDeclarativeItemKeyFilter::~QDeclarativeItemKeyFilter() +{ +} + +void QDeclarativeItemKeyFilter::keyPressed(QKeyEvent *event, bool post) +{ + if (m_next) m_next->keyPressed(event, post); +} + +void QDeclarativeItemKeyFilter::keyReleased(QKeyEvent *event, bool post) +{ + if (m_next) m_next->keyReleased(event, post); +} + +void QDeclarativeItemKeyFilter::inputMethodEvent(QInputMethodEvent *event, bool post) +{ + if (m_next) m_next->inputMethodEvent(event, post); +} + +QVariant QDeclarativeItemKeyFilter::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (m_next) return m_next->inputMethodQuery(query); + return QVariant(); +} + +void QDeclarativeItemKeyFilter::componentComplete() +{ + if (m_next) m_next->componentComplete(); +} + + +/*! + \qmlclass KeyNavigation QDeclarative1KeyNavigationAttached + \ingroup qml-basic-interaction-elements + \since 4.7 + \brief The KeyNavigation attached property supports key navigation by arrow keys. + + Key-based user interfaces commonly allow the use of arrow keys to navigate between + focusable items. The KeyNavigation attached property enables this behavior by providing a + convenient way to specify the item that should gain focus when an arrow or tab key is pressed. + + The following example provides key navigation for a 2x2 grid of items: + + \snippet doc/src/snippets/declarative/keynavigation.qml 0 + + The top-left item initially receives focus by setting \l {Item::}{focus} to + \c true. When an arrow key is pressed, the focus will move to the + appropriate item, as defined by the value that has been set for + the KeyNavigation \l left, \l right, \l up or \l down properties. + + Note that if a KeyNavigation attached property receives the key press and release + events for a requested arrow or tab key, the event is accepted and does not + propagate any further. + + By default, KeyNavigation receives key events after the item to which it is attached. + If the item accepts the key event, the KeyNavigation attached property will not + receive an event for that key. Setting the \l priority property to + \c KeyNavigation.BeforeItem allows the event to be used for key navigation + before the item, rather than after. + + If item to which the focus is switching is not enabled or visible, an attempt will + be made to skip this item and focus on the next. This is possible if there are + a chain of items with the same KeyNavigation handler. If multiple items in a row are not enabled + or visible, they will also be skipped. + + KeyNavigation will implicitly set the other direction to return focus to this item. So if you set + \l left to another item, \l right will be set on that item's KeyNavigation to set focus back to this + item. However, if that item's KeyNavigation has had right explicitly set then no change will occur. + This means that the above example could have been written, with the same behaviour, without specifing + KeyNavigation.right or KeyNavigation.down for any of the items. + + \sa {Keys}{Keys attached property} +*/ + +/*! + \qmlproperty Item KeyNavigation::left + \qmlproperty Item KeyNavigation::right + \qmlproperty Item KeyNavigation::up + \qmlproperty Item KeyNavigation::down + \qmlproperty Item KeyNavigation::tab + \qmlproperty Item KeyNavigation::backtab + + These properties hold the item to assign focus to + when the left, right, up or down cursor keys, or the + tab key are pressed. +*/ + +/*! + \qmlproperty Item KeyNavigation::tab + \qmlproperty Item KeyNavigation::backtab + + These properties hold the item to assign focus to + when the Tab key or Shift+Tab key combination (Backtab) are pressed. +*/ + +QDeclarative1KeyNavigationAttached::QDeclarative1KeyNavigationAttached(QObject *parent) +: QObject(*(new QDeclarative1KeyNavigationAttachedPrivate), parent), + QDeclarativeItemKeyFilter(qobject_cast<QDeclarativeItem*>(parent)) +{ + m_processPost = true; +} + +QDeclarative1KeyNavigationAttached * +QDeclarative1KeyNavigationAttached::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarative1KeyNavigationAttached(obj); +} + +QDeclarativeItem *QDeclarative1KeyNavigationAttached::left() const +{ + Q_D(const QDeclarative1KeyNavigationAttached); + return d->left; +} + +void QDeclarative1KeyNavigationAttached::setLeft(QDeclarativeItem *i) +{ + Q_D(QDeclarative1KeyNavigationAttached); + if (d->left == i) + return; + d->left = i; + d->leftSet = true; + QDeclarative1KeyNavigationAttached* other = + qobject_cast<QDeclarative1KeyNavigationAttached*>(qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(i)); + if(other && !other->d_func()->rightSet){ + other->d_func()->right = qobject_cast<QDeclarativeItem*>(parent()); + emit other->rightChanged(); + } + emit leftChanged(); +} + +QDeclarativeItem *QDeclarative1KeyNavigationAttached::right() const +{ + Q_D(const QDeclarative1KeyNavigationAttached); + return d->right; +} + +void QDeclarative1KeyNavigationAttached::setRight(QDeclarativeItem *i) +{ + Q_D(QDeclarative1KeyNavigationAttached); + if (d->right == i) + return; + d->right = i; + d->rightSet = true; + QDeclarative1KeyNavigationAttached* other = + qobject_cast<QDeclarative1KeyNavigationAttached*>(qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(i)); + if(other && !other->d_func()->leftSet){ + other->d_func()->left = qobject_cast<QDeclarativeItem*>(parent()); + emit other->leftChanged(); + } + emit rightChanged(); +} + +QDeclarativeItem *QDeclarative1KeyNavigationAttached::up() const +{ + Q_D(const QDeclarative1KeyNavigationAttached); + return d->up; +} + +void QDeclarative1KeyNavigationAttached::setUp(QDeclarativeItem *i) +{ + Q_D(QDeclarative1KeyNavigationAttached); + if (d->up == i) + return; + d->up = i; + d->upSet = true; + QDeclarative1KeyNavigationAttached* other = + qobject_cast<QDeclarative1KeyNavigationAttached*>(qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(i)); + if(other && !other->d_func()->downSet){ + other->d_func()->down = qobject_cast<QDeclarativeItem*>(parent()); + emit other->downChanged(); + } + emit upChanged(); +} + +QDeclarativeItem *QDeclarative1KeyNavigationAttached::down() const +{ + Q_D(const QDeclarative1KeyNavigationAttached); + return d->down; +} + +void QDeclarative1KeyNavigationAttached::setDown(QDeclarativeItem *i) +{ + Q_D(QDeclarative1KeyNavigationAttached); + if (d->down == i) + return; + d->down = i; + d->downSet = true; + QDeclarative1KeyNavigationAttached* other = + qobject_cast<QDeclarative1KeyNavigationAttached*>(qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(i)); + if(other && !other->d_func()->upSet){ + other->d_func()->up = qobject_cast<QDeclarativeItem*>(parent()); + emit other->upChanged(); + } + emit downChanged(); +} + +QDeclarativeItem *QDeclarative1KeyNavigationAttached::tab() const +{ + Q_D(const QDeclarative1KeyNavigationAttached); + return d->tab; +} + +void QDeclarative1KeyNavigationAttached::setTab(QDeclarativeItem *i) +{ + Q_D(QDeclarative1KeyNavigationAttached); + if (d->tab == i) + return; + d->tab = i; + d->tabSet = true; + QDeclarative1KeyNavigationAttached* other = + qobject_cast<QDeclarative1KeyNavigationAttached*>(qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(i)); + if(other && !other->d_func()->backtabSet){ + other->d_func()->backtab = qobject_cast<QDeclarativeItem*>(parent()); + emit other->backtabChanged(); + } + emit tabChanged(); +} + +QDeclarativeItem *QDeclarative1KeyNavigationAttached::backtab() const +{ + Q_D(const QDeclarative1KeyNavigationAttached); + return d->backtab; +} + +void QDeclarative1KeyNavigationAttached::setBacktab(QDeclarativeItem *i) +{ + Q_D(QDeclarative1KeyNavigationAttached); + if (d->backtab == i) + return; + d->backtab = i; + d->backtabSet = true; + QDeclarative1KeyNavigationAttached* other = + qobject_cast<QDeclarative1KeyNavigationAttached*>(qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(i)); + if(other && !other->d_func()->tabSet){ + other->d_func()->tab = qobject_cast<QDeclarativeItem*>(parent()); + emit other->tabChanged(); + } + emit backtabChanged(); +} + +/*! + \qmlproperty enumeration KeyNavigation::priority + + This property determines whether the keys are processed before + or after the attached item's own key handling. + + \list + \o KeyNavigation.BeforeItem - process the key events before normal + item key processing. If the event is used for key navigation, it will be accepted and will not + be passed on to the item. + \o KeyNavigation.AfterItem (default) - process the key events after normal item key + handling. If the item accepts the key event it will not be + handled by the KeyNavigation attached property handler. + \endlist +*/ +QDeclarative1KeyNavigationAttached::Priority QDeclarative1KeyNavigationAttached::priority() const +{ + return m_processPost ? AfterItem : BeforeItem; +} + +void QDeclarative1KeyNavigationAttached::setPriority(Priority order) +{ + bool processPost = order == AfterItem; + if (processPost != m_processPost) { + m_processPost = processPost; + emit priorityChanged(); + } +} + +void QDeclarative1KeyNavigationAttached::keyPressed(QKeyEvent *event, bool post) +{ + Q_D(QDeclarative1KeyNavigationAttached); + event->ignore(); + + if (post != m_processPost) { + QDeclarativeItemKeyFilter::keyPressed(event, post); + return; + } + + bool mirror = false; + switch(event->key()) { + case Qt::Key_Left: { + if (QDeclarativeItem *parentItem = qobject_cast<QDeclarativeItem*>(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + QDeclarativeItem* leftItem = mirror ? d->right : d->left; + if (leftItem) { + setFocusNavigation(leftItem, mirror ? "right" : "left"); + event->accept(); + } + break; + } + case Qt::Key_Right: { + if (QDeclarativeItem *parentItem = qobject_cast<QDeclarativeItem*>(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + QDeclarativeItem* 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()) QDeclarativeItemKeyFilter::keyPressed(event, post); +} + +void QDeclarative1KeyNavigationAttached::keyReleased(QKeyEvent *event, bool post) +{ + Q_D(QDeclarative1KeyNavigationAttached); + event->ignore(); + + if (post != m_processPost) { + QDeclarativeItemKeyFilter::keyReleased(event, post); + return; + } + + bool mirror = false; + switch(event->key()) { + case Qt::Key_Left: + if (QDeclarativeItem *parentItem = qobject_cast<QDeclarativeItem*>(parent())) + mirror = QDeclarativeItemPrivate::get(parentItem)->effectiveLayoutMirror; + if (mirror ? d->right : d->left) + event->accept(); + break; + case Qt::Key_Right: + if (QDeclarativeItem *parentItem = qobject_cast<QDeclarativeItem*>(parent())) + mirror = QDeclarativeItemPrivate::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()) QDeclarativeItemKeyFilter::keyReleased(event, post); +} + +void QDeclarative1KeyNavigationAttached::setFocusNavigation(QDeclarativeItem *currentItem, const char *dir) +{ + QDeclarativeItem *initialItem = currentItem; + bool isNextItem = false; + do { + isNextItem = false; + if (currentItem->isVisible() && currentItem->isEnabled()) { + currentItem->setFocus(true); + } else { + QObject *attached = + qmlAttachedPropertiesObject<QDeclarative1KeyNavigationAttached>(currentItem, false); + if (attached) { + QDeclarativeItem *tempItem = qvariant_cast<QDeclarativeItem*>(attached->property(dir)); + if (tempItem) { + currentItem = tempItem; + isNextItem = true; + } + } + } + } + while (currentItem != initialItem && isNextItem); +} + +/*! + \qmlclass LayoutMirroring QDeclarative1LayoutMirroringAttached + \since QtQuick 1.1 + \ingroup qml-utility-elements + \brief The LayoutMirroring attached property is used to mirror layout behavior. + + The LayoutMirroring attached property is used to horizontally mirror \l {anchor-layout}{Item anchors}, + \l{Using QML Positioner and Repeater Items}{positioner} elements (such as \l Row and \l Grid) + and views (such as \l GridView and horizontal \l ListView). Mirroring is a visual change: left + anchors become right anchors, and positioner elements like \l Grid and \l Row reverse the + horizontal layout of child items. + + Mirroring is enabled for an item by setting the \l enabled property to true. By default, this + only affects the item itself; setting the \l childrenInherit property to true propagates the mirroring + behavior to all child elements as well. If the \c LayoutMirroring attached property has not been defined + for an item, mirroring is not enabled. + + The following example shows mirroring in action. The \l Row below is specified as being anchored + to the left of its parent. However, since mirroring has been enabled, the anchor is horizontally + reversed and it is now anchored to the right. Also, since items in a \l Row are positioned + from left to right by default, they are now positioned from right to left instead, as demonstrated + by the numbering and opacity of the items: + + \snippet doc/src/snippets/declarative/layoutmirroring.qml 0 + + \image layoutmirroring.png + + Layout mirroring is useful when it is necessary to support both left-to-right and right-to-left + layout versions of an application to target different language areas. The \l childrenInherit + property allows layout mirroring to be applied without manually setting layout configurations + for every item in an application. Keep in mind, however, that mirroring does not affect any + positioning that is defined by the \l Item \l {Item::}{x} coordinate value, so even with + mirroring enabled, it will often be necessary to apply some layout fixes to support the + desired layout direction. Also, it may be necessary to disable the mirroring of individual + child items (by setting \l {enabled}{LayoutMirroring.enabled} to false for such items) if + mirroring is not the desired behavior, or if the child item already implements mirroring in + some custom way. + + See \l {QML Right-to-left User Interfaces} for further details on using \c LayoutMirroring and + other related features to implement right-to-left support for an application. +*/ + +/*! + \qmlproperty bool LayoutMirroring::enabled + + This property holds whether the item's layout is mirrored horizontally. Setting this to true + horizontally reverses \l {anchor-layout}{anchor} settings such that left anchors become right, + and right anchors become left. For \l{Using QML Positioner and Repeater Items}{positioner} elements + (such as \l Row and \l Grid) and view elements (such as \l {GridView}{GridView} and \l {ListView}{ListView}) + this also mirrors the horizontal layout direction of the item. + + The default value is false. +*/ + +/*! + \qmlproperty bool LayoutMirroring::childrenInherit + + This property holds whether the \l {enabled}{LayoutMirroring.enabled} value for this item + is inherited by its children. + + The default value is false. +*/ + +QDeclarative1LayoutMirroringAttached::QDeclarative1LayoutMirroringAttached(QObject *parent) : QObject(parent), itemPrivate(0) +{ + if (QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent)) { + itemPrivate = QDeclarativeItemPrivate::get(item); + itemPrivate->attachedLayoutDirection = this; + } else + qmlInfo(parent) << tr("LayoutDirection attached property only works with Items"); +} + +QDeclarative1LayoutMirroringAttached * QDeclarative1LayoutMirroringAttached::qmlAttachedProperties(QObject *object) +{ + return new QDeclarative1LayoutMirroringAttached(object); +} + +bool QDeclarative1LayoutMirroringAttached::enabled() const +{ + return itemPrivate ? itemPrivate->effectiveLayoutMirror : false; +} + +void QDeclarative1LayoutMirroringAttached::setEnabled(bool enabled) +{ + if (!itemPrivate) + return; + + itemPrivate->isMirrorImplicit = false; + if (enabled != itemPrivate->effectiveLayoutMirror) { + itemPrivate->setLayoutMirror(enabled); + if (itemPrivate->inheritMirrorFromItem) + itemPrivate->resolveLayoutMirror(); + } +} + +void QDeclarative1LayoutMirroringAttached::resetEnabled() +{ + if (itemPrivate && !itemPrivate->isMirrorImplicit) { + itemPrivate->isMirrorImplicit = true; + itemPrivate->resolveLayoutMirror(); + } +} + +bool QDeclarative1LayoutMirroringAttached::childrenInherit() const +{ + return itemPrivate ? itemPrivate->inheritMirrorFromItem : false; +} + +void QDeclarative1LayoutMirroringAttached::setChildrenInherit(bool childrenInherit) { + if (itemPrivate && childrenInherit != itemPrivate->inheritMirrorFromItem) { + itemPrivate->inheritMirrorFromItem = childrenInherit; + itemPrivate->resolveLayoutMirror(); + childrenInheritChanged(); + } +} + +void QDeclarativeItemPrivate::resolveLayoutMirror() +{ + Q_Q(QDeclarativeItem); + if (QDeclarativeItem *parentItem = q->parentItem()) { + QDeclarativeItemPrivate *parentPrivate = QDeclarativeItemPrivate::get(parentItem); + setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); + } else { + setImplicitLayoutMirror(isMirrorImplicit ? false : effectiveLayoutMirror, inheritMirrorFromItem); + } +} + +void QDeclarativeItemPrivate::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 < children.count(); ++i) { + if (QDeclarativeItem *child = qobject_cast<QDeclarativeItem *>(children.at(i))) { + QDeclarativeItemPrivate *childPrivate = QDeclarativeItemPrivate::get(child); + childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent); + } + } +} + +void QDeclarativeItemPrivate::setLayoutMirror(bool mirror) +{ + if (mirror != effectiveLayoutMirror) { + effectiveLayoutMirror = mirror; + if (_anchors) { + _anchors->d_func()->fillChanged(); + _anchors->d_func()->centerInChanged(); + _anchors->d_func()->updateHorizontalAnchors(); + emit _anchors->mirroredChanged(); + } + mirrorChange(); + if (attachedLayoutDirection) { + emit attachedLayoutDirection->enabledChanged(); + } + } +} + +/*! + \qmlclass Keys QDeclarative1KeysAttached + \ingroup qml-basic-interaction-elements + \since 4.7 + \brief The Keys attached property provides key handling to Items. + + All visual primitives support key handling via the Keys + attached property. Keys can be handled via the onPressed + and onReleased signal properties. + + The signal properties have a \l KeyEvent parameter, named + \e event which contains details of the event. If a key is + handled \e event.accepted should be set to true to prevent the + event from propagating up the item hierarchy. + + \section1 Example Usage + + The following example shows how the general onPressed handler can + be used to test for a certain key; in this case, the left cursor + key: + + \snippet doc/src/snippets/declarative/keys/keys-pressed.qml key item + + Some keys may alternatively be handled via specific signal properties, + for example \e onSelectPressed. These handlers automatically set + \e event.accepted to true. + + \snippet doc/src/snippets/declarative/keys/keys-handler.qml key item + + See \l{Qt::Key}{Qt.Key} for the list of keyboard codes. + + \section1 Key Handling Priorities + + The Keys attached property can be configured to handle key events + before or after the item it is attached to. This makes it possible + to intercept events in order to override an item's default behavior, + or act as a fallback for keys not handled by the item. + + If \l priority is Keys.BeforeItem (default) the order of key event processing is: + + \list 1 + \o Items specified in \c forwardTo + \o specific key handlers, e.g. onReturnPressed + \o onKeyPress, onKeyRelease handlers + \o Item specific key handling, e.g. TextInput key handling + \o parent item + \endlist + + If priority is Keys.AfterItem the order of key event processing is: + + \list 1 + \o Item specific key handling, e.g. TextInput key handling + \o Items specified in \c forwardTo + \o specific key handlers, e.g. onReturnPressed + \o onKeyPress, onKeyRelease handlers + \o parent item + \endlist + + If the event is accepted during any of the above steps, key + propagation stops. + + \sa KeyEvent, {KeyNavigation}{KeyNavigation attached property} +*/ + +/*! + \qmlproperty bool Keys::enabled + + This flags enables key handling if true (default); otherwise + no key handlers will be called. +*/ + +/*! + \qmlproperty enumeration Keys::priority + + This property determines whether the keys are processed before + or after the attached item's own key handling. + + \list + \o Keys.BeforeItem (default) - process the key events before normal + item key processing. If the event is accepted it will not + be passed on to the item. + \o Keys.AfterItem - process the key events after normal item key + handling. If the item accepts the key event it will not be + handled by the Keys attached property handler. + \endlist +*/ + +/*! + \qmlproperty list<Object> Keys::forwardTo + + This property provides a way to forward key presses, key releases, and keyboard input + coming from input methods to other items. This can be useful when you want + one item to handle some keys (e.g. the up and down arrow keys), and another item to + handle other keys (e.g. the left and right arrow keys). Once an item that has been + forwarded keys accepts the event it is no longer forwarded to items later in the + list. + + This example forwards key events to two lists: + \qml + Item { + ListView { + id: list1 + // ... + } + ListView { + id: list2 + // ... + } + Keys.forwardTo: [list1, list2] + focus: true + } + \endqml +*/ + +/*! + \qmlsignal Keys::onPressed(KeyEvent event) + + This handler is called when a key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onReleased(KeyEvent event) + + This handler is called when a key has been released. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit0Pressed(KeyEvent event) + + This handler is called when the digit '0' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit1Pressed(KeyEvent event) + + This handler is called when the digit '1' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit2Pressed(KeyEvent event) + + This handler is called when the digit '2' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit3Pressed(KeyEvent event) + + This handler is called when the digit '3' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit4Pressed(KeyEvent event) + + This handler is called when the digit '4' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit5Pressed(KeyEvent event) + + This handler is called when the digit '5' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit6Pressed(KeyEvent event) + + This handler is called when the digit '6' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit7Pressed(KeyEvent event) + + This handler is called when the digit '7' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit8Pressed(KeyEvent event) + + This handler is called when the digit '8' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDigit9Pressed(KeyEvent event) + + This handler is called when the digit '9' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onLeftPressed(KeyEvent event) + + This handler is called when the Left arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onRightPressed(KeyEvent event) + + This handler is called when the Right arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onUpPressed(KeyEvent event) + + This handler is called when the Up arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDownPressed(KeyEvent event) + + This handler is called when the Down arrow has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onTabPressed(KeyEvent event) + + This handler is called when the Tab key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onBacktabPressed(KeyEvent event) + + This handler is called when the Shift+Tab key combination (Backtab) has + been pressed. The \a event parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onAsteriskPressed(KeyEvent event) + + This handler is called when the Asterisk '*' has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onEscapePressed(KeyEvent event) + + This handler is called when the Escape key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onReturnPressed(KeyEvent event) + + This handler is called when the Return key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onEnterPressed(KeyEvent event) + + This handler is called when the Enter key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onDeletePressed(KeyEvent event) + + This handler is called when the Delete key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onSpacePressed(KeyEvent event) + + This handler is called when the Space key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onBackPressed(KeyEvent event) + + This handler is called when the Back key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onCancelPressed(KeyEvent event) + + This handler is called when the Cancel key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onSelectPressed(KeyEvent event) + + This handler is called when the Select key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onYesPressed(KeyEvent event) + + This handler is called when the Yes key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onNoPressed(KeyEvent event) + + This handler is called when the No key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext1Pressed(KeyEvent event) + + This handler is called when the Context1 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext2Pressed(KeyEvent event) + + This handler is called when the Context2 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext3Pressed(KeyEvent event) + + This handler is called when the Context3 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onContext4Pressed(KeyEvent event) + + This handler is called when the Context4 key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onCallPressed(KeyEvent event) + + This handler is called when the Call key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onHangupPressed(KeyEvent event) + + This handler is called when the Hangup key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onFlipPressed(KeyEvent event) + + This handler is called when the Flip key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onMenuPressed(KeyEvent event) + + This handler is called when the Menu key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onVolumeUpPressed(KeyEvent event) + + This handler is called when the VolumeUp key has been pressed. The \a event + parameter provides information about the event. +*/ + +/*! + \qmlsignal Keys::onVolumeDownPressed(KeyEvent event) + + This handler is called when the VolumeDown key has been pressed. The \a event + parameter provides information about the event. +*/ + +const QDeclarative1KeysAttached::SigMap QDeclarative1KeysAttached::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 QDeclarative1KeysAttachedPrivate::isConnected(const char *signalName) +{ + return isSignalConnected(signalIndex(signalName)); +} + +QDeclarative1KeysAttached::QDeclarative1KeysAttached(QObject *parent) +: QObject(*(new QDeclarative1KeysAttachedPrivate), parent), + QDeclarativeItemKeyFilter(qobject_cast<QDeclarativeItem*>(parent)) +{ + Q_D(QDeclarative1KeysAttached); + m_processPost = false; + d->item = qobject_cast<QDeclarativeItem*>(parent); +} + +QDeclarative1KeysAttached::~QDeclarative1KeysAttached() +{ +} + +QDeclarative1KeysAttached::Priority QDeclarative1KeysAttached::priority() const +{ + return m_processPost ? AfterItem : BeforeItem; +} + +void QDeclarative1KeysAttached::setPriority(Priority order) +{ + bool processPost = order == AfterItem; + if (processPost != m_processPost) { + m_processPost = processPost; + emit priorityChanged(); + } +} + +void QDeclarative1KeysAttached::componentComplete() +{ + Q_D(QDeclarative1KeysAttached); + if (d->item) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *targetItem = d->finalFocusProxy(d->targets.at(ii)); + if (targetItem && (targetItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { + d->item->setFlag(QGraphicsItem::ItemAcceptsInputMethod); + break; + } + } + } +} + +void QDeclarative1KeysAttached::keyPressed(QKeyEvent *event, bool post) +{ + Q_D(QDeclarative1KeysAttached); + if (post != m_processPost || !d->enabled || d->inPress) { + event->ignore(); + QDeclarativeItemKeyFilter::keyPressed(event, post); + return; + } + + // first process forwards + if (d->item && d->item->scene()) { + d->inPress = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible()) { + d->item->scene()->sendEvent(i, event); + if (event->isAccepted()) { + d->inPress = false; + return; + } + } + } + d->inPress = false; + } + + QDeclarative1KeyEvent ke(*event); + QByteArray keySignal = keyToSignal(event->key()); + if (!keySignal.isEmpty()) { + keySignal += "(QDeclarative1KeyEvent*)"; + if (d->isConnected(keySignal)) { + // If we specifically handle a key then default to accepted + ke.setAccepted(true); + int idx = QDeclarative1KeysAttached::staticMetaObject.indexOfSignal(keySignal); + metaObject()->method(idx).invoke(this, Qt::DirectConnection, Q_ARG(QDeclarative1KeyEvent*, &ke)); + } + } + if (!ke.isAccepted()) + emit pressed(&ke); + event->setAccepted(ke.isAccepted()); + + if (!event->isAccepted()) QDeclarativeItemKeyFilter::keyPressed(event, post); +} + +void QDeclarative1KeysAttached::keyReleased(QKeyEvent *event, bool post) +{ + Q_D(QDeclarative1KeysAttached); + if (post != m_processPost || !d->enabled || d->inRelease) { + event->ignore(); + QDeclarativeItemKeyFilter::keyReleased(event, post); + return; + } + + if (d->item && d->item->scene()) { + d->inRelease = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible()) { + d->item->scene()->sendEvent(i, event); + if (event->isAccepted()) { + d->inRelease = false; + return; + } + } + } + d->inRelease = false; + } + + QDeclarative1KeyEvent ke(*event); + emit released(&ke); + event->setAccepted(ke.isAccepted()); + + if (!event->isAccepted()) QDeclarativeItemKeyFilter::keyReleased(event, post); +} + +void QDeclarative1KeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post) +{ + Q_D(QDeclarative1KeysAttached); + if (post == m_processPost && d->item && !d->inIM && d->item->scene()) { + d->inIM = true; + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible() && (i->flags() & QGraphicsItem::ItemAcceptsInputMethod)) { + d->item->scene()->sendEvent(i, event); + if (event->isAccepted()) { + d->imeItem = i; + d->inIM = false; + return; + } + } + } + d->inIM = false; + } + if (!event->isAccepted()) QDeclarativeItemKeyFilter::inputMethodEvent(event, post); +} + +class QDeclarativeItemAccessor : public QGraphicsItem +{ +public: + QVariant doInputMethodQuery(Qt::InputMethodQuery query) const { + return QGraphicsItem::inputMethodQuery(query); + } +}; + +QVariant QDeclarative1KeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QDeclarative1KeysAttached); + if (d->item) { + for (int ii = 0; ii < d->targets.count(); ++ii) { + QGraphicsItem *i = d->finalFocusProxy(d->targets.at(ii)); + if (i && i->isVisible() && (i->flags() & QGraphicsItem::ItemAcceptsInputMethod) && i == d->imeItem) { //### how robust is i == d->imeItem check? + QVariant v = static_cast<QDeclarativeItemAccessor *>(i)->doInputMethodQuery(query); + if (v.userType() == QVariant::RectF) + v = d->item->mapRectFromItem(i, v.toRectF()); //### cost? + return v; + } + } + } + return QDeclarativeItemKeyFilter::inputMethodQuery(query); +} + +QDeclarative1KeysAttached *QDeclarative1KeysAttached::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarative1KeysAttached(obj); +} + +/*! + \class QDeclarativeItem + \since 4.7 + \brief The QDeclarativeItem class provides the most basic of all visual items in QML. + + All visual items in Qt Declarative inherit from QDeclarativeItem. Although QDeclarativeItem + has no visual appearance, it defines all the properties that are + common across visual items - such as the x and y position, the + width and height, \l {anchor-layout}{anchoring} and key handling. + + You can subclass QDeclarativeItem to provide your own custom visual item that inherits + these features. Note that, because it does not draw anything, QDeclarativeItem sets the + QGraphicsItem::ItemHasNoContents flag. If you subclass QDeclarativeItem to create a visual + item, you will need to unset this flag. + +*/ + +/*! + \qmlclass Item QDeclarativeItem + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Item is the most basic of all visual items in QML. + + All visual items in Qt Declarative inherit from Item. Although Item + has no visual appearance, it defines all the properties that are + common across visual items - such as the x and y position, the + width and height, \l {anchor-layout}{anchoring} and key handling. + + Item is also useful for grouping items together. + + \qml + Item { + Image { + source: "tile.png" + } + Image { + x: 80 + width: 100 + height: 100 + source: "tile.png" + } + Image { + x: 190 + width: 100 + height: 100 + fillMode: Image.Tile + source: "tile.png" + } + } + \endqml + + + \section1 Key Handling + + Key handling is available to all Item-based visual elements via the \l {Keys}{Keys} + attached property. The \e Keys attached property provides basic handlers such + as \l {Keys::onPressed}{onPressed} and \l {Keys::onReleased}{onReleased}, + as well as handlers for specific keys, such as + \l {Keys::onCancelPressed}{onCancelPressed}. The example below + assigns \l {qmlfocus}{focus} to the item and handles + the Left key via the general \e onPressed handler and the Select key via the + onSelectPressed handler: + + \qml + Item { + focus: true + Keys.onPressed: { + if (event.key == Qt.Key_Left) { + console.log("move left"); + event.accepted = true; + } + } + Keys.onSelectPressed: console.log("Selected"); + } + \endqml + + See the \l {Keys}{Keys} attached property for detailed documentation. + + \section1 Layout Mirroring + + Item layouts can be mirrored using the \l {LayoutMirroring}{LayoutMirroring} attached property. + +*/ + +/*! + \fn void QDeclarativeItem::childrenRectChanged(const QRectF &) + \internal +*/ + +/*! + \fn void QDeclarativeItem::baselineOffsetChanged(qreal) + \internal +*/ + +/*! + \fn void QDeclarativeItem::stateChanged(const QString &state) + \internal +*/ + +/*! + \fn void QDeclarativeItem::parentChanged(QDeclarativeItem *) + \internal +*/ + +/*! + \fn void QDeclarativeItem::smoothChanged(bool) + \internal +*/ + +/*! + \fn void QDeclarativeItem::clipChanged(bool) + \internal +*/ + +/*! \fn void QDeclarativeItem::transformOriginChanged(TransformOrigin) + \internal +*/ + +/*! + \fn void QDeclarativeItem::focusChanged(bool) + \internal +*/ + +/*! + \fn void QDeclarativeItem::activeFocusChanged(bool) + \internal +*/ + +// ### Must fix +namespace { +struct RegisterAnchorLineAtStartup { + RegisterAnchorLineAtStartup() { + qRegisterMetaType<QDeclarative1AnchorLine>("QDeclarative1AnchorLine"); + } +}; +static RegisterAnchorLineAtStartup registerAnchorLineAtStartup; +} + + +/*! + \fn QDeclarativeItem::QDeclarativeItem(QDeclarativeItem *parent) + + Constructs a QDeclarativeItem with the given \a parent. +*/ +QDeclarativeItem::QDeclarativeItem(QDeclarativeItem* parent) + : QGraphicsObject(*(new QDeclarativeItemPrivate), parent, 0) +{ + Q_D(QDeclarativeItem); + d->init(parent); +} + +/*! \internal +*/ +QDeclarativeItem::QDeclarativeItem(QDeclarativeItemPrivate &dd, QDeclarativeItem *parent) + : QGraphicsObject(dd, parent, 0) +{ + Q_D(QDeclarativeItem); + d->init(parent); +} + +/*! + Destroys the QDeclarativeItem. +*/ +QDeclarativeItem::~QDeclarativeItem() +{ + Q_D(QDeclarativeItem); + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + QDeclarative1AnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + if (anchor) + anchor->clearItem(this); + } + if (!d->parent || (parentItem() && !parentItem()->QGraphicsItem::d_ptr->inDestructor)) { + for (int ii = 0; ii < d->changeListeners.count(); ++ii) { + QDeclarative1AnchorsPrivate *anchor = d->changeListeners.at(ii).listener->anchorPrivate(); + if (anchor && anchor->item && anchor->item->parentItem() != this) //child will be deleted anyway + anchor->updateOnComplete(); + } + } + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::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; +} + +/*! + \qmlproperty enumeration Item::transformOrigin + This property holds the origin point around which scale and rotation transform. + + Nine transform origins are available, as shown in the image below. + + \image declarative-transformorigin.png + + This example rotates an image around its bottom-right corner. + \qml + Image { + source: "myimage.png" + transformOrigin: Item.BottomRight + rotation: 45 + } + \endqml + + The default transform origin is \c Item.Center. + + To set an arbitrary transform origin point use the \l Scale or \l Rotation + transform elements. +*/ + +/*! + \qmlproperty Item Item::parent + This property holds the parent of the item. +*/ + +/*! + \property QDeclarativeItem::parent + This property holds the parent of the item. +*/ +void QDeclarativeItem::setParentItem(QDeclarativeItem *parent) +{ + QGraphicsObject::setParentItem(parent); +} + +/*! + Returns the QDeclarativeItem parent of this item. +*/ +QDeclarativeItem *QDeclarativeItem::parentItem() const +{ + return qobject_cast<QDeclarativeItem *>(QGraphicsObject::parentItem()); +} + +/*! + \qmlproperty real Item::childrenRect.x + \qmlproperty real Item::childrenRect.y + \qmlproperty real Item::childrenRect.width + \qmlproperty real Item::childrenRect.height + + The childrenRect properties allow an item access to the geometry of its + children. This property is useful if you have an item that needs to be + sized to fit its children. +*/ + + +/*! + \qmlproperty list<Item> Item::children + \qmlproperty list<Object> Item::resources + + The children property contains the list of visual children of this item. + The resources property contains non-visual resources that you want to + reference by name. + + Generally you can rely on Item's default property to handle all this for + you, but it can come in handy in some cases. + + \qml + Item { + children: [ + Text {}, + Rectangle {} + ] + resources: [ + Component { + id: myComponent + Text {} + } + ] + } + \endqml +*/ + +/*! + Returns true if construction of the QML component is complete; otherwise + returns false. + + It is often desirable to delay some processing until the component is + completed. + + \sa componentComplete() +*/ +bool QDeclarativeItem::isComponentComplete() const +{ + Q_D(const QDeclarativeItem); + return d->componentComplete; +} + +void QDeclarativeItemPrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o) +{ + if (!o) + return; + + QDeclarativeItem *that = static_cast<QDeclarativeItem *>(prop->object); + + // This test is measurably (albeit only slightly) faster than qobject_cast<>() + const QMetaObject *mo = o->metaObject(); + while (mo && mo != &QGraphicsObject::staticMetaObject) mo = mo->d.superdata; + + if (mo) { + QGraphicsObject *graphicsObject = static_cast<QGraphicsObject *>(o); + QDeclarativeItemPrivate *contentItemPrivate = static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(graphicsObject)); + if (contentItemPrivate->componentComplete) { + graphicsObject->setParentItem(that); + } else { + contentItemPrivate->setParentItemHelper(that, /*newParentVariant=*/0, /*thisPointerVariant=*/0); + } + } else { + o->setParent(that); + } +} + +static inline int children_count_helper(QDeclarativeListProperty<QObject> *prop) +{ + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast<QGraphicsObject *>(prop->object)); + return d->children.count(); +} + +static inline QObject *children_at_helper(QDeclarativeListProperty<QObject> *prop, int index) +{ + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(static_cast<QGraphicsObject *>(prop->object)); + if (index >= 0 && index < d->children.count()) + return d->children.at(index)->toGraphicsObject(); + else + return 0; +} + +static inline void children_clear_helper(QDeclarativeListProperty<QObject> *prop) +{ + QDeclarativeItemPrivate *d = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(static_cast<QGraphicsObject *>(prop->object))); + int childCount = d->children.count(); + if (d->componentComplete) { + for (int index = 0 ;index < childCount; index++) + d->children.at(0)->setParentItem(0); + } else { + for (int index = 0 ;index < childCount; index++) + QGraphicsItemPrivate::get(d->children.at(0))->setParentItemHelper(0, /*newParentVariant=*/0, /*thisPointerVariant=*/0); + } +} + +int QDeclarativeItemPrivate::data_count(QDeclarativeListProperty<QObject> *prop) +{ + return resources_count(prop) + children_count_helper(prop); +} + +QObject *QDeclarativeItemPrivate::data_at(QDeclarativeListProperty<QObject> *prop, int i) +{ + int resourcesCount = resources_count(prop); + if (i < resourcesCount) + return resources_at(prop, i); + const int j = i - resourcesCount; + if (j < children_count_helper(prop)) + return children_at_helper(prop, j); + return 0; +} + +void QDeclarativeItemPrivate::data_clear(QDeclarativeListProperty<QObject> *prop) +{ + resources_clear(prop); + children_clear_helper(prop); +} + +QObject *QDeclarativeItemPrivate::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 QDeclarativeItemPrivate::resources_append(QDeclarativeListProperty<QObject> *prop, QObject *o) +{ + o->setParent(prop->object); +} + +int QDeclarativeItemPrivate::resources_count(QDeclarativeListProperty<QObject> *prop) +{ + return prop->object->children().count(); +} + +void QDeclarativeItemPrivate::resources_clear(QDeclarativeListProperty<QObject> *prop) +{ + const QObjectList children = prop->object->children(); + for (int index = 0; index < children.count(); index++) + children.at(index)->setParent(0); +} + +int QDeclarativeItemPrivate::transform_count(QDeclarativeListProperty<QGraphicsTransform> *list) +{ + QGraphicsObject *object = qobject_cast<QGraphicsObject *>(list->object); + if (object) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object); + return d->transformData ? d->transformData->graphicsTransforms.size() : 0; + } else { + return 0; + } +} + +void QDeclarativeItemPrivate::transform_append(QDeclarativeListProperty<QGraphicsTransform> *list, QGraphicsTransform *item) +{ + QGraphicsObject *object = qobject_cast<QGraphicsObject *>(list->object); + if (object && item) // QGraphicsItem applies the list in the wrong order, so we prepend. + QGraphicsItemPrivate::get(object)->prependGraphicsTransform(item); +} + +QGraphicsTransform *QDeclarativeItemPrivate::transform_at(QDeclarativeListProperty<QGraphicsTransform> *list, int idx) +{ + QGraphicsObject *object = qobject_cast<QGraphicsObject *>(list->object); + if (object) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object); + if (!d->transformData) + return 0; + return d->transformData->graphicsTransforms.at(idx); + } else { + return 0; + } +} + +void QDeclarativeItemPrivate::transform_clear(QDeclarativeListProperty<QGraphicsTransform> *list) +{ + QGraphicsObject *object = qobject_cast<QGraphicsObject *>(list->object); + if (object) { + QGraphicsItemPrivate *d = QGraphicsItemPrivate::get(object); + if (!d->transformData) + return; + object->setTransformations(QList<QGraphicsTransform *>()); + } +} + +void QDeclarativeItemPrivate::parentProperty(QObject *o, void *rv, QDeclarativeNotifierEndpoint *e) +{ + QDeclarativeItem *item = static_cast<QDeclarativeItem*>(o); + if (e) + e->connect(&item->d_func()->parentNotifier); + *((QDeclarativeItem **)rv) = item->parentItem(); +} + +/*! + \qmlproperty list<Object> Item::data + \default + + The data property allows you to freely mix visual children and resources + in an item. If you assign a visual item to the data list it becomes + a child and if you assign any other object type, it is added as a resource. + + So you can write: + \qml + Item { + Text {} + Rectangle {} + Timer {} + } + \endqml + + instead of: + \qml + Item { + children: [ + Text {}, + Rectangle {} + ] + resources: [ + Timer {} + ] + } + \endqml + + data is a behind-the-scenes property: you should never need to explicitly + specify it. + */ + +QDeclarativeListProperty<QObject> QDeclarativeItemPrivate::data() +{ + return QDeclarativeListProperty<QObject>(q_func(), 0, QDeclarativeItemPrivate::data_append, + QDeclarativeItemPrivate::data_count, + QDeclarativeItemPrivate::data_at, + QDeclarativeItemPrivate::data_clear + ); +} + +/*! + \property QDeclarativeItem::childrenRect + \brief The geometry of an item's children. + + This property holds the (collective) position and size of the item's children. +*/ +QRectF QDeclarativeItem::childrenRect() +{ + Q_D(QDeclarativeItem); + if (!d->_contents) { + d->_contents = new QDeclarative1Contents(this); + if (d->componentComplete) + d->_contents->complete(); + } + return d->_contents->rectF(); +} + +bool QDeclarativeItem::clip() const +{ + return flags() & ItemClipsChildrenToShape; +} + +void QDeclarativeItem::setClip(bool c) +{ + if (clip() == c) + return; + setFlag(ItemClipsChildrenToShape, c); + emit clipChanged(c); +} + +/*! + \qmlproperty real Item::x + \qmlproperty real Item::y + \qmlproperty real Item::width + \qmlproperty real Item::height + + Defines the item's position and size relative to its parent. + + \qml + Item { x: 100; y: 100; width: 100; height: 100 } + \endqml + */ + +/*! + \qmlproperty real Item::z + + Sets the stacking order of sibling items. By default the stacking order is 0. + + Items with a higher stacking value are drawn on top of siblings with a + lower stacking order. Items with the same stacking value are drawn + bottom up in the order they appear. Items with a negative stacking + value are drawn under their parent's content. + + The following example shows the various effects of stacking order. + + \table + \row + \o \image declarative-item_stacking1.png + \o Same \c z - later children above earlier children: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + } + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + \endqml + \row + \o \image declarative-item_stacking2.png + \o Higher \c z on top: + \qml + Item { + Rectangle { + z: 1 + color: "red" + width: 100; height: 100 + } + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + \endqml + \row + \o \image declarative-item_stacking3.png + \o Same \c z - children above parents: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \row + \o \image declarative-item_stacking4.png + \o Lower \c z below: + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + z: -1 + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \endtable + */ + +/*! + \qmlproperty bool Item::visible + + This property holds whether the item is visible. By default this is true. + + Setting this property directly affects the \c visible value of child + items. When set to \c false, the \c visible values of all child items also + become \c false. When set to \c true, the \c visible values of child items + are returned to \c true, unless they have explicitly been set to \c false. + + (Because of this flow-on behavior, using the \c visible property may not + have the intended effect if a property binding should only respond to + explicit property changes. In such cases it may be better to use the + \l opacity property instead.) + + Setting this property to \c false automatically causes \l focus to be set + to \c false, and this item will longer receive mouse and keyboard events. + (In contrast, setting the \l opacity to 0 does not affect the \l focus + property and the receiving of key events.) + + \note This property's value is only affected by changes to this property or + the parent's \c visible property. It does not change, for example, if this + item moves off-screen, or if the \l opacity changes to 0. +*/ + + +/*! + This function is called to handle this item's changes in + geometry from \a oldGeometry to \a newGeometry. If the two + geometries are the same, it doesn't do anything. + */ +void QDeclarativeItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarativeItem); + + if (d->_anchors) + d->_anchors->d_func()->updateMe(); + + if (transformOrigin() != QDeclarativeItem::TopLeft + && (newGeometry.width() != oldGeometry.width() || newGeometry.height() != oldGeometry.height())) { + if (d->transformData) { + QPointF origin = d->computeTransformOrigin(); + if (transformOriginPoint() != origin) + setTransformOriginPoint(origin); + } else { + d->transformOriginDirty = true; + } + } + + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Geometry) + change.listener->itemGeometryChanged(this, newGeometry, oldGeometry); + } + + if (newGeometry.width() != oldGeometry.width()) + emit widthChanged(); + if (newGeometry.height() != oldGeometry.height()) + emit heightChanged(); +} + +void QDeclarativeItemPrivate::removeItemChangeListener(QDeclarativeItemChangeListener *listener, ChangeTypes types) +{ + ChangeListener change(listener, types); + changeListeners.removeOne(change); +} + +/*! \internal */ +void QDeclarativeItem::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + keyPressPreHandler(event); + if (event->isAccepted()) + return; + if (d->keyHandler) + d->keyHandler->keyPressed(event, true); + else + event->ignore(); +} + +/*! \internal */ +void QDeclarativeItem::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + keyReleasePreHandler(event); + if (event->isAccepted()) + return; + if (d->keyHandler) + d->keyHandler->keyReleased(event, true); + else + event->ignore(); +} + +/*! \internal */ +void QDeclarativeItem::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QDeclarativeItem); + inputMethodPreHandler(event); + if (event->isAccepted()) + return; + if (d->keyHandler) + d->keyHandler->inputMethodEvent(event, true); + else + event->ignore(); +} + +/*! \internal */ +QVariant QDeclarativeItem::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QDeclarativeItem); + QVariant v; + if (d->keyHandler) + v = d->keyHandler->inputMethodQuery(query); + + if (!v.isValid()) + v = QGraphicsObject::inputMethodQuery(query); + + return v; +} + +/*! + \internal + */ +void QDeclarativeItem::keyPressPreHandler(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + if (d->keyHandler && !d->doneEventPreHandler) + d->keyHandler->keyPressed(event, false); + else + event->ignore(); + d->doneEventPreHandler = true; +} + +/*! + \internal + */ +void QDeclarativeItem::keyReleasePreHandler(QKeyEvent *event) +{ + Q_D(QDeclarativeItem); + if (d->keyHandler && !d->doneEventPreHandler) + d->keyHandler->keyReleased(event, false); + else + event->ignore(); + d->doneEventPreHandler = true; +} + +/*! + \internal + */ +void QDeclarativeItem::inputMethodPreHandler(QInputMethodEvent *event) +{ + Q_D(QDeclarativeItem); + if (d->keyHandler && !d->doneEventPreHandler) + d->keyHandler->inputMethodEvent(event, false); + else + event->ignore(); + d->doneEventPreHandler = true; +} + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::left() const +{ + return anchorLines()->left; +} + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::right() const +{ + return anchorLines()->right; +} + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::horizontalCenter() const +{ + return anchorLines()->hCenter; +} + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::top() const +{ + return anchorLines()->top; +} + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::bottom() const +{ + return anchorLines()->bottom; +} + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::verticalCenter() const +{ + return anchorLines()->vCenter; +} + + +/*! + \internal +*/ +QDeclarative1AnchorLine QDeclarativeItemPrivate::baseline() const +{ + return anchorLines()->baseline; +} + +/*! + \qmlproperty AnchorLine Item::anchors.top + \qmlproperty AnchorLine Item::anchors.bottom + \qmlproperty AnchorLine Item::anchors.left + \qmlproperty AnchorLine Item::anchors.right + \qmlproperty AnchorLine Item::anchors.horizontalCenter + \qmlproperty AnchorLine Item::anchors.verticalCenter + \qmlproperty AnchorLine Item::anchors.baseline + + \qmlproperty Item Item::anchors.fill + \qmlproperty Item Item::anchors.centerIn + + \qmlproperty real Item::anchors.margins + \qmlproperty real Item::anchors.topMargin + \qmlproperty real Item::anchors.bottomMargin + \qmlproperty real Item::anchors.leftMargin + \qmlproperty real Item::anchors.rightMargin + \qmlproperty real Item::anchors.horizontalCenterOffset + \qmlproperty real Item::anchors.verticalCenterOffset + \qmlproperty real Item::anchors.baselineOffset + + \qmlproperty bool Item::anchors.mirrored + + Anchors provide a way to position an item by specifying its + relationship with other items. + + Margins apply to top, bottom, left, right, and fill anchors. + The \c anchors.margins property can be used to set all of the various margins at once, to the same value. + Note that margins are anchor-specific and are not applied if an item does not + use anchors. + + Offsets apply for horizontal center, vertical center, and baseline anchors. + + \table + \row + \o \image declarative-anchors_example.png + \o Text anchored to Image, horizontally centered and vertically below, with a margin. + \qml + Item { + Image { + id: pic + // ... + } + Text { + id: label + anchors.horizontalCenter: pic.horizontalCenter + anchors.top: pic.bottom + anchors.topMargin: 5 + // ... + } + } + \endqml + \row + \o \image declarative-anchors_example2.png + \o + Left of Text anchored to right of Image, with a margin. The y + property of both defaults to 0. + + \qml + Item { + Image { + id: pic + // ... + } + Text { + id: label + anchors.left: pic.right + anchors.leftMargin: 5 + // ... + } + } + \endqml + \endtable + + \c anchors.fill provides a convenient way for one item to have the + same geometry as another item, and is equivalent to connecting all + four directional anchors. + + To clear an anchor value, set it to \c undefined. + + \c anchors.mirrored returns true it the layout has been \l {LayoutMirroring}{mirrored}. + + \note You can only anchor an item to siblings or a parent. + + For more information see \l {anchor-layout}{Anchor Layouts}. +*/ + +/*! + \property QDeclarativeItem::baselineOffset + \brief The position of the item's baseline in local coordinates. + + The baseline of a \l Text item is the imaginary line on which the text + sits. Controls containing text usually set their baseline to the + baseline of their text. + + For non-text items, a default baseline offset of 0 is used. +*/ +qreal QDeclarativeItem::baselineOffset() const +{ + Q_D(const QDeclarativeItem); + if (!d->baselineOffset.isValid()) { + return 0.0; + } else + return d->baselineOffset; +} + +void QDeclarativeItem::setBaselineOffset(qreal offset) +{ + Q_D(QDeclarativeItem); + if (offset == d->baselineOffset) + return; + + d->baselineOffset = offset; + + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Geometry) { + QDeclarative1AnchorsPrivate *anchor = change.listener->anchorPrivate(); + if (anchor) + anchor->updateVerticalAnchors(); + } + } + emit baselineOffsetChanged(offset); +} + +/*! + \qmlproperty real Item::rotation + This property holds the rotation of the item in degrees clockwise. + + This specifies how many degrees to rotate the item around its transformOrigin. + The default rotation is 0 degrees (i.e. not rotated at all). + + \table + \row + \o \image declarative-rotation.png + \o + \qml + Rectangle { + color: "blue" + width: 100; height: 100 + Rectangle { + color: "red" + x: 25; y: 25; width: 50; height: 50 + rotation: 30 + } + } + \endqml + \endtable + + \sa transform, Rotation +*/ + +/*! + \qmlproperty real Item::scale + This property holds the scale of the item. + + A scale of less than 1 means the item will be displayed smaller than + normal, and a scale of greater than 1 means the item will be + displayed larger than normal. A negative scale means the item will + be mirrored. + + By default, items are displayed at a scale of 1 (i.e. at their + normal size). + + Scaling is from the item's transformOrigin. + + \table + \row + \o \image declarative-scale.png + \o + \qml + Rectangle { + color: "blue" + width: 100; height: 100 + Rectangle { + color: "green" + width: 25; height: 25 + } + Rectangle { + color: "red" + x: 25; y: 25; width: 50; height: 50 + scale: 1.4 + } + } + \endqml + \endtable + + \sa transform, Scale +*/ + +/*! + \qmlproperty real Item::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + When this property is set, the specified opacity is also applied + individually to child items. In almost all cases this is what you want, + but in some cases it may produce undesired results. For example in the + second set of rectangles below, the red rectangle has specified an opacity + of 0.5, which affects the opacity of its blue child rectangle even though + the child has not specified an opacity. + + \table + \row + \o \image declarative-item_opacity1.png + \o + \qml + Item { + Rectangle { + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \row + \o \image declarative-item_opacity2.png + \o + \qml + Item { + Rectangle { + opacity: 0.5 + color: "red" + width: 100; height: 100 + Rectangle { + color: "blue" + x: 50; y: 50; width: 100; height: 100 + } + } + } + \endqml + \endtable + + If an item's opacity is set to 0, the item will no longer receive mouse + events, but will continue to receive key events and will retain the keyboard + \l focus if it has been set. (In contrast, setting the \l visible property + to \c false stops both mouse and keyboard events, and also removes focus + from the item.) +*/ + +/*! + Returns a value indicating whether mouse input should + remain with this item exclusively. + + \sa setKeepMouseGrab() + */ +bool QDeclarativeItem::keepMouseGrab() const +{ + Q_D(const QDeclarativeItem); + return d->keepMouse; +} + +/*! + The flag indicating whether the mouse should remain + with this item is set to \a keep. + + This is useful for items that wish to grab and keep mouse + interaction following a predefined gesture. For example, + an item that is interested in horizontal mouse movement + may set keepMouseGrab to true once a threshold has been + exceeded. Once keepMouseGrab has been set to true, filtering + items will not react to mouse events. + + If the item does not indicate that it wishes to retain mouse grab, + a filtering item may steal the grab. For example, Flickable may attempt + to steal a mouse grab if it detects that the user has begun to + move the viewport. + + \sa keepMouseGrab() + */ +void QDeclarativeItem::setKeepMouseGrab(bool keep) +{ + Q_D(QDeclarativeItem); + d->keepMouse = keep; +} + +/*! + \qmlmethod object Item::mapFromItem(Item item, real x, real y) + + Maps the point (\a x, \a y), which is in \a item's coordinate system, to + this item's coordinate system, and returns an object with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps the point from the coordinate + system of the root QML view. +*/ +void QDeclarativeItem::mapFromItem(QDeclarativeV8Function *args) const +{ + if (args->Length() != 0) { + v8::Local<v8::Value> item = (*args)[0]; + QV8Engine *engine = args->engine(); + + QDeclarativeItem *itemObj = 0; + if (!item->IsNull()) + itemObj = qobject_cast<QDeclarativeItem*>(engine->toQObject(item)); + + if (!itemObj && !item->IsNull()) { + qmlInfo(this) << "mapFromItem() given argument \"" << engine->toString(item->ToString()) + << "\" which is neither null nor an Item"; + return; + } + + v8::Local<v8::Object> rv = v8::Object::New(); + args->returnValue(rv); + + qreal x = (args->Length() > 1)?(*args)[1]->NumberValue():0; + qreal y = (args->Length() > 2)?(*args)[2]->NumberValue():0; + + QPointF p = QGraphicsItem::mapFromItem(itemObj, x, y); + + rv->Set(v8::String::New("x"), v8::Number::New(p.x())); + rv->Set(v8::String::New("y"), v8::Number::New(p.y())); + } +} + +/*! + \qmlmethod object Item::mapToItem(Item item, real x, real y) + + Maps the point (\a x, \a y), which is in this item's coordinate system, to + \a item's coordinate system, and returns an object with \c x and \c y + properties matching the mapped cooordinate. + + If \a item is a \c null value, this maps \a x and \a y to the coordinate + system of the root QML view. +*/ +void QDeclarativeItem::mapToItem(QDeclarativeV8Function *args) const +{ + if (args->Length() != 0) { + v8::Local<v8::Value> item = (*args)[0]; + QV8Engine *engine = args->engine(); + + QDeclarativeItem *itemObj = 0; + if (!item->IsNull()) + itemObj = qobject_cast<QDeclarativeItem*>(engine->toQObject(item)); + + if (!itemObj && !item->IsNull()) { + qmlInfo(this) << "mapToItem() given argument \"" << engine->toString(item->ToString()) + << "\" which is neither null nor an Item"; + return; + } + + v8::Local<v8::Object> rv = v8::Object::New(); + args->returnValue(rv); + + qreal x = (args->Length() > 1)?(*args)[1]->NumberValue():0; + qreal y = (args->Length() > 2)?(*args)[2]->NumberValue():0; + + QPointF p = QGraphicsItem::mapToItem(itemObj, x, y); + + rv->Set(v8::String::New("x"), v8::Number::New(p.x())); + rv->Set(v8::String::New("y"), v8::Number::New(p.y())); + } +} + +/*! + \qmlmethod Item::forceActiveFocus() + + Forces active focus on the item. + + This method sets focus on the item and makes sure that all the focus scopes + higher in the object hierarchy are also given the focus. +*/ + +/*! + Forces active focus on the item. + + This method sets focus on the item and makes sure that all the focus scopes + higher in the object hierarchy are also given the focus. +*/ +void QDeclarativeItem::forceActiveFocus() +{ + setFocus(true); + QGraphicsItem *parent = parentItem(); + while (parent) { + if (parent->flags() & QGraphicsItem::ItemIsFocusScope) + parent->setFocus(Qt::OtherFocusReason); + parent = parent->parentItem(); + } +} + + +/*! + \qmlmethod Item::childAt(real x, real y) + + Returns the visible child item at point (\a x, \a y), which is in this + item's coordinate system, or \c null if there is no such item. +*/ + +/*! + Returns the visible child item at point (\a x, \a y), which is in this + item's coordinate system, or 0 if there is no such item. +*/ +QDeclarativeItem *QDeclarativeItem::childAt(qreal x, qreal y) const +{ + const QList<QGraphicsItem *> children = childItems(); + for (int i = children.count()-1; i >= 0; --i) { + if (QDeclarativeItem *child = qobject_cast<QDeclarativeItem *>(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; +} + +void QDeclarativeItemPrivate::focusChanged(bool flag) +{ + Q_Q(QDeclarativeItem); + if (!(flags & QGraphicsItem::ItemIsFocusScope) && parent) + emit q->activeFocusChanged(flag); //see also QDeclarativeItemPrivate::subFocusItemChange() + emit q->focusChanged(flag); +} + +QDeclarativeListProperty<QObject> QDeclarativeItemPrivate::resources() +{ + return QDeclarativeListProperty<QObject>(q_func(), 0, QDeclarativeItemPrivate::resources_append, + QDeclarativeItemPrivate::resources_count, + QDeclarativeItemPrivate::resources_at, + QDeclarativeItemPrivate::resources_clear + ); +} + +/*! + \qmlproperty list<State> Item::states + This property holds a list of states defined by the item. + + \qml + Item { + states: [ + State { + // ... + }, + State { + // ... + } + // ... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ + +QDeclarativeListProperty<QDeclarative1State> QDeclarativeItemPrivate::states() +{ + return _states()->statesProperty(); +} + +/*! + \qmlproperty list<Transition> Item::transitions + This property holds a list of transitions defined by the item. + + \qml + Item { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ + + +QDeclarativeListProperty<QDeclarative1Transition> QDeclarativeItemPrivate::transitions() +{ + return _states()->transitionsProperty(); +} + +/* + \qmlproperty list<Filter> Item::filter + This property holds a list of graphical filters to be applied to the item. + + \l {Filter}{Filters} include things like \l {Blur}{blurring} + the item, or giving it a \l Reflection. Some + filters may not be available on all canvases; if a filter is not + available on a certain canvas, it will simply not be applied for + that canvas (but the QML will still be considered valid). + + \qml + Item { + filter: [ + Blur { + // ... + }, + Reflection { + // ... + } + // ... + ] + } + \endqml +*/ + +/*! + \qmlproperty bool Item::clip + This property holds whether clipping is enabled. The default clip value is \c false. + + If clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + \property QDeclarativeItem::clip + This property holds whether clipping is enabled. The default clip value is \c false. + + If clipping is enabled, an item will clip its own painting, as well + as the painting of its children, to its bounding rectangle. If you set + clipping during an item's paint operation, remember to re-set it to + prevent clipping the rest of your scene. + + Non-rectangular clipping regions are not supported for performance reasons. +*/ + +/*! + \qmlproperty string Item::state + + This property holds the name of the current state of the item. + + This property is often used in scripts to change between states. For + example: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + If the item is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return an + item to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ + +QString QDeclarativeItemPrivate::state() const +{ + if (!_stateGroup) + return QString(); + else + return _stateGroup->state(); +} + +void QDeclarativeItemPrivate::setState(const QString &state) +{ + _states()->setState(state); +} + +/*! + \qmlproperty list<Transform> Item::transform + This property holds the list of transformations to apply. + + For more information see \l Transform. +*/ + +/*! \internal */ +QDeclarativeListProperty<QGraphicsTransform> QDeclarativeItem::transform() +{ + Q_D(QDeclarativeItem); + return QDeclarativeListProperty<QGraphicsTransform>(this, 0, d->transform_append, d->transform_count, + d->transform_at, d->transform_clear); +} + +/*! + \internal + + classBegin() is called when the item is constructed, but its + properties have not yet been set. + + \sa componentComplete(), isComponentComplete() +*/ +void QDeclarativeItem::classBegin() +{ + Q_D(QDeclarativeItem); + d->componentComplete = false; + if (d->_stateGroup) + d->_stateGroup->classBegin(); + if (d->_anchors) + d->_anchors->classBegin(); +} + +/*! + \internal + + componentComplete() is called when all items in the component + have been constructed. It is often desirable to delay some + processing until the component is complete an all bindings in the + component have been resolved. +*/ +void QDeclarativeItem::componentComplete() +{ + Q_D(QDeclarativeItem); + d->componentComplete = true; + if (d->_stateGroup) + d->_stateGroup->componentComplete(); + if (d->_anchors) { + d->_anchors->componentComplete(); + d->_anchors->d_func()->updateOnComplete(); + } + if (d->keyHandler) + d->keyHandler->componentComplete(); + if (d->_contents) + d->_contents->complete(); +} + +QDeclarative1StateGroup *QDeclarativeItemPrivate::_states() +{ + Q_Q(QDeclarativeItem); + if (!_stateGroup) { + _stateGroup = new QDeclarative1StateGroup; + if (!componentComplete) + _stateGroup->classBegin(); + QObject::connect(_stateGroup, SIGNAL(stateChanged(QString)), + q, SIGNAL(stateChanged(QString))); + } + + return _stateGroup; +} + +QDeclarativeItemPrivate::AnchorLines::AnchorLines(QGraphicsObject *q) +{ + left.item = q; + left.anchorLine = QDeclarative1AnchorLine::Left; + right.item = q; + right.anchorLine = QDeclarative1AnchorLine::Right; + hCenter.item = q; + hCenter.anchorLine = QDeclarative1AnchorLine::HCenter; + top.item = q; + top.anchorLine = QDeclarative1AnchorLine::Top; + bottom.item = q; + bottom.anchorLine = QDeclarative1AnchorLine::Bottom; + vCenter.item = q; + vCenter.anchorLine = QDeclarative1AnchorLine::VCenter; + baseline.item = q; + baseline.anchorLine = QDeclarative1AnchorLine::Baseline; +} + +QPointF QDeclarativeItemPrivate::computeTransformOrigin() const +{ + Q_Q(const QDeclarativeItem); + + QRectF br = q->boundingRect(); + + switch(origin) { + default: + case QDeclarativeItem::TopLeft: + return QPointF(0, 0); + case QDeclarativeItem::Top: + return QPointF(br.width() / 2., 0); + case QDeclarativeItem::TopRight: + return QPointF(br.width(), 0); + case QDeclarativeItem::Left: + return QPointF(0, br.height() / 2.); + case QDeclarativeItem::Center: + return QPointF(br.width() / 2., br.height() / 2.); + case QDeclarativeItem::Right: + return QPointF(br.width(), br.height() / 2.); + case QDeclarativeItem::BottomLeft: + return QPointF(0, br.height()); + case QDeclarativeItem::Bottom: + return QPointF(br.width() / 2., br.height()); + case QDeclarativeItem::BottomRight: + return QPointF(br.width(), br.height()); + } +} + +/*! \internal */ +bool QDeclarativeItem::sceneEvent(QEvent *event) +{ + Q_D(QDeclarativeItem); + if (event->type() == QEvent::KeyPress) { + QKeyEvent *k = static_cast<QKeyEvent *>(event); + if ((k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) && + !(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { + keyPressEvent(static_cast<QKeyEvent *>(event)); + if (!event->isAccepted()) + return QGraphicsItem::sceneEvent(event); + else + return true; + } else { + return QGraphicsItem::sceneEvent(event); + } + } else { + bool rv = QGraphicsItem::sceneEvent(event); + + if (event->type() == QEvent::FocusIn || + event->type() == QEvent::FocusOut) { + d->focusChanged(hasActiveFocus()); + } + return rv; + } +} + +/*! + \internal + + Note that unlike QGraphicsItems, QDeclarativeItem::itemChange() is \e not called + during initial widget polishing. Items wishing to optimize start-up construction + should instead consider using componentComplete(). +*/ +QVariant QDeclarativeItem::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarativeItem); + switch (change) { + case ItemParentHasChanged: + d->resolveLayoutMirror(); + emit parentChanged(parentItem()); + d->parentNotifier.notify(); + break; + case ItemVisibleHasChanged: { + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Visibility) { + change.listener->itemVisibilityChanged(this); + } + } + } + break; + case ItemOpacityHasChanged: { + for(int ii = 0; ii < d->changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = d->changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::Opacity) { + change.listener->itemOpacityChanged(this); + } + } + } + break; + case ItemChildAddedChange: + if (d->_contents && d->componentComplete) + d->_contents->childAdded(qobject_cast<QDeclarativeItem*>( + value.value<QGraphicsItem*>())); + break; + case ItemChildRemovedChange: + if (d->_contents && d->componentComplete) + d->_contents->childRemoved(qobject_cast<QDeclarativeItem*>( + value.value<QGraphicsItem*>())); + break; + default: + break; + } + + return QGraphicsItem::itemChange(change, value); +} + +/*! \internal */ +QRectF QDeclarativeItem::boundingRect() const +{ + Q_D(const QDeclarativeItem); + return QRectF(0, 0, d->mWidth, d->mHeight); +} + +/*! + \enum QDeclarativeItem::TransformOrigin + + Controls the point about which simple transforms like scale apply. + + \value TopLeft The top-left corner of the item. + \value Top The center point of the top of the item. + \value TopRight The top-right corner of the item. + \value Left The left most point of the vertical middle. + \value Center The center of the item. + \value Right The right most point of the vertical middle. + \value BottomLeft The bottom-left corner of the item. + \value Bottom The center point of the bottom of the item. + \value BottomRight The bottom-right corner of the item. +*/ + +/*! + Returns the current transform origin. +*/ +QDeclarativeItem::TransformOrigin QDeclarativeItem::transformOrigin() const +{ + Q_D(const QDeclarativeItem); + return d->origin; +} + +/*! + Set the transform \a origin. +*/ +void QDeclarativeItem::setTransformOrigin(TransformOrigin origin) +{ + Q_D(QDeclarativeItem); + if (origin != d->origin) { + d->origin = origin; + if (d->transformData) + QGraphicsItem::setTransformOriginPoint(d->computeTransformOrigin()); + else + d->transformOriginDirty = true; + emit transformOriginChanged(d->origin); + } +} + +void QDeclarativeItemPrivate::transformChanged() +{ + Q_Q(QDeclarativeItem); + if (transformOriginDirty) { + q->QGraphicsItem::setTransformOriginPoint(computeTransformOrigin()); + transformOriginDirty = false; + } +} + +/*! + \property QDeclarativeItem::smooth + \brief whether the item is smoothly transformed. + + This property is provided purely for the purpose of optimization. Turning + smooth transforms off is faster, but looks worse; turning smooth + transformations on is slower, but looks better. + + By default smooth transformations are off. +*/ + +/*! + Returns true if the item should be drawn with antialiasing and + smooth pixmap filtering, false otherwise. + + The default is false. + + \sa setSmooth() +*/ +bool QDeclarativeItem::smooth() const +{ + Q_D(const QDeclarativeItem); + return d->smooth; +} + +/*! + Sets whether the item should be drawn with antialiasing and + smooth pixmap filtering to \a smooth. + + \sa smooth() +*/ +void QDeclarativeItem::setSmooth(bool smooth) +{ + Q_D(QDeclarativeItem); + if (d->smooth == smooth) + return; + d->smooth = smooth; + emit smoothChanged(smooth); + update(); +} + +/*! + \property QDeclarativeItem::anchors + \internal +*/ + +/*! + \property QDeclarativeItem::left + \internal +*/ + +/*! + \property QDeclarativeItem::right + \internal +*/ + +/*! + \property QDeclarativeItem::horizontalCenter + \internal +*/ + +/*! + \property QDeclarativeItem::top + \internal +*/ + +/*! + \property QDeclarativeItem::bottom + \internal +*/ + +/*! + \property QDeclarativeItem::verticalCenter + \internal +*/ + +/*! + \property QDeclarativeItem::focus + \internal +*/ + +/*! + \property QDeclarativeItem::transform + \internal +*/ + +/*! + \property QDeclarativeItem::transformOrigin + \internal +*/ + +/*! + \property QDeclarativeItem::activeFocus + \internal +*/ + +/*! + \property QDeclarativeItem::baseline + \internal +*/ + +/*! + \property QDeclarativeItem::data + \internal +*/ + +/*! + \property QDeclarativeItem::resources + \internal +*/ + +/*! + \property QDeclarativeItem::state + \internal +*/ + +/*! + \property QDeclarativeItem::states + \internal +*/ + +/*! + \property QDeclarativeItem::transformOriginPoint + \internal +*/ + +/*! + \property QDeclarativeItem::transitions + \internal +*/ + +/*! + \internal + Return the width of the item +*/ +qreal QDeclarativeItem::width() const +{ + Q_D(const QDeclarativeItem); + return d->width(); +} + +/*! + \internal + Set the width of the item +*/ +void QDeclarativeItem::setWidth(qreal w) +{ + Q_D(QDeclarativeItem); + d->setWidth(w); +} + +/*! + \internal + Reset the width of the item +*/ +void QDeclarativeItem::resetWidth() +{ + Q_D(QDeclarativeItem); + d->resetWidth(); +} + +/*! + \internal + Return the width of the item +*/ +qreal QDeclarativeItemPrivate::width() const +{ + return mWidth; +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::setWidth(qreal w) +{ + Q_Q(QDeclarativeItem); + if (qIsNaN(w)) + return; + + widthValid = true; + if (mWidth == w) + return; + + qreal oldWidth = mWidth; + + q->prepareGeometryChange(); + mWidth = w; + + q->geometryChanged(QRectF(q->x(), q->y(), width(), height()), + QRectF(q->x(), q->y(), oldWidth, height())); +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::resetWidth() +{ + Q_Q(QDeclarativeItem); + widthValid = false; + q->setImplicitWidth(q->implicitWidth()); +} + +void QDeclarativeItemPrivate::implicitWidthChanged() +{ + Q_Q(QDeclarativeItem); + emit q->implicitWidthChanged(); +} + +qreal QDeclarativeItemPrivate::implicitWidth() const +{ + return mImplicitWidth; +} + +/*! + Returns the width of the item that is implied by other properties that determine the content. +*/ +qreal QDeclarativeItem::implicitWidth() const +{ + Q_D(const QDeclarativeItem); + return d->implicitWidth(); +} + +/*! + Sets the implied width of the item to \a w. + This is the width implied by other properties that determine the content. +*/ +void QDeclarativeItem::setImplicitWidth(qreal w) +{ + Q_D(QDeclarativeItem); + bool changed = w != d->mImplicitWidth; + d->mImplicitWidth = w; + if (d->mWidth == w || widthValid()) { + if (changed) + d->implicitWidthChanged(); + return; + } + + qreal oldWidth = d->mWidth; + + prepareGeometryChange(); + d->mWidth = w; + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, height())); + + if (changed) + d->implicitWidthChanged(); +} + +/*! + Returns whether the width property has been set explicitly. +*/ +bool QDeclarativeItem::widthValid() const +{ + Q_D(const QDeclarativeItem); + return d->widthValid; +} + +/*! + \internal + Return the height of the item +*/ +qreal QDeclarativeItem::height() const +{ + Q_D(const QDeclarativeItem); + return d->height(); +} + +/*! + \internal + Set the height of the item +*/ +void QDeclarativeItem::setHeight(qreal h) +{ + Q_D(QDeclarativeItem); + d->setHeight(h); +} + +/*! + \internal + Reset the height of the item +*/ +void QDeclarativeItem::resetHeight() +{ + Q_D(QDeclarativeItem); + d->resetHeight(); +} + +/*! + \internal +*/ +qreal QDeclarativeItemPrivate::height() const +{ + return mHeight; +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::setHeight(qreal h) +{ + Q_Q(QDeclarativeItem); + if (qIsNaN(h)) + return; + + heightValid = true; + if (mHeight == h) + return; + + qreal oldHeight = mHeight; + + q->prepareGeometryChange(); + mHeight = h; + + q->geometryChanged(QRectF(q->x(), q->y(), width(), height()), + QRectF(q->x(), q->y(), width(), oldHeight)); +} + +/*! + \internal +*/ +void QDeclarativeItemPrivate::resetHeight() +{ + Q_Q(QDeclarativeItem); + heightValid = false; + q->setImplicitHeight(q->implicitHeight()); +} + +void QDeclarativeItemPrivate::implicitHeightChanged() +{ + Q_Q(QDeclarativeItem); + emit q->implicitHeightChanged(); +} + +qreal QDeclarativeItemPrivate::implicitHeight() const +{ + return mImplicitHeight; +} + +/*! + Returns the height of the item that is implied by other properties that determine the content. +*/ +qreal QDeclarativeItem::implicitHeight() const +{ + Q_D(const QDeclarativeItem); + return d->implicitHeight(); +} + +/*! + \qmlproperty real Item::implicitWidth + \qmlproperty real Item::implicitHeight + \since Quick 1.1 + + Defines the natural width or height of the Item if no \l width or \l height is specified. + + The default implicit size for most items is 0x0, however some elements have an inherent + implicit size which cannot be overridden, e.g. Image, Text. + + Setting the implicit size is useful for defining components that have a preferred size + based on their content, for example: + + \qml + // Label.qml + import QtQuick 1.1 + + Item { + property alias icon: image.source + property alias label: text.text + implicitWidth: text.implicitWidth + image.implicitWidth + implicitHeight: Math.max(text.implicitHeight, image.implicitHeight) + Image { id: image } + Text { + id: text + wrapMode: Text.Wrap + anchors.left: image.right; anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + } + \endqml + + \bold Note: using implicitWidth of Text or TextEdit and setting the width explicitly + incurs a performance penalty as the text must be laid out twice. +*/ + + +/*! + Sets the implied height of the item to \a h. + This is the height implied by other properties that determine the content. +*/ +void QDeclarativeItem::setImplicitHeight(qreal h) +{ + Q_D(QDeclarativeItem); + bool changed = h != d->mImplicitHeight; + d->mImplicitHeight = h; + if (d->mHeight == h || heightValid()) { + if (changed) + d->implicitHeightChanged(); + return; + } + + qreal oldHeight = d->mHeight; + + prepareGeometryChange(); + d->mHeight = h; + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), width(), oldHeight)); + + if (changed) + d->implicitHeightChanged(); +} + +/*! + Returns whether the height property has been set explicitly. +*/ +bool QDeclarativeItem::heightValid() const +{ + Q_D(const QDeclarativeItem); + return d->heightValid; +} + +/*! \internal */ +void QDeclarativeItem::setSize(const QSizeF &size) +{ + Q_D(QDeclarativeItem); + d->heightValid = true; + d->widthValid = true; + + if (d->height() == size.height() && d->width() == size.width()) + return; + + qreal oldHeight = d->height(); + qreal oldWidth = d->width(); + + prepareGeometryChange(); + d->setHeight(size.height()); + d->setWidth(size.width()); + + geometryChanged(QRectF(x(), y(), width(), height()), + QRectF(x(), y(), oldWidth, oldHeight)); +} + +/*! + \qmlproperty bool Item::activeFocus + + This property indicates whether the item has active focus. + + An item with active focus will receive keyboard input, + or is a FocusScope ancestor of the item that will receive keyboard input. + + Usually, activeFocus is gained by setting focus on an item and its enclosing + FocusScopes. In the following example \c input will have activeFocus. + \qml + Rectangle { + FocusScope { + focus: true + TextInput { + id: input + focus: true + } + } + } + \endqml + + \sa focus, {qmlfocus}{Keyboard Focus} +*/ + +/*! \internal */ +bool QDeclarativeItem::hasActiveFocus() const +{ + Q_D(const QDeclarativeItem); + return focusItem() == this || + (d->flags & QGraphicsItem::ItemIsFocusScope && focusItem() != 0); +} + +/*! + \qmlproperty bool Item::focus + This property indicates whether the item has focus within the enclosing focus scope. If true, this item + will gain active focus when the enclosing focus scope gains active focus. + In the following example, \c input will be given active focus when \c scope gains active focus. + \qml + Rectangle { + FocusScope { + id: scope + TextInput { + id: input + focus: true + } + } + } + \endqml + + For the purposes of this property, the scene as a whole is assumed to act like a focus scope. + On a practical level, that means the following QML will give active focus to \c input on startup. + + \qml + Rectangle { + TextInput { + id: input + focus: true + } + } + \endqml + + \sa activeFocus, {qmlfocus}{Keyboard Focus} +*/ + +/*! \internal */ +bool QDeclarativeItem::hasFocus() const +{ + Q_D(const QDeclarativeItem); + QGraphicsItem *p = d->parent; + while (p) { + if (p->flags() & QGraphicsItem::ItemIsFocusScope) { + return p->focusScopeItem() == this; + } + p = p->parentItem(); + } + + return hasActiveFocus(); +} + +/*! \internal */ +void QDeclarativeItem::setFocus(bool focus) +{ + if (focus) + QGraphicsItem::setFocus(Qt::OtherFocusReason); + else + QGraphicsItem::clearFocus(); +} + +/*! + \internal +*/ +void QDeclarativeItem::paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *) +{ +} + +/*! + \internal +*/ +bool QDeclarativeItem::event(QEvent *ev) +{ + Q_D(QDeclarativeItem); + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::InputMethod: + d->doneEventPreHandler = false; + break; + default: + break; + } + + return QGraphicsObject::event(ev); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, QDeclarativeItem *item) +{ + if (!item) { + debug << "QDeclarativeItem(0)"; + return debug; + } + + debug << item->metaObject()->className() << "(this =" << ((void*)item) + << ", parent =" << ((void*)item->parentItem()) + << ", geometry =" << QRectF(item->pos(), QSizeF(item->width(), item->height())) + << ", z =" << item->zValue() << ')'; + return debug; +} +#endif + +qint64 QDeclarativeItemPrivate::consistentTime = -1; +void QDeclarativeItemPrivate::setConsistentTime(qint64 t) +{ + consistentTime = t; +} + +class QElapsedTimerConsistentTimeHack_1 +{ +public: + void start() { + t1 = QDeclarativeItemPrivate::consistentTime; + t2 = 0; + } + qint64 elapsed() { + return QDeclarativeItemPrivate::consistentTime - t1; + } + qint64 restart() { + qint64 val = QDeclarativeItemPrivate::consistentTime - t1; + t1 = QDeclarativeItemPrivate::consistentTime; + t2 = 0; + return val; + } + +private: + qint64 t1; + qint64 t2; +}; + +void QDeclarativeItemPrivate::start(QElapsedTimer &t) +{ + if (QDeclarativeItemPrivate::consistentTime == -1) + t.start(); + else + ((QElapsedTimerConsistentTimeHack_1*)&t)->start(); +} + +qint64 QDeclarativeItemPrivate::elapsed(QElapsedTimer &t) +{ + if (QDeclarativeItemPrivate::consistentTime == -1) + return t.elapsed(); + else + return ((QElapsedTimerConsistentTimeHack_1*)&t)->elapsed(); +} + +qint64 QDeclarativeItemPrivate::restart(QElapsedTimer &t) +{ + if (QDeclarativeItemPrivate::consistentTime == -1) + return t.restart(); + else + return ((QElapsedTimerConsistentTimeHack_1*)&t)->restart(); +} + +QT_END_NAMESPACE + +#include <moc_qdeclarativeitem.cpp> diff --git a/src/qtquick1/graphicsitems/qdeclarativeitem.h b/src/qtquick1/graphicsitems/qdeclarativeitem.h new file mode 100644 index 0000000000..a3f4931316 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeitem.h @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEM_H +#define QDECLARATIVEITEM_H + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativecomponent.h> + +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicstransform.h> +#include <QtGui/qfont.h> +#include <QtGui/qaction.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1State; +class QDeclarative1AnchorLine; +class QDeclarative1Transition; +class QDeclarative1KeyEvent; +class QDeclarative1Anchors; +class QDeclarativeItemPrivate; +class QDeclarativeV8Function; +class Q_DECLARATIVE_EXPORT QDeclarativeItem : public QGraphicsObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + + Q_PROPERTY(QDeclarativeItem * parent READ parentItem WRITE setParentItem NOTIFY parentChanged DESIGNABLE false FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty<QObject> data READ data DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty<QObject> resources READ resources DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty<QDeclarative1State> states READ states DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarativeListProperty<QDeclarative1Transition> transitions READ transitions DESIGNABLE false) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QRectF childrenRect READ childrenRect NOTIFY childrenRectChanged DESIGNABLE false FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1Anchors * anchors READ anchors DESIGNABLE false CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine left READ left CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine right READ right CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine horizontalCenter READ horizontalCenter CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine top READ top CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine bottom READ bottom CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine verticalCenter READ verticalCenter CONSTANT FINAL) + Q_PRIVATE_PROPERTY(QDeclarativeItem::d_func(), QDeclarative1AnchorLine 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) // ### move to QGI/QGO, NOTIFY + Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL) + Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged) + Q_PROPERTY(QDeclarativeListProperty<QGraphicsTransform> transform READ transform DESIGNABLE false FINAL) + Q_PROPERTY(TransformOrigin transformOrigin READ transformOrigin WRITE setTransformOrigin NOTIFY transformOriginChanged) + Q_PROPERTY(QPointF transformOriginPoint READ transformOriginPoint) // transformOriginPoint is read-only for Item + Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged REVISION 1) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged REVISION 1) + + Q_ENUMS(TransformOrigin) + Q_CLASSINFO("DefaultProperty", "data") + +public: + enum TransformOrigin { + TopLeft, Top, TopRight, + Left, Center, Right, + BottomLeft, Bottom, BottomRight + }; + + QDeclarativeItem(QDeclarativeItem *parent = 0); + virtual ~QDeclarativeItem(); + + QDeclarativeItem *parentItem() const; + void setParentItem(QDeclarativeItem *parent); + + QRectF childrenRect(); + + bool clip() const; + void setClip(bool); + + qreal baselineOffset() const; + void setBaselineOffset(qreal); + + QDeclarativeListProperty<QGraphicsTransform> transform(); + + 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); + + bool smooth() const; + void setSmooth(bool); + + QRectF boundingRect() const; + virtual void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + bool hasActiveFocus() const; + bool hasFocus() const; + void setFocus(bool); + + bool keepMouseGrab() const; + void setKeepMouseGrab(bool); + + Q_INVOKABLE void mapFromItem(QDeclarativeV8Function*) const; + Q_INVOKABLE void mapToItem(QDeclarativeV8Function*) const; + Q_INVOKABLE void forceActiveFocus(); + Q_INVOKABLE QDeclarativeItem *childAt(qreal x, qreal y) const; + +Q_SIGNALS: + void childrenRectChanged(const QRectF &); + void baselineOffsetChanged(qreal); + void stateChanged(const QString &); + void focusChanged(bool); + void activeFocusChanged(bool); + void parentChanged(QDeclarativeItem *); + void transformOriginChanged(TransformOrigin); + void smoothChanged(bool); + void clipChanged(bool); + Q_REVISION(1) void implicitWidthChanged(); + Q_REVISION(1) void implicitHeightChanged(); + +protected: + bool isComponentComplete() const; + virtual bool sceneEvent(QEvent *); + virtual bool event(QEvent *); + virtual QVariant itemChange(GraphicsItemChange, const QVariant &); + + 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 QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + void keyPressPreHandler(QKeyEvent *); + void keyReleasePreHandler(QKeyEvent *); + void inputMethodPreHandler(QInputMethodEvent *); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +protected: + QDeclarativeItem(QDeclarativeItemPrivate &dd, QDeclarativeItem *parent = 0); + +private: + Q_DISABLE_COPY(QDeclarativeItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +}; + +template<typename T> + T qobject_cast(QGraphicsObject *o) +{ + QObject *obj = o; + return qobject_cast<T>(obj); +} + +// ### move to QGO +template<typename T> +T qobject_cast(QGraphicsItem *item) +{ + if (!item) return 0; + QObject *o = item->toGraphicsObject(); + return qobject_cast<T>(o); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug Q_DECLARATIVE_EXPORT operator<<(QDebug debug, QDeclarativeItem *item); +#endif + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeItem) +QML_DECLARE_TYPE(QGraphicsObject) +QML_DECLARE_TYPE(QGraphicsTransform) +QML_DECLARE_TYPE(QGraphicsScale) +QML_DECLARE_TYPE(QGraphicsRotation) +QML_DECLARE_TYPE(QGraphicsWidget) +QML_DECLARE_TYPE(QAction) + +QT_END_HEADER + +#endif // QDECLARATIVEITEM_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeitem_p.h b/src/qtquick1/graphicsitems/qdeclarativeitem_p.h new file mode 100644 index 0000000000..58d57abbf3 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeitem_p.h @@ -0,0 +1,635 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEM_P_H +#define QDECLARATIVEITEM_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 "QtQuick1/qdeclarativeitem.h" + +#include "QtQuick1/private/qdeclarativeanchors_p.h" +#include "QtQuick1/private/qdeclarativeanchors_p_p.h" +#include "QtQuick1/private/qdeclarativeitemchangelistener_p.h" +#include <QtDeclarative/private/qpodvector_p.h> + +#include <QtQuick1/private/qdeclarativestate_p.h> +#include <QtDeclarative/private/qdeclarativenullablevalue_p_p.h> +#include <QtDeclarative/private/qdeclarativenotifier_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativecontext.h> + +#include <QtCore/qlist.h> +#include <QtCore/qrect.h> +#include <QtCore/qdebug.h> + +#include <private/qgraphicsitem_p.h> + +QT_BEGIN_NAMESPACE + +class QNetworkReply; + +class QDeclarativeItemKeyFilter; +class QDeclarative1LayoutMirroringAttached; + +//### merge into private? +class QDeclarative1Contents : public QObject, public QDeclarativeItemChangeListener +{ + Q_OBJECT +public: + QDeclarative1Contents(QDeclarativeItem *item); + ~QDeclarative1Contents(); + + QRectF rectF() const; + + void childRemoved(QDeclarativeItem *item); + void childAdded(QDeclarativeItem *item); + + void calcGeometry() { calcWidth(); calcHeight(); } + void complete(); + +Q_SIGNALS: + void rectChanged(QRectF); + +protected: + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void itemDestroyed(QDeclarativeItem *item); + //void itemVisibilityChanged(QDeclarativeItem *item) + +private: + void calcHeight(QDeclarativeItem *changed = 0); + void calcWidth(QDeclarativeItem *changed = 0); + + QDeclarativeItem *m_item; + qreal m_x; + qreal m_y; + qreal m_width; + qreal m_height; +}; + +class Q_DECLARATIVE_EXPORT QDeclarativeItemPrivate : public QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeItem) + +public: + QDeclarativeItemPrivate() + : _anchors(0), _contents(0), + baselineOffset(0), + _anchorLines(0), + _stateGroup(0), origin(QDeclarativeItem::Center), + widthValid(false), heightValid(false), + componentComplete(true), keepMouse(false), + smooth(false), transformOriginDirty(true), doneEventPreHandler(false), + inheritedLayoutMirror(false), effectiveLayoutMirror(false), isMirrorImplicit(true), + inheritMirrorFromParent(false), inheritMirrorFromItem(false), keyHandler(0), + mWidth(0), mHeight(0), mImplicitWidth(0), mImplicitHeight(0), attachedLayoutDirection(0), hadSubFocusItem(false) + { + QGraphicsItemPrivate::acceptedMouseButtons = 0; + isDeclarativeItem = 1; + QGraphicsItemPrivate::flags = QGraphicsItem::GraphicsItemFlags( + QGraphicsItem::ItemHasNoContents + | QGraphicsItem::ItemIsFocusable + | QGraphicsItem::ItemNegativeZStacksBehindParent); + } + + void init(QDeclarativeItem *parent) + { + Q_Q(QDeclarativeItem); + if (parent) { + QDeclarative_setParent_noEvent(q, parent); + q->setParentItem(parent); + QDeclarativeItemPrivate *parentPrivate = QDeclarativeItemPrivate::get(parent); + setImplicitLayoutMirror(parentPrivate->inheritedLayoutMirror, parentPrivate->inheritMirrorFromParent); + } + baselineOffset.invalidate(); + mouseSetsFocus = false; + } + + bool isMirrored() const { + return effectiveLayoutMirror; + } + + // Private Properties + qreal width() const; + void setWidth(qreal); + void resetWidth(); + + qreal height() const; + void setHeight(qreal); + void resetHeight(); + + virtual qreal implicitWidth() const; + virtual qreal implicitHeight() const; + virtual void implicitWidthChanged(); + virtual void implicitHeightChanged(); + + void resolveLayoutMirror(); + void setImplicitLayoutMirror(bool mirror, bool inherit); + void setLayoutMirror(bool mirror); + + QDeclarativeListProperty<QObject> data(); + QDeclarativeListProperty<QObject> resources(); + + QDeclarativeListProperty<QDeclarative1State> states(); + QDeclarativeListProperty<QDeclarative1Transition> transitions(); + + QString state() const; + void setState(const QString &); + + QDeclarative1AnchorLine left() const; + QDeclarative1AnchorLine right() const; + QDeclarative1AnchorLine horizontalCenter() const; + QDeclarative1AnchorLine top() const; + QDeclarative1AnchorLine bottom() const; + QDeclarative1AnchorLine verticalCenter() const; + QDeclarative1AnchorLine 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> *); + + // transform property + static int transform_count(QDeclarativeListProperty<QGraphicsTransform> *list); + static void transform_append(QDeclarativeListProperty<QGraphicsTransform> *list, QGraphicsTransform *); + static QGraphicsTransform *transform_at(QDeclarativeListProperty<QGraphicsTransform> *list, int); + static void transform_clear(QDeclarativeListProperty<QGraphicsTransform> *list); + + static QDeclarativeItemPrivate* get(QDeclarativeItem *item) + { + return item->d_func(); + } + + // Accelerated property accessors + QDeclarativeNotifier parentNotifier; + static void parentProperty(QObject *o, void *rv, QDeclarativeNotifierEndpoint *e); + + QDeclarative1Anchors *anchors() { + if (!_anchors) { + Q_Q(QDeclarativeItem); + _anchors = new QDeclarative1Anchors(q); + if (!componentComplete) + _anchors->classBegin(); + } + return _anchors; + } + QDeclarative1Anchors *_anchors; + QDeclarative1Contents *_contents; + + QDeclarativeNullableValue<qreal> baselineOffset; + + struct AnchorLines { + AnchorLines(QGraphicsObject *); + QDeclarative1AnchorLine left; + QDeclarative1AnchorLine right; + QDeclarative1AnchorLine hCenter; + QDeclarative1AnchorLine top; + QDeclarative1AnchorLine bottom; + QDeclarative1AnchorLine vCenter; + QDeclarative1AnchorLine baseline; + }; + mutable AnchorLines *_anchorLines; + AnchorLines *anchorLines() const { + Q_Q(const QDeclarativeItem); + if (!_anchorLines) _anchorLines = + new AnchorLines(const_cast<QDeclarativeItem *>(q)); + return _anchorLines; + } + + enum ChangeType { + Geometry = 0x01, + SiblingOrder = 0x02, + Visibility = 0x04, + Opacity = 0x08, + Destroyed = 0x10 + }; + + Q_DECLARE_FLAGS(ChangeTypes, ChangeType) + + struct ChangeListener { + ChangeListener(QDeclarativeItemChangeListener *l, QDeclarativeItemPrivate::ChangeTypes t) : listener(l), types(t) {} + QDeclarativeItemChangeListener *listener; + QDeclarativeItemPrivate::ChangeTypes types; + bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; } + }; + + void addItemChangeListener(QDeclarativeItemChangeListener *listener, ChangeTypes types) { + changeListeners.append(ChangeListener(listener, types)); + } + void removeItemChangeListener(QDeclarativeItemChangeListener *, ChangeTypes types); + QPODVector<ChangeListener,4> changeListeners; + + QDeclarative1StateGroup *_states(); + QDeclarative1StateGroup *_stateGroup; + + QDeclarativeItem::TransformOrigin origin:5; + bool widthValid:1; + bool heightValid:1; + bool componentComplete:1; + bool keepMouse:1; + bool smooth:1; + bool transformOriginDirty : 1; + bool doneEventPreHandler : 1; + bool inheritedLayoutMirror:1; + bool effectiveLayoutMirror:1; + bool isMirrorImplicit:1; + bool inheritMirrorFromParent:1; + bool inheritMirrorFromItem:1; + + QDeclarativeItemKeyFilter *keyHandler; + + qreal mWidth; + qreal mHeight; + qreal mImplicitWidth; + qreal mImplicitHeight; + + QDeclarative1LayoutMirroringAttached* attachedLayoutDirection; + + bool hadSubFocusItem; + + QPointF computeTransformOrigin() const; + + virtual void setPosHelper(const QPointF &pos) + { + Q_Q(QDeclarativeItem); + QRectF oldGeometry(this->pos.x(), this->pos.y(), mWidth, mHeight); + QGraphicsItemPrivate::setPosHelper(pos); + q->geometryChanged(QRectF(this->pos.x(), this->pos.y(), mWidth, mHeight), oldGeometry); + } + + // Reimplemented from QGraphicsItemPrivate + virtual void subFocusItemChange() + { + bool hasSubFocusItem = subFocusItem != 0; + if (((flags & QGraphicsItem::ItemIsFocusScope) || !parent) && hasSubFocusItem != hadSubFocusItem) + emit q_func()->activeFocusChanged(hasSubFocusItem); + //see also QDeclarativeItemPrivate::focusChanged + hadSubFocusItem = hasSubFocusItem; + } + + // Reimplemented from QGraphicsItemPrivate + virtual void focusScopeItemChange(bool isSubFocusItem) + { + emit q_func()->focusChanged(isSubFocusItem); + } + + + // Reimplemented from QGraphicsItemPrivate + virtual void siblingOrderChange() + { + Q_Q(QDeclarativeItem); + for(int ii = 0; ii < changeListeners.count(); ++ii) { + const QDeclarativeItemPrivate::ChangeListener &change = changeListeners.at(ii); + if (change.types & QDeclarativeItemPrivate::SiblingOrder) { + change.listener->itemSiblingOrderChanged(q); + } + } + } + + // Reimplemented from QGraphicsItemPrivate + virtual void transformChanged(); + + virtual void focusChanged(bool); + + 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 QDeclarativeItem, 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 QDeclarativeItemKeyFilter +{ +public: + QDeclarativeItemKeyFilter(QDeclarativeItem * = 0); + virtual ~QDeclarativeItemKeyFilter(); + + 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: + QDeclarativeItemKeyFilter *m_next; +}; + +class QDeclarative1KeyNavigationAttachedPrivate : public QObjectPrivate +{ +public: + QDeclarative1KeyNavigationAttachedPrivate() + : 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) {} + + QDeclarativeItem *left; + QDeclarativeItem *right; + QDeclarativeItem *up; + QDeclarativeItem *down; + QDeclarativeItem *tab; + QDeclarativeItem *backtab; + bool leftSet : 1; + bool rightSet : 1; + bool upSet : 1; + bool downSet : 1; + bool tabSet : 1; + bool backtabSet : 1; +}; + +class QDeclarative1KeyNavigationAttached : public QObject, public QDeclarativeItemKeyFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1KeyNavigationAttached) + + Q_PROPERTY(QDeclarativeItem *left READ left WRITE setLeft NOTIFY leftChanged) + Q_PROPERTY(QDeclarativeItem *right READ right WRITE setRight NOTIFY rightChanged) + Q_PROPERTY(QDeclarativeItem *up READ up WRITE setUp NOTIFY upChanged) + Q_PROPERTY(QDeclarativeItem *down READ down WRITE setDown NOTIFY downChanged) + Q_PROPERTY(QDeclarativeItem *tab READ tab WRITE setTab NOTIFY tabChanged) + Q_PROPERTY(QDeclarativeItem *backtab READ backtab WRITE setBacktab NOTIFY backtabChanged) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) + + Q_ENUMS(Priority) + +public: + QDeclarative1KeyNavigationAttached(QObject * = 0); + + QDeclarativeItem *left() const; + void setLeft(QDeclarativeItem *); + QDeclarativeItem *right() const; + void setRight(QDeclarativeItem *); + QDeclarativeItem *up() const; + void setUp(QDeclarativeItem *); + QDeclarativeItem *down() const; + void setDown(QDeclarativeItem *); + QDeclarativeItem *tab() const; + void setTab(QDeclarativeItem *); + QDeclarativeItem *backtab() const; + void setBacktab(QDeclarativeItem *); + + enum Priority { BeforeItem, AfterItem }; + Priority priority() const; + void setPriority(Priority); + + static QDeclarative1KeyNavigationAttached *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(QDeclarativeItem *currentItem, const char *dir); +}; + +class QDeclarative1LayoutMirroringAttached : 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 QDeclarative1LayoutMirroringAttached(QObject *parent = 0); + + bool enabled() const; + void setEnabled(bool); + void resetEnabled(); + + bool childrenInherit() const; + void setChildrenInherit(bool); + + static QDeclarative1LayoutMirroringAttached *qmlAttachedProperties(QObject *); +Q_SIGNALS: + void enabledChanged(); + void childrenInheritChanged(); +private: + friend class QDeclarativeItemPrivate; + QDeclarativeItemPrivate *itemPrivate; +}; + +class QDeclarative1KeysAttachedPrivate : public QObjectPrivate +{ +public: + QDeclarative1KeysAttachedPrivate() + : QObjectPrivate(), inPress(false), inRelease(false) + , inIM(false), enabled(true), imeItem(0), item(0) + {} + + bool isConnected(const char *signalName); + + QGraphicsItem *finalFocusProxy(QGraphicsItem *item) const + { + QGraphicsItem *fp; + while ((fp = item->focusProxy())) + item = fp; + return item; + } + + //loop detection + bool inPress:1; + bool inRelease:1; + bool inIM:1; + + bool enabled : 1; + + QGraphicsItem *imeItem; + QList<QDeclarativeItem *> targets; + QDeclarativeItem *item; +}; + +class QDeclarative1KeysAttached : public QObject, public QDeclarativeItemKeyFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1KeysAttached) + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeItem> forwardTo READ forwardTo) + Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged) + + Q_ENUMS(Priority) + +public: + QDeclarative1KeysAttached(QObject *parent=0); + ~QDeclarative1KeysAttached(); + + bool enabled() const { Q_D(const QDeclarative1KeysAttached); return d->enabled; } + void setEnabled(bool enabled) { + Q_D(QDeclarative1KeysAttached); + if (enabled != d->enabled) { + d->enabled = enabled; + emit enabledChanged(); + } + } + + enum Priority { BeforeItem, AfterItem}; + Priority priority() const; + void setPriority(Priority); + + QDeclarativeListProperty<QDeclarativeItem> forwardTo() { + Q_D(QDeclarative1KeysAttached); + return QDeclarativeListProperty<QDeclarativeItem>(this, d->targets); + } + + virtual void componentComplete(); + + static QDeclarative1KeysAttached *qmlAttachedProperties(QObject *); + +Q_SIGNALS: + void enabledChanged(); + void priorityChanged(); + void pressed(QDeclarative1KeyEvent *event); + void released(QDeclarative1KeyEvent *event); + void digit0Pressed(QDeclarative1KeyEvent *event); + void digit1Pressed(QDeclarative1KeyEvent *event); + void digit2Pressed(QDeclarative1KeyEvent *event); + void digit3Pressed(QDeclarative1KeyEvent *event); + void digit4Pressed(QDeclarative1KeyEvent *event); + void digit5Pressed(QDeclarative1KeyEvent *event); + void digit6Pressed(QDeclarative1KeyEvent *event); + void digit7Pressed(QDeclarative1KeyEvent *event); + void digit8Pressed(QDeclarative1KeyEvent *event); + void digit9Pressed(QDeclarative1KeyEvent *event); + + void leftPressed(QDeclarative1KeyEvent *event); + void rightPressed(QDeclarative1KeyEvent *event); + void upPressed(QDeclarative1KeyEvent *event); + void downPressed(QDeclarative1KeyEvent *event); + void tabPressed(QDeclarative1KeyEvent *event); + void backtabPressed(QDeclarative1KeyEvent *event); + + void asteriskPressed(QDeclarative1KeyEvent *event); + void numberSignPressed(QDeclarative1KeyEvent *event); + void escapePressed(QDeclarative1KeyEvent *event); + void returnPressed(QDeclarative1KeyEvent *event); + void enterPressed(QDeclarative1KeyEvent *event); + void deletePressed(QDeclarative1KeyEvent *event); + void spacePressed(QDeclarative1KeyEvent *event); + void backPressed(QDeclarative1KeyEvent *event); + void cancelPressed(QDeclarative1KeyEvent *event); + void selectPressed(QDeclarative1KeyEvent *event); + void yesPressed(QDeclarative1KeyEvent *event); + void noPressed(QDeclarative1KeyEvent *event); + void context1Pressed(QDeclarative1KeyEvent *event); + void context2Pressed(QDeclarative1KeyEvent *event); + void context3Pressed(QDeclarative1KeyEvent *event); + void context4Pressed(QDeclarative1KeyEvent *event); + void callPressed(QDeclarative1KeyEvent *event); + void hangupPressed(QDeclarative1KeyEvent *event); + void flipPressed(QDeclarative1KeyEvent *event); + void menuPressed(QDeclarative1KeyEvent *event); + void volumeUpPressed(QDeclarative1KeyEvent *event); + void volumeDownPressed(QDeclarative1KeyEvent *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[]; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarativeItemPrivate::ChangeTypes); + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1KeysAttached) +QML_DECLARE_TYPEINFO(QDeclarative1KeysAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarative1KeyNavigationAttached) +QML_DECLARE_TYPEINFO(QDeclarative1KeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarative1LayoutMirroringAttached) +QML_DECLARE_TYPEINFO(QDeclarative1LayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QDECLARATIVEITEM_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeitemchangelistener_p.h b/src/qtquick1/graphicsitems/qdeclarativeitemchangelistener_p.h new file mode 100644 index 0000000000..4b10a38660 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeitemchangelistener_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEMCHANGELISTENER +#define QDECLARATIVEITEMCHANGELISTENER + +// +// 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 QDeclarativeItem; +class QDeclarative1AnchorsPrivate; +class QDeclarativeItemChangeListener +{ +public: + virtual void itemGeometryChanged(QDeclarativeItem *, const QRectF &, const QRectF &) {} + virtual void itemSiblingOrderChanged(QDeclarativeItem *) {} + virtual void itemVisibilityChanged(QDeclarativeItem *) {} + virtual void itemOpacityChanged(QDeclarativeItem *) {} + virtual void itemDestroyed(QDeclarativeItem *) {} + virtual QDeclarative1AnchorsPrivate *anchorPrivate() { return 0; } +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEITEMCHANGELISTENER diff --git a/src/qtquick1/graphicsitems/qdeclarativeitemsmodule.cpp b/src/qtquick1/graphicsitems/qdeclarativeitemsmodule.cpp new file mode 100644 index 0000000000..78f13cdbd8 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeitemsmodule.cpp @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeitemsmodule_p.h" + +#include <QtGui/qaction.h> +#include <QtGui/qvalidator.h> +#include <QtGui/qgraphicseffect.h> +#include <QtGui/qgraphicsitem.h> + +#include "QtQuick1/private/qdeclarativeevents_p_p.h" +#include "QtQuick1/private/qdeclarativescalegrid_p_p.h" +#include "QtQuick1/private/qdeclarativeanimatedimage_p.h" +#include "QtQuick1/private/qdeclarativeborderimage_p.h" +#include "QtQuick1/private/qdeclarativepositioners_p.h" +#include "QtQuick1/private/qdeclarativemousearea_p.h" +#include "QtQuick1/private/qdeclarativeflickable_p.h" +#include "QtQuick1/private/qdeclarativeflickable_p_p.h" +#include "QtQuick1/private/qdeclarativeflipable_p.h" +#include "QtQuick1/private/qdeclarativefocuspanel_p.h" +#include "QtQuick1/private/qdeclarativefocusscope_p.h" +#include "QtQuick1/private/qdeclarativegridview_p.h" +#include "QtQuick1/private/qdeclarativeimage_p.h" +#include "QtQuick1/private/qdeclarativeitem_p.h" +#include "QtQuick1/private/qdeclarativelayoutitem_p.h" +#include "QtQuick1/private/qdeclarativelistview_p.h" +#include "QtQuick1/private/qdeclarativeloader_p.h" +#include "QtQuick1/private/qdeclarativemousearea_p.h" +#include "QtQuick1/private/qdeclarativepath_p.h" +#include "QtQuick1/private/qdeclarativepathview_p.h" +#include "QtQuick1/private/qdeclarativerectangle_p.h" +#include "QtQuick1/private/qdeclarativerepeater_p.h" +#include "QtQuick1/private/qdeclarativetranslate_p.h" +#include "QtQuick1/private/qdeclarativetext_p.h" +#include "QtQuick1/private/qdeclarativetextedit_p.h" +#include "QtQuick1/private/qdeclarativetextinput_p.h" +#include "QtQuick1/private/qdeclarativevisualitemmodel_p.h" +#include "QtQuick1/private/qdeclarativegraphicswidget_p.h" +#ifdef QT_WEBKIT_LIB +#include "QtQuick1/private/qdeclarativewebview_p.h" +#include "QtQuick1/private/qdeclarativewebview_p_p.h" +#endif +#include "QtQuick1/private/qdeclarativeanchors_p.h" +#include "QtQuick1/private/qdeclarativepincharea_p.h" + +static QDeclarativePrivate::AutoParentResult qgraphicsobject_autoParent(QObject *obj, QObject *parent) +{ + QGraphicsObject* gobj = qobject_cast<QGraphicsObject*>(obj); + if (!gobj) + return QDeclarativePrivate::IncompatibleObject; + + QGraphicsObject* gparent = qobject_cast<QGraphicsObject*>(parent); + if (!gparent) + return QDeclarativePrivate::IncompatibleParent; + + gobj->setParentItem(gparent); + return QDeclarativePrivate::Parented; +} + +void QDeclarative1ItemModule::defineModule(QDeclarativeQtQuick1Module::Module module) +{ + QDeclarativePrivate::RegisterAutoParent autoparent = { 0, &qgraphicsobject_autoParent }; + QDeclarativePrivate::qmlregister(QDeclarativePrivate::AutoParentRegistration, &autoparent); + + qmlRegisterType<QDeclarative1Anchors>(); + qmlRegisterType<QDeclarative1KeyEvent>(); + qmlRegisterType<QDeclarative1MouseEvent>(); + qmlRegisterType<QGraphicsObject>(); + qmlRegisterType<QGraphicsTransform>(); + qmlRegisterType<QDeclarative1PathElement>(); + qmlRegisterType<QDeclarative1Curve>(); + qmlRegisterType<QDeclarative1ScaleGrid>(); +#ifndef QT_NO_VALIDATOR + qmlRegisterType<QValidator>(); +#endif + qmlRegisterType<QDeclarative1VisualModel>(); +#ifndef QT_NO_ACTION + qmlRegisterType<QAction>(); +#endif + qmlRegisterType<QDeclarative1Pen>(); + qmlRegisterType<QDeclarative1FlickableVisibleArea>(); +#ifndef QT_NO_GRAPHICSEFFECT + qmlRegisterType<QGraphicsEffect>(); +#endif + + if (module == QDeclarativeQtQuick1Module::QtQuick1) { +#ifdef QT_NO_MOVIE + qmlRegisterTypeNotAvailable("QtQuick",1,0,"AnimatedImage", + qApp->translate("QDeclarative1AnimatedImage","Qt was built without support for QMovie")); +#else + qmlRegisterType<QDeclarative1AnimatedImage>("QtQuick",1,0,"AnimatedImage"); +#endif + qmlRegisterType<QDeclarative1BorderImage>("QtQuick",1,0,"BorderImage"); + qmlRegisterType<QDeclarative1Column>("QtQuick",1,0,"Column"); + qmlRegisterType<QDeclarative1Drag>("QtQuick",1,0,"Drag"); + qmlRegisterType<QDeclarative1Flickable>("QtQuick",1,0,"Flickable"); + qmlRegisterType<QDeclarative1Flipable>("QtQuick",1,0,"Flipable"); + qmlRegisterType<QDeclarative1Flow>("QtQuick",1,0,"Flow"); + qmlRegisterType<QDeclarative1FocusPanel>("QtQuick",1,0,"FocusPanel"); + qmlRegisterType<QDeclarative1FocusScope>("QtQuick",1,0,"FocusScope"); + qmlRegisterType<QDeclarative1Gradient>("QtQuick",1,0,"Gradient"); + qmlRegisterType<QDeclarative1GradientStop>("QtQuick",1,0,"GradientStop"); + qmlRegisterType<QDeclarative1Grid>("QtQuick",1,0,"Grid"); + qmlRegisterType<QDeclarative1GridView>("QtQuick",1,0,"GridView"); + qmlRegisterType<QDeclarative1Image>("QtQuick",1,0,"Image"); + qmlRegisterType<QDeclarativeItem>("QtQuick",1,0,"Item"); + qmlRegisterType<QDeclarative1LayoutItem>("QtQuick",1,0,"LayoutItem"); + qmlRegisterType<QDeclarative1ListView>("QtQuick",1,0,"ListView"); + qmlRegisterType<QDeclarative1Loader>("QtQuick",1,0,"Loader"); + qmlRegisterType<QDeclarative1MouseArea>("QtQuick",1,0,"MouseArea"); + qmlRegisterType<QDeclarative1Path>("QtQuick",1,0,"Path"); + qmlRegisterType<QDeclarative1PathAttribute>("QtQuick",1,0,"PathAttribute"); + qmlRegisterType<QDeclarative1PathCubic>("QtQuick",1,0,"PathCubic"); + qmlRegisterType<QDeclarative1PathLine>("QtQuick",1,0,"PathLine"); + qmlRegisterType<QDeclarative1PathPercent>("QtQuick",1,0,"PathPercent"); + qmlRegisterType<QDeclarative1PathQuad>("QtQuick",1,0,"PathQuad"); + qmlRegisterType<QDeclarative1PathView>("QtQuick",1,0,"PathView"); +#ifndef QT_NO_VALIDATOR + qmlRegisterType<QIntValidator>("QtQuick",1,0,"IntValidator"); + qmlRegisterType<QDoubleValidator>("QtQuick",1,0,"DoubleValidator"); + qmlRegisterType<QRegExpValidator>("QtQuick",1,0,"RegExpValidator"); +#endif + qmlRegisterType<QDeclarative1Rectangle>("QtQuick",1,0,"Rectangle"); + qmlRegisterType<QDeclarative1Repeater>("QtQuick",1,0,"Repeater"); + qmlRegisterType<QGraphicsRotation>("QtQuick",1,0,"Rotation"); + qmlRegisterType<QDeclarative1Row>("QtQuick",1,0,"Row"); + qmlRegisterType<QDeclarative1Translate>("QtQuick",1,0,"Translate"); + qmlRegisterType<QGraphicsScale>("QtQuick",1,0,"Scale"); + qmlRegisterType<QDeclarative1Text>("QtQuick",1,0,"Text"); + qmlRegisterType<QDeclarative1TextEdit>("QtQuick",1,0,"TextEdit"); +#ifndef QT_NO_LINEEDIT + qmlRegisterType<QDeclarative1TextInput>("QtQuick",1,0,"TextInput"); +#endif + qmlRegisterType<QDeclarative1ViewSection>("QtQuick",1,0,"ViewSection"); + qmlRegisterType<QDeclarative1VisualDataModel>("QtQuick",1,0,"VisualDataModel"); + qmlRegisterType<QDeclarative1VisualItemModel>("QtQuick",1,0,"VisualItemModel"); + + qmlRegisterType<QGraphicsWidget>("QtQuick",1,0,"QGraphicsWidget"); + qmlRegisterExtendedType<QGraphicsWidget,QDeclarative1GraphicsWidget>("QtQuick",1,0,"QGraphicsWidget"); + + qmlRegisterUncreatableType<QDeclarative1KeyNavigationAttached>("QtQuick",1,0,"KeyNavigation",QDeclarative1KeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); + qmlRegisterUncreatableType<QDeclarative1KeysAttached>("QtQuick",1,0,"Keys",QDeclarative1KeysAttached::tr("Keys is only available via attached properties")); + + // QtQuick 1.1 items + qmlRegisterType<QDeclarative1PinchArea>("QtQuick",1,1,"PinchArea"); + qmlRegisterType<QDeclarative1Pinch>("QtQuick",1,1,"Pinch"); + qmlRegisterType<QDeclarative1PinchEvent>(); + qmlRegisterType<QDeclarativeItem,1>("QtQuick",1,1,"Item"); + qmlRegisterType<QDeclarative1MouseArea,1>("QtQuick",1,1,"MouseArea"); + qmlRegisterType<QDeclarative1Flickable,1>("QtQuick",1,1,"Flickable"); + qmlRegisterType<QDeclarative1ListView,1>("QtQuick",1,1,"ListView"); + qmlRegisterType<QDeclarative1GridView,1>("QtQuick",1,1,"GridView"); + qmlRegisterType<QDeclarative1Row,1>("QtQuick",1,1,"Row"); + qmlRegisterType<QDeclarative1Grid,1>("QtQuick",1,1,"Grid"); + qmlRegisterType<QDeclarative1Flow,1>("QtQuick",1,1,"Flow"); + qmlRegisterType<QDeclarative1Repeater,1>("QtQuick",1,1,"Repeater"); + qmlRegisterType<QDeclarative1Text,1>("QtQuick",1,1,"Text"); + qmlRegisterType<QDeclarative1TextEdit,1>("QtQuick",1,1,"TextEdit"); +#ifndef QT_NO_LINEEDIT + qmlRegisterType<QDeclarative1TextInput,1>("QtQuick",1,1,"TextInput"); +#endif + qmlRegisterRevision<QDeclarative1ImageBase,1>("QtQuick",1,1); + qmlRegisterRevision<QDeclarative1ImplicitSizeItem,0>("QtQuick",1,0); + qmlRegisterRevision<QDeclarative1ImplicitSizeItem,1>("QtQuick",1,1); + qmlRegisterRevision<QDeclarative1ImplicitSizePaintedItem,0>("QtQuick",1,0); + qmlRegisterRevision<QDeclarative1ImplicitSizePaintedItem,1>("QtQuick",1,1); + qmlRegisterUncreatableType<QDeclarative1LayoutMirroringAttached>("QtQuick",1,1,"LayoutMirroring", QDeclarative1LayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties")); + } else if (module == QDeclarativeQtQuick1Module::Qt47) { +#ifdef QT_NO_MOVIE + qmlRegisterTypeNotAvailable("Qt",4,7,"AnimatedImage", + qApp->translate("QDeclarative1AnimatedImage","Qt was built without support for QMovie")); +#else + qmlRegisterType<QDeclarative1AnimatedImage>("Qt",4,7,"AnimatedImage"); +#endif + qmlRegisterType<QDeclarative1BorderImage>("Qt",4,7,"BorderImage"); + qmlRegisterType<QDeclarative1Column>("Qt",4,7,"Column"); + qmlRegisterType<QDeclarative1Drag>("Qt",4,7,"Drag"); + qmlRegisterType<QDeclarative1Flickable>("Qt",4,7,"Flickable"); + qmlRegisterType<QDeclarative1Flipable>("Qt",4,7,"Flipable"); + qmlRegisterType<QDeclarative1Flow>("Qt",4,7,"Flow"); + qmlRegisterType<QDeclarative1FocusPanel>("Qt",4,7,"FocusPanel"); + qmlRegisterType<QDeclarative1FocusScope>("Qt",4,7,"FocusScope"); + qmlRegisterType<QDeclarative1Gradient>("Qt",4,7,"Gradient"); + qmlRegisterType<QDeclarative1GradientStop>("Qt",4,7,"GradientStop"); + qmlRegisterType<QDeclarative1Grid>("Qt",4,7,"Grid"); + qmlRegisterType<QDeclarative1GridView>("Qt",4,7,"GridView"); + qmlRegisterType<QDeclarative1Image>("Qt",4,7,"Image"); + qmlRegisterType<QDeclarativeItem>("Qt",4,7,"Item"); + qmlRegisterType<QDeclarative1LayoutItem>("Qt",4,7,"LayoutItem"); + qmlRegisterType<QDeclarative1ListView>("Qt",4,7,"ListView"); + qmlRegisterType<QDeclarative1Loader>("Qt",4,7,"Loader"); + qmlRegisterType<QDeclarative1MouseArea>("Qt",4,7,"MouseArea"); + qmlRegisterType<QDeclarative1Path>("Qt",4,7,"Path"); + qmlRegisterType<QDeclarative1PathAttribute>("Qt",4,7,"PathAttribute"); + qmlRegisterType<QDeclarative1PathCubic>("Qt",4,7,"PathCubic"); + qmlRegisterType<QDeclarative1PathLine>("Qt",4,7,"PathLine"); + qmlRegisterType<QDeclarative1PathPercent>("Qt",4,7,"PathPercent"); + qmlRegisterType<QDeclarative1PathQuad>("Qt",4,7,"PathQuad"); + qmlRegisterType<QDeclarative1PathView>("Qt",4,7,"PathView"); +#ifndef QT_NO_VALIDATOR + qmlRegisterType<QIntValidator>("Qt",4,7,"IntValidator"); + qmlRegisterType<QDoubleValidator>("Qt",4,7,"DoubleValidator"); + qmlRegisterType<QRegExpValidator>("Qt",4,7,"RegExpValidator"); +#endif + qmlRegisterType<QDeclarative1Rectangle>("Qt",4,7,"Rectangle"); + qmlRegisterType<QDeclarative1Repeater>("Qt",4,7,"Repeater"); + qmlRegisterType<QGraphicsRotation>("Qt",4,7,"Rotation"); + qmlRegisterType<QDeclarative1Row>("Qt",4,7,"Row"); + qmlRegisterType<QDeclarative1Translate>("Qt",4,7,"Translate"); + qmlRegisterType<QGraphicsScale>("Qt",4,7,"Scale"); + qmlRegisterType<QDeclarative1Text>("Qt",4,7,"Text"); + qmlRegisterType<QDeclarative1TextEdit>("Qt",4,7,"TextEdit"); +#ifndef QT_NO_LINEEDIT + qmlRegisterType<QDeclarative1TextInput>("Qt",4,7,"TextInput"); +#endif + qmlRegisterType<QDeclarative1ViewSection>("Qt",4,7,"ViewSection"); + qmlRegisterType<QDeclarative1VisualDataModel>("Qt",4,7,"VisualDataModel"); + qmlRegisterType<QDeclarative1VisualItemModel>("Qt",4,7,"VisualItemModel"); + + qmlRegisterType<QGraphicsWidget>("Qt",4,7,"QGraphicsWidget"); + qmlRegisterExtendedType<QGraphicsWidget,QDeclarative1GraphicsWidget>("Qt",4,7,"QGraphicsWidget"); + + qmlRegisterUncreatableType<QDeclarative1KeyNavigationAttached>("Qt",4,7,"KeyNavigation",QDeclarative1KeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); + qmlRegisterUncreatableType<QDeclarative1KeysAttached>("Qt",4,7,"Keys",QDeclarative1KeysAttached::tr("Keys is only available via attached properties")); + } +} + + + diff --git a/src/qtquick1/graphicsitems/qdeclarativeitemsmodule_p.h b/src/qtquick1/graphicsitems/qdeclarativeitemsmodule_p.h new file mode 100644 index 0000000000..39d55f3b5e --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeitemsmodule_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEITEMMODULE_H +#define QDECLARATIVEITEMMODULE_H + +#include <QtDeclarative/qdeclarative.h> +#include "../qtquick1_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1ItemModule +{ +public: + static void defineModule(QDeclarativeQtQuick1Module::Module module); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEITEMMODULE_H diff --git a/src/qtquick1/graphicsitems/qdeclarativelayoutitem.cpp b/src/qtquick1/graphicsitems/qdeclarativelayoutitem.cpp new file mode 100644 index 0000000000..eea3830fca --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativelayoutitem.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativelayoutitem_p.h" + +#include <QDebug> + +#include <limits.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass LayoutItem QDeclarative1LayoutItem + \ingroup qml-utility-elements + \since 4.7 + \brief The LayoutItem element allows declarative UI elements to be placed inside Qt's Graphics View layouts. + + LayoutItem is a variant of \l Item with additional size hint properties. These properties provide the size hints + necessary for items to work in conjunction with Qt \l{Graphics View Framework}{Graphics View} layout classes + such as QGraphicsLinearLayout and QGraphicsGridLayout. The Qt layout mechanisms will resize the LayoutItem as appropriate, + taking its size hints into account, and you can propagate this to the other elements in your UI via anchors and bindings. + + This is a QGraphicsLayoutItem subclass, and its properties merely expose the QGraphicsLayoutItem functionality to QML. + + The \l{declarative/cppextensions/qgraphicslayouts/layoutitem}{LayoutItem example} + demonstrates how a LayoutItem can be used within a \l{Graphics View Framework}{Graphics View} + scene. +*/ + +/*! + \qmlproperty QSizeF LayoutItem::maximumSize + + The maximumSize property can be set to specify the maximum desired size of this LayoutItem +*/ + +/*! + \qmlproperty QSizeF LayoutItem::minimumSize + + The minimumSize property can be set to specify the minimum desired size of this LayoutItem +*/ + +/*! + \qmlproperty QSizeF LayoutItem::preferredSize + + The preferredSize property can be set to specify the preferred size of this LayoutItem +*/ + +QDeclarative1LayoutItem::QDeclarative1LayoutItem(QDeclarativeItem* parent) + : QDeclarativeItem(parent), m_maximumSize(INT_MAX,INT_MAX), m_minimumSize(0,0), m_preferredSize(0,0) +{ + setGraphicsItem(this); +} + +void QDeclarative1LayoutItem::setGeometry(const QRectF & rect) +{ + setX(rect.x()); + setY(rect.y()); + setWidth(rect.width()); + setHeight(rect.height()); +} + +QSizeF QDeclarative1LayoutItem::sizeHint(Qt::SizeHint w, const QSizeF &constraint) const +{ + Q_UNUSED(constraint); + if(w == Qt::MinimumSize){ + return m_minimumSize; + }else if(w == Qt::MaximumSize){ + return m_maximumSize; + }else{ + return m_preferredSize; + } +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativelayoutitem_p.h b/src/qtquick1/graphicsitems/qdeclarativelayoutitem_p.h new file mode 100644 index 0000000000..9db5f19d44 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativelayoutitem_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGRAPHICSLAYOUTITEM_H +#define QDECLARATIVEGRAPHICSLAYOUTITEM_H +#include "qdeclarativeitem.h" + +#include <QGraphicsLayoutItem> +#include <QSizeF> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1LayoutItem : public QDeclarativeItem, public QGraphicsLayoutItem +{ + Q_OBJECT + Q_INTERFACES(QGraphicsLayoutItem) + Q_PROPERTY(QSizeF maximumSize READ maximumSize WRITE setMaximumSize NOTIFY maximumSizeChanged) + Q_PROPERTY(QSizeF minimumSize READ minimumSize WRITE setMinimumSize NOTIFY minimumSizeChanged) + Q_PROPERTY(QSizeF preferredSize READ preferredSize WRITE setPreferredSize NOTIFY preferredSizeChanged) +public: + QDeclarative1LayoutItem(QDeclarativeItem* parent=0); + + QSizeF maximumSize() const { return m_maximumSize; } + void setMaximumSize(const QSizeF &s) { if(s==m_maximumSize) return; m_maximumSize = s; emit maximumSizeChanged(); } + + QSizeF minimumSize() const { return m_minimumSize; } + void setMinimumSize(const QSizeF &s) { if(s==m_minimumSize) return; m_minimumSize = s; emit minimumSizeChanged(); } + + QSizeF preferredSize() const { return m_preferredSize; } + void setPreferredSize(const QSizeF &s) { if(s==m_preferredSize) return; m_preferredSize = s; emit preferredSizeChanged(); } + + virtual void setGeometry(const QRectF & rect); +protected: + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +Q_SIGNALS: + void maximumSizeChanged(); + void minimumSizeChanged(); + void preferredSizeChanged(); + +private: + QSizeF m_maximumSize; + QSizeF m_minimumSize; + QSizeF m_preferredSize; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1LayoutItem) + +QT_END_HEADER +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativelistview.cpp b/src/qtquick1/graphicsitems/qdeclarativelistview.cpp new file mode 100644 index 0000000000..42f1e1919b --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativelistview.cpp @@ -0,0 +1,3617 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativelistview_p.h" + +#include "QtQuick1/private/qdeclarativeflickable_p_p.h" +#include "QtQuick1/private/qdeclarativevisualitemmodel_p.h" + +#include "QtQuick1/private/qdeclarativesmoothedanimation_p_p.h" +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <qmath.h> +#include <QKeyEvent> + +QT_BEGIN_NAMESPACE + + + +void QDeclarative1ViewSection::setProperty(const QString &property) +{ + if (property != m_property) { + m_property = property; + emit propertyChanged(); + } +} + +void QDeclarative1ViewSection::setCriteria(QDeclarative1ViewSection::SectionCriteria criteria) +{ + if (criteria != m_criteria) { + m_criteria = criteria; + emit criteriaChanged(); + } +} + +void QDeclarative1ViewSection::setDelegate(QDeclarativeComponent *delegate) +{ + if (delegate != m_delegate) { + m_delegate = delegate; + emit delegateChanged(); + } +} + +QString QDeclarative1ViewSection::sectionString(const QString &value) +{ + if (m_criteria == FirstCharacter) + return value.isEmpty() ? QString() : value.at(0); + else + return value; +} + +//---------------------------------------------------------------------------- + +class FxListItem1 +{ +public: + FxListItem1(QDeclarativeItem *i, QDeclarative1ListView *v) : item(i), section(0), view(v) { + attached = static_cast<QDeclarative1ListViewAttached*>(qmlAttachedPropertiesObject<QDeclarative1ListView>(item)); + if (attached) + attached->setView(view); + } + ~FxListItem1() {} + qreal position() const { + if (section) { + if (view->orientation() == QDeclarative1ListView::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() == QDeclarative1ListView::Vertical) + return item->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x()); + } + qreal size() const { + if (section) + return (view->orientation() == QDeclarative1ListView::Vertical ? item->height()+section->height() : item->width()+section->width()); + else + return (view->orientation() == QDeclarative1ListView::Vertical ? item->height() : item->width()); + } + qreal itemSize() const { + return (view->orientation() == QDeclarative1ListView::Vertical ? item->height() : item->width()); + } + qreal sectionSize() const { + if (section) + return (view->orientation() == QDeclarative1ListView::Vertical ? section->height() : section->width()); + return 0.0; + } + qreal endPosition() const { + if (view->orientation() == QDeclarative1ListView::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() == QDeclarative1ListView::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() == QDeclarative1ListView::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()); + } + + QDeclarativeItem *item; + QDeclarativeItem *section; + QDeclarative1ListView *view; + QDeclarative1ListViewAttached *attached; + int index; +}; + +//---------------------------------------------------------------------------- + +class QDeclarative1ListViewPrivate : public QDeclarative1FlickablePrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ListView) + +public: + QDeclarative1ListViewPrivate() + : currentItem(0), orient(QDeclarative1ListView::Vertical), layoutDirection(Qt::LeftToRight) + , visiblePos(0), visibleIndex(0) + , averageSize(100.0), currentIndex(-1), requestedIndex(-1) + , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeStartValid(false), highlightRangeEndValid(false) + , 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(QDeclarative1ListView::NoHighlightRange) + , snapMode(QDeclarative1ListView::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) + , minExtentDirty(true), maxExtentDirty(true) + {} + + void init(); + void clear(); + FxListItem1 *createItem(int modelIndex); + void releaseItem(FxListItem1 *item); + + FxListItem1 *visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxListItem1 *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; + } + + FxListItem1 *firstVisibleItem() const { + const qreal pos = isRightToLeft() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem1 *item = visibleItems.at(i); + if (item->index != -1 && item->endPosition() > pos) + return item; + } + return visibleItems.count() ? visibleItems.first() : 0; + } + + FxListItem1 *nextVisibleItem() const { + const qreal pos = isRightToLeft() ? -position()-size() : position(); + bool foundFirst = false; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem1 *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. + FxListItem1 *itemBefore(int modelIndex) const { + if (modelIndex < visibleIndex) + return 0; + int idx = 1; + int lastIndex = -1; + while (idx < visibleItems.count()) { + FxListItem1 *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(QDeclarative1ListView); + if (q->isComponentComplete()) { + if (header) { + if (q->scene()) + q->scene()->removeItem(header->item); + header->item->deleteLater(); + delete header; + header = 0; + } + if (footer) { + if (q->scene()) + q->scene()->removeItem(footer->item); + footer->item->deleteLater(); + delete footer; + footer = 0; + } + updateHeader(); + updateFooter(); + clear(); + setPosition(0); + q->refill(); + updateCurrent(currentIndex); + } + } + + void mirrorChange() { + Q_Q(QDeclarative1ListView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); + } + + bool isRightToLeft() const { + Q_Q(const QDeclarative1ListView); + return orient == QDeclarative1ListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; + } + + qreal position() const { + Q_Q(const QDeclarative1ListView); + return orient == QDeclarative1ListView::Vertical ? q->contentY() : q->contentX(); + } + + void setPosition(qreal pos) { + Q_Q(QDeclarative1ListView); + if (orient == QDeclarative1ListView::Vertical) { + q->QDeclarative1Flickable::setContentY(pos); + } else { + if (isRightToLeft()) + q->QDeclarative1Flickable::setContentX(-pos-size()); + else + q->QDeclarative1Flickable::setContentX(pos); + } + } + qreal size() const { + Q_Q(const QDeclarative1ListView); + return orient == QDeclarative1ListView::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 (FxListItem1 *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 (FxListItem1 *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 (FxListItem1 *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 (FxListItem1 *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(); + } + + FxListItem1 *snapItemAt(qreal pos) { + FxListItem1 *snapItem = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxListItem1 *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) { + FxListItem1 *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) { + FxListItem1 *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(QDeclarative1ListView); + if (orient == QDeclarative1ListView::Vertical) { + q->setContentHeight(endPosition() - startPosition() + 1); + } else { + q->setContentWidth(endPosition() - startPosition() + 1); + } + } + + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + Q_Q(QDeclarative1ListView); + QDeclarative1FlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + if (item != contentItem && (!highlight || item != highlight->item)) { + if ((orient == QDeclarative1ListView::Vertical && newGeometry.height() != oldGeometry.height()) + || (orient == QDeclarative1ListView::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) { + FxListItem1 *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(FxListItem1 *); + 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(QDeclarative1FlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity); + + QDeclarativeGuard<QDeclarative1VisualModel> model; + QVariant modelVariant; + QList<FxListItem1*> visibleItems; + QHash<QDeclarativeItem*,int> unrequestedItems; + FxListItem1 *currentItem; + QDeclarative1ListView::Orientation orient; + Qt::LayoutDirection layoutDirection; + qreal visiblePos; + int visibleIndex; + qreal averageSize; + int currentIndex; + int requestedIndex; + int itemCount; + qreal highlightRangeStart; + qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; + QDeclarativeComponent *highlightComponent; + FxListItem1 *highlight; + FxListItem1 *trackedItem; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + int buffer; + QSmoothedAnimation_1 *highlightPosAnimator; + QSmoothedAnimation_1 *highlightSizeAnimator; + QDeclarative1ViewSection *sectionCriteria; + QString currentSection; + static const int sectionCacheSize = 4; + QDeclarativeItem *sectionCache[sectionCacheSize]; + qreal spacing; + qreal highlightMoveSpeed; + int highlightMoveDuration; + qreal highlightResizeSpeed; + int highlightResizeDuration; + QDeclarative1ListView::HighlightRangeMode highlightRange; + QDeclarative1ListView::SnapMode snapMode; + qreal overshootDist; + QDeclarativeComponent *footerComponent; + FxListItem1 *footer; + QDeclarativeComponent *headerComponent; + FxListItem1 *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; + mutable bool minExtentDirty : 1; + mutable bool maxExtentDirty : 1; +}; + +void QDeclarative1ListViewPrivate::init() +{ + Q_Q(QDeclarative1ListView); + q->setFlag(QGraphicsItem::ItemIsFocusScope); + addItemChangeListener(this, Geometry); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlickableDirection(QDeclarative1Flickable::VerticalFlick); + ::memset(sectionCache, 0, sizeof(QDeclarativeItem*) * sectionCacheSize); +} + +void QDeclarative1ListViewPrivate::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; +} + +FxListItem1 *QDeclarative1ListViewPrivate::createItem(int modelIndex) +{ + Q_Q(QDeclarative1ListView); + // create object + requestedIndex = modelIndex; + FxListItem1 *listItem = 0; + if (QDeclarativeItem *item = model->item(modelIndex, false)) { + listItem = new FxListItem1(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 (FxListItem1 *item = itemBefore(modelIndex)) + listItem->attached->m_prevSection = item->attached->section(); + else + listItem->attached->m_prevSection = sectionAt(modelIndex-1); + } + if (modelIndex < model->count()-1) { + if (FxListItem1 *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->setZValue(1); + listItem->item->setParentItem(q->contentItem()); + model->completeItem(); + } else { + listItem->item->setParentItem(q->contentItem()); + } + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::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 QDeclarative1ListViewPrivate::releaseItem(FxListItem1 *item) +{ + Q_Q(QDeclarative1ListView); + if (!item || !model) + return; + if (trackedItem == item) + trackedItem = 0; + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item->item)); + itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::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 QDeclarative1ListViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QDeclarative1ListView); + 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; + FxListItem1 *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 QDeclarative1ListViewPrivate::scheduleLayout() +{ + Q_Q(QDeclarative1ListView); + if (!layoutScheduled) { + layoutScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); + } +} + +void QDeclarative1ListViewPrivate::layout() +{ + Q_Q(QDeclarative1ListView); + 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) { + FxListItem1 *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 QDeclarative1ListViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QDeclarative1ListView); + QHash<QDeclarativeItem*,int>::iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QDeclarative1ListViewPrivate::updateUnrequestedPositions() +{ + Q_Q(QDeclarative1ListView); + if (unrequestedItems.count()) { + qreal pos = position(); + QHash<QDeclarativeItem*,int>::const_iterator it; + for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { + QDeclarativeItem *item = it.key(); + if (orient == QDeclarative1ListView::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 QDeclarative1ListViewPrivate::updateTrackedItem() +{ + Q_Q(QDeclarative1ListView); + FxListItem1 *item = currentItem; + if (highlight) + item = highlight; + trackedItem = item; + if (trackedItem) + q->trackedPositionChanged(); +} + +void QDeclarative1ListViewPrivate::createHighlight() +{ + Q_Q(QDeclarative1ListView); + bool changed = false; + if (highlight) { + if (trackedItem == highlight) + trackedItem = 0; + if (highlight->item->scene()) + highlight->item->scene()->removeItem(highlight->item); + highlight->item->deleteLater(); + delete highlight; + highlight = 0; + delete highlightPosAnimator; + delete highlightSizeAnimator; + highlightPosAnimator = 0; + highlightSizeAnimator = 0; + changed = true; + } + + if (currentItem) { + QDeclarativeItem *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<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QDeclarativeItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + highlight = new FxListItem1(item, q); + if (currentItem && autoHighlight) { + if (orient == QDeclarative1ListView::Vertical) { + highlight->item->setHeight(currentItem->item->height()); + } else { + highlight->item->setWidth(currentItem->item->width()); + } + highlight->setPosition(currentItem->itemPosition()); + } + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + const QLatin1String posProp(orient == QDeclarative1ListView::Vertical ? "y" : "x"); + highlightPosAnimator = new QSmoothedAnimation_1(q); + highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp); + highlightPosAnimator->velocity = highlightMoveSpeed; + highlightPosAnimator->userDuration = highlightMoveDuration; + const QLatin1String sizeProp(orient == QDeclarative1ListView::Vertical ? "height" : "width"); + highlightSizeAnimator = new QSmoothedAnimation_1(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 QDeclarative1ListViewPrivate::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 == QDeclarative1ListView::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 QDeclarative1ListViewPrivate::createSection(FxListItem1 *listItem) +{ + Q_Q(QDeclarative1ListView); + 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<QDeclarativeItem *>(nobj); + if (!listItem->section) { + delete nobj; + } else { + listItem->section->setZValue(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 QDeclarative1ListViewPrivate::updateSections() +{ + if (sectionCriteria && !visibleItems.isEmpty()) { + QString prevSection; + if (visibleIndex > 0) + prevSection = sectionAt(visibleIndex-1); + QDeclarative1ListViewAttached *prevAtt = 0; + int idx = -1; + for (int i = 0; i < visibleItems.count(); ++i) { + QDeclarative1ListViewAttached *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 QDeclarative1ListViewPrivate::updateCurrentSection() +{ + Q_Q(QDeclarative1ListView); + 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 QDeclarative1ListViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QDeclarative1ListView); + 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; + } + FxListItem1 *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 QDeclarative1ListViewPrivate::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 QDeclarative1ListViewPrivate::updateFooter() +{ + Q_Q(QDeclarative1ListView); + if (!footer && footerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = footerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + footer = new FxListItem1(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 QDeclarative1ListViewPrivate::updateHeader() +{ + Q_Q(QDeclarative1ListView); + if (!header && headerComponent) { + QDeclarativeItem *item = 0; + QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); + QObject *nobj = headerComponent->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + item->setZValue(1); + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + header = new FxListItem1(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 QDeclarative1ListViewPrivate::fixupPosition() +{ + if ((haveHighlightRange && highlightRange == QDeclarative1ListView::StrictlyEnforceRange) + || snapMode != QDeclarative1ListView::NoSnap) + moveReason = Other; + if (orient == QDeclarative1ListView::Vertical) + fixupY(); + else + fixupX(); +} + +void QDeclarative1ListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent) +{ + if ((orient == QDeclarative1ListView::Horizontal && &data == &vData) + || (orient == QDeclarative1ListView::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 == QDeclarative1ListView::StrictlyEnforceRange + && moveReason != QDeclarative1ListViewPrivate::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 != QDeclarative1ListView::NoSnap && moveReason != QDeclarative1ListViewPrivate::SetIndex) { + qreal tempPosition = isRightToLeft() ? -position()-size() : position(); + FxListItem1 *topItem = snapItemAt(tempPosition+highlightStart); + FxListItem1 *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 { + QDeclarative1FlickablePrivate::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 { + QDeclarative1FlickablePrivate::fixup(data, minExtent, maxExtent); + } + data.inOvershoot = false; + fixupMode = Normal; +} + +void QDeclarative1ListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, + QDeclarative1TimeLineCallback::Callback fixupCallback, qreal velocity) +{ + Q_Q(QDeclarative1ListView); + + data.fixingUp = false; + moveReason = Mouse; + if ((!haveHighlightRange || highlightRange != QDeclarative1ListView::StrictlyEnforceRange) && snapMode == QDeclarative1ListView::NoSnap) { + correctFlick = true; + QDeclarative1FlickablePrivate::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 == QDeclarative1ListView::SnapOneItem) { + if (FxListItem1 *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem()) + maxDistance = qAbs(item->position() + dataValue); + } else { + maxDistance = qAbs(minExtent - data.move.value()); + } + } + if (snapMode == QDeclarative1ListView::NoSnap && highlightRange != QDeclarative1ListView::StrictlyEnforceRange) + data.flickTarget = minExtent; + } else { + if (data.move.value() > maxExtent) { + if (snapMode == QDeclarative1ListView::SnapOneItem) { + if (FxListItem1 *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem()) + maxDistance = qAbs(item->position() + dataValue); + } else { + maxDistance = qAbs(maxExtent - data.move.value()); + } + } + if (snapMode == QDeclarative1ListView::NoSnap && highlightRange != QDeclarative1ListView::StrictlyEnforceRange) + data.flickTarget = maxExtent; + } + + bool overShoot = boundsBehavior == QDeclarative1Flickable::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 == QDeclarative1ListView::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(QDeclarative1TimeLineCallback(&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 != QDeclarative1ListView::NoSnap || highlightRange == QDeclarative1ListView::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(QDeclarative1TimeLineCallback(&data.move, fixupCallback, this)); + } + } else { + correctFlick = false; + timeline.reset(data.move); + fixup(data, minExtent, maxExtent); + } +} + +//---------------------------------------------------------------------------- + +/*! + \qmlclass ListView QDeclarative1ListView + \ingroup qml-view-elements + \since 4.7 + \inherits Flickable + \brief The ListView item provides a list view of items provided by a model. + + A ListView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + A ListView has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. Items in a + ListView are laid out horizontally or vertically. List views are inherently + flickable because ListView inherits from \l Flickable. + + \section1 Example Usage + + The following example shows the definition of a simple list model defined + in a file called \c ContactModel.qml: + + \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0 + + Another component can display this model data in a ListView, like this: + + \snippet doc/src/snippets/declarative/listview/listview.qml import + \codeline + \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple + + \image listview-simple.png + + Here, the ListView creates a \c ContactModel component for its model, and a \l Text element + for its delegate. The view will create a new \l Text component for each item in the model. Notice + the delegate is able to access the model's \c name and \c number data directly. + + An improved list view is shown below. The delegate is visually improved and is moved + into a separate \c contactDelegate component. + + \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced + \image listview-highlight.png + + The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property, + and \c focus is set to \c true to enable keyboard navigation for the list view. + The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + ListView attaches a number of properties to the root item of the delegate, for example + \c {ListView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c ListView.isCurrentItem, while the child + \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem. + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem + + \note Views do not enable \e clip automatically. If the view + is not clipped by another item or the screen, it will be necessary + to set \e {clip: true} in order to have the out of view items clipped + nicely. + + \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples} +*/ + +QDeclarative1ListView::QDeclarative1ListView(QDeclarativeItem *parent) + : QDeclarative1Flickable(*(new QDeclarative1ListViewPrivate), parent) +{ + Q_D(QDeclarative1ListView); + d->init(); +} + +QDeclarative1ListView::~QDeclarative1ListView() +{ + Q_D(QDeclarative1ListView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + +/*! + \qmlattachedproperty bool ListView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item, for example: + + \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem +*/ + +/*! + \qmlattachedproperty ListView ListView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty string ListView::previousSection + This attached property holds the section of the previous element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty string ListView::nextSection + This attached property holds the section of the next element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty string ListView::section + This attached property holds the section of this element. + + It is attached to each instance of the delegate. + + The section is evaluated using the \l {ListView::section.property}{section} properties. +*/ + +/*! + \qmlattachedproperty bool ListView::delayRemove + This attached property holds whether the delegate may be destroyed. + + It is attached to each instance of the delegate. + + It is sometimes necessary to delay the destruction of an item + until an animation completes. + + The example delegate below ensures that the animation completes before + the item is removed from the list. + + \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove +*/ + +/*! + \qmlattachedsignal ListView::onAdd() + This attached handler is called immediately after an item is added to the view. +*/ + +/*! + \qmlattachedsignal ListView::onRemove() + This attached handler is called immediately before an item is removed from the view. +*/ + +/*! + \qmlproperty model ListView::model + This property holds the model providing data for the list. + + The model provides the set of data that is used to create the items + in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel + or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is + used, it must be a subclass of \l QAbstractItemModel or a simple list. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarative1ListView::model() const +{ + Q_D(const QDeclarative1ListView); + return d->modelVariant; +} + +void QDeclarative1ListView::setModel(const QVariant &model) +{ + Q_D(QDeclarative1ListView); + 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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + } + d->clear(); + QDeclarative1VisualModel *oldModel = d->model; + d->model = 0; + d->setPosition(0); + d->modelVariant = model; + QObject *object = qvariant_cast<QObject*>(model); + QDeclarative1VisualModel *vim = 0; + if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) { + if (d->ownModel) { + delete oldModel; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this), this); + d->ownModel = true; + } else { + d->model = oldModel; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + dataModel->setModel(model); + } + if (d->model) { + d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore | QDeclarative1ListViewPrivate::BufferAfter; + if (isComponentComplete()) { + updateSections(); + refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QDeclarative1ListViewPrivate::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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + emit countChanged(); + } + emit modelChanged(); +} + +/*! + \qmlproperty Component ListView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + The ListView will lay out the items based on the size of the root item + in the delegate. + + It is recommended that the delagate's size be a whole number to avoid sub-pixel + alignment of items. + + \note Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. +*/ +QDeclarativeComponent *QDeclarative1ListView::delegate() const +{ + Q_D(const QDeclarative1ListView); + if (d->model) { + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarative1ListView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarative1ListView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(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 = QDeclarative1ListViewPrivate::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(); +} + +/*! + \qmlproperty int ListView::currentIndex + \qmlproperty Item ListView::currentItem + + The \c currentIndex property holds the index of the current item, and + \c currentItem holds the current item. Setting the currentIndex to -1 + will clear the highlight and set currentItem to null. + + If highlightFollowsCurrentItem is \c true, setting either of these + properties will smoothly scroll the ListView so that the current + item becomes visible. + + Note that the position of the current item + may only be approximate until it becomes visible in the view. +*/ +int QDeclarative1ListView::currentIndex() const +{ + Q_D(const QDeclarative1ListView); + return d->currentIndex; +} + +void QDeclarative1ListView::setCurrentIndex(int index) +{ + Q_D(QDeclarative1ListView); + if (d->requestedIndex >= 0) // currently creating item + return; + d->currentIndexCleared = (index == -1); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + d->moveReason = QDeclarative1ListViewPrivate::SetIndex; + d->updateCurrent(index); + } else if (d->currentIndex != index) { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + +QDeclarativeItem *QDeclarative1ListView::currentItem() +{ + Q_D(QDeclarative1ListView); + if (!d->currentItem) + return 0; + return d->currentItem->item; +} + +/*! + \qmlproperty Item ListView::highlightItem + + This holds the highlight item created from the \l highlight component. + + The \c highlightItem is managed by the view unless + \l highlightFollowsCurrentItem is set to false. + + \sa highlight, highlightFollowsCurrentItem +*/ +QDeclarativeItem *QDeclarative1ListView::highlightItem() +{ + Q_D(QDeclarative1ListView); + if (!d->highlight) + return 0; + return d->highlight->item; +} + +/*! + \qmlproperty int ListView::count + This property holds the number of items in the view. +*/ +int QDeclarative1ListView::count() const +{ + Q_D(const QDeclarative1ListView); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlproperty Component ListView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component is created for each list. + The geometry of the resulting component instance is managed by the list + so as to stay with the current item, unless the highlightFollowsCurrentItem + property is false. + + \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples} +*/ +QDeclarativeComponent *QDeclarative1ListView::highlight() const +{ + Q_D(const QDeclarative1ListView); + return d->highlightComponent; +} + +void QDeclarative1ListView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QDeclarative1ListView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->createHighlight(); + if (d->currentItem) + d->updateHighlight(); + emit highlightChanged(); + } +} + +/*! + \qmlproperty bool ListView::highlightFollowsCurrentItem + This property holds whether the highlight is managed by the view. + + If this property is true (the default value), the highlight is moved smoothly + to follow the current item. Otherwise, the + highlight is not moved by the view, and any movement must be implemented + by the highlight. + + Here is a highlight with its motion defined by a \l {SpringAnimation} item: + + \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem + + Note that the highlight animation also affects the way that the view + is scrolled. This is because the view moves to maintain the + highlight within the preferred highlight range (or visible viewport). + + \sa highlight, highlightMoveSpeed +*/ +bool QDeclarative1ListView::highlightFollowsCurrentItem() const +{ + Q_D(const QDeclarative1ListView); + return d->autoHighlight; +} + +void QDeclarative1ListView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QDeclarative1ListView); + 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? +/*! + \qmlproperty real ListView::preferredHighlightBegin + \qmlproperty real ListView::preferredHighlightEnd + \qmlproperty enumeration ListView::highlightRangeMode + + These properties define the preferred range of the highlight (for the current item) + within the view. The \c preferredHighlightBegin value must be less than the + \c preferredHighlightEnd value. + + These properties affect the position of the current item when the list is scrolled. + For example, if the currently selected item should stay in the middle of the + list when the view is scrolled, set the \c preferredHighlightBegin and + \c preferredHighlightEnd values to the top and bottom coordinates of where the middle + item would be. If the \c currentItem is changed programmatically, the list will + automatically scroll so that the current item is in the middle of the view. + Furthermore, the behavior of the current item index will occur whether or not a + highlight exists. + + Valid values for \c highlightRangeMode are: + + \list + \o ListView.ApplyRange - the view attempts to maintain the highlight within the range. + However, the highlight can move outside of the range at the ends of the list or due + to mouse interaction. + \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range. + The current item changes if a keyboard or mouse action would cause the highlight to move + outside of the range. + \o ListView.NoHighlightRange - this is the default value. + \endlist +*/ +qreal QDeclarative1ListView::preferredHighlightBegin() const +{ + Q_D(const QDeclarative1ListView); + return d->highlightRangeStart; +} + +void QDeclarative1ListView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QDeclarative1ListView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QDeclarative1ListView::resetPreferredHighlightBegin() +{ + Q_D(QDeclarative1ListView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QDeclarative1ListView::preferredHighlightEnd() const +{ + Q_D(const QDeclarative1ListView); + return d->highlightRangeEnd; +} + +void QDeclarative1ListView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QDeclarative1ListView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QDeclarative1ListView::resetPreferredHighlightEnd() +{ + Q_D(QDeclarative1ListView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +QDeclarative1ListView::HighlightRangeMode QDeclarative1ListView::highlightRangeMode() const +{ + Q_D(const QDeclarative1ListView); + return d->highlightRange; +} + +void QDeclarative1ListView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QDeclarative1ListView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +/*! + \qmlproperty real ListView::spacing + + This property holds the spacing between items. + + The default value is 0. +*/ +qreal QDeclarative1ListView::spacing() const +{ + Q_D(const QDeclarative1ListView); + return d->spacing; +} + +void QDeclarative1ListView::setSpacing(qreal spacing) +{ + Q_D(QDeclarative1ListView); + if (spacing != d->spacing) { + d->spacing = spacing; + d->layout(); + emit spacingChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::orientation + This property holds the orientation of the list. + + Possible values: + + \list + \o ListView.Horizontal - Items are laid out horizontally + \o ListView.Vertical (default) - Items are laid out vertically + \endlist + + \table + \row + \o Horizontal orientation: + \image ListViewHorizontal.png + + \row + \o Vertical orientation: + \image listview-highlight.png + \endtable +*/ +QDeclarative1ListView::Orientation QDeclarative1ListView::orientation() const +{ + Q_D(const QDeclarative1ListView); + return d->orient; +} + +void QDeclarative1ListView::setOrientation(QDeclarative1ListView::Orientation orientation) +{ + Q_D(QDeclarative1ListView); + if (d->orient != orientation) { + d->orient = orientation; + if (d->orient == QDeclarative1ListView::Vertical) { + setContentWidth(-1); + setFlickableDirection(VerticalFlick); + setContentX(0); + } else { + setContentHeight(-1); + setFlickableDirection(HorizontalFlick); + setContentY(0); + } + d->regenerate(); + emit orientationChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::layoutDirection + This property holds the layout direction of the horizontal list. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items will be laid out from left to right. + \o Qt.RightToLeft - Items will be laid out from right to let. + \endlist + + \sa ListView::effectiveLayoutDirection +*/ + +Qt::LayoutDirection QDeclarative1ListView::layoutDirection() const +{ + Q_D(const QDeclarative1ListView); + return d->layoutDirection; +} + +void QDeclarative1ListView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarative1ListView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::effectiveLayoutDirection + This property holds the effective layout direction of the horizontal list. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the horizontal list will be mirrored. However, the + property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged. + + \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarative1ListView::effectiveLayoutDirection() const +{ + Q_D(const QDeclarative1ListView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + +/*! + \qmlproperty bool ListView::keyNavigationWraps + This property holds whether the list wraps key navigation. + + If this is true, key navigation that would move the current item selection + past the end of the list instead wraps around and moves the selection to + the start of the list, and vice-versa. + + By default, key navigation is not wrapped. +*/ +bool QDeclarative1ListView::isWrapEnabled() const +{ + Q_D(const QDeclarative1ListView); + return d->wrap; +} + +void QDeclarative1ListView::setWrapEnabled(bool wrap) +{ + Q_D(QDeclarative1ListView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +/*! + \qmlproperty int ListView::cacheBuffer + This property determines whether delegates are retained outside the + visible area of the view. + + If this value is non-zero, the view keeps as many delegates + instantiated as it can fit within the buffer specified. For example, + if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is + set to 40, then up to 2 delegates above and 2 delegates below the visible + area may be retained. + + Note that cacheBuffer is not a pixel buffer - it only maintains additional + instantiated delegates. + + Setting this value can improve the smoothness of scrolling behavior at the expense + of additional memory usage. It is not a substitute for creating efficient + delegates; the fewer elements in a delegate, the faster a view can be + scrolled. +*/ +int QDeclarative1ListView::cacheBuffer() const +{ + Q_D(const QDeclarative1ListView); + return d->buffer; +} + +void QDeclarative1ListView::setCacheBuffer(int b) +{ + Q_D(QDeclarative1ListView); + if (d->buffer != b) { + d->buffer = b; + if (isComponentComplete()) { + d->bufferMode = QDeclarative1ListViewPrivate::BufferBefore | QDeclarative1ListViewPrivate::BufferAfter; + refill(); + } + emit cacheBufferChanged(); + } +} + +/*! + \qmlproperty string ListView::section.property + \qmlproperty enumeration ListView::section.criteria + \qmlproperty Component ListView::section.delegate + + These properties hold the expression to be evaluated for the \l section attached property. + + The \l section attached property enables a ListView to be visually + separated into different parts. These properties determine how sections + are created. + + \c section.property holds the name of the property that is the basis + of each section. + + \c section.criteria holds the criteria for forming each section based on + \c section.property. This value can be one of: + + \list + \o ViewSection.FullString (default) - sections are created based on the + \c section.property value. + \o ViewSection.FirstCharacter - sections are created based on the first + character of the \c section.property value (for example, 'A', 'B', 'C' + sections, etc. for an address book) + \endlist + + \c section.delegate holds the delegate component for each section. + + Each item in the list has attached properties named \c ListView.section, + \c ListView.previousSection and \c ListView.nextSection. These may be + used to place a section header for related items. + + For example, here is a ListView that displays a list of animals, separated + into sections. Each item in the ListView is placed in a different section + depending on the "size" property of the model item. The \c sectionHeading + delegate component provides the light blue bar that marks the beginning of + each section. + + + \snippet examples/declarative/modelviews/listview/sections.qml 0 + + \image qml-listview-sections-example.png + + \note Adding sections to a ListView does not automatically re-order the + list items by the section criteria. + If the model is not ordered by section, then it is possible that + the sections created will not be unique; each boundary between + differing sections will result in a section header being created + even if that section exists elsewhere. + + \sa {declarative/modelviews/listview}{ListView examples} +*/ +QDeclarative1ViewSection *QDeclarative1ListView::sectionCriteria() +{ + Q_D(QDeclarative1ListView); + if (!d->sectionCriteria) { + d->sectionCriteria = new QDeclarative1ViewSection(this); + connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections())); + } + return d->sectionCriteria; +} + +/*! + \qmlproperty string ListView::currentSection + This property holds the section that is currently at the beginning of the view. +*/ +QString QDeclarative1ListView::currentSection() const +{ + Q_D(const QDeclarative1ListView); + return d->currentSection; +} + +/*! + \qmlproperty real ListView::highlightMoveSpeed + \qmlproperty int ListView::highlightMoveDuration + \qmlproperty real ListView::highlightResizeSpeed + \qmlproperty int ListView::highlightResizeDuration + + These properties hold the move and resize animation speed of the highlight delegate. + + \l highlightFollowsCurrentItem must be true for these properties + to have effect. + + The default value for the speed properties is 400 pixels/second. + The default value for the duration properties is -1, i.e. the + highlight will take as much time as necessary to move at the set speed. + + These properties have the same characteristics as a SmoothedAnimation. + + \sa highlightFollowsCurrentItem +*/ +qreal QDeclarative1ListView::highlightMoveSpeed() const +{ + Q_D(const QDeclarative1ListView);\ + return d->highlightMoveSpeed; +} + +void QDeclarative1ListView::setHighlightMoveSpeed(qreal speed) +{ + Q_D(QDeclarative1ListView);\ + if (d->highlightMoveSpeed != speed) { + d->highlightMoveSpeed = speed; + if (d->highlightPosAnimator) + d->highlightPosAnimator->velocity = d->highlightMoveSpeed; + emit highlightMoveSpeedChanged(); + } +} + +int QDeclarative1ListView::highlightMoveDuration() const +{ + Q_D(const QDeclarative1ListView); + return d->highlightMoveDuration; +} + +void QDeclarative1ListView::setHighlightMoveDuration(int duration) +{ + Q_D(QDeclarative1ListView);\ + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + if (d->highlightPosAnimator) + d->highlightPosAnimator->userDuration = d->highlightMoveDuration; + emit highlightMoveDurationChanged(); + } +} + +qreal QDeclarative1ListView::highlightResizeSpeed() const +{ + Q_D(const QDeclarative1ListView);\ + return d->highlightResizeSpeed; +} + +void QDeclarative1ListView::setHighlightResizeSpeed(qreal speed) +{ + Q_D(QDeclarative1ListView);\ + if (d->highlightResizeSpeed != speed) { + d->highlightResizeSpeed = speed; + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->velocity = d->highlightResizeSpeed; + emit highlightResizeSpeedChanged(); + } +} + +int QDeclarative1ListView::highlightResizeDuration() const +{ + Q_D(const QDeclarative1ListView); + return d->highlightResizeDuration; +} + +void QDeclarative1ListView::setHighlightResizeDuration(int duration) +{ + Q_D(QDeclarative1ListView);\ + if (d->highlightResizeDuration != duration) { + d->highlightResizeDuration = duration; + if (d->highlightSizeAnimator) + d->highlightSizeAnimator->userDuration = d->highlightResizeDuration; + emit highlightResizeDurationChanged(); + } +} + +/*! + \qmlproperty enumeration ListView::snapMode + + This property determines how the view scrolling will settle following a drag or flick. + The possible values are: + + \list + \o ListView.NoSnap (default) - the view stops anywhere within the visible area. + \o ListView.SnapToItem - the view settles with an item aligned with the start of + the view. + \o ListView.SnapOneItem - the view settles no more than one item away from the first + visible item at the time the mouse button is released. This mode is particularly + useful for moving one page at a time. + \endlist + + \c snapMode does not affect the \l currentIndex. To update the + \l currentIndex as the list is moved, set \l highlightRangeMode + to \c ListView.StrictlyEnforceRange. + + \sa highlightRangeMode +*/ +QDeclarative1ListView::SnapMode QDeclarative1ListView::snapMode() const +{ + Q_D(const QDeclarative1ListView); + return d->snapMode; +} + +void QDeclarative1ListView::setSnapMode(SnapMode mode) +{ + Q_D(QDeclarative1ListView); + if (d->snapMode != mode) { + d->snapMode = mode; + emit snapModeChanged(); + } +} + +/*! + \qmlproperty Component ListView::footer + This property holds the component to use as the footer. + + An instance of the footer component is created for each view. The + footer is positioned at the end of the view, after any items. + + \sa header +*/ +QDeclarativeComponent *QDeclarative1ListView::footer() const +{ + Q_D(const QDeclarative1ListView); + return d->footerComponent; +} + +void QDeclarative1ListView::setFooter(QDeclarativeComponent *footer) +{ + Q_D(QDeclarative1ListView); + if (d->footerComponent != footer) { + if (d->footer) { + if (scene()) + scene()->removeItem(d->footer->item); + 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(); + } +} + +/*! + \qmlproperty Component ListView::header + This property holds the component to use as the header. + + An instance of the header component is created for each view. The + header is positioned at the beginning of the view, before any items. + + \sa footer +*/ +QDeclarativeComponent *QDeclarative1ListView::header() const +{ + Q_D(const QDeclarative1ListView); + return d->headerComponent; +} + +void QDeclarative1ListView::setHeader(QDeclarativeComponent *header) +{ + Q_D(QDeclarative1ListView); + if (d->headerComponent != header) { + if (d->header) { + if (scene()) + scene()->removeItem(d->header->item); + 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 QDeclarative1ListView::setContentX(qreal pos) +{ + Q_D(QDeclarative1ListView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarative1ListViewPrivate::Other; + QDeclarative1Flickable::setContentX(pos); +} + +void QDeclarative1ListView::setContentY(qreal pos) +{ + Q_D(QDeclarative1ListView); + // Positioning the view manually should override any current movement state + d->moveReason = QDeclarative1ListViewPrivate::Other; + QDeclarative1Flickable::setContentY(pos); +} + +bool QDeclarative1ListView::event(QEvent *event) +{ + Q_D(QDeclarative1ListView); + if (event->type() == QEvent::User) { + d->layout(); + return true; + } + + return QDeclarative1Flickable::event(event); +} + +void QDeclarative1ListView::viewportMoved() +{ + Q_D(QDeclarative1ListView); + QDeclarative1Flickable::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 = QDeclarative1ListViewPrivate::Mouse; + if (d->moveReason != QDeclarative1ListViewPrivate::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 (FxListItem1 *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 = QDeclarative1ListViewPrivate::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 = QDeclarative1ListViewPrivate::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() + ? QDeclarative1ListViewPrivate::BufferAfter : QDeclarative1ListViewPrivate::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() + ? QDeclarative1ListViewPrivate::BufferBefore : QDeclarative1ListViewPrivate::BufferAfter; + } + } + d->inFlickCorrection = false; + } + d->inViewportMoved = false; +} + +qreal QDeclarative1ListView::minYExtent() const +{ + Q_D(const QDeclarative1ListView); + if (d->orient == QDeclarative1ListView::Horizontal) + return QDeclarative1Flickable::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 QDeclarative1ListView::maxYExtent() const +{ + Q_D(const QDeclarative1ListView); + if (d->orient == QDeclarative1ListView::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 QDeclarative1ListView::minXExtent() const +{ + Q_D(const QDeclarative1ListView); + if (d->orient == QDeclarative1ListView::Vertical) + return QDeclarative1Flickable::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 QDeclarative1ListView::maxXExtent() const +{ + Q_D(const QDeclarative1ListView); + if (d->orient == QDeclarative1ListView::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 QDeclarative1ListView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarative1ListView); + keyPressPreHandler(event); + if (event->isAccepted()) + return; + + if (d->model && d->model->count() && d->interactive) { + if ((d->orient == QDeclarative1ListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QDeclarative1ListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QDeclarative1ListView::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 == QDeclarative1ListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QDeclarative1ListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QDeclarative1ListView::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(); + QDeclarative1Flickable::keyPressEvent(event); +} + +void QDeclarative1ListView::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarative1ListView); + d->maxExtentDirty = true; + d->minExtentDirty = true; + if (d->isRightToLeft() && d->orient == QDeclarative1ListView::Horizontal) { + // maintain position relative to the right edge + int dx = newGeometry.width() - oldGeometry.width(); + setContentX(contentX() - dx); + } + QDeclarative1Flickable::geometryChanged(newGeometry, oldGeometry); +} + + +/*! + \qmlmethod ListView::incrementCurrentIndex() + + Increments the current index. The current index will wrap + if keyNavigationWraps is true and it is currently at the end. + This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1ListView::incrementCurrentIndex() +{ + Q_D(QDeclarative1ListView); + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() < count - 1 || d->wrap)) { + d->moveReason = QDeclarative1ListViewPrivate::SetIndex; + int index = currentIndex()+1; + setCurrentIndex((index >= 0 && index < count) ? index : 0); + } +} + +/*! + \qmlmethod ListView::decrementCurrentIndex() + + Decrements the current index. The current index will wrap + if keyNavigationWraps is true and it is currently at the beginning. + This method has no effect if the \l count is zero. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1ListView::decrementCurrentIndex() +{ + Q_D(QDeclarative1ListView); + int count = d->model ? d->model->count() : 0; + if (count && (currentIndex() > 0 || d->wrap)) { + d->moveReason = QDeclarative1ListViewPrivate::SetIndex; + int index = currentIndex()-1; + setCurrentIndex((index >= 0 && index < count) ? index : count-1); + } +} + +void QDeclarative1ListViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QDeclarative1ListView); + if (!isValid()) + return; + if (mode < QDeclarative1ListView::Beginning || mode > QDeclarative1ListView::Contain) + return; + int idx = qMax(qMin(index, model->count()-1), 0); + + if (layoutScheduled) + layout(); + qreal pos = isRightToLeft() ? -position() - size() : position(); + FxListItem1 *item = visibleItem(idx); + qreal maxExtent; + if (orient == QDeclarative1ListView::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<FxListItem1*> 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 QDeclarative1ListView::Beginning: + pos = itemPos; + if (index < 0 && header) + pos -= header->size(); + break; + case QDeclarative1ListView::Center: + pos = itemPos - (size() - item->size())/2; + break; + case QDeclarative1ListView::End: + pos = itemPos - size() + item->size(); + if (index >= model->count() && footer) + pos += footer->size(); + break; + case QDeclarative1ListView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + item->size(); + else if (item->endPosition() < pos) + pos = itemPos; + break; + case QDeclarative1ListView::Contain: + if (item->endPosition() > pos + size()) + pos = itemPos - size() + item->size(); + if (itemPos < pos) + pos = itemPos; + } + pos = qMin(pos, maxExtent); + qreal minExtent; + if (orient == QDeclarative1ListView::Vertical) { + minExtent = -q->minYExtent(); + } else { + minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent(); + } + pos = qMax(pos, minExtent); + moveReason = QDeclarative1ListViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + if (highlight) { + if (autoHighlight) { + highlight->setPosition(currentItem->itemPosition()); + highlight->setSize(currentItem->itemSize()); + } + updateHighlight(); + } + } + fixupPosition(); +} + +/*! + \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode) + + Positions the view such that the \a index is at the position specified by + \a mode: + + \list + \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view. + \o ListView.Center - position item in the center of the view. + \o ListView.End - position item at bottom (or right for horizontal orientation) of the view. + \o ListView.Visible - if any part of the item is visible then take no action, otherwise + bring the item into view. + \o ListView.Contain - ensure the entire item is visible. If the item is larger than + the view the item is positioned at the top (or left for horizontal orientation) of the view. + \endlist + + If positioning the view at \a index would cause empty space to be displayed at + the beginning or end of the view, the view will be positioned at the boundary. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + The correct way to bring an item into view is with \c positionViewAtIndex. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end: + + \code + Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning) + \endcode +*/ +void QDeclarative1ListView::positionViewAtIndex(int index, int mode) +{ + Q_D(QDeclarative1ListView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + +/*! + \qmlmethod ListView::positionViewAtBeginning() + \qmlmethod ListView::positionViewAtEnd() + \since Quick 1.1 + + Positions the view at the beginning or end, taking into account any header or footer. + + It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view + at a particular index. This is unreliable since removing items from the start + of the list does not cause all other items to be repositioned, and because + the actual start of the view can vary based on the size of the delegates. + + \bold Note: methods should only be called after the Component has completed. To position + the view at startup, this method should be called by Component.onCompleted. For + example, to position the view at the end on startup: + + \code + Component.onCompleted: positionViewAtEnd() + \endcode +*/ +void QDeclarative1ListView::positionViewAtBeginning() +{ + Q_D(QDeclarative1ListView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QDeclarative1ListView::positionViewAtEnd() +{ + Q_D(QDeclarative1ListView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +/*! + \qmlmethod int ListView::indexAt(int x, int y) + + Returns the index of the visible item containing the point \a x, \a y in content + coordinates. If there is no item at the point specified, or the item is + not visible -1 is returned. + + If the item is outside the visible area, -1 is returned, regardless of + whether an item will exist at that point when scrolled into view. + + \bold Note: methods should only be called after the Component has completed. +*/ +int QDeclarative1ListView::indexAt(qreal x, qreal y) const +{ + Q_D(const QDeclarative1ListView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxListItem1 *listItem = d->visibleItems.at(i); + if(listItem->contains(x, y)) + return listItem->index; + } + + return -1; +} + +void QDeclarative1ListView::componentComplete() +{ + Q_D(QDeclarative1ListView); + QDeclarative1Flickable::componentComplete(); + updateSections(); + d->updateHeader(); + d->updateFooter(); + if (d->isValid()) { + refill(); + d->moveReason = QDeclarative1ListViewPrivate::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 = QDeclarative1ListViewPrivate::Other; + d->fixupPosition(); + } +} + +void QDeclarative1ListView::updateSections() +{ + Q_D(QDeclarative1ListView); + 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 QDeclarative1ListView::refill() +{ + Q_D(QDeclarative1ListView); + if (d->isRightToLeft()) + d->refill(-d->position()-d->size()+1, -d->position()); + else + d->refill(d->position(), d->position()+d->size()-1); +} + +void QDeclarative1ListView::trackedPositionChanged() +{ + Q_D(QDeclarative1ListView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QDeclarative1ListViewPrivate::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 QDeclarative1ListView::itemsInserted(int modelIndex, int count) +{ + Q_D(QDeclarative1ListView); + if (!isComponentComplete()) + return; + d->updateUnrequestedIndexes(); + d->moveReason = QDeclarative1ListViewPrivate::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) { + FxListItem1 *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<FxListItem1*> added; + bool addedVisible = false; + FxListItem1 *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; + } + FxListItem1 *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--) { + FxListItem1 *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--) { + FxListItem1 *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; + } + FxListItem1 *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) { + FxListItem1 *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 QDeclarative1ListView::itemsRemoved(int modelIndex, int count) +{ + Q_D(QDeclarative1ListView); + if (!isComponentComplete()) + return; + d->moveReason = QDeclarative1ListViewPrivate::Other; + d->updateUnrequestedIndexes(); + d->itemCount -= count; + + FxListItem1 *firstVisible = d->firstVisibleItem(); + int preRemovedSize = 0; + bool removedVisible = false; + // Remove the items from the visible list, skipping anything already marked for removal + QList<FxListItem1*>::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxListItem1 *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. + 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(); + update(); + } else { + if (modelIndex < d->visibleIndex) + d->visibleIndex = modelIndex+1; + d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0); + } + } + + d->updateSections(); + emit countChanged(); +} + +void QDeclarative1ListView::destroyRemoved() +{ + Q_D(QDeclarative1ListView); + for (QList<FxListItem1*>::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxListItem1 *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 QDeclarative1ListView::itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarative1ListView); + if (!isComponentComplete()) + return; + d->updateUnrequestedIndexes(); + + if (d->visibleItems.isEmpty()) { + refill(); + return; + } + + d->moveReason = QDeclarative1ListViewPrivate::Other; + FxListItem1 *firstVisible = d->firstVisibleItem(); + qreal firstItemPos = firstVisible->position(); + QHash<int,FxListItem1*> moved; + int moveBy = 0; + + QList<FxListItem1*>::Iterator it = d->visibleItems.begin(); + while (it != d->visibleItems.end()) { + FxListItem1 *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()) { + FxListItem1 *item = *it; + if (remaining && item->index >= to && item->index < to + count) { + // place items in the target position, reusing any existing items + FxListItem1 *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 (FxListItem1 *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(); + FxListItem1 *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 QDeclarative1ListView::itemsChanged(int, int) +{ + Q_D(QDeclarative1ListView); + d->updateSections(); + d->layout(); +} + +void QDeclarative1ListView::modelReset() +{ + Q_D(QDeclarative1ListView); + d->moveReason = QDeclarative1ListViewPrivate::SetIndex; + d->regenerate(); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->highlight->setPosition(d->currentItem->position()); + d->updateTrackedItem(); + } + d->moveReason = QDeclarative1ListViewPrivate::Other; + emit countChanged(); +} + +void QDeclarative1ListView::createdItem(int index, QDeclarativeItem *item) +{ + Q_D(QDeclarative1ListView); + if (d->requestedIndex != index) { + item->setParentItem(contentItem()); + d->unrequestedItems.insert(item, index); + if (d->orient == QDeclarative1ListView::Vertical) { + item->setY(d->positionAt(index)); + } else { + if (d->isRightToLeft()) + item->setX(-d->positionAt(index)-item->width()); + else + item->setX(d->positionAt(index)); + } + } +} + +void QDeclarative1ListView::destroyingItem(QDeclarativeItem *item) +{ + Q_D(QDeclarative1ListView); + d->unrequestedItems.remove(item); +} + +void QDeclarative1ListView::animStopped() +{ + Q_D(QDeclarative1ListView); + d->bufferMode = QDeclarative1ListViewPrivate::NoBuffer; + if (d->haveHighlightRange && d->highlightRange == QDeclarative1ListView::StrictlyEnforceRange) + d->updateHighlight(); +} + +QDeclarative1ListViewAttached *QDeclarative1ListView::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarative1ListViewAttached(obj); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativelistview_p.h b/src/qtquick1/graphicsitems/qdeclarativelistview_p.h new file mode 100644 index 0000000000..dc1ff3371f --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativelistview_p.h @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTVIEW_H +#define QDECLARATIVELISTVIEW_H + +#include "private/qdeclarativeflickable_p.h" +#include "QtDeclarative/private/qdeclarativeguard_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarative1ViewSection : 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: + QDeclarative1ViewSection(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 QDeclarative1VisualModel; +class QDeclarative1ListViewAttached; +class QDeclarative1ListViewPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1ListView : public QDeclarative1Flickable +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1ListView) + + 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(QDeclarativeItem *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(QDeclarativeItem *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 REVISION 1) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged REVISION 1) + Q_PROPERTY(bool keyNavigationWraps READ isWrapEnabled WRITE setWrapEnabled NOTIFY keyNavigationWrapsChanged) + Q_PROPERTY(int cacheBuffer READ cacheBuffer WRITE setCacheBuffer NOTIFY cacheBufferChanged) + Q_PROPERTY(QDeclarative1ViewSection *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: + QDeclarative1ListView(QDeclarativeItem *parent=0); + ~QDeclarative1ListView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + QDeclarativeItem *currentItem(); + QDeclarativeItem *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); + + QDeclarative1ViewSection *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 QDeclarative1ListViewAttached *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 Q_REVISION(1) void positionViewAtBeginning(); + Q_INVOKABLE Q_REVISION(1) void positionViewAtEnd(); + +public Q_SLOTS: + void incrementCurrentIndex(); + void decrementCurrentIndex(); + +Q_SIGNALS: + void countChanged(); + void spacingChanged(); + void orientationChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) 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 bool event(QEvent *event); + 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, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + void animStopped(); +}; + +class QDeclarative1ListViewAttached : public QObject +{ + Q_OBJECT +public: + QDeclarative1ListViewAttached(QObject *parent) + : QObject(parent), m_view(0), m_isCurrent(false), m_delayRemove(false) {} + ~QDeclarative1ListViewAttached() {} + + Q_PROPERTY(QDeclarative1ListView *view READ view NOTIFY viewChanged) + QDeclarative1ListView *view() { return m_view; } + void setView(QDeclarative1ListView *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 §) { + 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 §) { + 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 §) { + 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<QDeclarative1ListView> 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(QDeclarative1ListView, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarative1ListView) +QML_DECLARE_TYPE(QDeclarative1ViewSection) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativeloader.cpp b/src/qtquick1/graphicsitems/qdeclarativeloader.cpp new file mode 100644 index 0000000000..92b34d7001 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeloader.cpp @@ -0,0 +1,601 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeloader_p_p.h" + +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_NAMESPACE + + + +QDeclarative1LoaderPrivate::QDeclarative1LoaderPrivate() + : item(0), component(0), ownComponent(false), updatingSize(false), + itemWidthValid(false), itemHeightValid(false) +{ +} + +QDeclarative1LoaderPrivate::~QDeclarative1LoaderPrivate() +{ +} + +void QDeclarative1LoaderPrivate::itemGeometryChanged(QDeclarativeItem *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); + } + QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +void QDeclarative1LoaderPrivate::clear() +{ + if (ownComponent) { + component->deleteLater(); + component = 0; + ownComponent = false; + } + source = QUrl(); + + if (item) { + if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem)); + p->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + + // We can't delete immediately because our item may have triggered + // the Loader to load a different item. + if (item->scene()) { + item->scene()->removeItem(item); + } else { + item->setParentItem(0); + item->setVisible(false); + } + item->deleteLater(); + item = 0; + } +} + +void QDeclarative1LoaderPrivate::initResize() +{ + Q_Q(QDeclarative1Loader); + if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::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; + } else if (item && item->isWidget()) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item); + widget->installEventFilter(q); + } + _q_updateSize(); +} + +/*! + \qmlclass Loader QDeclarative1Loader + \ingroup qml-utility-elements + \since 4.7 + \inherits Item + + \brief The Loader item allows dynamically loading an Item-based + subtree from a URL or Component. + + Loader is used to dynamically load visual QML components. It can load a + QML file (using the \l source property) or a \l Component object (using + the \l sourceComponent property). It is useful for delaying the creation + of a component until it is required: for example, when a component should + be created on demand, or when a component should not be created + unnecessarily for performance reasons. + + Here is a Loader that loads "Page1.qml" as a component when the + \l MouseArea is clicked: + + \snippet doc/src/snippets/declarative/loader/simple.qml 0 + + The loaded item can be accessed using the \l item property. + + If the \l source or \l sourceComponent changes, any previously instantiated + items are destroyed. Setting \l source to an empty string or setting + \l sourceComponent to \c undefined destroys the currently loaded item, + freeing resources and leaving the Loader empty. + + \section2 Loader sizing behavior + + Loader is like any other visual item and must be positioned and sized + accordingly to become visible. + + \list + \o If an explicit size is not specified for the Loader, the Loader + is automatically resized to the size of the loaded item once the + component is loaded. + \o If the size of the Loader is specified explicitly by setting + the width, height or by anchoring, the loaded item will be resized + to the size of the Loader. + \endlist + + In both scenarios the size of the item and the Loader are identical. + This ensures that anchoring to the Loader is equivalent to anchoring + to the loaded item. + + \table + \row + \o sizeloader.qml + \o sizeitem.qml + \row + \o \snippet doc/src/snippets/declarative/loader/sizeloader.qml 0 + \o \snippet doc/src/snippets/declarative/loader/sizeitem.qml 0 + \row + \o The red rectangle will be sized to the size of the root item. + \o The red rectangle will be 50x50, centered in the root item. + \endtable + + + \section2 Receiving signals from loaded items + + Any signals emitted from the loaded item can be received using the + \l Connections element. For example, the following \c application.qml + loads \c MyItem.qml, and is able to receive the \c message signal from + the loaded item through a \l Connections object: + + \table + \row + \o application.qml + \o MyItem.qml + \row + \o \snippet doc/src/snippets/declarative/loader/connections.qml 0 + \o \snippet doc/src/snippets/declarative/loader/MyItem.qml 0 + \endtable + + Alternatively, since \c MyItem.qml is loaded within the scope of the + Loader, it could also directly call any function defined in the Loader or + its parent \l Item. + + + \section2 Focus and key events + + Loader is a focus scope. Its \l {Item::}{focus} property must be set to + \c true for any of its children to get the \e {active focus}. (See + \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} + for more details.) Any key events received in the loaded item should likely + also be \l {KeyEvent::}{accepted} so they are not propagated to the Loader. + + For example, the following \c application.qml loads \c KeyReader.qml when + the \l MouseArea is clicked. Notice the \l {Item::}{focus} property is + set to \c true for the Loader as well as the \l Item in the dynamically + loaded object: + + \table + \row + \o application.qml + \o KeyReader.qml + \row + \o \snippet doc/src/snippets/declarative/loader/focus.qml 0 + \o \snippet doc/src/snippets/declarative/loader/KeyReader.qml 0 + \endtable + + Once \c KeyReader.qml is loaded, it accepts key events and sets + \c event.accepted to \c true so that the event is not propagated to the + parent \l Rectangle. + + \sa {dynamic-object-creation}{Dynamic Object Creation} +*/ + +QDeclarative1Loader::QDeclarative1Loader(QDeclarativeItem *parent) + : QDeclarative1ImplicitSizeItem(*(new QDeclarative1LoaderPrivate), parent) +{ + Q_D(QDeclarative1Loader); + d->flags |= QGraphicsItem::ItemIsFocusScope; +} + +QDeclarative1Loader::~QDeclarative1Loader() +{ + Q_D(QDeclarative1Loader); + if (d->item) { + if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(d->item)) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(qmlItem)); + p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + } + } +} + +/*! + \qmlproperty url Loader::source + This property holds the URL of the QML component to instantiate. + + Note the QML component must be an \l{Item}-based component. The loader + cannot load non-visual components. + + To unload the currently loaded item, set this property to an empty string, + or set \l sourceComponent to \c undefined. Setting \c source to a + new URL will also cause the item created by the previous URL to be unloaded. + + \sa sourceComponent, status, progress +*/ +QUrl QDeclarative1Loader::source() const +{ + Q_D(const QDeclarative1Loader); + return d->source; +} + +void QDeclarative1Loader::setSource(const QUrl &url) +{ + Q_D(QDeclarative1Loader); + 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(); +} + +/*! + \qmlproperty Component Loader::sourceComponent + This property holds the \l{Component} to instantiate. + + \qml + Item { + Component { + id: redSquare + Rectangle { color: "red"; width: 10; height: 10 } + } + + Loader { sourceComponent: redSquare } + Loader { sourceComponent: redSquare; x: 10 } + } + \endqml + + To unload the currently loaded item, set this property to an empty string + or \c undefined. + + \sa source, progress +*/ + +QDeclarativeComponent *QDeclarative1Loader::sourceComponent() const +{ + Q_D(const QDeclarative1Loader); + return d->component; +} + +void QDeclarative1Loader::setSourceComponent(QDeclarativeComponent *comp) +{ + Q_D(QDeclarative1Loader); + 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 QDeclarative1Loader::resetSourceComponent() +{ + setSourceComponent(0); +} + +void QDeclarative1LoaderPrivate::load() +{ + Q_Q(QDeclarative1Loader); + + 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 QDeclarative1LoaderPrivate::_q_sourceLoaded() +{ + Q_Q(QDeclarative1Loader); + + 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<QGraphicsObject *>(obj); + if (item) { + QDeclarative_setParent_noEvent(ctxt, obj); + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); +// item->setFocus(true); + initResize(); + } else { + qmlInfo(q) << QDeclarative1Loader::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(); + } +} + +/*! + \qmlproperty enumeration Loader::status + + This property holds the status of QML loading. It can be one of: + \list + \o Loader.Null - no QML source has been set + \o Loader.Ready - the QML source has been loaded + \o Loader.Loading - the QML source is currently being loaded + \o Loader.Error - an error occurred while loading the QML source + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == Loader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + Loader { + id: loader + onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == Loader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist + + Note that if the source is a local file, the status will initially be Ready (or Error). While + there will be no onStatusChanged signal in that case, the onLoaded will still be invoked. + + \sa progress +*/ + +QDeclarative1Loader::Status QDeclarative1Loader::status() const +{ + Q_D(const QDeclarative1Loader); + + if (d->component) + return static_cast<QDeclarative1Loader::Status>(d->component->status()); + + if (d->item) + return Ready; + + return d->source.isEmpty() ? Null : Error; +} + +void QDeclarative1Loader::componentComplete() +{ + Q_D(QDeclarative1Loader); + + QDeclarativeItem::componentComplete(); + d->load(); +} + + +/*! + \qmlsignal Loader::onLoaded() + + This handler is called when the \l status becomes \c Loader.Ready, or on successful + initial load. +*/ + + +/*! +\qmlproperty real Loader::progress + +This property holds the progress of loading QML data from the network, from +0.0 (nothing loaded) to 1.0 (finished). Most QML files are quite small, so +this value will rapidly change from 0 to 1. + +\sa status +*/ +qreal QDeclarative1Loader::progress() const +{ + Q_D(const QDeclarative1Loader); + + if (d->item) + return 1.0; + + if (d->component) + return d->component->progress(); + + return 0.0; +} + +void QDeclarative1LoaderPrivate::_q_updateSize(bool loaderGeometryChanged) +{ + Q_Q(QDeclarative1Loader); + if (!item || updatingSize) + return; + + updatingSize = true; + if (QDeclarativeItem *qmlItem = qobject_cast<QDeclarativeItem*>(item)) { + if (!itemWidthValid) + q->setImplicitWidth(qmlItem->implicitWidth()); + else + q->setImplicitWidth(qmlItem->width()); + if (loaderGeometryChanged && q->widthValid()) + qmlItem->setWidth(q->width()); + if (!itemHeightValid) + q->setImplicitHeight(qmlItem->implicitHeight()); + else + q->setImplicitHeight(qmlItem->height()); + if (loaderGeometryChanged && q->heightValid()) + qmlItem->setHeight(q->height()); + } else if (item && item->isWidget()) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget*>(item); + QSizeF widgetSize = widget->size(); + q->setImplicitWidth(widgetSize.width()); + if (loaderGeometryChanged && q->widthValid()) + widgetSize.setWidth(q->width()); + q->setImplicitHeight(widgetSize.height()); + if (loaderGeometryChanged && q->heightValid()) + widgetSize.setHeight(q->height()); + if (widget->size() != widgetSize) + widget->resize(widgetSize); + } + updatingSize = false; +} + +/*! + \qmlproperty Item Loader::item + This property holds the top-level item that is currently loaded. +*/ +QGraphicsObject *QDeclarative1Loader::item() const +{ + Q_D(const QDeclarative1Loader); + return d->item; +} + +void QDeclarative1Loader::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QDeclarative1Loader); + if (newGeometry != oldGeometry) { + d->_q_updateSize(); + } + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +QVariant QDeclarative1Loader::itemChange(GraphicsItemChange change, const QVariant &value) +{ + Q_D(QDeclarative1Loader); + if (change == ItemSceneHasChanged) { + if (d->item && d->item->isWidget()) { + d->item->removeEventFilter(this); + d->item->installEventFilter(this); + } + } + return QDeclarativeItem::itemChange(change, value); +} + +bool QDeclarative1Loader::eventFilter(QObject *watched, QEvent *e) +{ + Q_D(QDeclarative1Loader); + if (watched == d->item && e->type() == QEvent::GraphicsSceneResize) { + if (d->item && d->item->isWidget()) + d->_q_updateSize(false); + } + return QDeclarativeItem::eventFilter(watched, e); +} + +#include <moc_qdeclarativeloader_p.cpp> + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativeloader_p.h b/src/qtquick1/graphicsitems/qdeclarativeloader_p.h new file mode 100644 index 0000000000..e2398005fd --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeloader_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELOADER_H +#define QDECLARATIVELOADER_H + +#include "qdeclarativeimplicitsizeitem_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1LoaderPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Loader : public QDeclarative1ImplicitSizeItem +{ + 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(QGraphicsObject *item READ item NOTIFY itemChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + +public: + QDeclarative1Loader(QDeclarativeItem *parent=0); + virtual ~QDeclarative1Loader(); + + 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; + + QGraphicsObject *item() const; + +Q_SIGNALS: + void itemChanged(); + void sourceChanged(); + void statusChanged(); + void progressChanged(); + void loaded(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + bool eventFilter(QObject *watched, QEvent *e); + void componentComplete(); + +private: + Q_DISABLE_COPY(QDeclarative1Loader) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Loader) + Q_PRIVATE_SLOT(d_func(), void _q_sourceLoaded()) + Q_PRIVATE_SLOT(d_func(), void _q_updateSize()) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Loader) + +QT_END_HEADER + +#endif // QDECLARATIVELOADER_H diff --git a/src/qtquick1/graphicsitems/qdeclarativeloader_p_p.h b/src/qtquick1/graphicsitems/qdeclarativeloader_p_p.h new file mode 100644 index 0000000000..f80173536f --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativeloader_p_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELOADER_P_H +#define QDECLARATIVELOADER_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/qdeclarativeloader_p.h" + +#include "QtQuick1/private/qdeclarativeimplicitsizeitem_p_p.h" +#include "QtQuick1/private/qdeclarativeitemchangelistener_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarative1LoaderPrivate : public QDeclarative1ImplicitSizeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarative1Loader) + +public: + QDeclarative1LoaderPrivate(); + ~QDeclarative1LoaderPrivate(); + + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void clear(); + void initResize(); + void load(); + + QUrl source; + QGraphicsObject *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 // QDECLARATIVELOADER_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativemousearea.cpp b/src/qtquick1/graphicsitems/qdeclarativemousearea.cpp new file mode 100644 index 0000000000..6cd41ff615 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativemousearea.cpp @@ -0,0 +1,990 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativemousearea_p.h" +#include "QtQuick1/private/qdeclarativemousearea_p_p.h" + +#include "QtQuick1/private/qdeclarativeevents_p_p.h" + +#include <QGraphicsSceneMouseEvent> + +#include <float.h> + +QT_BEGIN_NAMESPACE + + +static const int PressAndHoldDelay = 800; + +QDeclarative1Drag::QDeclarative1Drag(QObject *parent) +: QObject(parent), _target(0), _axis(XandYAxis), _xmin(-FLT_MAX), _xmax(FLT_MAX), _ymin(-FLT_MAX), _ymax(FLT_MAX), +_active(false), _filterChildren(false) +{ +} + +QDeclarative1Drag::~QDeclarative1Drag() +{ +} + +QGraphicsObject *QDeclarative1Drag::target() const +{ + return _target; +} + +void QDeclarative1Drag::setTarget(QGraphicsObject *t) +{ + if (_target == t) + return; + _target = t; + emit targetChanged(); +} + +void QDeclarative1Drag::resetTarget() +{ + if (!_target) + return; + _target = 0; + emit targetChanged(); +} + +QDeclarative1Drag::Axis QDeclarative1Drag::axis() const +{ + return _axis; +} + +void QDeclarative1Drag::setAxis(QDeclarative1Drag::Axis a) +{ + if (_axis == a) + return; + _axis = a; + emit axisChanged(); +} + +qreal QDeclarative1Drag::xmin() const +{ + return _xmin; +} + +void QDeclarative1Drag::setXmin(qreal m) +{ + if (_xmin == m) + return; + _xmin = m; + emit minimumXChanged(); +} + +qreal QDeclarative1Drag::xmax() const +{ + return _xmax; +} + +void QDeclarative1Drag::setXmax(qreal m) +{ + if (_xmax == m) + return; + _xmax = m; + emit maximumXChanged(); +} + +qreal QDeclarative1Drag::ymin() const +{ + return _ymin; +} + +void QDeclarative1Drag::setYmin(qreal m) +{ + if (_ymin == m) + return; + _ymin = m; + emit minimumYChanged(); +} + +qreal QDeclarative1Drag::ymax() const +{ + return _ymax; +} + +void QDeclarative1Drag::setYmax(qreal m) +{ + if (_ymax == m) + return; + _ymax = m; + emit maximumYChanged(); +} + +bool QDeclarative1Drag::active() const +{ + return _active; +} + +void QDeclarative1Drag::setActive(bool drag) +{ + if (_active == drag) + return; + _active = drag; + emit activeChanged(); +} + +bool QDeclarative1Drag::filterChildren() const +{ + return _filterChildren; +} + +void QDeclarative1Drag::setFilterChildren(bool filter) +{ + if (_filterChildren == filter) + return; + _filterChildren = filter; + emit filterChildrenChanged(); +} + +QDeclarative1MouseAreaPrivate::~QDeclarative1MouseAreaPrivate() +{ + delete drag; +} + +/*! + \qmlclass MouseArea QDeclarative1MouseArea + \ingroup qml-basic-interaction-elements + \since 4.7 + \brief The MouseArea item enables simple mouse handling. + \inherits Item + + A MouseArea is an invisible item that is typically used in conjunction with + a visible item in order to provide mouse handling for that item. + By effectively acting as a proxy, the logic for mouse handling can be + contained within a MouseArea item. + + For basic key handling, see the \l{Keys}{Keys attached property}. + + The \l enabled property is used to enable and disable mouse handling for + the proxied item. When disabled, the mouse area becomes transparent to + mouse events. + + The \l pressed read-only property indicates whether or not the user is + holding down a mouse button over the mouse area. This property is often + used in bindings between properties in a user interface. The containsMouse + read-only property indicates the presence of the mouse cursor over the + mouse area but, by default, only when a mouse button is held down; see below + for further details. + + Information about the mouse position and button clicks are provided via + signals for which event handler properties are defined. The most commonly + used involved handling mouse presses and clicks: onClicked, onDoubleClicked, + onPressed, onReleased and onPressAndHold. + + By default, MouseArea items only report mouse clicks and not changes to the + position of the mouse cursor. Setting the hoverEnabled property ensures that + handlers defined for onPositionChanged, onEntered and onExited are used and + that the containsMouse property is updated even when no mouse buttons are + pressed. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-mousearea-snippet.png + \enddiv + + The following example uses a MouseArea in a \l Rectangle that changes + the \l Rectangle color to red when clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml import + \codeline + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro + + \clearfloat + Many MouseArea signals pass a \l{MouseEvent}{mouse} parameter that contains + additional information about the mouse event, such as the position, button, + and any key modifiers. + + Here is an extension of the previous example that produces a different + color when the area is right clicked: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml intro-extended + + \sa MouseEvent, {declarative/touchinteraction/mousearea}{MouseArea example} +*/ + +/*! + \qmlsignal MouseArea::onEntered() + + This handler is called when the mouse enters the mouse area. + + By default the onEntered handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onEntered when no mouse button is pressed. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal MouseArea::onExited() + + This handler is called when the mouse exists the mouse area. + + By default the onExited handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onExited when no mouse button is pressed. + + \sa hoverEnabled +*/ + +/*! + \qmlsignal MouseArea::onPositionChanged(MouseEvent mouse) + + This handler is called when the mouse position changes. + + The \l {MouseEvent}{mouse} parameter provides information about the mouse, including the x and y + position, and any buttons currently pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + By default the onPositionChanged handler is only called while a button is + pressed. Setting hoverEnabled to true enables handling of + onPositionChanged when no mouse button is pressed. +*/ + +/*! + \qmlsignal MouseArea::onClicked(MouseEvent mouse) + + This handler is called when there is a click. A click is defined as a press followed by a release, + both inside the MouseArea (pressing, moving outside the MouseArea, and then moving back inside and + releasing is also considered a click). + + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal MouseArea::onPressed(MouseEvent mouse) + + This handler is called when there is a press. + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position and which button was pressed. + + The \e accepted property of the MouseEvent parameter determines whether this MouseArea + will handle the press and all future mouse events until release. The default is to accept + the event and not allow other MouseArea beneath this one to handle the event. If \e accepted + is set to false, no further events will be sent to this MouseArea until the button is next + pressed. +*/ + +/*! + \qmlsignal MouseArea::onReleased(MouseEvent mouse) + + This handler is called when there is a release. + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. + + \sa onCanceled +*/ + +/*! + \qmlsignal MouseArea::onPressAndHold(MouseEvent mouse) + + This handler is called when there is a long press (currently 800ms). + The \l {MouseEvent}{mouse} parameter provides information about the press, including the x and y + position of the press, and which button is pressed. + + The \e accepted property of the MouseEvent parameter is ignored in this handler. +*/ + +/*! + \qmlsignal MouseArea::onDoubleClicked(MouseEvent mouse) + + This handler is called when there is a double-click (a press followed by a release followed by a press). + The \l {MouseEvent}{mouse} parameter provides information about the click, including the x and y + position of the release of the click, and whether the click was held. + + If the \e accepted property of the \l {MouseEvent}{mouse} parameter is set to false + in the handler, the onPressed/onReleased/onClicked handlers will be called for the second + click; otherwise they are suppressed. The accepted property defaults to true. +*/ + +/*! + \qmlsignal MouseArea::onCanceled() + + This handler is called when mouse events have been canceled, either because an event was not accepted, or + because another element stole the mouse event handling. + + This signal is for advanced use: it is useful when there is more than one MouseArea + that is handling input, or when there is a MouseArea inside a \l Flickable. In the latter + case, if you execute some logic on the pressed signal and then start dragging, the + \l Flickable will steal the mouse handling from the MouseArea. In these cases, to reset + the logic when the MouseArea has lost the mouse handling to the \l Flickable, + \c onCanceled should be used in addition to onReleased. +*/ + +QDeclarative1MouseArea::QDeclarative1MouseArea(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1MouseAreaPrivate), parent) +{ + Q_D(QDeclarative1MouseArea); + d->init(); +} + +QDeclarative1MouseArea::~QDeclarative1MouseArea() +{ +} + +/*! + \qmlproperty real MouseArea::mouseX + \qmlproperty real MouseArea::mouseY + These properties hold the coordinates of the mouse cursor. + + If the hoverEnabled property is false then these properties will only be valid + while a button is pressed, and will remain valid as long as the button is held + down even if the mouse is moved outside the area. + + By default, this property is false. + + If hoverEnabled is true then these properties will be valid when: + \list + \i no button is pressed, but the mouse is within the MouseArea (containsMouse is true). + \i a button is pressed and held, even if it has since moved out of the area. + \endlist + + The coordinates are relative to the MouseArea. +*/ +qreal QDeclarative1MouseArea::mouseX() const +{ + Q_D(const QDeclarative1MouseArea); + return d->lastPos.x(); +} + +qreal QDeclarative1MouseArea::mouseY() const +{ + Q_D(const QDeclarative1MouseArea); + return d->lastPos.y(); +} + +/*! + \qmlproperty bool MouseArea::enabled + This property holds whether the item accepts mouse events. + + By default, this property is true. +*/ +bool QDeclarative1MouseArea::isEnabled() const +{ + Q_D(const QDeclarative1MouseArea); + return d->absorb; +} + +void QDeclarative1MouseArea::setEnabled(bool a) +{ + Q_D(QDeclarative1MouseArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +/*! + \qmlproperty bool MouseArea::preventStealing + \since Quick 1.1 + This property holds whether the mouse events may be stolen from this + MouseArea. + + If a MouseArea is placed within an item that filters child mouse + events, such as Flickable, the mouse + events may be stolen from the MouseArea if a gesture is recognized + by the parent element, e.g. a flick gesture. If preventStealing is + set to true, no element will steal the mouse events. + + Note that setting preventStealing to true once an element has started + stealing events will have no effect until the next press event. + + By default this property is false. +*/ +bool QDeclarative1MouseArea::preventStealing() const +{ + Q_D(const QDeclarative1MouseArea); + return d->preventStealing; +} + +void QDeclarative1MouseArea::setPreventStealing(bool prevent) +{ + Q_D(QDeclarative1MouseArea); + if (prevent != d->preventStealing) { + d->preventStealing = prevent; + setKeepMouseGrab(d->preventStealing && d->absorb); + emit preventStealingChanged(); + } +} + +/*! + \qmlproperty MouseButtons MouseArea::pressedButtons + This property holds the mouse buttons currently pressed. + + It contains a bitwise combination of: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + The code below displays "right" when the right mouse buttons is pressed: + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml mousebuttons + + \sa acceptedButtons +*/ +Qt::MouseButtons QDeclarative1MouseArea::pressedButtons() const +{ + Q_D(const QDeclarative1MouseArea); + return d->lastButtons; +} + +void QDeclarative1MouseArea::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1MouseArea); + d->moved = false; + d->stealMouse = d->preventStealing; + if (!d->absorb) + QDeclarativeItem::mousePressEvent(event); + else { + d->longPress = false; + d->saveEvent(event); + if (d->drag) { + d->dragX = drag()->axis() & QDeclarative1Drag::XAxis; + d->dragY = drag()->axis() & QDeclarative1Drag::YAxis; + } + if (d->drag) + d->drag->setActive(false); + setHovered(true); + d->startScene = event->scenePos(); + // we should only start timer if pressAndHold is connected to. + if (d->isPressAndHoldConnected()) + d->pressAndHoldTimer.start(PressAndHoldDelay, this); + setKeepMouseGrab(d->stealMouse); + event->setAccepted(setPressed(true)); + } +} + +void QDeclarative1MouseArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1MouseArea); + if (!d->absorb) { + QDeclarativeItem::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; + } + QDeclarative1MouseEvent 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); +} + + +void QDeclarative1MouseArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1MouseArea); + d->stealMouse = false; + if (!d->absorb) { + QDeclarativeItem::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); + QGraphicsScene *s = scene(); + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } + d->doubleClick = false; +} + +void QDeclarative1MouseArea::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1MouseArea); + if (!d->absorb) { + QDeclarativeItem::mouseDoubleClickEvent(event); + } else { + if (d->isDoubleClickConnected()) + d->doubleClick = true; + d->saveEvent(event); + QDeclarative1MouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, true, false); + me.setAccepted(d->isDoubleClickConnected()); + emit this->doubleClicked(&me); + QDeclarativeItem::mouseDoubleClickEvent(event); + } +} + +void QDeclarative1MouseArea::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QDeclarative1MouseArea); + if (!d->absorb) + QDeclarativeItem::hoverEnterEvent(event); + else { + d->lastPos = event->pos(); + setHovered(true); + QDeclarative1MouseEvent me(d->lastPos.x(), d->lastPos.y(), Qt::NoButton, Qt::NoButton, event->modifiers(), false, false); + emit mousePositionChanged(&me); + } +} + +void QDeclarative1MouseArea::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QDeclarative1MouseArea); + if (!d->absorb) { + QDeclarativeItem::hoverMoveEvent(event); + } else { + d->lastPos = event->pos(); + QDeclarative1MouseEvent 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 QDeclarative1MouseArea::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QDeclarative1MouseArea); + if (!d->absorb) + QDeclarativeItem::hoverLeaveEvent(event); + else + setHovered(false); +} + +#ifndef QT_NO_CONTEXTMENU +void QDeclarative1MouseArea::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + bool acceptsContextMenuButton; +#if defined(Q_OS_SYMBIAN) + // In Symbian a Long Tap on the screen will trigger. See QSymbianControl::HandleLongTapEventL(). + acceptsContextMenuButton = acceptedButtons() & Qt::LeftButton; +#elif defined(Q_WS_WINCE) + // ### WinCE can trigger context menu event with a gesture in the left button or a + // click with the right button. Since we have no way here to differentiate them when + // event happens, accepting either of the them will block the event. + acceptsContextMenuButton = acceptedButtons() & (Qt::LeftButton | Qt::RightButton); +#else + acceptsContextMenuButton = acceptedButtons() & Qt::RightButton; +#endif + + if (isEnabled() && event->reason() == QGraphicsSceneContextMenuEvent::Mouse + && acceptsContextMenuButton) { + // Do not let the context menu event propagate to items behind. + return; + } + + QDeclarativeItem::contextMenuEvent(event); +} +#endif // QT_NO_CONTEXTMENU + +bool QDeclarative1MouseArea::sceneEvent(QEvent *event) +{ + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + Q_D(QDeclarative1MouseArea); + 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(); + } + } + } + return rv; +} + +bool QDeclarative1MouseArea::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1MouseArea); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->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 = qobject_cast<QDeclarativeItem*>(s->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 (s && s->mouseGrabberItem() == this) + ungrabMouse(); + emit canceled(); + emit pressedChanged(); + if (d->hovered) { + d->hovered = false; + emit hoveredChanged(); + } + } + } + return false; +} + +bool QDeclarative1MouseArea::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarative1MouseArea); + if (!d->absorb || !isVisible() || !d->drag || !d->drag->filterChildren()) + return QDeclarativeItem::sceneEventFilter(i, e); + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e)); + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +void QDeclarative1MouseArea::timerEvent(QTimerEvent *event) +{ + Q_D(QDeclarative1MouseArea); + 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; + QDeclarative1MouseEvent me(d->lastPos.x(), d->lastPos.y(), d->lastButton, d->lastButtons, d->lastModifiers, false, d->longPress); + emit pressAndHold(&me); + } + } +} + +void QDeclarative1MouseArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarative1MouseArea); + QDeclarativeItem::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); +} + +QVariant QDeclarative1MouseArea::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarative1MouseArea); + switch (change) { + case ItemVisibleHasChanged: + if (acceptHoverEvents() && d->hovered != (isVisible() && isUnderMouse())) + setHovered(!d->hovered); + break; + default: + break; + } + + return QDeclarativeItem::itemChange(change, value); +} + +/*! + \qmlproperty bool MouseArea::hoverEnabled + This property holds whether hover events are handled. + + By default, mouse events are only handled in response to a button event, or when a button is + pressed. Hover enables handling of all mouse events even when no mouse button is + pressed. + + This property affects the containsMouse property and the onEntered, onExited and + onPositionChanged signals. +*/ +bool QDeclarative1MouseArea::hoverEnabled() const +{ + return acceptHoverEvents(); +} + +void QDeclarative1MouseArea::setHoverEnabled(bool h) +{ + Q_D(QDeclarative1MouseArea); + if (h == acceptHoverEvents()) + return; + + setAcceptHoverEvents(h); + emit hoverEnabledChanged(); + if (d->hovered != isUnderMouse()) + setHovered(!d->hovered); +} + +/*! + \qmlproperty bool MouseArea::containsMouse + This property holds whether the mouse is currently inside the mouse area. + + \warning This property is not updated if the area moves under the mouse: \e containsMouse will not change. + In addition, if hoverEnabled is false, containsMouse will only be valid when the mouse is pressed. +*/ +bool QDeclarative1MouseArea::hovered() const +{ + Q_D(const QDeclarative1MouseArea); + return d->hovered; +} + +/*! + \qmlproperty bool MouseArea::pressed + This property holds whether the mouse area is currently pressed. +*/ +bool QDeclarative1MouseArea::pressed() const +{ + Q_D(const QDeclarative1MouseArea); + return d->pressed; +} + +void QDeclarative1MouseArea::setHovered(bool h) +{ + Q_D(QDeclarative1MouseArea); + if (d->hovered != h) { + d->hovered = h; + emit hoveredChanged(); + d->hovered ? emit entered() : emit exited(); + } +} + +/*! + \qmlproperty Qt::MouseButtons MouseArea::acceptedButtons + This property holds the mouse buttons that the mouse area reacts to. + + The available buttons are: + \list + \o Qt.LeftButton + \o Qt.RightButton + \o Qt.MiddleButton + \endlist + + To accept more than one button the flags can be combined with the + "|" (or) operator: + + \code + MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton } + \endcode + + The default value is \c Qt.LeftButton. +*/ +Qt::MouseButtons QDeclarative1MouseArea::acceptedButtons() const +{ + return acceptedMouseButtons(); +} + +void QDeclarative1MouseArea::setAcceptedButtons(Qt::MouseButtons buttons) +{ + if (buttons != acceptedMouseButtons()) { + setAcceptedMouseButtons(buttons); + emit acceptedButtonsChanged(); + } +} + +bool QDeclarative1MouseArea::setPressed(bool p) +{ + Q_D(QDeclarative1MouseArea); + 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; + QDeclarative1MouseEvent 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) + emit clicked(&me); + } + + return me.isAccepted(); + } + return false; +} + +QDeclarative1Drag *QDeclarative1MouseArea::drag() +{ + Q_D(QDeclarative1MouseArea); + if (!d->drag) + d->drag = new QDeclarative1Drag; + return d->drag; +} + +/*! + \qmlproperty Item MouseArea::drag.target + \qmlproperty bool MouseArea::drag.active + \qmlproperty enumeration MouseArea::drag.axis + \qmlproperty real MouseArea::drag.minimumX + \qmlproperty real MouseArea::drag.maximumX + \qmlproperty real MouseArea::drag.minimumY + \qmlproperty real MouseArea::drag.maximumY + \qmlproperty bool MouseArea::drag.filterChildren + + \c drag provides a convenient way to make an item draggable. + + \list + \i \c drag.target specifies the id of the item to drag. + \i \c drag.active specifies if the target item is currently being dragged. + \i \c drag.axis specifies whether dragging can be done horizontally (\c Drag.XAxis), vertically (\c Drag.YAxis), or both (\c Drag.XandYAxis) + \i \c drag.minimum and \c drag.maximum limit how far the target can be dragged along the corresponding axes. + \endlist + + The following example displays a \l Rectangle that can be dragged along the X-axis. The opacity + of the rectangle is reduced when it is dragged to the right. + + \snippet doc/src/snippets/declarative/mousearea/mousearea.qml drag + + \note Items cannot be dragged if they are anchored for the requested + \c drag.axis. For example, if \c anchors.left or \c anchors.right was set + for \c rect in the above example, it cannot be dragged along the X-axis. + This can be avoided by settng the anchor value to \c undefined in + an \l onPressed handler. + + If \c drag.filterChildren is set to true, a drag can override descendant MouseAreas. This + enables a parent MouseArea to handle drags, for example, while descendants handle clicks: + + \snippet doc/src/snippets/declarative/mousearea/mouseareadragfilter.qml dragfilter + +*/ + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativemousearea_p.h b/src/qtquick1/graphicsitems/qdeclarativemousearea_p.h new file mode 100644 index 0000000000..082df9e7b7 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativemousearea_p.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEMOUSEAREA_H +#define QDECLARATIVEMOUSEAREA_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarative1Drag : public QObject +{ + Q_OBJECT + + Q_ENUMS(Axis) + Q_PROPERTY(QGraphicsObject *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: + QDeclarative1Drag(QObject *parent=0); + ~QDeclarative1Drag(); + + QGraphicsObject *target() const; + void setTarget(QGraphicsObject *); + 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: + QGraphicsObject *_target; + Axis _axis; + qreal _xmin; + qreal _xmax; + qreal _ymin; + qreal _ymax; + bool _active : 1; + bool _filterChildren: 1; + Q_DISABLE_COPY(QDeclarative1Drag) +}; + +class QDeclarative1MouseEvent; +class QDeclarative1MouseAreaPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1MouseArea : public QDeclarativeItem +{ + 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(QDeclarative1Drag *drag READ drag CONSTANT) //### add flicking to QDeclarative1Drag or add a QDeclarative1Flick ??? + Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged REVISION 1) + +public: + QDeclarative1MouseArea(QDeclarativeItem *parent=0); + ~QDeclarative1MouseArea(); + + 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); + + QDeclarative1Drag *drag(); + + bool preventStealing() const; + void setPreventStealing(bool prevent); + +Q_SIGNALS: + void hoveredChanged(); + void pressedChanged(); + void enabledChanged(); + void acceptedButtonsChanged(); + void hoverEnabledChanged(); + void positionChanged(QDeclarative1MouseEvent *mouse); + void mousePositionChanged(QDeclarative1MouseEvent *mouse); + Q_REVISION(1) void preventStealingChanged(); + + void pressed(QDeclarative1MouseEvent *mouse); + void pressAndHold(QDeclarative1MouseEvent *mouse); + void released(QDeclarative1MouseEvent *mouse); + void clicked(QDeclarative1MouseEvent *mouse); + void doubleClicked(QDeclarative1MouseEvent *mouse); + void entered(); + void exited(); + void canceled(); + +protected: + void setHovered(bool); + bool setPressed(bool); + + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); +#ifndef QT_NO_CONTEXTMENU + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); +#endif // QT_NO_CONTEXTMENU + bool sceneEvent(QEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool sceneEventFilter(QGraphicsItem *i, QEvent *e); + void timerEvent(QTimerEvent *event); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QVariant itemChange(GraphicsItemChange change, const QVariant& value); + +private: + void handlePress(); + void handleRelease(); + +private: + Q_DISABLE_COPY(QDeclarative1MouseArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1MouseArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Drag) +QML_DECLARE_TYPE(QDeclarative1MouseArea) + +QT_END_HEADER + +#endif // QDECLARATIVEMOUSEAREA_H diff --git a/src/qtquick1/graphicsitems/qdeclarativemousearea_p_p.h b/src/qtquick1/graphicsitems/qdeclarativemousearea_p_p.h new file mode 100644 index 0000000000..2bad671ffe --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativemousearea_p_p.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEMOUSEREGION_P_H +#define QDECLARATIVEMOUSEREGION_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/qdeclarativeitem_p.h" + +#include <qdatetime.h> +#include <qbasictimer.h> +#include <qgraphicssceneevent.h> + +QT_BEGIN_NAMESPACE + +class QDeclarative1MouseAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1MouseArea) + +public: + QDeclarative1MouseAreaPrivate() + : absorb(true), hovered(false), pressed(false), longPress(false), + moved(false), stealMouse(false), doubleClick(false), preventStealing(false), drag(0) + { + } + + ~QDeclarative1MouseAreaPrivate(); + + void init() + { + Q_Q(QDeclarative1MouseArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFiltersChildEvents(true); + } + + void saveEvent(QGraphicsSceneMouseEvent *event) { + lastPos = event->pos(); + lastScenePos = event->scenePos(); + lastButton = event->button(); + lastButtons = event->buttons(); + lastModifiers = event->modifiers(); + } + + bool isPressAndHoldConnected() { + Q_Q(QDeclarative1MouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("pressAndHold(QDeclarative1MouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); + } + + bool isDoubleClickConnected() { + Q_Q(QDeclarative1MouseArea); + static int idx = QObjectPrivate::get(q)->signalIndex("doubleClicked(QDeclarative1MouseEvent*)"); + return QObjectPrivate::get(q)->isSignalConnected(idx); + } + + 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; + QDeclarative1Drag *drag; + QPointF startScene; + qreal startX; + qreal startY; + QPointF lastPos; + QDeclarativeNullableValue<QPointF> lastScenePos; + Qt::MouseButton lastButton; + Qt::MouseButtons lastButtons; + Qt::KeyboardModifiers lastModifiers; + QBasicTimer pressAndHoldTimer; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEMOUSEREGION_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativepainteditem.cpp b/src/qtquick1/graphicsitems/qdeclarativepainteditem.cpp new file mode 100644 index 0000000000..396aed2160 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepainteditem.cpp @@ -0,0 +1,501 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepainteditem_p.h" +#include "QtQuick1/private/qdeclarativepainteditem_p_p.h" + +#include <QDebug> +#include <QPen> +#include <QEvent> +#include <QApplication> +#include <QGraphicsSceneMouseEvent> +#include <QPainter> +#include <QPaintEngine> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \class QDeclarative1PaintedItem + \brief The QDeclarative1PaintedItem class is an abstract base class for QDeclarative1View items that want cached painting. + \internal + + This is a convenience class for implementing items that cache their painting. + The contents of the item are cached behind the scenes. + The dirtyCache() function should be called if the contents change to + ensure the cache is refreshed the next time painting occurs. + + To subclass QDeclarative1PaintedItem, you must reimplement drawContents() to draw + the contents of the item. +*/ + +/*! + \fn void QDeclarative1PaintedItem::drawContents(QPainter *painter, const QRect &rect) + + This function is called when the cache needs to be refreshed. When + sub-classing QDeclarative1PaintedItem this function should be implemented so as to + paint the contents of the item using the given \a painter for the + area of the contents specified by \a rect. +*/ + +/*! + \property QDeclarative1PaintedItem::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 drawContents() function. This is distinct from the size of the + item in regards to height() and width(). +*/ + +// XXX bug in WebKit - can call repaintRequested and other cache-changing functions from within render! +static int inpaint=0; +static int inpaint_clearcache=0; + +extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + +/*! + Marks areas of the cache that intersect with the given \a rect as dirty and + in need of being refreshed. + + \sa clearCache() +*/ +void QDeclarative1PaintedItem::dirtyCache(const QRect& rect) +{ + Q_D(QDeclarative1PaintedItem); + QRect srect(qCeil(rect.x()*d->contentsScale), + qCeil(rect.y()*d->contentsScale), + qCeil(rect.width()*d->contentsScale), + qCeil(rect.height()*d->contentsScale)); + for (int i=0; i < d->imagecache.count(); ) { + QDeclarative1PaintedItemPrivate::ImageCacheItem *c = d->imagecache[i]; + QRect isect = (c->area & srect) | c->dirty; + if (isect == c->area && !inpaint) { + delete d->imagecache.takeAt(i); + } else { + c->dirty = isect; + ++i; + } + } +} + +/*! + Marks the entirety of the contents cache as dirty. + + \sa dirtyCache() +*/ +void QDeclarative1PaintedItem::clearCache() +{ + if (inpaint) { + inpaint_clearcache=1; + return; + } + Q_D(QDeclarative1PaintedItem); + qDeleteAll(d->imagecache); + d->imagecache.clear(); +} + +/*! + Returns the size of the contents. + + \sa setContentsSize() +*/ +QSize QDeclarative1PaintedItem::contentsSize() const +{ + Q_D(const QDeclarative1PaintedItem); + return d->contentsSize; +} + +/*! + Sets the size of the contents to the given \a size. + + \sa contentsSize() +*/ +void QDeclarative1PaintedItem::setContentsSize(const QSize &size) +{ + Q_D(QDeclarative1PaintedItem); + if (d->contentsSize == size) return; + prepareGeometryChange(); + d->contentsSize = size; + clearCache(); + update(); + emit contentsSizeChanged(); +} + +qreal QDeclarative1PaintedItem::contentsScale() const +{ + Q_D(const QDeclarative1PaintedItem); + return d->contentsScale; +} + +void QDeclarative1PaintedItem::setContentsScale(qreal scale) +{ + Q_D(QDeclarative1PaintedItem); + if (d->contentsScale == scale) return; + d->contentsScale = scale; + clearCache(); + update(); + emit contentsScaleChanged(); +} + + +/*! + Constructs a new QDeclarative1PaintedItem with the given \a parent. +*/ +QDeclarative1PaintedItem::QDeclarative1PaintedItem(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1PaintedItemPrivate), parent) +{ +} + +/*! + \internal + Constructs a new QDeclarative1PaintedItem with the given \a parent and + initialized private data member \a dd. +*/ +QDeclarative1PaintedItem::QDeclarative1PaintedItem(QDeclarative1PaintedItemPrivate &dd, QDeclarativeItem *parent) + : QDeclarativeItem(dd, parent) +{ +} + +/*! + Destroys the image item. +*/ +QDeclarative1PaintedItem::~QDeclarative1PaintedItem() +{ + clearCache(); +} + +void QDeclarative1PaintedItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width() || + newGeometry.height() != oldGeometry.height()) + clearCache(); + + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +QVariant QDeclarative1PaintedItem::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + if (change == ItemVisibleHasChanged) + clearCache(); + + return QDeclarativeItem::itemChange(change, value); +} + +void QDeclarative1PaintedItem::setCacheFrozen(bool frozen) +{ + Q_D(QDeclarative1PaintedItem); + if (d->cachefrozen == frozen) + return; + d->cachefrozen = frozen; + // XXX clear cache? +} + +QRectF QDeclarative1PaintedItem::boundingRect() const +{ + Q_D(const QDeclarative1PaintedItem); + qreal w = d->mWidth; + QSizeF sz = d->contentsSize * d->contentsScale; + if (w < sz.width()) + w = sz.width(); + qreal h = d->mHeight; + if (h < sz.height()) + h = sz.height(); + return QRectF(0.0,0.0,w,h); +} + +/*! + \internal +*/ +void QDeclarative1PaintedItem::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarative1PaintedItem); + const QRect content = boundingRect().toRect(); + if (content.width() <= 0 || content.height() <= 0) + return; + + ++inpaint; + + const QTransform &x = p->deviceTransform(); + QTransform xinv = x.inverted(); + QRegion effectiveClip; + QRegion sysClip = p->paintEngine()->systemClip(); + if (xinv.type() <= QTransform::TxScale && sysClip.numRects() < 5) { + // simple transform, region gets no more complicated... + effectiveClip = xinv.map(sysClip); + } else { + // do not make complicated regions... + effectiveClip = xinv.mapRect(sysClip.boundingRect()); + } + + QRegion topaint = p->clipRegion(); + if (topaint.isEmpty()) { + if (effectiveClip.isEmpty()) + topaint = QRect(0,0,p->device()->width(),p->device()->height()); + else + topaint = effectiveClip; + } else if (!effectiveClip.isEmpty()) { + topaint &= effectiveClip; + } + + topaint &= content; + QRegion uncached(content); + p->setRenderHints(QPainter::SmoothPixmapTransform, d->smooth); + + int cachesize=0; + for (int i=0; i<d->imagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + if (topaint.contains(area)) { + QRectF target(area.x(), area.y(), area.width(), area.height()); + if (!d->cachefrozen) { + if (!d->imagecache[i]->dirty.isNull() && topaint.contains(d->imagecache[i]->dirty)) { +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter qp(&d->imagecache[i]->image); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache); + qp.translate(-area.x(), -area.y()); + qp.scale(d->contentsScale,d->contentsScale); + QRect clip = d->imagecache[i]->dirty; + QRect sclip(qFloor(clip.x()/d->contentsScale), + qFloor(clip.y()/d->contentsScale), + qCeil(clip.width()/d->contentsScale+clip.x()/d->contentsScale-qFloor(clip.x()/d->contentsScale)), + qCeil(clip.height()/d->contentsScale+clip.y()/d->contentsScale-qFloor(clip.y()/d->contentsScale))); + qp.setClipRect(sclip); + if (d->fillColor.isValid()){ + if(d->fillColor.alpha() < 255){ + // ### Might not work outside of raster paintengine + QPainter::CompositionMode prev = qp.compositionMode(); + qp.setCompositionMode(QPainter::CompositionMode_Source); + qp.fillRect(sclip,d->fillColor); + qp.setCompositionMode(prev); + }else{ + qp.fillRect(sclip,d->fillColor); + } + } + drawContents(&qp, sclip); + d->imagecache[i]->dirty = QRect(); + } + } + p->drawPixmap(target.toRect(), d->imagecache[i]->image); + topaint -= area; + d->imagecache[i]->age=0; + } else { + d->imagecache[i]->age++; + } + cachesize += area.width()*area.height(); + uncached -= area; + } + + if (!topaint.isEmpty()) { + if (!d->cachefrozen) { + // Find a sensible larger area, otherwise will paint lots of tiny images. + QRect biggerrect = topaint.boundingRect().adjusted(-64,-64,128,128); + cachesize += biggerrect.width() * biggerrect.height(); + while (d->imagecache.count() && cachesize > d->max_imagecache_size) { + int oldest=-1; + int age=-1; + for (int i=0; i<d->imagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + uncached += d->imagecache[oldest]->area; + delete d->imagecache.takeAt(oldest); + } + const QRegion bigger = QRegion(biggerrect) & uncached; + const QVector<QRect> rects = bigger.rects(); + for (int i = 0; i < rects.count(); ++i) { + const QRect &r = rects.at(i); + QPixmap img(r.size()); + if (d->fillColor.isValid()) + img.fill(d->fillColor); + { +#ifdef Q_WS_MAC + bool oldSmooth = qt_applefontsmoothing_enabled; + qt_applefontsmoothing_enabled = false; +#endif + QPainter qp(&img); +#ifdef Q_WS_MAC + qt_applefontsmoothing_enabled = oldSmooth; +#endif + qp.setRenderHints(QPainter::HighQualityAntialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, d->smoothCache); + + qp.translate(-r.x(),-r.y()); + qp.scale(d->contentsScale,d->contentsScale); + QRect sclip(qFloor(r.x()/d->contentsScale), + qFloor(r.y()/d->contentsScale), + qCeil(r.width()/d->contentsScale+r.x()/d->contentsScale-qFloor(r.x()/d->contentsScale)), + qCeil(r.height()/d->contentsScale+r.y()/d->contentsScale-qFloor(r.y()/d->contentsScale))); + drawContents(&qp, sclip); + } + QDeclarative1PaintedItemPrivate::ImageCacheItem *newitem = new QDeclarative1PaintedItemPrivate::ImageCacheItem; + newitem->area = r; + newitem->image = img; + d->imagecache.append(newitem); + p->drawPixmap(r, newitem->image); + } + } else { + const QVector<QRect> rects = uncached.rects(); + for (int i = 0; i < rects.count(); ++i) + p->fillRect(rects.at(i), Qt::lightGray); + } + } + + if (inpaint_clearcache) { + clearCache(); + inpaint_clearcache = 0; + } + + --inpaint; +} + +/*! + \qmlproperty int PaintedItem::pixelCacheSize + + This property holds the maximum number of pixels of image cache to + allow. The default is 0.1 megapixels. The cache will not be larger + than the (unscaled) size of the WebView. +*/ +/*! + \property QDeclarative1PaintedItem::pixelCacheSize + + The maximum number of pixels of image cache to allow. The default + is 0.1 megapixels. The cache will not be larger than the (unscaled) + size of the QDeclarative1PaintedItem. +*/ +int QDeclarative1PaintedItem::pixelCacheSize() const +{ + Q_D(const QDeclarative1PaintedItem); + return d->max_imagecache_size; +} + +void QDeclarative1PaintedItem::setPixelCacheSize(int pixels) +{ + Q_D(QDeclarative1PaintedItem); + if (pixels < d->max_imagecache_size) { + int cachesize=0; + for (int i=0; i<d->imagecache.count(); ++i) { + QRect area = d->imagecache[i]->area; + cachesize += area.width()*area.height(); + } + while (d->imagecache.count() && cachesize > pixels) { + int oldest=-1; + int age=-1; + for (int i=0; i<d->imagecache.count(); ++i) { + int a = d->imagecache[i]->age; + if (a > age) { + oldest = i; + age = a; + } + } + cachesize -= d->imagecache[oldest]->area.width()*d->imagecache[oldest]->area.height(); + delete d->imagecache.takeAt(oldest); + } + } + d->max_imagecache_size = pixels; +} + + + +/*! + \property QDeclarative1PaintedItem::fillColor + + The color to be used to fill the item prior to calling drawContents(). + By default, this is Qt::transparent. + + Performance improvements can be achieved if subclasses call this with either an + invalid color (QColor()), or an appropriate solid color. +*/ +void QDeclarative1PaintedItem::setFillColor(const QColor& c) +{ + Q_D(QDeclarative1PaintedItem); + if (d->fillColor == c) + return; + d->fillColor = c; + emit fillColorChanged(); + update(); +} + +QColor QDeclarative1PaintedItem::fillColor() const +{ + Q_D(const QDeclarative1PaintedItem); + return d->fillColor; +} + +/*! + \qmlproperty bool PaintedItem::smoothCache + + Controls whether the cached tiles of which the item is composed are + rendered smoothly when they are generated. + + This is in addition toe Item::smooth, which controls the smooth painting of + the already-painted cached tiles under transformation. +*/ +bool QDeclarative1PaintedItem::smoothCache() const +{ + Q_D(const QDeclarative1PaintedItem); + return d->smoothCache; +} + +void QDeclarative1PaintedItem::setSmoothCache(bool on) +{ + Q_D(QDeclarative1PaintedItem); + if (d->smoothCache != on) { + d->smoothCache = on; + clearCache(); + } +} + + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativepainteditem_p.h b/src/qtquick1/graphicsitems/qdeclarativepainteditem_p.h new file mode 100644 index 0000000000..5e123219b1 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepainteditem_p.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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEITEM_H +#define QDECLARATIVEIMAGEITEM_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1PaintedItemPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1PaintedItem : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QSize contentsSize READ contentsSize WRITE setContentsSize NOTIFY contentsSizeChanged) + Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged) + Q_PROPERTY(int pixelCacheSize READ pixelCacheSize WRITE setPixelCacheSize) + Q_PROPERTY(bool smoothCache READ smoothCache WRITE setSmoothCache) + Q_PROPERTY(qreal contentsScale READ contentsScale WRITE setContentsScale NOTIFY contentsScaleChanged) + + +public: + QDeclarative1PaintedItem(QDeclarativeItem *parent=0); + ~QDeclarative1PaintedItem(); + + QSize contentsSize() const; + void setContentsSize(const QSize &); + + qreal contentsScale() const; + void setContentsScale(qreal); + + int pixelCacheSize() const; + void setPixelCacheSize(int pixels); + + bool smoothCache() const; + void setSmoothCache(bool on); + + QColor fillColor() const; + void setFillColor(const QColor&); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +protected: + QDeclarative1PaintedItem(QDeclarative1PaintedItemPrivate &dd, QDeclarativeItem *parent); + + virtual void drawContents(QPainter *p, const QRect &) = 0; + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QVariant itemChange(GraphicsItemChange change, + const QVariant &value); + + void setCacheFrozen(bool); + QRectF boundingRect() const; + +Q_SIGNALS: + void fillColorChanged(); + void contentsSizeChanged(); + void contentsScaleChanged(); + +protected Q_SLOTS: + void dirtyCache(const QRect &); + void clearCache(); + +private: + Q_DISABLE_COPY(QDeclarative1PaintedItem) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1PaintedItem) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1PaintedItem) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativepainteditem_p_p.h b/src/qtquick1/graphicsitems/qdeclarativepainteditem_p_p.h new file mode 100644 index 0000000000..44c825da9c --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepainteditem_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEIMAGEITEM_P_H +#define QDECLARATIVEIMAGEITEM_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/qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarative1PaintedItemPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1PaintedItem) + +public: + QDeclarative1PaintedItemPrivate() + : max_imagecache_size(100000), contentsScale(1.0), fillColor(Qt::transparent), cachefrozen(false), smoothCache(true) + { + } + + struct ImageCacheItem { + ImageCacheItem() : age(0) {} + ~ImageCacheItem() { } + int age; + QRect area; + QRect dirty; // one dirty area (allows optimization of common cases) + QPixmap image; + }; + + QList<ImageCacheItem*> imagecache; + + int max_imagecache_size; + QSize contentsSize; + qreal contentsScale; + QColor fillColor; + bool cachefrozen; + bool smoothCache; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativepath.cpp b/src/qtquick1/graphicsitems/qdeclarativepath.cpp new file mode 100644 index 0000000000..9328f2c5dd --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepath.cpp @@ -0,0 +1,926 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepath_p.h" +#include "QtQuick1/private/qdeclarativepath_p_p.h" + +#include <QSet> +#include <QTime> + +#include <private/qbezier_p.h> +#include <QtCore/qmath.h> +#include <QtCore/qnumeric.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass PathElement QDeclarative1PathElement + \ingroup qml-view-elements + \since 4.7 + \brief PathElement is the base path type. + + This type is the base for all path types. It cannot + be instantiated. + + \sa Path, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ + +/*! + \qmlclass Path QDeclarative1Path + \ingroup qml-view-elements + \since 4.7 + \brief A Path object defines a path for use by \l PathView. + + A Path is composed of one or more path segments - PathLine, PathQuad, + PathCubic. + + The spacing of the items along the Path can be adjusted via a + PathPercent object. + + PathAttribute allows named attributes with values to be defined + along the path. + + \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic +*/ +QDeclarative1Path::QDeclarative1Path(QObject *parent) + : QObject(*(new QDeclarative1PathPrivate), parent) +{ +} + +QDeclarative1Path::~QDeclarative1Path() +{ +} + +/*! + \qmlproperty real Path::startX + \qmlproperty real Path::startY + These properties hold the starting position of the path. +*/ +qreal QDeclarative1Path::startX() const +{ + Q_D(const QDeclarative1Path); + return d->startX; +} + +void QDeclarative1Path::setStartX(qreal x) +{ + Q_D(QDeclarative1Path); + if (qFuzzyCompare(x, d->startX)) + return; + d->startX = x; + emit startXChanged(); + processPath(); +} + +qreal QDeclarative1Path::startY() const +{ + Q_D(const QDeclarative1Path); + return d->startY; +} + +void QDeclarative1Path::setStartY(qreal y) +{ + Q_D(QDeclarative1Path); + if (qFuzzyCompare(y, d->startY)) + return; + d->startY = y; + emit startYChanged(); + processPath(); +} + +/*! + \qmlproperty bool Path::closed + This property holds whether the start and end of the path are identical. +*/ +bool QDeclarative1Path::isClosed() const +{ + Q_D(const QDeclarative1Path); + return d->closed; +} + +/*! + \qmlproperty list<PathElement> Path::pathElements + This property holds the objects composing the path. + + \default + + A path can contain the following path objects: + \list + \i \l PathLine - a straight line to a given position. + \i \l PathQuad - a quadratic Bezier curve to a given position with a control point. + \i \l PathCubic - a cubic Bezier curve to a given position with two control points. + \i \l PathAttribute - an attribute at a given position in the path. + \i \l PathPercent - a way to spread out items along various segments of the path. + \endlist + + \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 2 +*/ + +QDeclarativeListProperty<QDeclarative1PathElement> QDeclarative1Path::pathElements() +{ + Q_D(QDeclarative1Path); + return QDeclarativeListProperty<QDeclarative1PathElement>(this, d->_pathElements); +} + +void QDeclarative1Path::interpolate(int idx, const QString &name, qreal value) +{ + Q_D(QDeclarative1Path); + if (!idx) + return; + + qreal lastValue = 0; + qreal lastPercent = 0; + int search = idx - 1; + while(search >= 0) { + const AttributePoint &point = d->_attributePoints.at(search); + if (point.values.contains(name)) { + lastValue = point.values.value(name); + lastPercent = point.origpercent; + break; + } + --search; + } + + ++search; + + const AttributePoint &curPoint = d->_attributePoints.at(idx); + + for (int ii = search; ii < idx; ++ii) { + AttributePoint &point = d->_attributePoints[ii]; + + qreal val = lastValue + (value - lastValue) * (point.origpercent - lastPercent) / (curPoint.origpercent - lastPercent); + point.values.insert(name, val); + } +} + +void QDeclarative1Path::endpoint(const QString &name) +{ + Q_D(QDeclarative1Path); + const AttributePoint &first = d->_attributePoints.first(); + qreal val = first.values.value(name); + for (int ii = d->_attributePoints.count() - 1; ii >= 0; ii--) { + const AttributePoint &point = d->_attributePoints.at(ii); + if (point.values.contains(name)) { + for (int jj = ii + 1; jj < d->_attributePoints.count(); ++jj) { + AttributePoint &setPoint = d->_attributePoints[jj]; + setPoint.values.insert(name, val); + } + return; + } + } +} + +void QDeclarative1Path::processPath() +{ + Q_D(QDeclarative1Path); + + if (!d->componentComplete) + return; + + d->_pointCache.clear(); + d->_attributePoints.clear(); + d->_path = QPainterPath(); + + AttributePoint first; + for (int ii = 0; ii < d->_attributes.count(); ++ii) + first.values[d->_attributes.at(ii)] = 0; + d->_attributePoints << first; + + d->_path.moveTo(d->startX, d->startY); + + QDeclarative1Curve *lastCurve = 0; + foreach (QDeclarative1PathElement *pathElement, d->_pathElements) { + if (QDeclarative1Curve *curve = qobject_cast<QDeclarative1Curve *>(pathElement)) { + curve->addToPath(d->_path); + AttributePoint p; + p.origpercent = d->_path.length(); + d->_attributePoints << p; + lastCurve = curve; + } else if (QDeclarative1PathAttribute *attribute = qobject_cast<QDeclarative1PathAttribute *>(pathElement)) { + AttributePoint &point = d->_attributePoints.last(); + point.values[attribute->name()] = attribute->value(); + interpolate(d->_attributePoints.count() - 1, attribute->name(), attribute->value()); + } else if (QDeclarative1PathPercent *percent = qobject_cast<QDeclarative1PathPercent *>(pathElement)) { + AttributePoint &point = d->_attributePoints.last(); + point.values[QLatin1String("_qfx_percent")] = percent->value(); + interpolate(d->_attributePoints.count() - 1, QLatin1String("_qfx_percent"), percent->value()); + } + } + + // Fixup end points + const AttributePoint &last = d->_attributePoints.last(); + for (int ii = 0; ii < d->_attributes.count(); ++ii) { + if (!last.values.contains(d->_attributes.at(ii))) + endpoint(d->_attributes.at(ii)); + } + + // Adjust percent + qreal length = d->_path.length(); + qreal prevpercent = 0; + qreal prevorigpercent = 0; + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + if (point.values.contains(QLatin1String("_qfx_percent"))) { //special string for QDeclarative1PathPercent + if ( ii > 0) { + qreal scale = (d->_attributePoints[ii].origpercent/length - prevorigpercent) / + (point.values.value(QLatin1String("_qfx_percent"))-prevpercent); + d->_attributePoints[ii].scale = scale; + } + d->_attributePoints[ii].origpercent /= length; + d->_attributePoints[ii].percent = point.values.value(QLatin1String("_qfx_percent")); + prevorigpercent = d->_attributePoints[ii].origpercent; + prevpercent = d->_attributePoints[ii].percent; + } else { + d->_attributePoints[ii].origpercent /= length; + d->_attributePoints[ii].percent = d->_attributePoints[ii].origpercent; + } + } + + d->closed = lastCurve && d->startX == lastCurve->x() && d->startY == lastCurve->y(); + + emit changed(); +} + +void QDeclarative1Path::classBegin() +{ + Q_D(QDeclarative1Path); + d->componentComplete = false; +} + +void QDeclarative1Path::componentComplete() +{ + Q_D(QDeclarative1Path); + QSet<QString> attrs; + d->componentComplete = true; + + // First gather up all the attributes + foreach (QDeclarative1PathElement *pathElement, d->_pathElements) { + if (QDeclarative1PathAttribute *attribute = + qobject_cast<QDeclarative1PathAttribute *>(pathElement)) + attrs.insert(attribute->name()); + } + d->_attributes = attrs.toList(); + + processPath(); + + foreach (QDeclarative1PathElement *pathElement, d->_pathElements) + connect(pathElement, SIGNAL(changed()), this, SLOT(processPath())); +} + +QPainterPath QDeclarative1Path::path() const +{ + Q_D(const QDeclarative1Path); + return d->_path; +} + +QStringList QDeclarative1Path::attributes() const +{ + Q_D(const QDeclarative1Path); + if (!d->componentComplete) { + QSet<QString> attrs; + + // First gather up all the attributes + foreach (QDeclarative1PathElement *pathElement, d->_pathElements) { + if (QDeclarative1PathAttribute *attribute = + qobject_cast<QDeclarative1PathAttribute *>(pathElement)) + attrs.insert(attribute->name()); + } + return attrs.toList(); + } + return d->_attributes; +} + +static inline QBezier nextBezier(const QPainterPath &path, int *from, qreal *bezLength) +{ + const int lastElement = path.elementCount() - 1; + for (int i=*from; i <= lastElement; ++i) { + const QPainterPath::Element &e = path.elementAt(i); + + switch (e.type) { + case QPainterPath::MoveToElement: + break; + case QPainterPath::LineToElement: + { + QLineF line(path.elementAt(i-1), e); + *bezLength = line.length(); + QPointF a = path.elementAt(i-1); + QPointF delta = e - a; + *from = i+1; + return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e); + } + case QPainterPath::CurveToElement: + { + QBezier b = QBezier::fromPoints(path.elementAt(i-1), + e, + path.elementAt(i+1), + path.elementAt(i+2)); + *bezLength = b.length(); + *from = i+3; + return b; + } + default: + break; + } + } + *from = lastElement; + *bezLength = 0; + return QBezier(); +} + +void QDeclarative1Path::createPointCache() const +{ + Q_D(const QDeclarative1Path); + qreal pathLength = d->_path.length(); + if (pathLength <= 0 || qIsNaN(pathLength)) + return; + // more points means less jitter between items as they move along the + // path, but takes longer to generate + const int points = qCeil(pathLength*5); + const int lastElement = d->_path.elementCount() - 1; + d->_pointCache.resize(points+1); + + int currElement = 0; + qreal bezLength = 0; + QBezier currBez = nextBezier(d->_path, &currElement, &bezLength); + qreal currLength = bezLength; + qreal epc = currLength / pathLength; + + for (int i = 0; i < d->_pointCache.size(); i++) { + //find which set we are in + qreal prevPercent = 0; + qreal prevOrigPercent = 0; + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + qreal percent = qreal(i)/points; + const AttributePoint &point = d->_attributePoints.at(ii); + if (percent < point.percent || ii == d->_attributePoints.count() - 1) { //### || is special case for very last item + qreal elementPercent = (percent - prevPercent); + + qreal spc = prevOrigPercent + elementPercent * point.scale; + + while (spc > epc) { + if (currElement > lastElement) + break; + currBez = nextBezier(d->_path, &currElement, &bezLength); + if (bezLength == 0.0) { + currLength = pathLength; + epc = 1.0; + break; + } + currLength += bezLength; + epc = currLength / pathLength; + } + qreal realT = (pathLength * spc - (currLength - bezLength)) / bezLength; + d->_pointCache[i] = currBez.pointAt(qBound(qreal(0), realT, qreal(1))); + break; + } + prevOrigPercent = point.origpercent; + prevPercent = point.percent; + } + } +} + +QPointF QDeclarative1Path::pointAt(qreal p) const +{ + Q_D(const QDeclarative1Path); + if (d->_pointCache.isEmpty()) { + createPointCache(); + if (d->_pointCache.isEmpty()) + return QPointF(); + } + int idx = qRound(p*d->_pointCache.size()); + if (idx >= d->_pointCache.size()) + idx = d->_pointCache.size() - 1; + else if (idx < 0) + idx = 0; + return d->_pointCache.at(idx); +} + +qreal QDeclarative1Path::attributeAt(const QString &name, qreal percent) const +{ + Q_D(const QDeclarative1Path); + if (percent < 0 || percent > 1) + return 0; + + for (int ii = 0; ii < d->_attributePoints.count(); ++ii) { + const AttributePoint &point = d->_attributePoints.at(ii); + + if (point.percent == percent) { + return point.values.value(name); + } else if (point.percent > percent) { + qreal lastValue = + ii?(d->_attributePoints.at(ii - 1).values.value(name)):0; + qreal lastPercent = + ii?(d->_attributePoints.at(ii - 1).percent):0; + qreal curValue = point.values.value(name); + qreal curPercent = point.percent; + + return lastValue + (curValue - lastValue) * (percent - lastPercent) / (curPercent - lastPercent); + } + } + + return 0; +} + +/****************************************************************************/ + +qreal QDeclarative1Curve::x() const +{ + return _x; +} + +void QDeclarative1Curve::setX(qreal x) +{ + if (_x != x) { + _x = x; + emit xChanged(); + emit changed(); + } +} + +qreal QDeclarative1Curve::y() const +{ + return _y; +} + +void QDeclarative1Curve::setY(qreal y) +{ + if (_y != y) { + _y = y; + emit yChanged(); + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathAttribute QDeclarative1PathAttribute + \ingroup qml-view-elements + \since 4.7 + \brief The PathAttribute allows setting an attribute at a given position in a Path. + + The PathAttribute object allows attributes consisting of a name and + a value to be specified for various points along a path. The + attributes are exposed to the delegate as + \l{qdeclarativeintroduction.html#attached-properties} {Attached Properties}. + The value of an attribute at any particular point along the path is interpolated + from the PathAttributes bounding that point. + + The example below shows a path with the items scaled to 30% with + opacity 50% at the top of the path and scaled 100% with opacity + 100% at the bottom. Note the use of the PathView.iconScale and + PathView.iconOpacity attached properties to set the scale and opacity + of the delegate. + + \table + \row + \o \image declarative-pathattribute.png + \o + \snippet doc/src/snippets/declarative/pathview/pathattributes.qml 0 + (see the PathView documentation for the specification of ContactModel.qml + used for ContactModel above.) + \endtable + + + \sa Path +*/ + +/*! + \qmlproperty string PathAttribute::name + This property holds the name of the attribute to change. + + This attribute will be available to the delegate as PathView.<name> + + Note that using an existing Item property name such as "opacity" as an + attribute is allowed. This is because path attributes add a new + \l{qdeclarativeintroduction.html#attached-properties} {Attached Property} + which in no way clashes with existing properties. +*/ + +/*! + the name of the attribute to change. +*/ + +QString QDeclarative1PathAttribute::name() const +{ + return _name; +} + +void QDeclarative1PathAttribute::setName(const QString &name) +{ + if (_name == name) + return; + _name = name; + emit nameChanged(); +} + +/*! + \qmlproperty real PathAttribute::value + This property holds the value for the attribute. + + The value specified can be used to influence the visual appearance + of an item along the path. For example, the following Path specifies + an attribute named \e itemRotation, which has the value \e 0 at the + beginning of the path, and the value 90 at the end of the path. + + \qml + Path { + startX: 0 + startY: 0 + PathAttribute { name: "itemRotation"; value: 0 } + PathLine { x: 100; y: 100 } + PathAttribute { name: "itemRotation"; value: 90 } + } + \endqml + + In our delegate, we can then bind the \e rotation property to the + \l{qdeclarativeintroduction.html#attached-properties} {Attached Property} + \e PathView.itemRotation created for this attribute. + + \qml + Rectangle { + width: 10; height: 10 + rotation: PathView.itemRotation + } + \endqml + + As each item is positioned along the path, it will be rotated accordingly: + an item at the beginning of the path with be not be rotated, an item at + the end of the path will be rotated 90 degrees, and an item mid-way along + the path will be rotated 45 degrees. +*/ + +/*! + the new value of the attribute. +*/ +qreal QDeclarative1PathAttribute::value() const +{ + return _value; +} + +void QDeclarative1PathAttribute::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit valueChanged(); + emit changed(); + } +} + +/****************************************************************************/ + +/*! + \qmlclass PathLine QDeclarative1PathLine + \ingroup qml-view-elements + \since 4.7 + \brief The PathLine defines a straight line. + + The example below creates a path consisting of a straight line from + 0,100 to 200,100: + + \qml + Path { + startX: 0; startY: 100 + PathLine { x: 200; y: 100 } + } + \endqml + + \sa Path, PathQuad, PathCubic +*/ + +/*! + \qmlproperty real PathLine::x + \qmlproperty real PathLine::y + + Defines the end point of the line. +*/ + +void QDeclarative1PathLine::addToPath(QPainterPath &path) +{ + path.lineTo(x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathQuad QDeclarative1PathQuad + \ingroup qml-view-elements + \since 4.7 + \brief The PathQuad defines a quadratic Bezier curve with a control point. + + The following QML produces the path shown below: + \table + \row + \o \image declarative-pathquad.png + \o + \qml + Path { + startX: 0; startY: 0 + PathQuad { x: 200; y: 0; controlX: 100; controlY: 150 } + } + \endqml + \endtable + + \sa Path, PathCubic, PathLine +*/ + +/*! + \qmlproperty real PathQuad::x + \qmlproperty real PathQuad::y + + Defines the end point of the curve. +*/ + +/*! + \qmlproperty real PathQuad::controlX + \qmlproperty real PathQuad::controlY + + Defines the position of the control point. +*/ + +/*! + the x position of the control point. +*/ +qreal QDeclarative1PathQuad::controlX() const +{ + return _controlX; +} + +void QDeclarative1PathQuad::setControlX(qreal x) +{ + if (_controlX != x) { + _controlX = x; + emit controlXChanged(); + emit changed(); + } +} + + +/*! + the y position of the control point. +*/ +qreal QDeclarative1PathQuad::controlY() const +{ + return _controlY; +} + +void QDeclarative1PathQuad::setControlY(qreal y) +{ + if (_controlY != y) { + _controlY = y; + emit controlYChanged(); + emit changed(); + } +} + +void QDeclarative1PathQuad::addToPath(QPainterPath &path) +{ + path.quadTo(controlX(), controlY(), x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathCubic QDeclarative1PathCubic + \ingroup qml-view-elements + \since 4.7 + \brief The PathCubic defines a cubic Bezier curve with two control points. + + The following QML produces the path shown below: + \table + \row + \o \image declarative-pathcubic.png + \o + \qml + Path { + startX: 20; startY: 0 + PathCubic { + x: 180; y: 0 + control1X: -10; control1Y: 90 + control2X: 210; control2Y: 90 + } + } + \endqml + \endtable + + \sa Path, PathQuad, PathLine +*/ + +/*! + \qmlproperty real PathCubic::x + \qmlproperty real PathCubic::y + + Defines the end point of the curve. +*/ + +/*! + \qmlproperty real PathCubic::control1X + \qmlproperty real PathCubic::control1Y + + Defines the position of the first control point. +*/ +qreal QDeclarative1PathCubic::control1X() const +{ + return _control1X; +} + +void QDeclarative1PathCubic::setControl1X(qreal x) +{ + if (_control1X != x) { + _control1X = x; + emit control1XChanged(); + emit changed(); + } +} + +qreal QDeclarative1PathCubic::control1Y() const +{ + return _control1Y; +} + +void QDeclarative1PathCubic::setControl1Y(qreal y) +{ + if (_control1Y != y) { + _control1Y = y; + emit control1YChanged(); + emit changed(); + } +} + +/*! + \qmlproperty real PathCubic::control2X + \qmlproperty real PathCubic::control2Y + + Defines the position of the second control point. +*/ +qreal QDeclarative1PathCubic::control2X() const +{ + return _control2X; +} + +void QDeclarative1PathCubic::setControl2X(qreal x) +{ + if (_control2X != x) { + _control2X = x; + emit control2XChanged(); + emit changed(); + } +} + +qreal QDeclarative1PathCubic::control2Y() const +{ + return _control2Y; +} + +void QDeclarative1PathCubic::setControl2Y(qreal y) +{ + if (_control2Y != y) { + _control2Y = y; + emit control2YChanged(); + emit changed(); + } +} + +void QDeclarative1PathCubic::addToPath(QPainterPath &path) +{ + path.cubicTo(control1X(), control1Y(), control2X(), control2Y(), x(), y()); +} + +/****************************************************************************/ + +/*! + \qmlclass PathPercent QDeclarative1PathPercent + \ingroup qml-view-elements + \since 4.7 + \brief The PathPercent manipulates the way a path is interpreted. + + PathPercent allows you to manipulate the spacing between items on a + PathView's path. You can use it to bunch together items on part of + the path, and spread them out on other parts of the path. + + The examples below show the normal distrubution of items along a path + compared to a distribution which places 50% of the items along the + PathLine section of the path. + \table + \row + \o \image declarative-nopercent.png + \o + \qml + PathView { + // ... + Path { + startX: 20; startY: 0 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathLine { x: 150; y: 80 } + PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 } + } + } + \endqml + \row + \o \image declarative-percent.png + \o + \qml + PathView { + // ... + Path { + startX: 20; startY: 0 + PathQuad { x: 50; y: 80; controlX: 0; controlY: 80 } + PathPercent { value: 0.25 } + PathLine { x: 150; y: 80 } + PathPercent { value: 0.75 } + PathQuad { x: 180; y: 0; controlX: 200; controlY: 80 } + PathPercent { value: 1 } + } + } + \endqml + \endtable + + \sa Path +*/ + +/*! + \qmlproperty real PathPercent::value + The proporation of items that should be laid out up to this point. + + This value should always be higher than the last value specified + by a PathPercent at a previous position in the Path. + + In the following example we have a Path made up of three PathLines. + Normally, the items of the PathView would be laid out equally along + this path, with an equal number of items per line segment. PathPercent + allows us to specify that the first and third lines should each hold + 10% of the laid out items, while the second line should hold the remaining + 80%. + + \qml + PathView { + // ... + Path { + startX: 0; startY: 0 + PathLine { x:100; y: 0; } + PathPercent { value: 0.1 } + PathLine { x: 100; y: 100 } + PathPercent { value: 0.9 } + PathLine { x: 100; y: 0 } + PathPercent { value: 1 } + } + } + \endqml +*/ + +qreal QDeclarative1PathPercent::value() const +{ + return _value; +} + +void QDeclarative1PathPercent::setValue(qreal value) +{ + if (_value != value) { + _value = value; + emit valueChanged(); + emit changed(); + } +} + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativepath_p.h b/src/qtquick1/graphicsitems/qdeclarativepath_p.h new file mode 100644 index 0000000000..e8dfad5e5f --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepath_p.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATH_H +#define QDECLARATIVEPATH_H + +#include "qdeclarativeitem.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/QObject> +#include <QtGui/QPainterPath> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_AUTOTEST_EXPORT QDeclarative1PathElement : public QObject +{ + Q_OBJECT +public: + QDeclarative1PathElement(QObject *parent=0) : QObject(parent) {} +Q_SIGNALS: + void changed(); +}; + +class Q_AUTOTEST_EXPORT QDeclarative1PathAttribute : public QDeclarative1PathElement +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) +public: + QDeclarative1PathAttribute(QObject *parent=0) : QDeclarative1PathElement(parent), _value(0) {} + + + QString name() const; + void setName(const QString &name); + + qreal value() const; + void setValue(qreal value); + +Q_SIGNALS: + void nameChanged(); + void valueChanged(); + +private: + QString _name; + qreal _value; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1Curve : public QDeclarative1PathElement +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) +public: + QDeclarative1Curve(QObject *parent=0) : QDeclarative1PathElement(parent), _x(0), _y(0) {} + + qreal x() const; + void setX(qreal x); + + qreal y() const; + void setY(qreal y); + + virtual void addToPath(QPainterPath &) {} + +Q_SIGNALS: + void xChanged(); + void yChanged(); + +private: + qreal _x; + qreal _y; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1PathLine : public QDeclarative1Curve +{ + Q_OBJECT +public: + QDeclarative1PathLine(QObject *parent=0) : QDeclarative1Curve(parent) {} + + void addToPath(QPainterPath &path); +}; + +class Q_AUTOTEST_EXPORT QDeclarative1PathQuad : public QDeclarative1Curve +{ + Q_OBJECT + + Q_PROPERTY(qreal controlX READ controlX WRITE setControlX NOTIFY controlXChanged) + Q_PROPERTY(qreal controlY READ controlY WRITE setControlY NOTIFY controlYChanged) +public: + QDeclarative1PathQuad(QObject *parent=0) : QDeclarative1Curve(parent), _controlX(0), _controlY(0) {} + + qreal controlX() const; + void setControlX(qreal x); + + qreal controlY() const; + void setControlY(qreal y); + + void addToPath(QPainterPath &path); + +Q_SIGNALS: + void controlXChanged(); + void controlYChanged(); + +private: + qreal _controlX; + qreal _controlY; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1PathCubic : public QDeclarative1Curve +{ + Q_OBJECT + + Q_PROPERTY(qreal control1X READ control1X WRITE setControl1X NOTIFY control1XChanged) + Q_PROPERTY(qreal control1Y READ control1Y WRITE setControl1Y NOTIFY control1YChanged) + Q_PROPERTY(qreal control2X READ control2X WRITE setControl2X NOTIFY control2XChanged) + Q_PROPERTY(qreal control2Y READ control2Y WRITE setControl2Y NOTIFY control2YChanged) +public: + QDeclarative1PathCubic(QObject *parent=0) : QDeclarative1Curve(parent), _control1X(0), _control1Y(0), _control2X(0), _control2Y(0) {} + + qreal control1X() const; + void setControl1X(qreal x); + + qreal control1Y() const; + void setControl1Y(qreal y); + + qreal control2X() const; + void setControl2X(qreal x); + + qreal control2Y() const; + void setControl2Y(qreal y); + + void addToPath(QPainterPath &path); + +Q_SIGNALS: + void control1XChanged(); + void control1YChanged(); + void control2XChanged(); + void control2YChanged(); + +private: + qreal _control1X; + qreal _control1Y; + qreal _control2X; + qreal _control2Y; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1PathPercent : public QDeclarative1PathElement +{ + Q_OBJECT + Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged) +public: + QDeclarative1PathPercent(QObject *parent=0) : QDeclarative1PathElement(parent) {} + + qreal value() const; + void setValue(qreal value); + +signals: + void valueChanged(); + +private: + qreal _value; +}; + +class QDeclarative1PathPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Path : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1PathElement> pathElements READ pathElements) + Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged) + Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged) + Q_PROPERTY(bool closed READ isClosed NOTIFY changed) + Q_CLASSINFO("DefaultProperty", "pathElements") + Q_INTERFACES(QDeclarativeParserStatus) +public: + QDeclarative1Path(QObject *parent=0); + ~QDeclarative1Path(); + + QDeclarativeListProperty<QDeclarative1PathElement> pathElements(); + + qreal startX() const; + void setStartX(qreal x); + + qreal startY() const; + void setStartY(qreal y); + + bool isClosed() const; + + QPainterPath path() const; + QStringList attributes() const; + qreal attributeAt(const QString &, qreal) const; + QPointF pointAt(qreal) const; + +Q_SIGNALS: + void changed(); + void startXChanged(); + void startYChanged(); + +protected: + virtual void componentComplete(); + virtual void classBegin(); + +private Q_SLOTS: + void processPath(); + +private: + struct AttributePoint { + AttributePoint() : percent(0), scale(1), origpercent(0) {} + AttributePoint(const AttributePoint &other) + : percent(other.percent), scale(other.scale), origpercent(other.origpercent), values(other.values) {} + AttributePoint &operator=(const AttributePoint &other) { + percent = other.percent; scale = other.scale; origpercent = other.origpercent; values = other.values; return *this; + } + qreal percent; //massaged percent along the painter path + qreal scale; + qreal origpercent; //'real' percent along the painter path + QHash<QString, qreal> values; + }; + + void interpolate(int idx, const QString &name, qreal value); + void endpoint(const QString &name); + void createPointCache() const; + +private: + Q_DISABLE_COPY(QDeclarative1Path) + Q_DECLARE_PRIVATE(QDeclarative1Path) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1PathElement) +QML_DECLARE_TYPE(QDeclarative1PathAttribute) +QML_DECLARE_TYPE(QDeclarative1Curve) +QML_DECLARE_TYPE(QDeclarative1PathLine) +QML_DECLARE_TYPE(QDeclarative1PathQuad) +QML_DECLARE_TYPE(QDeclarative1PathCubic) +QML_DECLARE_TYPE(QDeclarative1PathPercent) +QML_DECLARE_TYPE(QDeclarative1Path) + +QT_END_HEADER + +#endif // QDECLARATIVEPATH_H diff --git a/src/qtquick1/graphicsitems/qdeclarativepath_p_p.h b/src/qtquick1/graphicsitems/qdeclarativepath_p_p.h new file mode 100644 index 0000000000..d712d777d1 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepath_p_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATH_P_H +#define QDECLARATIVEPATH_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/qdeclarativepath_p.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarative1PathPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Path) + +public: + QDeclarative1PathPrivate() : startX(0), startY(0), closed(false), componentComplete(true) { } + + QPainterPath _path; + QList<QDeclarative1PathElement*> _pathElements; + mutable QVector<QPointF> _pointCache; + QList<QDeclarative1Path::AttributePoint> _attributePoints; + QStringList _attributes; + int startX; + int startY; + bool closed; + bool componentComplete; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativepathview.cpp b/src/qtquick1/graphicsitems/qdeclarativepathview.cpp new file mode 100644 index 0000000000..ba914a9773 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepathview.cpp @@ -0,0 +1,1732 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepathview_p.h" +#include "QtQuick1/private/qdeclarativepathview_p_p.h" + +#include <QtQuick1/private/qdeclarativestate_p.h> +#include <QtQuick1/private/qdeclarativeopenmetaobject_p.h> +#include <QDebug> +#include <QEvent> +#include <QGraphicsSceneEvent> + +#include <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 QDeclarative1OpenMetaObjectType *qPathViewAttachedType = 0; + +QDeclarative1PathViewAttached::QDeclarative1PathViewAttached(QObject *parent) +: QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false) +{ + if (qPathViewAttachedType) { + m_metaobject = new QDeclarative1OpenMetaObject(this, qPathViewAttachedType); + m_metaobject->setCached(true); + } else { + m_metaobject = new QDeclarative1OpenMetaObject(this); + } +} + +QDeclarative1PathViewAttached::~QDeclarative1PathViewAttached() +{ +} + +QVariant QDeclarative1PathViewAttached::value(const QByteArray &name) const +{ + return m_metaobject->value(name); +} +void QDeclarative1PathViewAttached::setValue(const QByteArray &name, const QVariant &val) +{ + m_metaobject->setValue(name, val); +} + + +void QDeclarative1PathViewPrivate::init() +{ + Q_Q(QDeclarative1PathView); + offset = 0; + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QGraphicsItem::ItemIsFocusScope); + q->setFiltersChildEvents(true); + q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked())); + lastPosTime.invalidate(); + static int timelineCompletedIdx = -1; + static int movementEndingIdx = -1; + if (timelineCompletedIdx == -1) { + timelineCompletedIdx = QDeclarative1TimeLine::staticMetaObject.indexOfSignal("completed()"); + movementEndingIdx = QDeclarative1PathView::staticMetaObject.indexOfSlot("movementEnding()"); + } + QMetaObject::connect(&tl, timelineCompletedIdx, + q, movementEndingIdx, Qt::DirectConnection); +} + +QDeclarativeItem *QDeclarative1PathViewPrivate::getItem(int modelIndex) +{ + Q_Q(QDeclarative1PathView); + requestedIndex = modelIndex; + QDeclarativeItem *item = model->item(modelIndex, false); + if (item) { + if (!attType) { + // pre-create one metatype to share with all attached objects + attType = new QDeclarative1OpenMetaObjectType(&QDeclarative1PathViewAttached::staticMetaObject, qmlEngine(q)); + foreach(const QString &attr, path->attributes()) + attType->createProperty(attr.toUtf8()); + } + qPathViewAttachedType = attType; + QDeclarative1PathViewAttached *att = static_cast<QDeclarative1PathViewAttached *>(qmlAttachedPropertiesObject<QDeclarative1PathView>(item)); + qPathViewAttachedType = 0; + if (att) { + att->m_view = q; + att->setOnPath(true); + } + item->setParentItem(q); + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + requestedIndex = -1; + return item; +} + +void QDeclarative1PathViewPrivate::releaseItem(QDeclarativeItem *item) +{ + if (!item || !model) + return; + QDeclarativeItemPrivate *itemPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(item)); + itemPrivate->removeItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + if (model->release(item) == 0) { + // item was not destroyed, and we no longer reference it. + if (QDeclarative1PathViewAttached *att = attached(item)) + att->setOnPath(false); + } +} + +QDeclarative1PathViewAttached *QDeclarative1PathViewPrivate::attached(QDeclarativeItem *item) +{ + return static_cast<QDeclarative1PathViewAttached *>(qmlAttachedPropertiesObject<QDeclarative1PathView>(item, false)); +} + +void QDeclarative1PathViewPrivate::clear() +{ + for (int i=0; i<items.count(); i++){ + QDeclarativeItem *p = items[i]; + releaseItem(p); + } + items.clear(); +} + +void QDeclarative1PathViewPrivate::updateMappedRange() +{ + if (model && pathItems != -1 && pathItems < modelCount) + mappedRange = qreal(pathItems)/modelCount; + else + mappedRange = 1.0; +} + +qreal QDeclarative1PathViewPrivate::positionOfIndex(qreal index) const +{ + qreal pos = -1.0; + + if (model && index >= 0 && index < modelCount) { + qreal start = 0.0; + if (haveHighlightRange && highlightRangeMode != QDeclarative1PathView::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 QDeclarative1PathViewPrivate::createHighlight() +{ + Q_Q(QDeclarative1PathView); + if (!q->isComponentComplete()) + return; + + bool changed = false; + if (highlightItem) { + if (highlightItem->scene()) + highlightItem->scene()->removeItem(highlightItem); + highlightItem->deleteLater(); + highlightItem = 0; + changed = true; + } + + QDeclarativeItem *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<QDeclarativeItem *>(nobj); + if (!item) + delete nobj; + } else { + delete highlightContext; + } + } else { + item = new QDeclarativeItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q); + item->setParentItem(q); + highlightItem = item; + changed = true; + } + if (changed) + emit q->highlightItemChanged(); +} + +void QDeclarative1PathViewPrivate::updateHighlight() +{ + Q_Q(QDeclarative1PathView); + if (!q->isComponentComplete() || !isValid()) + return; + if (highlightItem) { + if (haveHighlightRange && highlightRangeMode == QDeclarative1PathView::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 QDeclarative1PathViewPrivate::setHighlightPosition(qreal pos) +{ + if (pos != highlightPosition) { + qreal start = 0.0; + qreal end = 1.0; + if (haveHighlightRange && highlightRangeMode != QDeclarative1PathView::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 (QDeclarative1PathViewAttached *att = attached(highlightItem)) + att->setOnPath(pathPos != -1.0); + } +} + +void QDeclarative1PathView::pathUpdated() +{ + Q_D(QDeclarative1PathView); + QList<QDeclarativeItem*>::iterator it = d->items.begin(); + while (it != d->items.end()) { + QDeclarativeItem *item = *it; + if (QDeclarative1PathViewAttached *att = d->attached(item)) + att->m_percent = -1; + ++it; + } + refill(); +} + +void QDeclarative1PathViewPrivate::updateItem(QDeclarativeItem *item, qreal percent) +{ + if (QDeclarative1PathViewAttached *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 QDeclarative1PathViewPrivate::regenerate() +{ + Q_Q(QDeclarative1PathView); + if (!q->isComponentComplete()) + return; + + clear(); + + if (!isValid()) + return; + + firstIndex = -1; + updateMappedRange(); + q->refill(); +} + +/*! + \qmlclass PathView QDeclarative1PathView + \ingroup qml-view-elements + \since 4.7 + \brief The PathView element lays out model-provided items on a path. + \inherits Item + + A PathView displays data from models created from built-in QML elements like ListModel + and XmlListModel, or custom model classes defined in C++ that inherit from + QAbstractListModel. + + The view has a \l model, which defines the data to be displayed, and + a \l delegate, which defines how the data should be displayed. + The \l delegate is instantiated for each item on the \l path. + The items may be flicked to move them along the path. + + For example, if there is a simple list model defined in a file \c ContactModel.qml like this: + + \snippet doc/src/snippets/declarative/pathview/ContactModel.qml 0 + + This data can be represented as a PathView, like this: + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 0 + + \image pathview.gif + + (Note the above example uses PathAttribute to scale and modify the + opacity of the items as they rotate. This additional code can be seen in the + PathAttribute documentation.) + + PathView does not automatically handle keyboard navigation. This is because + the keys to use for navigation will depend upon the shape of the path. Navigation + can be added quite simply by setting \c focus to \c true and calling + \l decrementCurrentIndex() or \l incrementCurrentIndex(), for example to navigate + using the left and right arrow keys: + + \qml + PathView { + // ... + focus: true + Keys.onLeftPressed: decrementCurrentIndex() + Keys.onRightPressed: incrementCurrentIndex() + } + \endqml + + The path view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details). + + Delegates are instantiated as needed and may be destroyed at any time. + State should \e never be stored in a delegate. + + PathView attaches a number of properties to the root item of the delegate, for example + \c {PathView.isCurrentItem}. In the following example, the root delegate item can access + this attached property directly as \c PathView.isCurrentItem, while the child + \c nameText object must refer to this property as \c wrapper.PathView.isCurrentItem. + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 + + \bold Note that views do not enable \e clip automatically. If the view + is not clipped by another item or the screen, it will be necessary + to set \e {clip: true} in order to have the out of view items clipped + nicely. + + \sa Path, {declarative/modelviews/pathview}{PathView example} +*/ + +QDeclarative1PathView::QDeclarative1PathView(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1PathViewPrivate), parent) +{ + Q_D(QDeclarative1PathView); + d->init(); +} + +QDeclarative1PathView::~QDeclarative1PathView() +{ + Q_D(QDeclarative1PathView); + d->clear(); + if (d->attType) + d->attType->release(); + if (d->ownModel) + delete d->model; +} + +/*! + \qmlattachedproperty PathView PathView::view + This attached property holds the view that manages this delegate instance. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty bool PathView::onPath + This attached property holds whether the item is currently on the path. + + If a pathItemCount has been set, it is possible that some items may + be instantiated, but not considered to be currently on the path. + Usually, these items would be set invisible, for example: + + \qml + Component { + Rectangle { + visible: PathView.onPath + // ... + } + } + \endqml + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty bool PathView::isCurrentItem + This attached property is true if this delegate is the current item; otherwise false. + + It is attached to each instance of the delegate. + + This property may be used to adjust the appearance of the current item. + + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 +*/ + +/*! + \qmlproperty model PathView::model + This property holds the model providing data for the view. + + The model provides a set of data that is used to create the items for the view. + For large or dynamic datasets the model is usually provided by a C++ model object. + Models can also be created directly in QML, using the ListModel element. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarative1PathView::model() const +{ + Q_D(const QDeclarative1PathView); + return d->modelVariant; +} + +void QDeclarative1PathView::setModel(const QVariant &model) +{ + Q_D(QDeclarative1PathView); + 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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + for (int i=0; i<d->items.count(); i++){ + QDeclarativeItem *p = d->items[i]; + d->model->release(p); + } + d->items.clear(); + } + + d->modelVariant = model; + QObject *object = qvariant_cast<QObject*>(model); + QDeclarative1VisualModel *vim = 0; + if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this), this); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + 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(); +} + +/*! + \qmlproperty int PathView::count + This property holds the number of items in the model. +*/ +int QDeclarative1PathView::count() const +{ + Q_D(const QDeclarative1PathView); + return d->model ? d->modelCount : 0; +} + +/*! + \qmlproperty Path PathView::path + This property holds the path used to lay out the items. + For more information see the \l Path documentation. +*/ +QDeclarative1Path *QDeclarative1PathView::path() const +{ + Q_D(const QDeclarative1PathView); + return d->path; +} + +void QDeclarative1PathView::setPath(QDeclarative1Path *path) +{ + Q_D(QDeclarative1PathView); + 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(); +} + +/*! + \qmlproperty int PathView::currentIndex + This property holds the index of the current item. +*/ +int QDeclarative1PathView::currentIndex() const +{ + Q_D(const QDeclarative1PathView); + return d->currentIndex; +} + +void QDeclarative1PathView::setCurrentIndex(int idx) +{ + Q_D(QDeclarative1PathView); + 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 (QDeclarativeItem *item = d->items.at(itemIndex)) { + if (QDeclarative1PathViewAttached *att = d->attached(item)) + att->setIsCurrentItem(false); + } + } + } + d->currentItem = 0; + d->moveReason = QDeclarative1PathViewPrivate::SetIndex; + d->currentIndex = idx; + if (d->modelCount) { + if (d->haveHighlightRange && d->highlightRangeMode == QDeclarative1PathView::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 (QDeclarative1PathViewAttached *att = d->attached(d->currentItem)) + att->setIsCurrentItem(true); + } + d->currentItemOffset = d->positionOfIndex(d->currentIndex); + d->updateHighlight(); + } + emit currentIndexChanged(); + } +} + +/*! + \qmlmethod PathView::incrementCurrentIndex() + + Increments the current index. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1PathView::incrementCurrentIndex() +{ + Q_D(QDeclarative1PathView); + d->moveDirection = QDeclarative1PathViewPrivate::Positive; + setCurrentIndex(currentIndex()+1); +} + + +/*! + \qmlmethod PathView::decrementCurrentIndex() + + Decrements the current index. + + \bold Note: methods should only be called after the Component has completed. +*/ +void QDeclarative1PathView::decrementCurrentIndex() +{ + Q_D(QDeclarative1PathView); + if (d->model && d->modelCount) { + int idx = currentIndex()-1; + if (idx < 0) + idx = d->modelCount - 1; + d->moveDirection = QDeclarative1PathViewPrivate::Negative; + setCurrentIndex(idx); + } +} + +/*! + \qmlproperty real PathView::offset + + The offset specifies how far along the path the items are from their initial positions. + This is a real number that ranges from 0.0 to the count of items in the model. +*/ +qreal QDeclarative1PathView::offset() const +{ + Q_D(const QDeclarative1PathView); + return d->offset; +} + +void QDeclarative1PathView::setOffset(qreal offset) +{ + Q_D(QDeclarative1PathView); + d->setOffset(offset); + d->updateCurrent(); +} + +void QDeclarative1PathViewPrivate::setOffset(qreal o) +{ + Q_Q(QDeclarative1PathView); + 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 QDeclarative1PathViewPrivate::setAdjustedOffset(qreal o) +{ + setOffset(o+offsetAdj); +} + +/*! + \qmlproperty Component PathView::highlight + This property holds the component to use as the highlight. + + An instance of the highlight component will be created for each view. + The geometry of the resultant component instance will be managed by the view + so as to stay with the current item. + + The below example demonstrates how to make a simple highlight. Note the use + of the \l{PathView::onPath}{PathView.onPath} attached property to ensure that + the highlight is hidden when flicked away from the path. + + \qml + Component { + Rectangle { + visible: PathView.onPath + // ... + } + } + \endqml + + \sa highlightItem, highlightRangeMode +*/ + +QDeclarativeComponent *QDeclarative1PathView::highlight() const +{ + Q_D(const QDeclarative1PathView); + return d->highlightComponent; +} + +void QDeclarative1PathView::setHighlight(QDeclarativeComponent *highlight) +{ + Q_D(QDeclarative1PathView); + if (highlight != d->highlightComponent) { + d->highlightComponent = highlight; + d->createHighlight(); + d->updateHighlight(); + emit highlightChanged(); + } +} + +/*! + \qmlproperty Item PathView::highlightItem + + \c highlightItem holds the highlight item, which was created + from the \l highlight component. + + \sa highlight +*/ +QDeclarativeItem *QDeclarative1PathView::highlightItem() +{ + Q_D(const QDeclarative1PathView); + return d->highlightItem; +} +/*! + \qmlproperty real PathView::preferredHighlightBegin + \qmlproperty real PathView::preferredHighlightEnd + \qmlproperty enumeration PathView::highlightRangeMode + + These properties set the preferred range of the highlight (current item) + within the view. The preferred values must be in the range 0.0-1.0. + + If highlightRangeMode is set to \e PathView.NoHighlightRange + + If highlightRangeMode is set to \e PathView.ApplyRange the view will + attempt to maintain the highlight within the range, however + the highlight can move outside of the range at the ends of the path + or due to a mouse interaction. + + If highlightRangeMode is set to \e PathView.StrictlyEnforceRange the highlight will never + move outside of the range. This means that the current item will change + if a keyboard or mouse action would cause the highlight to move + outside of the range. + + Note that this is the correct way to influence where the + current item ends up when the view moves. For example, if you want the + currently selected item to be in the middle of the path, then set the + highlight range to be 0.5,0.5 and highlightRangeMode to PathView.StrictlyEnforceRange. + Then, when the path scrolls, + the currently selected item will be the item at that position. This also applies to + when the currently selected item changes - it will scroll to within the preferred + highlight range. Furthermore, the behaviour of the current item index will occur + whether or not a highlight exists. + + The default value is \e PathView.StrictlyEnforceRange. + + Note that a valid range requires preferredHighlightEnd to be greater + than or equal to preferredHighlightBegin. +*/ +qreal QDeclarative1PathView::preferredHighlightBegin() const +{ + Q_D(const QDeclarative1PathView); + return d->highlightRangeStart; +} + +void QDeclarative1PathView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QDeclarative1PathView); + 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 QDeclarative1PathView::preferredHighlightEnd() const +{ + Q_D(const QDeclarative1PathView); + return d->highlightRangeEnd; +} + +void QDeclarative1PathView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QDeclarative1PathView); + 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(); +} + +QDeclarative1PathView::HighlightRangeMode QDeclarative1PathView::highlightRangeMode() const +{ + Q_D(const QDeclarative1PathView); + return d->highlightRangeMode; +} + +void QDeclarative1PathView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QDeclarative1PathView); + if (d->highlightRangeMode == mode) + return; + d->highlightRangeMode = mode; + d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + + +/*! + \qmlproperty int PathView::highlightMoveDuration + This property holds the move animation duration of the highlight delegate. + + If the highlightRangeMode is StrictlyEnforceRange then this property + determines the speed that the items move along the path. + + The default value for the duration is 300ms. +*/ +int QDeclarative1PathView::highlightMoveDuration() const +{ + Q_D(const QDeclarative1PathView); + return d->highlightMoveDuration; +} + +void QDeclarative1PathView::setHighlightMoveDuration(int duration) +{ + Q_D(QDeclarative1PathView); + if (d->highlightMoveDuration == duration) + return; + d->highlightMoveDuration = duration; + emit highlightMoveDurationChanged(); +} + +/*! + \qmlproperty real PathView::dragMargin + This property holds the maximum distance from the path that initiate mouse dragging. + + By default the path can only be dragged by clicking on an item. If + dragMargin is greater than zero, a drag can be initiated by clicking + within dragMargin pixels of the path. +*/ +qreal QDeclarative1PathView::dragMargin() const +{ + Q_D(const QDeclarative1PathView); + return d->dragMargin; +} + +void QDeclarative1PathView::setDragMargin(qreal dragMargin) +{ + Q_D(QDeclarative1PathView); + if (d->dragMargin == dragMargin) + return; + d->dragMargin = dragMargin; + emit dragMarginChanged(); +} + +/*! + \qmlproperty real PathView::flickDeceleration + This property holds the rate at which a flick will decelerate. + + The default is 100. +*/ +qreal QDeclarative1PathView::flickDeceleration() const +{ + Q_D(const QDeclarative1PathView); + return d->deceleration; +} + +void QDeclarative1PathView::setFlickDeceleration(qreal dec) +{ + Q_D(QDeclarative1PathView); + if (d->deceleration == dec) + return; + d->deceleration = dec; + emit flickDecelerationChanged(); +} + +/*! + \qmlproperty bool PathView::interactive + + A user cannot drag or flick a PathView that is not interactive. + + This property is useful for temporarily disabling flicking. This allows + special interaction with PathView's children. +*/ +bool QDeclarative1PathView::isInteractive() const +{ + Q_D(const QDeclarative1PathView); + return d->interactive; +} + +void QDeclarative1PathView::setInteractive(bool interactive) +{ + Q_D(QDeclarative1PathView); + if (interactive != d->interactive) { + d->interactive = interactive; + if (!interactive) + d->tl.clear(); + emit interactiveChanged(); + } +} + +/*! + \qmlproperty bool PathView::moving + + This property holds whether the view is currently moving + due to the user either dragging or flicking the view. +*/ +bool QDeclarative1PathView::isMoving() const +{ + Q_D(const QDeclarative1PathView); + return d->moving; +} + +/*! + \qmlproperty bool PathView::flicking + + This property holds whether the view is currently moving + due to the user flicking the view. +*/ +bool QDeclarative1PathView::isFlicking() const +{ + Q_D(const QDeclarative1PathView); + return d->flicking; +} + +/*! + \qmlsignal PathView::onMovementStarted() + + This handler is called when the view begins moving due to user + interaction. +*/ + +/*! + \qmlsignal PathView::onMovementEnded() + + This handler is called when the view stops moving due to user + interaction. If a flick was generated, this handler will + be triggered once the flick stops. If a flick was not + generated, the handler will be triggered when the + user stops dragging - i.e. a mouse or touch release. +*/ + +/*! + \qmlsignal PathView::onFlickStarted() + + This handler is called when the view is flicked. A flick + starts from the point that the mouse or touch is released, + while still in motion. +*/ + +/*! + \qmlsignal PathView::onFlickEnded() + + This handler is called when the view stops moving due to a flick. +*/ + +/*! + \qmlproperty Component PathView::delegate + + The delegate provides a template defining each item instantiated by the view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. + + The number of elements in the delegate has a direct effect on the + flicking performance of the view when pathItemCount is specified. If at all possible, place functionality + that is not needed for the normal display of the delegate in a \l Loader which + can load additional elements when needed. + + Note that the PathView will layout the items based on the size of the root + item in the delegate. + + Here is an example delegate: + \snippet doc/src/snippets/declarative/pathview/pathview.qml 1 +*/ +QDeclarativeComponent *QDeclarative1PathView::delegate() const +{ + Q_D(const QDeclarative1PathView); + if (d->model) { + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarative1PathView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarative1PathView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + d->modelCount = dataModel->count(); + d->regenerate(); + if (oldCount != dataModel->count()) + emit countChanged(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int PathView::pathItemCount + This property holds the number of items visible on the path at any one time. +*/ +int QDeclarative1PathView::pathItemCount() const +{ + Q_D(const QDeclarative1PathView); + return d->pathItems; +} + +void QDeclarative1PathView::setPathItemCount(int i) +{ + Q_D(QDeclarative1PathView); + if (i == d->pathItems) + return; + if (i < 1) + i = 1; + d->pathItems = i; + d->updateMappedRange(); + if (d->isValid() && isComponentComplete()) { + d->regenerate(); + } + emit pathItemCountChanged(); +} + +QPointF QDeclarative1PathViewPrivate::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 QDeclarative1PathView::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PathView); + if (d->interactive) { + d->handleMousePressEvent(event); + event->accept(); + } else { + QDeclarativeItem::mousePressEvent(event); + } +} + +void QDeclarative1PathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarative1PathView); + 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)->mapToScene(rect).boundingRect(); + 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; + QDeclarativeItemPrivate::start(lastPosTime); + tl.clear(); +} + +void QDeclarative1PathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PathView); + if (d->interactive) { + d->handleMouseMoveEvent(event); + if (d->stealMouse) + setKeepMouseGrab(true); + event->accept(); + } else { + QDeclarativeItem::mouseMoveEvent(event); + } +} + +void QDeclarative1PathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QDeclarative1PathView); + 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 = QDeclarative1PathViewPrivate::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 = QDeclarativeItemPrivate::restart(lastPosTime); + lastDist = diff; + startPc = newPc; + } + if (!moving) { + moving = true; + emit q->movingChanged(); + emit q->movementStarted(); + } + } +} + +void QDeclarative1PathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PathView); + if (d->interactive) { + d->handleMouseReleaseEvent(event); + event->accept(); + ungrabMouse(); + } else { + QDeclarativeItem::mouseReleaseEvent(event); + } +} + +void QDeclarative1PathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *) +{ + Q_Q(QDeclarative1PathView); + stealMouse = false; + q->setKeepMouseGrab(false); + if (!interactive || !lastPosTime.isValid()) + return; + + qreal elapsed = qreal(lastElapsed + QDeclarativeItemPrivate::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 == QDeclarative1PathView::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(QDeclarative1TimeLineCallback(&moveOffset, fixOffsetCallback, this)); + if (!flicking) { + flicking = true; + emit q->flickingChanged(); + emit q->flickStarted(); + } + } else { + fixOffset(); + } + + lastPosTime.invalidate(); + if (!tl.isActive()) + q->movementEnding(); +} + +bool QDeclarative1PathView::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PathView); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->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 = qobject_cast<QDeclarativeItem*>(s->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 QDeclarative1PathView::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarative1PathView); + if (!isVisible() || !d->interactive) + return QDeclarativeItem::sceneEventFilter(i, e); + + switch (e->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e)); + default: + break; + } + + return QDeclarativeItem::sceneEventFilter(i, e); +} + +bool QDeclarative1PathView::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + refill(); + return true; + } + + return QDeclarativeItem::event(event); +} + +void QDeclarative1PathView::componentComplete() +{ + Q_D(QDeclarative1PathView); + QDeclarativeItem::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 QDeclarative1PathView::refill() +{ + Q_D(QDeclarative1PathView); + 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<QDeclarativeItem*>::iterator it = d->items.begin(); + while (it != d->items.end()) { + qreal pos = d->positionOfIndex(idx); + QDeclarativeItem *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 != QDeclarative1PathView::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; + QDeclarativeItem *item = d->getItem(idx); + if (d->model->completePending()) + item->setZValue(idx+1); + if (d->currentIndex == idx) { + item->setFocus(true); + if (QDeclarative1PathViewAttached *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; + QDeclarativeItem *item = d->getItem(idx); + if (d->model->completePending()) + item->setZValue(idx+1); + if (d->currentIndex == idx) { + item->setFocus(true); + if (QDeclarative1PathViewAttached *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 == QDeclarative1PathView::StrictlyEnforceRange) { + d->updateItem(d->highlightItem, d->highlightRangeStart); + if (QDeclarative1PathViewAttached *att = d->attached(d->highlightItem)) + att->setOnPath(true); + } else if (d->highlightItem && d->moveReason != QDeclarative1PathViewPrivate::SetIndex) { + d->updateItem(d->highlightItem, d->currentItemOffset); + if (QDeclarative1PathViewAttached *att = d->attached(d->highlightItem)) + att->setOnPath(currentVisible); + } + while (d->itemCache.count()) + d->releaseItem(d->itemCache.takeLast()); +} + +void QDeclarative1PathView::itemsInserted(int modelIndex, int count) +{ + //XXX support animated insertion + Q_D(QDeclarative1PathView); + 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 QDeclarative1PathView::itemsRemoved(int modelIndex, int count) +{ + //XXX support animated removal + Q_D(QDeclarative1PathView); + 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 (QDeclarative1PathViewAttached *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); + update(); + } else { + d->regenerate(); + d->updateCurrent(); + if (!d->flicking && !d->moving && d->haveHighlightRange && d->highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange) + d->snapToCurrent(); + } + if (changedOffset) + emit offsetChanged(); + if (currentChanged) + emit currentIndexChanged(); + emit countChanged(); +} + +void QDeclarative1PathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/) +{ + Q_D(QDeclarative1PathView); + if (!d->isValid() || !isComponentComplete()) + return; + + QList<QDeclarativeItem *> 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 QDeclarative1PathView::modelReset() +{ + Q_D(QDeclarative1PathView); + d->modelCount = d->model->count(); + d->regenerate(); + emit countChanged(); +} + +void QDeclarative1PathView::createdItem(int index, QDeclarativeItem *item) +{ + Q_D(QDeclarative1PathView); + if (d->requestedIndex != index) { + if (!d->attType) { + // pre-create one metatype to share with all attached objects + d->attType = new QDeclarative1OpenMetaObjectType(&QDeclarative1PathViewAttached::staticMetaObject, qmlEngine(this)); + foreach(const QString &attr, d->path->attributes()) + d->attType->createProperty(attr.toUtf8()); + } + qPathViewAttachedType = d->attType; + QDeclarative1PathViewAttached *att = static_cast<QDeclarative1PathViewAttached *>(qmlAttachedPropertiesObject<QDeclarative1PathView>(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 QDeclarative1PathView::destroyingItem(QDeclarativeItem *item) +{ + Q_UNUSED(item); +} + +void QDeclarative1PathView::ticked() +{ + Q_D(QDeclarative1PathView); + d->updateCurrent(); +} + +void QDeclarative1PathView::movementEnding() +{ + Q_D(QDeclarative1PathView); + 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 QDeclarative1PathViewPrivate::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 QDeclarative1PathViewPrivate::updateCurrent() +{ + Q_Q(QDeclarative1PathView); + if (moveReason != Mouse) + return; + if (!modelCount || !haveHighlightRange || highlightRangeMode != QDeclarative1PathView::StrictlyEnforceRange) + return; + + int idx = calcCurrentIndex(); + if (model && idx != currentIndex) { + int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount; + if (itemIndex < items.count()) { + if (QDeclarativeItem *item = items.at(itemIndex)) { + if (QDeclarative1PathViewAttached *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 (QDeclarative1PathViewAttached *att = attached(currentItem)) + att->setIsCurrentItem(true); + } + emit q->currentIndexChanged(); + } +} + +void QDeclarative1PathViewPrivate::fixOffsetCallback(void *d) +{ + ((QDeclarative1PathViewPrivate *)d)->fixOffset(); +} + +void QDeclarative1PathViewPrivate::fixOffset() +{ + Q_Q(QDeclarative1PathView); + if (model && items.count()) { + if (haveHighlightRange && highlightRangeMode == QDeclarative1PathView::StrictlyEnforceRange) { + int curr = calcCurrentIndex(); + if (curr != currentIndex) + q->setCurrentIndex(curr); + else + snapToCurrent(); + } + } +} + +void QDeclarative1PathViewPrivate::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; +} + +QDeclarative1PathViewAttached *QDeclarative1PathView::qmlAttachedProperties(QObject *obj) +{ + return new QDeclarative1PathViewAttached(obj); +} + + + +QT_END_NAMESPACE + diff --git a/src/qtquick1/graphicsitems/qdeclarativepathview_p.h b/src/qtquick1/graphicsitems/qdeclarativepathview_p.h new file mode 100644 index 0000000000..b4897f9e1d --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepathview_p.h @@ -0,0 +1,252 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPATHVIEW_H +#define QDECLARATIVEPATHVIEW_H + +#include "qdeclarativeitem.h" +#include "QtQuick1/private/qdeclarativepath_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1PathViewPrivate; +class QDeclarative1PathViewAttached; +class Q_AUTOTEST_EXPORT QDeclarative1PathView : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QDeclarative1Path *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(QDeclarativeItem *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: + QDeclarative1PathView(QDeclarativeItem *parent=0); + virtual ~QDeclarative1PathView(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarative1Path *path() const; + void setPath(QDeclarative1Path *); + + int currentIndex() const; + void setCurrentIndex(int idx); + + qreal offset() const; + void setOffset(qreal offset); + + QDeclarativeComponent *highlight() const; + void setHighlight(QDeclarativeComponent *highlight); + QDeclarativeItem *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 QDeclarative1PathViewAttached *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: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool sceneEventFilter(QGraphicsItem *, QEvent *); + bool event(QEvent *event); + 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, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + void pathUpdated(); + +private: + friend class QDeclarative1PathViewAttached; + Q_DISABLE_COPY(QDeclarative1PathView) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1PathView) +}; + +class QDeclarative1OpenMetaObject; +class QDeclarative1PathViewAttached : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarative1PathView *view READ view CONSTANT) + Q_PROPERTY(bool isCurrentItem READ isCurrentItem NOTIFY currentItemChanged) + Q_PROPERTY(bool onPath READ isOnPath NOTIFY pathChanged) + +public: + QDeclarative1PathViewAttached(QObject *parent); + ~QDeclarative1PathViewAttached(); + + QDeclarative1PathView *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 QDeclarative1PathViewPrivate; + friend class QDeclarative1PathView; + QDeclarative1PathView *m_view; + QDeclarative1OpenMetaObject *m_metaobject; + bool m_onPath : 1; + bool m_isCurrent : 1; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1PathView) +QML_DECLARE_TYPEINFO(QDeclarative1PathView, QML_HAS_ATTACHED_PROPERTIES) +QT_END_HEADER + +#endif // QDECLARATIVEPATHVIEW_H diff --git a/src/qtquick1/graphicsitems/qdeclarativepathview_p_p.h b/src/qtquick1/graphicsitems/qdeclarativepathview_p_p.h new file mode 100644 index 0000000000..50965d6842 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepathview_p_p.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $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 "private/qdeclarativepathview_p.h" + +#include "QtQuick1/private/qdeclarativeitem_p.h" +#include "QtQuick1/private/qdeclarativevisualitemmodel_p.h" + +#include <QtDeclarative/qdeclarative.h> +#include <QtQuick1/private/qdeclarativeanimation_p_p.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> + +#include <qdatetime.h> + +QT_BEGIN_NAMESPACE + +class QDeclarative1OpenMetaObjectType; +class QDeclarative1PathViewAttached; +class QDeclarative1PathViewPrivate : public QDeclarativeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarative1PathView) + +public: + QDeclarative1PathViewPrivate() + : 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, &QDeclarative1PathViewPrivate::setAdjustedOffset) + , firstIndex(-1), pathItems(-1), requestedIndex(-1) + , moveReason(Other), moveDirection(Shortest), attType(0), highlightComponent(0), highlightItem(0) + , moveHighlight(this, &QDeclarative1PathViewPrivate::setHighlightPosition) + , highlightPosition(0) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeMode(QDeclarative1PathView::StrictlyEnforceRange) + , highlightMoveDuration(300), modelCount(0) + { + } + + void init(); + + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) { + if ((newGeometry.size() != oldGeometry.size()) + && (!highlightItem || item != highlightItem)) { + if (QDeclarative1PathViewAttached *att = attached(item)) + att->m_percent = -1; + scheduleLayout(); + } + } + + void scheduleLayout() { + Q_Q(QDeclarative1PathView); + if (!layoutScheduled) { + layoutScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User), Qt::HighEventPriority); + } + } + + QDeclarativeItem *getItem(int modelIndex); + void releaseItem(QDeclarativeItem *item); + QDeclarative1PathViewAttached *attached(QDeclarativeItem *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(QDeclarativeItem *, qreal); + void snapToCurrent(); + QPointF pointNear(const QPointF &point, qreal *nearPercent=0) const; + + QDeclarative1Path *path; + int currentIndex; + QDeclarativeGuard<QDeclarativeItem> 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; + QDeclarative1TimeLine tl; + QDeclarative1TimeLineValueProxy<QDeclarative1PathViewPrivate> moveOffset; + int firstIndex; + int pathItems; + int requestedIndex; + QList<QDeclarativeItem *> items; + QList<QDeclarativeItem *> itemCache; + QDeclarativeGuard<QDeclarative1VisualModel> model; + QVariant modelVariant; + enum MovementReason { Other, SetIndex, Mouse }; + MovementReason moveReason; + enum MovementDirection { Shortest, Negative, Positive }; + MovementDirection moveDirection; + QDeclarative1OpenMetaObjectType *attType; + QDeclarativeComponent *highlightComponent; + QDeclarativeItem *highlightItem; + QDeclarative1TimeLineValueProxy<QDeclarative1PathViewPrivate> moveHighlight; + qreal highlightPosition; + qreal highlightRangeStart; + qreal highlightRangeEnd; + QDeclarative1PathView::HighlightRangeMode highlightRangeMode; + int highlightMoveDuration; + int modelCount; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativepincharea.cpp b/src/qtquick1/graphicsitems/qdeclarativepincharea.cpp new file mode 100644 index 0000000000..8efe6e5c8a --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepincharea.cpp @@ -0,0 +1,611 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepincharea_p.h" +#include "QtQuick1/private/qdeclarativepincharea_p_p.h" + +#include <QApplication> +#include <QGraphicsScene> + +#include <float.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + + + + +/*! + \qmlclass PinchEvent QDeclarative1PinchEvent + \ingroup qml-event-elements + \brief The PinchEvent object provides information about a pinch event. + + \bold {The PinchEvent element was added in QtQuick 1.1} + + The \c center, \c startCenter, \c previousCenter properties provide the center position between the two touch points. + + The \c scale and \c previousScale properties provide the scale factor. + + The \c angle, \c previousAngle and \c rotation properties provide the angle between the two points and the amount of rotation. + + The \c point1, \c point2, \c startPoint1, \c startPoint2 properties provide the positions of the touch points. + + The \c accepted property may be set to false in the \c onPinchStarted handler if the gesture should not + be handled. + + \sa PinchArea +*/ + +/*! + \qmlproperty QPointF PinchEvent::center + \qmlproperty QPointF PinchEvent::startCenter + \qmlproperty QPointF PinchEvent::previousCenter + + These properties hold the position of the center point between the two touch points. + + \list + \o \c center is the current center point + \o \c previousCenter is the center point of the previous event. + \o \c startCenter is the center point when the gesture began + \endlist +*/ + +/*! + \qmlproperty real PinchEvent::scale + \qmlproperty real PinchEvent::previousScale + + These properties hold the scale factor determined by the change in distance between the two touch points. + + \list + \o \c scale is the current scale factor. + \o \c previousScale is the scale factor of the previous event. + \endlist + + When a pinch gesture is started, the scale is 1.0. +*/ + +/*! + \qmlproperty real PinchEvent::angle + \qmlproperty real PinchEvent::previousAngle + \qmlproperty real PinchEvent::rotation + + These properties hold the angle between the two touch points. + + \list + \o \c angle is the current angle between the two points in the range -180 to 180. + \o \c previousAngle is the angle of the previous event. + \o \c rotation is the total rotation since the pinch gesture started. + \endlist + + When a pinch gesture is started, the rotation is 0.0. +*/ + +/*! + \qmlproperty QPointF PinchEvent::point1 + \qmlproperty QPointF PinchEvent::startPoint1 + \qmlproperty QPointF PinchEvent::point2 + \qmlproperty QPointF PinchEvent::startPoint2 + + These properties provide the actual touch points generating the pinch. + + \list + \o \c point1 and \c point2 hold the current positions of the points. + \o \c startPoint1 and \c startPoint2 hold the positions of the points when the second point was touched. + \endlist +*/ + +/*! + \qmlproperty bool PinchEvent::accepted + + Setting this property to false in the \c PinchArea::onPinchStarted handler + will result in no further pinch events being generated, and the gesture + ignored. +*/ + +/*! + \qmlproperty int PinchEvent::pointCount + + Holds the number of points currently touched. The PinchArea will not react + until two touch points have initited a gesture, but will remain active until + all touch points have been released. +*/ + +QDeclarative1Pinch::QDeclarative1Pinch() + : 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) +{ +} + +QDeclarative1PinchAreaPrivate::~QDeclarative1PinchAreaPrivate() +{ + delete pinch; +} + +/*! + \qmlclass PinchArea QDeclarative1PinchArea + \brief The PinchArea item enables simple pinch gesture handling. + \inherits Item + + \bold {The PinchArea element was added in QtQuick 1.1} + + A PinchArea is an invisible item that is typically used in conjunction with + a visible item in order to provide pinch gesture handling for that item. + + The \l enabled property is used to enable and disable pinch handling for + the proxied item. When disabled, the pinch area becomes transparent to + mouse/touch events. + + PinchArea can be used in two ways: + + \list + \o setting a \c pinch.target to provide automatic interaction with an element + \o using the onPinchStarted, onPinchUpdated and onPinchFinished handlers + \endlist + + \sa PinchEvent +*/ + +/*! + \qmlsignal PinchArea::onPinchStarted() + + This handler is called when the pinch area detects that a pinch gesture has started. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. + + To ignore this gesture set the \c pinch.accepted property to false. The gesture + will be cancelled and no further events will be sent. +*/ + +/*! + \qmlsignal PinchArea::onPinchUpdated() + + This handler is called when the pinch area detects that a pinch gesture has changed. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. +*/ + +/*! + \qmlsignal PinchArea::onPinchFinished() + + This handler is called when the pinch area detects that a pinch gesture has finished. + + The \l {PinchEvent}{pinch} parameter provides information about the pinch gesture, + including the scale, center and angle of the pinch. +*/ + + +/*! + \qmlproperty Item PinchArea::pinch.target + \qmlproperty bool PinchArea::pinch.active + \qmlproperty real PinchArea::pinch.minimumScale + \qmlproperty real PinchArea::pinch.maximumScale + \qmlproperty real PinchArea::pinch.minimumRotation + \qmlproperty real PinchArea::pinch.maximumRotation + \qmlproperty enumeration PinchArea::pinch.dragAxis + \qmlproperty real PinchArea::pinch.minimumX + \qmlproperty real PinchArea::pinch.maximumX + \qmlproperty real PinchArea::pinch.minimumY + \qmlproperty real PinchArea::pinch.maximumY + + \c pinch provides a convenient way to make an item react to pinch gestures. + + \list + \i \c pinch.target specifies the id of the item to drag. + \i \c pinch.active specifies if the target item is currently being dragged. + \i \c pinch.minimumScale and \c pinch.maximumScale limit the range of the Item::scale property. + \i \c pinch.minimumRotation and \c pinch.maximumRotation limit the range of the Item::rotation property. + \i \c pinch.dragAxis specifies whether dragging in not allowed (\c Pinch.NoDrag), can be done horizontally (\c Pinch.XAxis), vertically (\c Pinch.YAxis), or both (\c Pinch.XandYAxis) + \i \c pinch.minimum and \c pinch.maximum limit how far the target can be dragged along the corresponding axes. + \endlist +*/ + +QDeclarative1PinchArea::QDeclarative1PinchArea(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1PinchAreaPrivate), parent) +{ + Q_D(QDeclarative1PinchArea); + d->init(); +} + +QDeclarative1PinchArea::~QDeclarative1PinchArea() +{ +} + +/*! + \qmlproperty bool PinchArea::enabled + This property holds whether the item accepts pinch gestures. + + This property defaults to true. +*/ +bool QDeclarative1PinchArea::isEnabled() const +{ + Q_D(const QDeclarative1PinchArea); + return d->absorb; +} + +void QDeclarative1PinchArea::setEnabled(bool a) +{ + Q_D(QDeclarative1PinchArea); + if (a != d->absorb) { + d->absorb = a; + emit enabledChanged(); + } +} + +bool QDeclarative1PinchArea::event(QEvent *event) +{ + Q_D(QDeclarative1PinchArea); + if (!d->absorb || !isVisible()) + return QDeclarativeItem::event(event); + switch (event->type()) { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: { + QTouchEvent *touch = static_cast<QTouchEvent*>(event); + 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 true; + case QEvent::TouchEnd: + d->touchPoints.clear(); + updatePinch(); + break; + default: + return QDeclarativeItem::event(event); + } + + return QDeclarativeItem::event(event); +} + +void QDeclarative1PinchArea::updatePinch() +{ + Q_D(QDeclarative1PinchArea); + if (d->touchPoints.count() == 0) { + if (d->inPinch) { + d->stealMouse = false; + setKeepMouseGrab(false); + d->inPinch = false; + QPointF pinchCenter = mapFromScene(d->sceneLastCenter); + QDeclarative1PinchEvent 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; + QDeclarative1PinchEvent 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; + QGraphicsScene *s = scene(); + if (s && s->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); + QDeclarative1PinchEvent 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() & QDeclarative1Pinch::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() & QDeclarative1Pinch::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 QDeclarative1PinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PinchArea); + d->stealMouse = false; + if (!d->absorb) + QDeclarativeItem::mousePressEvent(event); + else { + setKeepMouseGrab(false); + event->setAccepted(true); + } +} + +void QDeclarative1PinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PinchArea); + if (!d->absorb) { + QDeclarativeItem::mouseMoveEvent(event); + return; + } +} + +void QDeclarative1PinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PinchArea); + d->stealMouse = false; + if (!d->absorb) { + QDeclarativeItem::mouseReleaseEvent(event); + } else { + QGraphicsScene *s = scene(); + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } +} + +bool QDeclarative1PinchArea::sceneEvent(QEvent *event) +{ + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + setKeepMouseGrab(false); + } + return rv; +} + +bool QDeclarative1PinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1PinchArea); + QGraphicsSceneMouseEvent mouseEvent(event->type()); + QRectF myRect = mapToScene(QRectF(0, 0, width(), height())).boundingRect(); + + QGraphicsScene *s = scene(); + QDeclarativeItem *grabber = s ? qobject_cast<QDeclarativeItem*>(s->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 = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()); + if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) + grabMouse(); + + return stealThisEvent; + } + if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) { + d->stealMouse = false; + if (s && s->mouseGrabberItem() == this) + ungrabMouse(); + setKeepMouseGrab(false); + } + return false; +} + +bool QDeclarative1PinchArea::sceneEventFilter(QGraphicsItem *i, QEvent *e) +{ + Q_D(QDeclarative1PinchArea); + if (!d->absorb || !isVisible()) + return QDeclarativeItem::sceneEventFilter(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 QDeclarativeItem::sceneEventFilter(i, e); +} + +void QDeclarative1PinchArea::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +QVariant QDeclarative1PinchArea::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + return QDeclarativeItem::itemChange(change, value); +} + +QDeclarative1Pinch *QDeclarative1PinchArea::pinch() +{ + Q_D(QDeclarative1PinchArea); + if (!d->pinch) + d->pinch = new QDeclarative1Pinch; + return d->pinch; +} + + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativepincharea_p.h b/src/qtquick1/graphicsitems/qdeclarativepincharea_p.h new file mode 100644 index 0000000000..4c1690f1fd --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepincharea_p.h @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPINCHAREA_H +#define QDECLARATIVEPINCHAREA_H + +#include <qdeclarativeitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarative1Pinch : public QObject +{ + Q_OBJECT + + Q_ENUMS(Axis) + Q_PROPERTY(QGraphicsObject *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: + QDeclarative1Pinch(); + + QGraphicsObject *target() const { return m_target; } + void setTarget(QGraphicsObject *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: + QGraphicsObject *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 QDeclarative1PinchEvent : 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: + QDeclarative1PinchEvent(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 QDeclarative1MouseEvent; +class QDeclarative1PinchAreaPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1PinchArea : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QDeclarative1Pinch *pinch READ pinch CONSTANT) + +public: + QDeclarative1PinchArea(QDeclarativeItem *parent=0); + ~QDeclarative1PinchArea(); + + bool isEnabled() const; + void setEnabled(bool); + + QDeclarative1Pinch *pinch(); + +Q_SIGNALS: + void enabledChanged(); + void pinchStarted(QDeclarative1PinchEvent *pinch); + void pinchUpdated(QDeclarative1PinchEvent *pinch); + void pinchFinished(QDeclarative1PinchEvent *pinch); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + bool sceneEvent(QEvent *); + bool sendMouseEvent(QGraphicsSceneMouseEvent *event); + bool sceneEventFilter(QGraphicsItem *i, QEvent *e); + bool event(QEvent *); + + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + virtual QVariant itemChange(GraphicsItemChange change, const QVariant& value); + +private: + void updatePinch(); + void handlePress(); + void handleRelease(); + +private: + Q_DISABLE_COPY(QDeclarative1PinchArea) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1PinchArea) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Pinch) +QML_DECLARE_TYPE(QDeclarative1PinchEvent) +QML_DECLARE_TYPE(QDeclarative1PinchArea) + +QT_END_HEADER + +#endif // QDECLARATIVEPINCHAREA_H diff --git a/src/qtquick1/graphicsitems/qdeclarativepincharea_p_p.h b/src/qtquick1/graphicsitems/qdeclarativepincharea_p_p.h new file mode 100644 index 0000000000..963ca6e1bc --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepincharea_p_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPINCHAREA_P_H +#define QDECLARATIVEPINCHAREA_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 <qdatetime.h> +#include <qbasictimer.h> +#include <qevent.h> +#include <qgraphicssceneevent.h> +#include "qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarative1Pinch; +class QDeclarative1PinchAreaPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1PinchArea) +public: + QDeclarative1PinchAreaPrivate() + : 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) + { + } + + ~QDeclarative1PinchAreaPrivate(); + + void init() + { + Q_Q(QDeclarative1PinchArea); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setAcceptTouchEvents(true); + q->setFiltersChildEvents(true); + } + + bool absorb : 1; + bool stealMouse : 1; + bool inPinch : 1; + bool pinchRejected : 1; + bool pinchActivated : 1; + QDeclarative1Pinch *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 // QDECLARATIVEPINCHAREA_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativepositioners.cpp b/src/qtquick1/graphicsitems/qdeclarativepositioners.cpp new file mode 100644 index 0000000000..0defe40641 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepositioners.cpp @@ -0,0 +1,1396 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepositioners_p.h" +#include "QtQuick1/private/qdeclarativepositioners_p_p.h" + +#include <QtDeclarative/qdeclarative.h> +#include <QtQuick1/private/qdeclarativestate_p.h> +#include <QtQuick1/private/qdeclarativestategroup_p.h> +#include <QtQuick1/private/qdeclarativestateoperations_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtCore/qmath.h> + +#include <QDebug> +#include <QCoreApplication> + +QT_BEGIN_NAMESPACE + + + +static const QDeclarativeItemPrivate::ChangeTypes watchedChanges + = QDeclarativeItemPrivate::Geometry + | QDeclarativeItemPrivate::SiblingOrder + | QDeclarativeItemPrivate::Visibility + | QDeclarativeItemPrivate::Opacity + | QDeclarativeItemPrivate::Destroyed; + +void QDeclarative1BasePositionerPrivate::watchChanges(QGraphicsObject *other) +{ + if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) { + QDeclarativeItemPrivate *otherPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(other)); + otherPrivate->addItemChangeListener(this, watchedChanges); + } else { + Q_Q(QDeclarative1BasePositioner); + QObject::connect(other, SIGNAL(widthChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::connect(other, SIGNAL(heightChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::connect(other, SIGNAL(opacityChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::connect(other, SIGNAL(visibleChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + } +} + +void QDeclarative1BasePositionerPrivate::unwatchChanges(QGraphicsObject* other) +{ + if (QGraphicsItemPrivate::get(other)->isDeclarativeItem) { + QDeclarativeItemPrivate *otherPrivate = static_cast<QDeclarativeItemPrivate*>(QGraphicsItemPrivate::get(other)); + otherPrivate->removeItemChangeListener(this, watchedChanges); + } else { + Q_Q(QDeclarative1BasePositioner); + QObject::disconnect(other, SIGNAL(widthChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::disconnect(other, SIGNAL(heightChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::disconnect(other, SIGNAL(opacityChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + QObject::disconnect(other, SIGNAL(visibleChanged()), q, SLOT(graphicsWidgetGeometryChanged())); + } +} + +void QDeclarative1BasePositioner::graphicsWidgetGeometryChanged() +{ + prePositioning(); +} + +/*! + \internal + \class QDeclarative1BasePositioner + \brief The QDeclarative1BasePositioner class provides a base for QDeclarative1Graphics layouts. + + To create a QDeclarative1Graphics Positioner, simply subclass QDeclarative1BasePositioner and implement + doLayout(), which is automatically called when the layout might need + updating. In doLayout() use the setX and setY functions from QDeclarative1BasePositioner, and the + base class will apply the positions along with the appropriate transitions. The items to + position are provided in order as the protected member positionedItems. + + You also need to set a PositionerType, to declare whether you are positioning the x, y or both + for the child items. Depending on the chosen type, only x or y changes will be applied. + + Note that the subclass is responsible for adding the spacing in between items. +*/ +QDeclarative1BasePositioner::QDeclarative1BasePositioner(PositionerType at, QDeclarativeItem *parent) + : QDeclarative1ImplicitSizeItem(*(new QDeclarative1BasePositionerPrivate), parent) +{ + Q_D(QDeclarative1BasePositioner); + d->init(at); +} + +QDeclarative1BasePositioner::QDeclarative1BasePositioner(QDeclarative1BasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent) + : QDeclarative1ImplicitSizeItem(dd, parent) +{ + Q_D(QDeclarative1BasePositioner); + d->init(at); +} + +QDeclarative1BasePositioner::~QDeclarative1BasePositioner() +{ + Q_D(QDeclarative1BasePositioner); + for (int i = 0; i < positionedItems.count(); ++i) + d->unwatchChanges(positionedItems.at(i).item); + positionedItems.clear(); +} + +int QDeclarative1BasePositioner::spacing() const +{ + Q_D(const QDeclarative1BasePositioner); + return d->spacing; +} + +void QDeclarative1BasePositioner::setSpacing(int s) +{ + Q_D(QDeclarative1BasePositioner); + if (s==d->spacing) + return; + d->spacing = s; + prePositioning(); + emit spacingChanged(); +} + +QDeclarative1Transition *QDeclarative1BasePositioner::move() const +{ + Q_D(const QDeclarative1BasePositioner); + return d->moveTransition; +} + +void QDeclarative1BasePositioner::setMove(QDeclarative1Transition *mt) +{ + Q_D(QDeclarative1BasePositioner); + if (mt == d->moveTransition) + return; + d->moveTransition = mt; + emit moveChanged(); +} + +QDeclarative1Transition *QDeclarative1BasePositioner::add() const +{ + Q_D(const QDeclarative1BasePositioner); + return d->addTransition; +} + +void QDeclarative1BasePositioner::setAdd(QDeclarative1Transition *add) +{ + Q_D(QDeclarative1BasePositioner); + if (add == d->addTransition) + return; + + d->addTransition = add; + emit addChanged(); +} + +void QDeclarative1BasePositioner::componentComplete() +{ + Q_D(QDeclarative1BasePositioner); + QDeclarativeItem::componentComplete(); + positionedItems.reserve(d->QGraphicsItemPrivate::children.count()); + prePositioning(); + reportConflictingAnchors(); +} + +QVariant QDeclarative1BasePositioner::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QDeclarative1BasePositioner); + if (change == ItemChildAddedChange){ + QGraphicsItem* item = value.value<QGraphicsItem*>(); + QGraphicsObject* child = 0; + if(item) + child = item->toGraphicsObject(); + if (child) + prePositioning(); + } else if (change == ItemChildRemovedChange) { + QGraphicsItem* item = value.value<QGraphicsItem*>(); + QGraphicsObject* child = 0; + if(item) + child = item->toGraphicsObject(); + if (child) { + QDeclarative1BasePositioner::PositionedItem posItem(child); + int idx = positionedItems.find(posItem); + if (idx >= 0) { + d->unwatchChanges(child); + positionedItems.remove(idx); + } + prePositioning(); + } + } + return QDeclarativeItem::itemChange(change, value); +} + +void QDeclarative1BasePositioner::prePositioning() +{ + Q_D(QDeclarative1BasePositioner); + 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<QGraphicsItem *> children = d->QGraphicsItemPrivate::children; + qSort(children.begin(), children.end(), d->insertionOrder); + + QPODVector<PositionedItem,8> oldItems; + positionedItems.copyAndClear(oldItems); + for (int ii = 0; ii < children.count(); ++ii) { + QGraphicsObject *child = children.at(ii)->toGraphicsObject(); + if (!child) + continue; + QGraphicsItemPrivate *childPrivate = static_cast<QGraphicsItemPrivate*>(QGraphicsItemPrivate::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->explicitlyHidden || !childPrivate->width() || !childPrivate->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->explicitlyHidden || !childPrivate->width() || !childPrivate->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 QDeclarative1BasePositioner::positionX(int x, const PositionedItem &target) +{ + Q_D(QDeclarative1BasePositioner); + if(d->type == Horizontal || d->type == Both){ + if (target.isNew) { + if (!d->addTransition) + target.item->setX(x); + else + d->addActions << QDeclarative1Action(target.item, QLatin1String("x"), QVariant(x)); + } else if (x != target.item->x()) { + if (!d->moveTransition) + target.item->setX(x); + else + d->moveActions << QDeclarative1Action(target.item, QLatin1String("x"), QVariant(x)); + } + } +} + +void QDeclarative1BasePositioner::positionY(int y, const PositionedItem &target) +{ + Q_D(QDeclarative1BasePositioner); + if(d->type == Vertical || d->type == Both){ + if (target.isNew) { + if (!d->addTransition) + target.item->setY(y); + else + d->addActions << QDeclarative1Action(target.item, QLatin1String("y"), QVariant(y)); + } else if (y != target.item->y()) { + if (!d->moveTransition) + target.item->setY(y); + else + d->moveActions << QDeclarative1Action(target.item, QLatin1String("y"), QVariant(y)); + } + } +} + +void QDeclarative1BasePositioner::finishApplyTransitions() +{ + Q_D(QDeclarative1BasePositioner); + // 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(); +} + +/*! + \qmlclass Column QDeclarative1Column + \ingroup qml-positioning-elements + \since 4.7 + \brief The Column item arranges its children vertically. + \inherits Item + + The Column item positions its child items so that they are vertically + aligned and not overlapping. + + Spacing between items can be added using the \l spacing property. + Transitions can be used for cases where items managed by a Column are + added or moved. These are stored in the \l add and \l move properties + respectively. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example positions differently shaped rectangles using a Column + item. + + \image verticalpositioner_example.png + + \snippet doc/src/snippets/declarative/column/vertical-positioner.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Column item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Column item. + + The use of transitions with positioners is described in more detail in the + \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML + Positioner and Repeater Items} document. + + \image verticalpositioner_transition.gif + + \qml + Column { + spacing: 2 + add: Transition { + // Define an animation for adding a new item... + } + move: Transition { + // Define an animation for moving items within the column... + } + // ... + } + \endqml + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + height of a child depend on the position of a child, then the + positioner may exhibit strange behavior. If you need to perform any of these + actions, consider positioning the items without the use of a Column. + + Items with a width or height of 0 will not be positioned. + + \sa Row, Grid, Flow, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Column::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Column::move + + This property holds the transition to apply when moving an item + within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \image positioner-move.gif + + \qml + Column { + move: Transition { + NumberAnimation { + properties: "y" + duration: 1000 + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Column::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + \sa Grid::spacing +*/ +QDeclarative1Column::QDeclarative1Column(QDeclarativeItem *parent) +: QDeclarative1BasePositioner(Vertical, parent) +{ +} + +void QDeclarative1Column::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(), QGraphicsItemPrivate::get(child.item)->width())); + + voffset += QGraphicsItemPrivate::get(child.item)->height(); + voffset += spacing(); + } + + contentSize->setHeight(voffset - spacing()); +} + +void QDeclarative1Column::reportConflictingAnchors() +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate*>(QDeclarative1BasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarative1Anchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors; + if (anchors) { + QDeclarative1Anchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QDeclarative1Anchors::TopAnchor || + usedAnchors & QDeclarative1Anchors::BottomAnchor || + usedAnchors & QDeclarative1Anchors::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"; + } +} + +/*! + \qmlclass Row QDeclarative1Row + \ingroup qml-positioning-elements + \since 4.7 + \brief The Row item arranges its children horizontally. + \inherits Item + + The Row item positions its child items so that they are horizontally + aligned and not overlapping. + + Use \l spacing to set the spacing between items in a Row, and use the + \l add and \l move properties to set the transitions that should be applied + when items are added to, removed from, or re-positioned within the Row. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example lays out differently shaped rectangles using a Row. + + \image horizontalpositioner_example.png + + \snippet doc/src/snippets/declarative/row/row.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Grid item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Row item. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Row. + + Items with a width or height of 0 will not be positioned. + + \sa Column, Grid, Flow, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Row::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Row::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Row { + id: positioner + move: Transition { + NumberAnimation { + properties: "x" + duration: 1000 + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Row::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + \sa Grid::spacing +*/ +QDeclarative1Row::QDeclarative1Row(QDeclarativeItem *parent) +: QDeclarative1BasePositioner(Horizontal, parent) +{ +} + +/*! + \qmlproperty enumeration Row::layoutDirection + \since Quick 1.1 + + This property holds the layoutDirection of the row. + + Possible values: + + \list + \o Qt.LeftToRight (default) - Items are laid out from left to right. If the width of the row is explicitly set, + the left anchor remains to the left of the row. + \o Qt.RightToLeft - Items are laid out from right to left. If the width of the row is explicitly set, + the right anchor remains to the right of the row. + \endlist + + \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} +*/ +Qt::LayoutDirection QDeclarative1Row::layoutDirection() const +{ + return QDeclarative1BasePositionerPrivate::getLayoutDirection(this); +} + +void QDeclarative1Row::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate* >(QDeclarative1BasePositionerPrivate::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, QDeclarativeItemPrivate::Geometry); + else + d->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + prePositioning(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration Row::effectiveLayoutDirection + This property holds the effective layout direction of the row positioner. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the row positioner will be mirrored. However, the + property \l {Row::layoutDirection}{layoutDirection} will remain unchanged. + + \sa Row::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarative1Row::effectiveLayoutDirection() const +{ + return QDeclarative1BasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QDeclarative1Row::doPositioning(QSizeF *contentSize) +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate*>(QDeclarative1BasePositionerPrivate::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(), QGraphicsItemPrivate::get(child.item)->height())); + + hoffset += QGraphicsItemPrivate::get(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++] - QGraphicsItemPrivate::get(child.item)->width(); + if(child.item->x() != hoffset) + positionX(hoffset, child); + } +} + +void QDeclarative1Row::reportConflictingAnchors() +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate*>(QDeclarative1BasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarative1Anchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(child.item))->_anchors; + if (anchors) { + QDeclarative1Anchors::Anchors usedAnchors = anchors->usedAnchors(); + if (usedAnchors & QDeclarative1Anchors::LeftAnchor || + usedAnchors & QDeclarative1Anchors::RightAnchor || + usedAnchors & QDeclarative1Anchors::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"; +} + +/*! + \qmlclass Grid QDeclarative1Grid + \ingroup qml-positioning-elements + \since 4.7 + \brief The Grid item positions its children in a grid. + \inherits Item + + The Grid item positions its child items so that they are + aligned in a grid and are not overlapping. + + The grid positioner calculates a grid of rectangular cells of sufficient + size to hold all items, placing the items in the cells, from left to right + and top to bottom. Each item is positioned in the top-left corner of its + cell with position (0, 0). + + A Grid defaults to four columns, and as many rows as are necessary to + fit all child items. The number of rows and columns can be constrained + by setting the \l rows and \l columns properties. + + Spacing can be added between child items by setting the \l spacing + property. The amount of spacing applied will be the same in the + horizontal and vertical directions. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example demonstrates this. + + \image gridLayout_example.png + + \snippet doc/src/snippets/declarative/grid/grid.qml document + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Grid item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Grid item. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Grid. + + Items with a width or height of 0 will not be positioned. + + \sa Flow, Row, Column, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Grid::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Grid::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Grid { + move: Transition { + NumberAnimation { + properties: "x,y" + duration: 1000 + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Grid::spacing + + The spacing is the amount in pixels left empty between adjacent + items. The default spacing is 0. + + The below example places a Grid containing a red, a blue and a + green rectangle on a gray background. The area the grid positioner + occupies is colored white. The positioner on the left has the + no spacing (the default), and the positioner on the right has + a spacing of 6. + + \inlineimage qml-grid-no-spacing.png + \inlineimage qml-grid-spacing.png + + \sa rows, columns +*/ +QDeclarative1Grid::QDeclarative1Grid(QDeclarativeItem *parent) : + QDeclarative1BasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight) +{ +} + +/*! + \qmlproperty int Grid::columns + + This property holds the number of columns in the grid. The default + number of columns is 4. + + If the grid does not have enough items to fill the specified + number of columns, some columns will be of zero width. +*/ + +/*! + \qmlproperty int Grid::rows + This property holds the number of rows in the grid. + + If the grid does not have enough items to fill the specified + number of rows, some rows will be of zero width. +*/ + +void QDeclarative1Grid::setColumns(const int columns) +{ + if (columns == m_columns) + return; + m_columns = columns; + prePositioning(); + emit columnsChanged(); +} + +void QDeclarative1Grid::setRows(const int rows) +{ + if (rows == m_rows) + return; + m_rows = rows; + prePositioning(); + emit rowsChanged(); +} + +/*! + \qmlproperty enumeration Grid::flow + This property holds the flow of the layout. + + Possible values are: + + \list + \o Grid.LeftToRight (default) - Items are positioned next to + each other in the \l layoutDirection, then wrapped to the next line. + \o Grid.TopToBottom - Items are positioned next to each + other from top to bottom, then wrapped to the next column. + \endlist +*/ +QDeclarative1Grid::Flow QDeclarative1Grid::flow() const +{ + return m_flow; +} + +void QDeclarative1Grid::setFlow(Flow flow) +{ + if (m_flow != flow) { + m_flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +/*! + \qmlproperty enumeration Grid::layoutDirection + \since Quick 1.1 + + This property holds the layout direction of the layout. + + Possible values are: + + \list + \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, + and left to right. The flow direction is dependent on the + \l Grid::flow property. + \o Qt.RightToLeft - Items are positioned from the top to bottom, + and right to left. The flow direction is dependent on the + \l Grid::flow property. + \endlist + + \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} +*/ +Qt::LayoutDirection QDeclarative1Grid::layoutDirection() const +{ + return QDeclarative1BasePositionerPrivate::getLayoutDirection(this); +} + +void QDeclarative1Grid::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate*>(QDeclarative1BasePositionerPrivate::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, QDeclarativeItemPrivate::Geometry); + else + d->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + prePositioning(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration Grid::effectiveLayoutDirection + This property holds the effective layout direction of the grid positioner. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid positioner will be mirrored. However, the + property \l {Grid::layoutDirection}{layoutDirection} will remain unchanged. + + \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarative1Grid::effectiveLayoutDirection() const +{ + return QDeclarative1BasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QDeclarative1Grid::doPositioning(QSizeF *contentSize) +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate*>(QDeclarative1BasePositionerPrivate::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++); + QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item); + if (childPrivate->width() > maxColWidth[j]) + maxColWidth[j] = childPrivate->width(); + if (childPrivate->height() > maxRowHeight[i]) + maxRowHeight[i] = childPrivate->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++); + QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item); + if (childPrivate->width() > maxColWidth[j]) + maxColWidth[j] = childPrivate->width(); + if (childPrivate->height() > maxRowHeight[i]) + maxRowHeight[i] = childPrivate->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 -= QGraphicsItemPrivate::get(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 QDeclarative1Grid::reportConflictingAnchors() +{ + QDeclarative1BasePositionerPrivate *d = static_cast<QDeclarative1BasePositionerPrivate*>(QDeclarative1BasePositionerPrivate::get(this)); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarative1Anchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(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"; +} + +/*! + \qmlclass Flow QDeclarative1Flow + \ingroup qml-positioning-elements + \since 4.7 + \brief The Flow item arranges its children side by side, wrapping as necessary. + \inherits Item + + The Flow item positions its child items like words on a page, wrapping them + to create rows or columns of items that do not overlap. + + Spacing between items can be added using the \l spacing property. + Transitions can be used for cases where items managed by a Column are + added or moved. These are stored in the \l add and \l move properties + respectively. + + See \l{Using QML Positioner and Repeater Items} for more details about this item and other + related items. + + \section1 Example Usage + + The following example positions \l Text items within a parent item using + a Flow item. + + \image qml-flow-snippet.png + + \snippet doc/src/snippets/declarative/flow.qml flow item + + \section1 Using Transitions + + Transitions can be used to animate items that are added to, moved within, + or removed from a Flow item. The \l add and \l move properties can be set to + the transitions that will be applied when items are added to, removed from, + or re-positioned within a Flow item. + + The use of transitions with positioners is described in more detail in the + \l{Using QML Positioner and Repeater Items#Using Transitions}{Using QML + Positioner and Repeater Items} document. + + \section1 Limitations + + Note that the positioner assumes that the x and y positions of its children + will not change. If you manually change the x or y properties in script, bind + the x or y properties, use anchors on a child of a positioner, or have the + width or height of a child depend on the position of a child, then the + positioner may exhibit strange behaviour. If you need to perform any of these + actions, consider positioning the items without the use of a Flow. + + Items with a width or height of 0 will not be positioned. + + \sa Column, Row, Grid, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty Transition Flow::add + + This property holds the transition to be applied when adding an + item to the positioner. The transition will only be applied to the + added item(s). Positioner transitions will only affect the + position (x, y) of items. + + For a positioner, adding an item can mean that either the object + has been created or reparented, and thus is now a child or the + positioner, or that the object has had its opacity increased from + zero, and thus is now visible. + + \sa move +*/ +/*! + \qmlproperty Transition Flow::move + + This property holds the transition to be applied when moving an + item within the positioner. Positioner transitions will only affect + the position (x, y) of items. + + This transition can be performed when other items are added or removed + from the positioner, or when items resize themselves. + + \qml + Flow { + id: positioner + move: Transition { + NumberAnimation { + properties: "x,y" + ease: "easeOutBounce" + } + } + } + \endqml + + \sa add, {declarative/positioners}{Positioners example} +*/ +/*! + \qmlproperty int Flow::spacing + + spacing is the amount in pixels left empty between each adjacent + item, and defaults to 0. + + \sa Grid::spacing +*/ + +class QDeclarative1FlowPrivate : public QDeclarative1BasePositionerPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Flow) + +public: + QDeclarative1FlowPrivate() + : QDeclarative1BasePositionerPrivate(), flow(QDeclarative1Flow::LeftToRight) + {} + + QDeclarative1Flow::Flow flow; +}; + +QDeclarative1Flow::QDeclarative1Flow(QDeclarativeItem *parent) +: QDeclarative1BasePositioner(*(new QDeclarative1FlowPrivate), Both, parent) +{ + Q_D(QDeclarative1Flow); + // Flow layout requires relayout if its own size changes too. + d->addItemChangeListener(d, QDeclarativeItemPrivate::Geometry); +} + +/*! + \qmlproperty enumeration Flow::flow + This property holds the flow of the layout. + + Possible values are: + + \list + \o Flow.LeftToRight (default) - Items are positioned next to + to each other according to the \l layoutDirection until the width of the Flow + is exceeded, then wrapped to the next line. + \o Flow.TopToBottom - Items are positioned next to each + other from top to bottom until the height of the Flow is exceeded, + then wrapped to the next column. + \endlist +*/ +QDeclarative1Flow::Flow QDeclarative1Flow::flow() const +{ + Q_D(const QDeclarative1Flow); + return d->flow; +} + +void QDeclarative1Flow::setFlow(Flow flow) +{ + Q_D(QDeclarative1Flow); + if (d->flow != flow) { + d->flow = flow; + prePositioning(); + emit flowChanged(); + } +} + +/*! + \qmlproperty enumeration Flow::layoutDirection + \since Quick 1.1 + + This property holds the layout direction of the layout. + + Possible values are: + + \list + \o Qt.LeftToRight (default) - Items are positioned from the top to bottom, + and left to right. The flow direction is dependent on the + \l Flow::flow property. + \o Qt.RightToLeft - Items are positioned from the top to bottom, + and right to left. The flow direction is dependent on the + \l Flow::flow property. + \endlist + + \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} +*/ + +Qt::LayoutDirection QDeclarative1Flow::layoutDirection() const +{ + Q_D(const QDeclarative1Flow); + return d->layoutDirection; +} + +void QDeclarative1Flow::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarative1Flow); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + prePositioning(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +/*! + \qmlproperty enumeration Flow::effectiveLayoutDirection + This property holds the effective layout direction of the flow positioner. + + When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts, + the visual layout direction of the grid positioner will be mirrored. However, the + property \l {Flow::layoutDirection}{layoutDirection} will remain unchanged. + + \sa Flow::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ + +Qt::LayoutDirection QDeclarative1Flow::effectiveLayoutDirection() const +{ + return QDeclarative1BasePositionerPrivate::getEffectiveLayoutDirection(this); +} + +void QDeclarative1Flow::doPositioning(QSizeF *contentSize) +{ + Q_D(QDeclarative1Flow); + + 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; + + QGraphicsItemPrivate *childPrivate = QGraphicsItemPrivate::get(child.item); + if (d->flow == LeftToRight) { + if (widthValid() && hoffset && hoffset + childPrivate->width() > width()) { + hoffset = 0; + voffset += linemax + spacing(); + linemax = 0; + } + } else { + if (heightValid() && voffset && voffset + childPrivate->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 + childPrivate->width())); + contentSize->setHeight(qMax(contentSize->height(), voffset + childPrivate->height())); + + if (d->flow == LeftToRight) { + hoffset += childPrivate->width(); + hoffset += spacing(); + linemax = qMax(linemax, qCeil(childPrivate->height())); + } else { + voffset += childPrivate->height(); + voffset += spacing(); + linemax = qMax(linemax, qCeil(childPrivate->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++] - QGraphicsItemPrivate::get(child.item)->width(); + if(child.item->x() != hoffset) + positionX(hoffset, child); + } +} + +void QDeclarative1Flow::reportConflictingAnchors() +{ + Q_D(QDeclarative1Flow); + for (int ii = 0; ii < positionedItems.count(); ++ii) { + const PositionedItem &child = positionedItems.at(ii); + if (child.item && QGraphicsItemPrivate::get(child.item)->isDeclarativeItem) { + QDeclarative1Anchors *anchors = QDeclarativeItemPrivate::get(static_cast<QDeclarativeItem *>(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/qtquick1/graphicsitems/qdeclarativepositioners_p.h b/src/qtquick1/graphicsitems/qdeclarativepositioners_p.h new file mode 100644 index 0000000000..c5f25228bd --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepositioners_p.h @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELAYOUTS_H +#define QDECLARATIVELAYOUTS_H + +#include "qdeclarativeimplicitsizeitem_p.h" + +#include <QtQuick1/private/qdeclarativestate_p.h> +#include <private/qpodvector_p.h> + +#include <QtCore/QObject> +#include <QtCore/QString> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QDeclarative1BasePositionerPrivate; + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1BasePositioner : public QDeclarative1ImplicitSizeItem +{ + Q_OBJECT + + Q_PROPERTY(int spacing READ spacing WRITE setSpacing NOTIFY spacingChanged) + Q_PROPERTY(QDeclarative1Transition *move READ move WRITE setMove NOTIFY moveChanged) + Q_PROPERTY(QDeclarative1Transition *add READ add WRITE setAdd NOTIFY addChanged) +public: + enum PositionerType { None = 0x0, Horizontal = 0x1, Vertical = 0x2, Both = 0x3 }; + QDeclarative1BasePositioner(PositionerType, QDeclarativeItem *parent); + ~QDeclarative1BasePositioner(); + + int spacing() const; + void setSpacing(int); + + QDeclarative1Transition *move() const; + void setMove(QDeclarative1Transition *); + + QDeclarative1Transition *add() const; + void setAdd(QDeclarative1Transition *); + +protected: + QDeclarative1BasePositioner(QDeclarative1BasePositionerPrivate &dd, PositionerType at, QDeclarativeItem *parent); + virtual void componentComplete(); + virtual QVariant itemChange(GraphicsItemChange, const QVariant &); + void finishApplyTransitions(); + +Q_SIGNALS: + void spacingChanged(); + void moveChanged(); + void addChanged(); + +protected Q_SLOTS: + void prePositioning(); + void graphicsWidgetGeometryChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize)=0; + virtual void reportConflictingAnchors()=0; + class PositionedItem { + public : + PositionedItem(QGraphicsObject *i) : item(i), isNew(false), isVisible(true) {} + bool operator==(const PositionedItem &other) const { return other.item == item; } + QGraphicsObject *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(QDeclarative1BasePositioner) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1BasePositioner) +}; + +class Q_AUTOTEST_EXPORT QDeclarative1Column : public QDeclarative1BasePositioner +{ + Q_OBJECT +public: + QDeclarative1Column(QDeclarativeItem *parent=0); +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +private: + Q_DISABLE_COPY(QDeclarative1Column) +}; + +class Q_AUTOTEST_EXPORT QDeclarative1Row: public QDeclarative1BasePositioner +{ + Q_OBJECT + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged REVISION 1) +public: + QDeclarative1Row(QDeclarativeItem *parent=0); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection (Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + +Q_SIGNALS: + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) void effectiveLayoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +private: + Q_DISABLE_COPY(QDeclarative1Row) +}; + +class Q_AUTOTEST_EXPORT QDeclarative1Grid : public QDeclarative1BasePositioner +{ + 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 REVISION 1) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged REVISION 1) +public: + QDeclarative1Grid(QDeclarativeItem *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(); + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) void effectiveLayoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); + +private: + int m_rows; + int m_columns; + Flow m_flow; + Q_DISABLE_COPY(QDeclarative1Grid) +}; + +class QDeclarative1FlowPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Flow: public QDeclarative1BasePositioner +{ + Q_OBJECT + Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) + Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged REVISION 1) +public: + QDeclarative1Flow(QDeclarativeItem *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(); + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) void effectiveLayoutDirectionChanged(); + +protected: + virtual void doPositioning(QSizeF *contentSize); + virtual void reportConflictingAnchors(); +protected: + QDeclarative1Flow(QDeclarative1FlowPrivate &dd, QDeclarativeItem *parent); +private: + Q_DISABLE_COPY(QDeclarative1Flow) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Flow) +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Column) +QML_DECLARE_TYPE(QDeclarative1Row) +QML_DECLARE_TYPE(QDeclarative1Grid) +QML_DECLARE_TYPE(QDeclarative1Flow) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativepositioners_p_p.h b/src/qtquick1/graphicsitems/qdeclarativepositioners_p_p.h new file mode 100644 index 0000000000..eb2c9fdc88 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativepositioners_p_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELAYOUTS_P_H +#define QDECLARATIVELAYOUTS_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/qdeclarativepositioners_p.h" + +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +#include <QtQuick1/private/qdeclarativestate_p.h> +#include <QtQuick1/private/qdeclarativetransitionmanager_p_p.h> +#include <QtQuick1/private/qdeclarativestateoperations_p.h> + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QTimer> +#include <QDebug> + +QT_BEGIN_NAMESPACE + +class QDeclarative1BasePositionerPrivate : public QDeclarative1ImplicitSizeItemPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarative1BasePositioner) + +public: + QDeclarative1BasePositionerPrivate() + : spacing(0), type(QDeclarative1BasePositioner::None) + , moveTransition(0), addTransition(0), queuedPositioning(false) + , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) + { + } + + void init(QDeclarative1BasePositioner::PositionerType at) + { + type = at; + } + + int spacing; + + QDeclarative1BasePositioner::PositionerType type; + QDeclarative1Transition *moveTransition; + QDeclarative1Transition *addTransition; + QDeclarative1StateOperation::ActionList addActions; + QDeclarative1StateOperation::ActionList moveActions; + QDeclarative1TransitionManager addTransitionManager; + QDeclarative1TransitionManager moveTransitionManager; + + void watchChanges(QGraphicsObject *other); + void unwatchChanges(QGraphicsObject* other); + bool queuedPositioning : 1; + bool doingPositioning : 1; + bool anchorConflict : 1; + + Qt::LayoutDirection layoutDirection; + + + void schedulePositioning() + { + Q_Q(QDeclarative1BasePositioner); + if(!queuedPositioning){ + QTimer::singleShot(0,q,SLOT(prePositioning())); + queuedPositioning = true; + } + } + + void mirrorChange() { + Q_Q(QDeclarative1BasePositioner); + if (type != QDeclarative1BasePositioner::Vertical) + q->prePositioning(); + } + bool isLeftToRight() const { + if (type == QDeclarative1BasePositioner::Vertical) + return true; + else + return effectiveLayoutMirror ? layoutDirection == Qt::RightToLeft : layoutDirection == Qt::LeftToRight; + } + + virtual void itemSiblingOrderChanged(QDeclarativeItem* 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(QDeclarativeItem *, const QRectF &newGeometry, const QRectF &oldGeometry) + { + Q_Q(QDeclarative1BasePositioner); + if (newGeometry.size() != oldGeometry.size()) + q->prePositioning(); + } + + virtual void itemVisibilityChanged(QDeclarativeItem *) + { + schedulePositioning(); + } + virtual void itemOpacityChanged(QDeclarativeItem *) + { + Q_Q(QDeclarative1BasePositioner); + q->prePositioning(); + } + + void itemDestroyed(QDeclarativeItem *item) + { + Q_Q(QDeclarative1BasePositioner); + q->positionedItems.removeOne(QDeclarative1BasePositioner::PositionedItem(item)); + } + + static Qt::LayoutDirection getLayoutDirection(const QDeclarative1BasePositioner *positioner) + { + return positioner->d_func()->layoutDirection; + } + + static Qt::LayoutDirection getEffectiveLayoutDirection(const QDeclarative1BasePositioner *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 diff --git a/src/qtquick1/graphicsitems/qdeclarativerectangle.cpp b/src/qtquick1/graphicsitems/qdeclarativerectangle.cpp new file mode 100644 index 0000000000..d0bb5d143d --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativerectangle.cpp @@ -0,0 +1,591 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativerectangle_p.h" +#include "QtQuick1/private/qdeclarativerectangle_p_p.h" + +#include <QPainter> +#include <QStringBuilder> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \internal + \class QDeclarative1Pen + \brief The QDeclarative1Pen class provides a pen used for drawing rectangle borders on a QDeclarative1View. + + By default, the pen is invalid and nothing is drawn. You must either set a color (then the default + width is 1) or a width (then the default color is black). + + A width of 1 indicates is a single-pixel line on the border of the item being painted. + + Example: + \qml + Rectangle { + border.width: 2 + border.color: "red" + } + \endqml +*/ + +void QDeclarative1Pen::setColor(const QColor &c) +{ + _color = c; + _valid = (_color.alpha() && _width >= 1) ? true : false; + emit penChanged(); +} + +void QDeclarative1Pen::setWidth(int w) +{ + if (_width == w && _valid) + return; + + _width = w; + _valid = (_color.alpha() && _width >= 1) ? true : false; + emit penChanged(); +} + + +/*! + \qmlclass GradientStop QDeclarative1GradientStop + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The GradientStop item defines the color at a position in a Gradient. + + \sa Gradient +*/ + +/*! + \qmlproperty real GradientStop::position + \qmlproperty color GradientStop::color + + The position and color properties describe the color used at a given + position in a gradient, as represented by a gradient stop. + + The default position is 0.0; the default color is black. + + \sa Gradient +*/ + +void QDeclarative1GradientStop::updateGradient() +{ + if (QDeclarative1Gradient *grad = qobject_cast<QDeclarative1Gradient*>(parent())) + grad->doUpdate(); +} + +/*! + \qmlclass Gradient QDeclarative1Gradient + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Gradient item defines a gradient fill. + + A gradient is defined by two or more colors, which will be blended seamlessly. + + The colors are specified as a set of GradientStop child items, each of + which defines a position on the gradient from 0.0 to 1.0 and a color. + The position of each GradientStop is defined by setting its + \l{GradientStop::}{position} property; its color is defined using its + \l{GradientStop::}{color} property. + + A gradient without any gradient stops is rendered as a solid white fill. + + Note that this item is not a visual representation of a gradient. To display a + gradient, use a visual element (like \l Rectangle) which supports the use + of gradients. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage qml-gradient.png + \enddiv + + The following example declares a \l Rectangle item with a gradient starting + with red, blending to yellow at one third of the height of the rectangle, + and ending with green: + + \snippet doc/src/snippets/declarative/gradient.qml code + + \clearfloat + \section1 Performance and Limitations + + Calculating gradients can be computationally expensive compared to the use + of solid color fills or images. Consider using gradients for static items + in a user interface. + + In Qt 4.7, only vertical, linear gradients can be applied to items. If you + need to apply different orientations of gradients, a combination of rotation + and clipping will need to be applied to the relevant items. This can + introduce additional performance requirements for your application. + + The use of animations involving gradient stops may not give the desired + result. An alternative way to animate gradients is to use pre-generated + images or SVG drawings containing gradients. + + \sa GradientStop +*/ + +/*! + \qmlproperty list<GradientStop> Gradient::stops + This property holds the gradient stops describing the gradient. + + By default, this property contains an empty list. + + To set the gradient stops, define them as children of the Gradient element. +*/ + +const QGradient *QDeclarative1Gradient::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 QDeclarative1GradientStop *stop = m_stops.at(i); + m_gradient->setCoordinateMode(QGradient::ObjectBoundingMode); + m_gradient->setColorAt(stop->position(), stop->color()); + } + } + + return m_gradient; +} + +void QDeclarative1Gradient::doUpdate() +{ + delete m_gradient; + m_gradient = 0; + emit updated(); +} + + +/*! + \qmlclass Rectangle QDeclarative1Rectangle + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Rectangle item provides a filled rectangle with an optional border. + \inherits Item + + Rectangle items are used to fill areas with solid color or gradients, and are + often used to hold other items. + + \section1 Appearance + + Each Rectangle item is painted using either a solid fill color, specified using + the \l color property, or a gradient, defined using a Gradient element and set + using the \l gradient property. If both a color and a gradient are specified, + the gradient is used. + + You can add an optional border to a rectangle with its own color and thickness + by settting the \l border.color and \l border.width properties. + + You can also create rounded rectangles using the \l radius property. Since this + introduces curved edges to the corners of a rectangle, it may be appropriate to + set the \l smooth property to improve its appearance. + + \section1 Example Usage + + \div {class="float-right"} + \inlineimage declarative-rect.png + \enddiv + + The following example shows the effects of some of the common properties on a + Rectangle item, which in this case is used to create a square: + + \snippet doc/src/snippets/declarative/rectangle/rectangle.qml document + + \clearfloat + \section1 Performance + + Using the \l smooth property improves the appearance of a rounded rectangle at + the cost of rendering performance. You should consider unsetting this property + for rectangles in motion, and only set it when they are stationary. + + \sa Image +*/ + +int QDeclarative1RectanglePrivate::doUpdateSlotIdx = -1; + +QDeclarative1Rectangle::QDeclarative1Rectangle(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1RectanglePrivate), parent) +{ +} + +void QDeclarative1Rectangle::doUpdate() +{ + Q_D(QDeclarative1Rectangle); + d->rectImage = QPixmap(); + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + d->setPaintMargin((pw+1)/2); + update(); +} + +/*! + \qmlproperty int Rectangle::border.width + \qmlproperty color Rectangle::border.color + + The width and color used to draw the border of the rectangle. + + A width of 1 creates a thin line. For no line, use a width of 0 or a transparent color. + + \note The width of the rectangle's border does not affect the geometry of the + rectangle itself or its position relative to other items if anchors are used. + + If \c border.width is an odd number, the rectangle is painted at a half-pixel offset to retain + border smoothness. Also, the border is rendered evenly on either side of the + rectangle's boundaries, and the spare pixel is rendered to the right and below the + rectangle (as documented for QRect rendering). This can cause unintended effects if + \c border.width is 1 and the rectangle is \l{Item::clip}{clipped} by a parent item: + + \div {class="float-right"} + \inlineimage rect-border-width.png + \enddiv + + \snippet doc/src/snippets/declarative/rectangle/rect-border-width.qml 0 + + \clearfloat + Here, the innermost rectangle's border is clipped on the bottom and right edges by its + parent. To avoid this, the border width can be set to two instead of one. +*/ +QDeclarative1Pen *QDeclarative1Rectangle::border() +{ + Q_D(QDeclarative1Rectangle); + return d->getPen(); +} + +/*! + \qmlproperty Gradient Rectangle::gradient + + The gradient to use to fill the rectangle. + + This property allows for the construction of simple vertical gradients. + Other gradients may by formed by adding rotation to the rectangle. + + \div {class="float-left"} + \inlineimage declarative-rect_gradient.png + \enddiv + + \snippet doc/src/snippets/declarative/rectangle/rectangle-gradient.qml rectangles + \clearfloat + + If both a gradient and a color are specified, the gradient will be used. + + \sa Gradient, color +*/ +QDeclarative1Gradient *QDeclarative1Rectangle::gradient() const +{ + Q_D(const QDeclarative1Rectangle); + return d->gradient; +} + +void QDeclarative1Rectangle::setGradient(QDeclarative1Gradient *gradient) +{ + Q_D(QDeclarative1Rectangle); + if (d->gradient == gradient) + return; + static int updatedSignalIdx = -1; + if (updatedSignalIdx < 0) + updatedSignalIdx = QDeclarative1Gradient::staticMetaObject.indexOfSignal("updated()"); + if (d->doUpdateSlotIdx < 0) + d->doUpdateSlotIdx = QDeclarative1Rectangle::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(); +} + + +/*! + \qmlproperty real Rectangle::radius + This property holds the corner radius used to draw a rounded rectangle. + + If radius is non-zero, the rectangle will be painted as a rounded rectangle, otherwise it will be + painted as a normal rectangle. The same radius is used by all 4 corners; there is currently + no way to specify different radii for different corners. +*/ +qreal QDeclarative1Rectangle::radius() const +{ + Q_D(const QDeclarative1Rectangle); + return d->radius; +} + +void QDeclarative1Rectangle::setRadius(qreal radius) +{ + Q_D(QDeclarative1Rectangle); + if (d->radius == radius) + return; + + d->radius = radius; + d->rectImage = QPixmap(); + update(); + emit radiusChanged(); +} + +/*! + \qmlproperty color Rectangle::color + This property holds the color used to fill the rectangle. + + The default color is white. + + \div {class="float-right"} + \inlineimage rect-color.png + \enddiv + + The following example shows rectangles with colors specified + using hexadecimal and named color notation: + + \snippet doc/src/snippets/declarative/rectangle/rectangle-colors.qml rectangles + + \clearfloat + If both a gradient and a color are specified, the gradient will be used. + + \sa gradient +*/ +QColor QDeclarative1Rectangle::color() const +{ + Q_D(const QDeclarative1Rectangle); + return d->color; +} + +void QDeclarative1Rectangle::setColor(const QColor &c) +{ + Q_D(QDeclarative1Rectangle); + if (d->color == c) + return; + + d->color = c; + d->rectImage = QPixmap(); + update(); + emit colorChanged(); +} + +void QDeclarative1Rectangle::generateRoundedRect() +{ + Q_D(QDeclarative1Rectangle); + if (d->rectImage.isNull()) { + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + const int radius = qCeil(d->radius); //ensure odd numbered width/height so we get 1-pixel center + + QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16) % QLatin1Char('_') % QString::number(radius); + if (d->pen && d->pen->isValid()) + key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16); + + if (!QPixmapCache::find(key, &d->rectImage)) { + d->rectImage = QPixmap(radius*2 + 3 + pw*2, radius*2 + 3 + pw*2); + d->rectImage.fill(Qt::transparent); + QPainter p(&(d->rectImage)); + p.setRenderHint(QPainter::Antialiasing); + if (d->pen && d->pen->isValid()) { + QPen pn(QColor(d->pen->color()), d->pen->width()); + p.setPen(pn); + } else { + p.setPen(Qt::NoPen); + } + p.setBrush(d->color); + if (pw%2) + p.drawRoundedRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1)), d->radius, d->radius); + else + p.drawRoundedRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw), d->radius, d->radius); + + // end painting before inserting pixmap + // to pixmap cache to avoid a deep copy + p.end(); + QPixmapCache::insert(key, d->rectImage); + } + } +} + +void QDeclarative1Rectangle::generateBorderedRect() +{ + Q_D(QDeclarative1Rectangle); + if (d->rectImage.isNull()) { + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + + QString key = QLatin1String("q_") % QString::number(pw) % d->color.name() % QString::number(d->color.alpha(), 16); + if (d->pen && d->pen->isValid()) + key += d->pen->color().name() % QString::number(d->pen->color().alpha(), 16); + + if (!QPixmapCache::find(key, &d->rectImage)) { + // Adding 5 here makes qDrawBorderPixmap() paint correctly with smooth: true + // See QTBUG-7999 and QTBUG-10765 for more details. + d->rectImage = QPixmap(pw*2 + 5, pw*2 + 5); + d->rectImage.fill(Qt::transparent); + QPainter p(&(d->rectImage)); + p.setRenderHint(QPainter::Antialiasing); + if (d->pen && d->pen->isValid()) { + QPen pn(QColor(d->pen->color()), d->pen->width()); + pn.setJoinStyle(Qt::MiterJoin); + p.setPen(pn); + } else { + p.setPen(Qt::NoPen); + } + p.setBrush(d->color); + if (pw%2) + p.drawRect(QRectF(qreal(pw)/2+1, qreal(pw)/2+1, d->rectImage.width()-(pw+1), d->rectImage.height()-(pw+1))); + else + p.drawRect(QRectF(qreal(pw)/2, qreal(pw)/2, d->rectImage.width()-pw, d->rectImage.height()-pw)); + + // end painting before inserting pixmap + // to pixmap cache to avoid a deep copy + p.end(); + QPixmapCache::insert(key, d->rectImage); + } + } +} + +void QDeclarative1Rectangle::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarative1Rectangle); + if (width() <= 0 || height() <= 0) + return; + if (d->radius > 0 || (d->pen && d->pen->isValid()) + || (d->gradient && d->gradient->gradient()) ) { + drawRect(*p); + } + else { + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing, true); + p->fillRect(QRectF(0, 0, width(), height()), d->color); + if (d->smooth) + p->setRenderHint(QPainter::Antialiasing, oldAA); + } +} + +void QDeclarative1Rectangle::drawRect(QPainter &p) +{ + Q_D(QDeclarative1Rectangle); + if ((d->gradient && d->gradient->gradient()) + || d->radius > width()/2 || d->radius > height()/2 + || width() < 3 || height() < 3) { + // XXX This path is still slower than the image path + // Image path won't work for gradients or invalid radius though + bool oldAA = p.testRenderHint(QPainter::Antialiasing); + if (d->smooth) + p.setRenderHint(QPainter::Antialiasing); + if (d->pen && d->pen->isValid()) { + QPen pn(QColor(d->pen->color()), d->pen->width()); + pn.setJoinStyle(Qt::MiterJoin); + p.setPen(pn); + } else { + p.setPen(Qt::NoPen); + } + if (d->gradient && d->gradient->gradient()) + p.setBrush(*d->gradient->gradient()); + else + p.setBrush(d->color); + const int pw = d->pen && d->pen->isValid() ? d->pen->width() : 0; + QRectF rect; + if (pw%2) + rect = QRectF(0.5, 0.5, width()-1, height()-1); + else + rect = QRectF(0, 0, width(), height()); + qreal radius = d->radius; + if (radius > width()/2 || radius > height()/2) + radius = qMin(width()/2, height()/2); + if (radius > 0.) + p.drawRoundedRect(rect, radius, radius); + else + p.drawRect(rect); + if (d->smooth) + p.setRenderHint(QPainter::Antialiasing, oldAA); + } else { + bool oldAA = p.testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p.testRenderHint(QPainter::SmoothPixmapTransform); + if (d->smooth) + p.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + + const int pw = d->pen && d->pen->isValid() ? (d->pen->width()+1)/2*2 : 0; + + if (d->radius > 0) + generateRoundedRect(); + else + generateBorderedRect(); + + int xOffset = (d->rectImage.width()-1)/2; + int yOffset = (d->rectImage.height()-1)/2; + Q_ASSERT(d->rectImage.width() == 2*xOffset + 1); + Q_ASSERT(d->rectImage.height() == 2*yOffset + 1); + + // check whether we've eliminated the center completely + if (2*xOffset > width()+pw) + xOffset = (width()+pw)/2; + if (2*yOffset > height()+pw) + yOffset = (height()+pw)/2; + + QMargins margins(xOffset, yOffset, xOffset, yOffset); + QTileRules rules(Qt::StretchTile, Qt::StretchTile); + //NOTE: even though our item may have qreal-based width and height, qDrawBorderPixmap only supports QRects + qDrawBorderPixmap(&p, QRect(-pw/2, -pw/2, width()+pw, height()+pw), margins, d->rectImage, d->rectImage.rect(), margins, rules); + + if (d->smooth) { + p.setRenderHint(QPainter::Antialiasing, oldAA); + p.setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + } +} + +/*! + \qmlproperty bool Rectangle::smooth + + Set this property if you want the item to be smoothly scaled or + transformed. Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. + + \image rect-smooth.png + On this image, smooth is turned off on the top half and on on the bottom half. +*/ + +QRectF QDeclarative1Rectangle::boundingRect() const +{ + Q_D(const QDeclarative1Rectangle); + return QRectF(-d->paintmargin, -d->paintmargin, d->width()+d->paintmargin*2, d->height()+d->paintmargin*2); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativerectangle_p.h b/src/qtquick1/graphicsitems/qdeclarativerectangle_p.h new file mode 100644 index 0000000000..862799faa2 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativerectangle_p.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECT_H +#define QDECLARATIVERECT_H + +#include "qdeclarativeitem.h" + +#include <QtGui/qbrush.h> + +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Pen : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY penChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY penChanged) +public: + QDeclarative1Pen(QObject *parent=0) + : QObject(parent), _width(1), _color("#000000"), _valid(false) + {} + + int width() const { return _width; } + void setWidth(int w); + + QColor color() const { return _color; } + void setColor(const QColor &c); + + bool isValid() { return _valid; } + +Q_SIGNALS: + void penChanged(); + +private: + int _width; + QColor _color; + bool _valid; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1GradientStop : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal position READ position WRITE setPosition) + Q_PROPERTY(QColor color READ color WRITE setColor) + +public: + QDeclarative1GradientStop(QObject *parent=0) : QObject(parent) {} + + qreal position() const { return m_position; } + void setPosition(qreal position) { m_position = position; updateGradient(); } + + QColor color() const { return m_color; } + void setColor(const QColor &color) { m_color = color; updateGradient(); } + +private: + void updateGradient(); + +private: + qreal m_position; + QColor m_color; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1Gradient : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1GradientStop> stops READ stops) + Q_CLASSINFO("DefaultProperty", "stops") + +public: + QDeclarative1Gradient(QObject *parent=0) : QObject(parent), m_gradient(0) {} + ~QDeclarative1Gradient() { delete m_gradient; } + + QDeclarativeListProperty<QDeclarative1GradientStop> stops() { return QDeclarativeListProperty<QDeclarative1GradientStop>(this, m_stops); } + + const QGradient *gradient() const; + +Q_SIGNALS: + void updated(); + +private: + void doUpdate(); + +private: + QList<QDeclarative1GradientStop *> m_stops; + mutable QGradient *m_gradient; + friend class QDeclarative1GradientStop; +}; + +class QDeclarative1RectanglePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Rectangle : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarative1Gradient *gradient READ gradient WRITE setGradient) + Q_PROPERTY(QDeclarative1Pen * border READ border CONSTANT) + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) +public: + QDeclarative1Rectangle(QDeclarativeItem *parent=0); + + QColor color() const; + void setColor(const QColor &); + + QDeclarative1Pen *border(); + + QDeclarative1Gradient *gradient() const; + void setGradient(QDeclarative1Gradient *gradient); + + qreal radius() const; + void setRadius(qreal radius); + + QRectF boundingRect() const; + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + +Q_SIGNALS: + void colorChanged(); + void radiusChanged(); + +private Q_SLOTS: + void doUpdate(); + +private: + void generateRoundedRect(); + void generateBorderedRect(); + void drawRect(QPainter &painter); + +private: + Q_DISABLE_COPY(QDeclarative1Rectangle) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Rectangle) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Pen) +QML_DECLARE_TYPE(QDeclarative1GradientStop) +QML_DECLARE_TYPE(QDeclarative1Gradient) +QML_DECLARE_TYPE(QDeclarative1Rectangle) + +QT_END_HEADER + +#endif // QDECLARATIVERECT_H diff --git a/src/qtquick1/graphicsitems/qdeclarativerectangle_p_p.h b/src/qtquick1/graphicsitems/qdeclarativerectangle_p_p.h new file mode 100644 index 0000000000..4437f6be75 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativerectangle_p_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECT_P_H +#define QDECLARATIVERECT_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/qdeclarativeitem_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarative1Gradient; +class QDeclarative1Rectangle; +class QDeclarative1RectanglePrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Rectangle) + +public: + QDeclarative1RectanglePrivate() : + color(Qt::white), gradient(0), pen(0), radius(0), paintmargin(0) + { + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; + } + + ~QDeclarative1RectanglePrivate() + { + delete pen; + } + + QColor color; + QDeclarative1Gradient *gradient; + QDeclarative1Pen *pen; + qreal radius; + qreal paintmargin; + QPixmap rectImage; + static int doUpdateSlotIdx; + + QDeclarative1Pen *getPen() { + if (!pen) { + Q_Q(QDeclarative1Rectangle); + pen = new QDeclarative1Pen; + static int penChangedSignalIdx = -1; + if (penChangedSignalIdx < 0) + penChangedSignalIdx = QDeclarative1Pen::staticMetaObject.indexOfSignal("penChanged()"); + if (doUpdateSlotIdx < 0) + doUpdateSlotIdx = QDeclarative1Rectangle::staticMetaObject.indexOfSlot("doUpdate()"); + QMetaObject::connect(pen, penChangedSignalIdx, q, doUpdateSlotIdx); + } + return pen; + } + + void setPaintMargin(qreal margin) + { + Q_Q(QDeclarative1Rectangle); + if (margin == paintmargin) + return; + q->prepareGeometryChange(); + paintmargin = margin; + } +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVERECT_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativerepeater.cpp b/src/qtquick1/graphicsitems/qdeclarativerepeater.cpp new file mode 100644 index 0000000000..cebff875e8 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativerepeater.cpp @@ -0,0 +1,449 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativerepeater_p.h" +#include "QtQuick1/private/qdeclarativerepeater_p_p.h" + +#include "QtQuick1/private/qdeclarativevisualitemmodel_p.h" +#include <QtDeclarative/private/qdeclarativeglobal_p.h> +#include <QtQuick1/private/qdeclarativelistaccessor_p.h> + +QT_BEGIN_NAMESPACE + + +QDeclarative1RepeaterPrivate::QDeclarative1RepeaterPrivate() +: model(0), ownModel(false) +{ +} + +QDeclarative1RepeaterPrivate::~QDeclarative1RepeaterPrivate() +{ + if (ownModel) + delete model; +} + +/*! + \qmlclass Repeater QDeclarative1Repeater + \ingroup qml-utility-elements + \since 4.7 + \inherits Item + + \brief The Repeater element allows you to repeat an Item-based component using a model. + + The Repeater element is used to create a large number of + similar items. Like other view elements, a Repeater has a \l model and a \l delegate: + for each entry in the model, the delegate is instantiated + in a context seeded with data from the model. A Repeater item is usually + enclosed in a positioner element such as \l Row or \l Column to visually + position the multiple delegate items created by the Repeater. + + The following Repeater creates three instances of a \l Rectangle item within + a \l Row: + + \snippet doc/src/snippets/declarative/repeaters/repeater.qml import + \codeline + \snippet doc/src/snippets/declarative/repeaters/repeater.qml simple + + \image repeater-simple.png + + A Repeater's \l model can be any of the supported \l {qmlmodels}{data models}. + Additionally, like delegates for other views, a Repeater delegate can access + its index within the repeater, as well as the model data relevant to the + delegate. See the \l delegate property documentation for details. + + Items instantiated by the Repeater are inserted, in order, as + children of the Repeater's parent. The insertion starts immediately after + the repeater's position in its parent stacking list. This allows + a Repeater to be used inside a layout. For example, the following Repeater's + items are stacked between a red rectangle and a blue rectangle: + + \snippet doc/src/snippets/declarative/repeaters/repeater.qml layout + + \image repeater.png + + + \note A Repeater item owns all items it instantiates. Removing or dynamically destroying + an item created by a Repeater results in unpredictable behavior. + + + \section2 Considerations when using Repeater + + The Repeater element creates all of its delegate items when the repeater is first + created. This can be inefficient if there are a large number of delegate items and + not all of the items are required to be visible at the same time. If this is the case, + consider using other view elements like ListView (which only creates delegate items + when they are scrolled into view) or use the \l {Dynamic Object Creation} methods to + create items as they are required. + + Also, note that Repeater is \l {Item}-based, and can only repeat \l {Item}-derived objects. + For example, it cannot be used to repeat QtObjects: + \badcode + Item { + //XXX does not work! Can't repeat QtObject as it doesn't derive from Item. + Repeater { + model: 10 + QtObject {} + } + } + \endcode + */ + +/*! + \qmlsignal Repeater::onItemAdded(int index, Item item) + \since Quick 1.1 + + This handler is called when an item is added to the repeater. The \a index + parameter holds the index at which the item has been inserted within the + repeater, and the \a item parameter holds the \l Item that has been added. +*/ + +/*! + \qmlsignal Repeater::onItemRemoved(int index, Item item) + \since Quick 1.1 + + This handler is called when an item is removed from the repeater. The \a index + parameter holds the index at which the item was removed from the repeater, + and the \a item parameter holds the \l Item that was removed. + + Do not keep a reference to \a item if it was created by this repeater, as + in these cases it will be deleted shortly after the handler is called. +*/ + +QDeclarative1Repeater::QDeclarative1Repeater(QDeclarativeItem *parent) + : QDeclarativeItem(*(new QDeclarative1RepeaterPrivate), parent) +{ +} + +QDeclarative1Repeater::~QDeclarative1Repeater() +{ +} + +/*! + \qmlproperty any Repeater::model + + The model providing data for the repeater. + + This property can be set to any of the supported \l {qmlmodels}{data models}: + + \list + \o A number that indicates the number of delegates to be created by the repeater + \o A model (e.g. a ListModel item, or a QAbstractItemModel subclass) + \o A string list + \o An object list + \endlist + + The type of model affects the properties that are exposed to the \l delegate. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarative1Repeater::model() const +{ + Q_D(const QDeclarative1Repeater); + return d->dataSource; +} + +void QDeclarative1Repeater::setModel(const QVariant &model) +{ + Q_D(QDeclarative1Repeater); + 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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + */ + } + d->dataSource = model; + QObject *object = qvariant_cast<QObject*>(model); + QDeclarative1VisualModel *vim = 0; + if (object && (vim = qobject_cast<QDeclarative1VisualModel *>(object))) { + if (d->ownModel) { + delete d->model; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this), this); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(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,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*))); + connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*))); + */ + regenerate(); + } + emit modelChanged(); + emit countChanged(); +} + +/*! + \qmlproperty Component Repeater::delegate + \default + + The delegate provides a template defining each item instantiated by the repeater. + + Delegates are exposed to a read-only \c index property that indicates the index + of the delegate within the repeater. For example, the following \l Text delegate + displays the index of each repeated item: + + \table + \row + \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml index + \o \image repeater-index.png + \endtable + + If the \l model is a \l{QStringList-based model}{string list} or + \l{QObjectList-based model}{object list}, the delegate is also exposed to + a read-only \c modelData property that holds the string or object data. For + example: + + \table + \row + \o \snippet doc/src/snippets/declarative/repeaters/repeater.qml modeldata + \o \image repeater-modeldata.png + \endtable + + If the \l model is a model object (such as a \l ListModel) the delegate + can access all model roles as named properties, in the same way that delegates + do for view classes like ListView. + + \sa {QML Data Models} + */ +QDeclarativeComponent *QDeclarative1Repeater::delegate() const +{ + Q_D(const QDeclarative1Repeater); + if (d->model) { + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QDeclarative1Repeater::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarative1Repeater); + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) + if (delegate == dataModel->delegate()) + return; + + if (!d->ownModel) { + d->model = new QDeclarative1VisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QDeclarative1VisualDataModel *dataModel = qobject_cast<QDeclarative1VisualDataModel*>(d->model)) { + dataModel->setDelegate(delegate); + regenerate(); + emit delegateChanged(); + } +} + +/*! + \qmlproperty int Repeater::count + + This property holds the number of items in the repeater. +*/ +int QDeclarative1Repeater::count() const +{ + Q_D(const QDeclarative1Repeater); + if (d->model) + return d->model->count(); + return 0; +} + +/*! + \qmlmethod Item Repeater::itemAt(index) + \since Quick 1.1 + + Returns the \l Item that has been created at the given \a index, or \c null + if no item exists at \a index. +*/ +QDeclarativeItem *QDeclarative1Repeater::itemAt(int index) const +{ + Q_D(const QDeclarative1Repeater); + if (index >= 0 && index < d->deletables.count()) + return d->deletables[index]; + return 0; + +} + +void QDeclarative1Repeater::componentComplete() +{ + QDeclarativeItem::componentComplete(); + regenerate(); +} + +QVariant QDeclarative1Repeater::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + QVariant rv = QDeclarativeItem::itemChange(change, value); + if (change == ItemParentHasChanged) { + regenerate(); + } + + return rv; +} + +void QDeclarative1Repeater::clear() +{ + Q_D(QDeclarative1Repeater); + bool complete = isComponentComplete(); + + if (d->model) { + while (d->deletables.count() > 0) { + QDeclarativeItem *item = d->deletables.takeLast(); + if (complete) + emit itemRemoved(d->deletables.count()-1, item); + d->model->release(item); + } + } + d->deletables.clear(); +} + +void QDeclarative1Repeater::regenerate() +{ + Q_D(QDeclarative1Repeater); + if (!isComponentComplete()) + return; + + clear(); + + if (!d->model || !d->model->count() || !d->model->isValid() || !parentItem() || !isComponentComplete()) + return; + + for (int ii = 0; ii < count(); ++ii) { + QDeclarativeItem *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 QDeclarative1Repeater::itemsInserted(int index, int count) +{ + Q_D(QDeclarative1Repeater); + if (!isComponentComplete()) + return; + for (int i = 0; i < count; ++i) { + int modelIndex = index + i; + QDeclarativeItem *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 QDeclarative1Repeater::itemsRemoved(int index, int count) +{ + Q_D(QDeclarative1Repeater); + if (!isComponentComplete() || count <= 0) + return; + while (count--) { + QDeclarativeItem *item = d->deletables.takeAt(index); + emit itemRemoved(index, item); + if (item) + d->model->release(item); + else + break; + } + emit countChanged(); +} + +void QDeclarative1Repeater::itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarative1Repeater); + if (!isComponentComplete() || count <= 0) + return; + if (from + count > d->deletables.count()) { + regenerate(); + return; + } + QList<QDeclarativeItem*> 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) { + QDeclarativeItem *item = d->deletables.at(i-1); + item->stackBefore(d->deletables.at(i)); + } +} + +void QDeclarative1Repeater::modelReset() +{ + if (!isComponentComplete()) + return; + regenerate(); + emit countChanged(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativerepeater_p.h b/src/qtquick1/graphicsitems/qdeclarativerepeater_p.h new file mode 100644 index 0000000000..6176cd1d14 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativerepeater_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREPEATER_H +#define QDECLARATIVEREPEATER_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1RepeaterPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Repeater : public QDeclarativeItem +{ + 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: + QDeclarative1Repeater(QDeclarativeItem *parent=0); + virtual ~QDeclarative1Repeater(); + + QVariant model() const; + void setModel(const QVariant &); + + QDeclarativeComponent *delegate() const; + void setDelegate(QDeclarativeComponent *); + + int count() const; + + Q_INVOKABLE Q_REVISION(1) QDeclarativeItem *itemAt(int index) const; + +Q_SIGNALS: + void modelChanged(); + void delegateChanged(); + void countChanged(); + + Q_REVISION(1) void itemAdded(int index, QDeclarativeItem *item); + Q_REVISION(1) void itemRemoved(int index, QDeclarativeItem *item); + +private: + void clear(); + void regenerate(); + +protected: + virtual void componentComplete(); + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + +private Q_SLOTS: + void itemsInserted(int,int); + void itemsRemoved(int,int); + void itemsMoved(int,int,int); + void modelReset(); + +private: + Q_DISABLE_COPY(QDeclarative1Repeater) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Repeater) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Repeater) + +QT_END_HEADER + +#endif // QDECLARATIVEREPEATER_H diff --git a/src/qtquick1/graphicsitems/qdeclarativerepeater_p_p.h b/src/qtquick1/graphicsitems/qdeclarativerepeater_p_p.h new file mode 100644 index 0000000000..e806bd4cb5 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativerepeater_p_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREPEATER_P_H +#define QDECLARATIVEREPEATER_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/qdeclarativerepeater_p.h" + +#include "private/qdeclarativeitem_p.h" + +#include <QPointer> + +QT_BEGIN_NAMESPACE + +class QDeclarativeContext; +class QDeclarative1VisualModel; +class QDeclarative1RepeaterPrivate : public QDeclarativeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Repeater) + +public: + QDeclarative1RepeaterPrivate(); + ~QDeclarative1RepeaterPrivate(); + + QDeclarative1VisualModel *model; + QVariant dataSource; + bool ownModel; + + QList<QPointer<QDeclarativeItem> > deletables; +}; + +QT_END_NAMESPACE +#endif // QDECLARATIVEREPEATER_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativescalegrid.cpp b/src/qtquick1/graphicsitems/qdeclarativescalegrid.cpp new file mode 100644 index 0000000000..83a94ec9de --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativescalegrid.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativescalegrid_p_p.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <QBuffer> +#include <QDebug> + +QT_BEGIN_NAMESPACE + + +/*! + \internal + \class QDeclarative1ScaleGrid + \brief The QDeclarative1ScaleGrid class allows you to specify a 3x3 grid to use in scaling an image. +*/ + +QDeclarative1ScaleGrid::QDeclarative1ScaleGrid(QObject *parent) : QObject(parent), _left(0), _top(0), _right(0), _bottom(0) +{ +} + +QDeclarative1ScaleGrid::~QDeclarative1ScaleGrid() +{ +} + +bool QDeclarative1ScaleGrid::isNull() const +{ + return !_left && !_top && !_right && !_bottom; +} + +void QDeclarative1ScaleGrid::setLeft(int pos) +{ + if (_left != pos) { + _left = pos; + emit borderChanged(); + } +} + +void QDeclarative1ScaleGrid::setTop(int pos) +{ + if (_top != pos) { + _top = pos; + emit borderChanged(); + } +} + +void QDeclarative1ScaleGrid::setRight(int pos) +{ + if (_right != pos) { + _right = pos; + emit borderChanged(); + } +} + +void QDeclarative1ScaleGrid::setBottom(int pos) +{ + if (_bottom != pos) { + _bottom = pos; + emit borderChanged(); + } +} + +QDeclarative1GridScaledImage::QDeclarative1GridScaledImage() +: _l(-1), _r(-1), _t(-1), _b(-1), + _h(QDeclarative1BorderImage::Stretch), _v(QDeclarative1BorderImage::Stretch) +{ +} + +QDeclarative1GridScaledImage::QDeclarative1GridScaledImage(const QDeclarative1GridScaledImage &o) +: _l(o._l), _r(o._r), _t(o._t), _b(o._b), _h(o._h), _v(o._v), _pix(o._pix) +{ +} + +QDeclarative1GridScaledImage &QDeclarative1GridScaledImage::operator=(const QDeclarative1GridScaledImage &o) +{ + _l = o._l; + _r = o._r; + _t = o._t; + _b = o._b; + _h = o._h; + _v = o._v; + _pix = o._pix; + return *this; +} + +QDeclarative1GridScaledImage::QDeclarative1GridScaledImage(QIODevice *data) +: _l(-1), _r(-1), _t(-1), _b(-1), _h(QDeclarative1BorderImage::Stretch), _v(QDeclarative1BorderImage::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; +} + +QDeclarative1BorderImage::TileMode QDeclarative1GridScaledImage::stringToRule(const QString &s) +{ + if (s == QLatin1String("Stretch")) + return QDeclarative1BorderImage::Stretch; + if (s == QLatin1String("Repeat")) + return QDeclarative1BorderImage::Repeat; + if (s == QLatin1String("Round")) + return QDeclarative1BorderImage::Round; + + qWarning("QDeclarative1GridScaledImage: Invalid tile rule specified. Using Stretch."); + return QDeclarative1BorderImage::Stretch; +} + +bool QDeclarative1GridScaledImage::isValid() const +{ + return _l >= 0; +} + +int QDeclarative1GridScaledImage::gridLeft() const +{ + return _l; +} + +int QDeclarative1GridScaledImage::gridRight() const +{ + return _r; +} + +int QDeclarative1GridScaledImage::gridTop() const +{ + return _t; +} + +int QDeclarative1GridScaledImage::gridBottom() const +{ + return _b; +} + +QString QDeclarative1GridScaledImage::pixmapUrl() const +{ + return _pix; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativescalegrid_p_p.h b/src/qtquick1/graphicsitems/qdeclarativescalegrid_p_p.h new file mode 100644 index 0000000000..5a1c298a74 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativescalegrid_p_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESCALEGRID_H +#define QDECLARATIVESCALEGRID_H + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/QString> +#include <QtCore/QObject> + +#include <QtQuick1/private/qdeclarativeborderimage_p.h> +#include <QtQuick1/private/qdeclarativepixmapcache_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1ScaleGrid : 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: + QDeclarative1ScaleGrid(QObject *parent=0); + ~QDeclarative1ScaleGrid(); + + 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 QDeclarative1GridScaledImage +{ +public: + QDeclarative1GridScaledImage(); + QDeclarative1GridScaledImage(const QDeclarative1GridScaledImage &); + QDeclarative1GridScaledImage(QIODevice*); + QDeclarative1GridScaledImage &operator=(const QDeclarative1GridScaledImage &); + bool isValid() const; + int gridLeft() const; + int gridRight() const; + int gridTop() const; + int gridBottom() const; + QDeclarative1BorderImage::TileMode horizontalTileRule() const { return _h; } + QDeclarative1BorderImage::TileMode verticalTileRule() const { return _v; } + + QString pixmapUrl() const; + +private: + static QDeclarative1BorderImage::TileMode stringToRule(const QString &); + +private: + int _l; + int _r; + int _t; + int _b; + QDeclarative1BorderImage::TileMode _h; + QDeclarative1BorderImage::TileMode _v; + QString _pix; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1ScaleGrid) + +QT_END_HEADER + +#endif // QDECLARATIVESCALEGRID_H diff --git a/src/qtquick1/graphicsitems/qdeclarativetext.cpp b/src/qtquick1/graphicsitems/qdeclarativetext.cpp new file mode 100644 index 0000000000..8a497c271e --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetext.cpp @@ -0,0 +1,1645 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativetext_p.h" +#include "QtQuick1/private/qdeclarativetext_p_p.h" +#include "QtQuick1/private/qdeclarativestyledtext_p.h" +#include <QtDeclarative/qdeclarativeinfo.h> +#include "QtQuick1/private/qdeclarativepixmapcache_p.h" + +#include <QSet> +#include <QTextLayout> +#include <QTextLine> +#include <QTextDocument> +#include <QGraphicsSceneMouseEvent> +#include <QPainter> +#include <QAbstractTextDocumentLayout> +#include <qmath.h> +#include <limits.h> + +QT_BEGIN_NAMESPACE + + + +extern Q_GUI_EXPORT bool qt_applefontsmoothing_enabled; + +class QTextDocumentWithImageResources_1 : public QTextDocument { + Q_OBJECT + +public: + QTextDocumentWithImageResources_1(QDeclarative1Text *parent); + virtual ~QTextDocumentWithImageResources_1(); + + void setText(const QString &); + int resourcesLoading() const { return outstanding; } + +protected: + QVariant loadResource(int type, const QUrl &name); + +private slots: + void requestFinished(); + +private: + QHash<QUrl, QDeclarative1Pixmap *> m_resources; + + int outstanding; + static QSet<QUrl> errors; +}; + + +QTextDocumentWithImageResources_1::QTextDocumentWithImageResources_1(QDeclarative1Text *parent) +: QTextDocument(parent), outstanding(0) +{ +} + +QTextDocumentWithImageResources_1::~QTextDocumentWithImageResources_1() +{ + if (!m_resources.isEmpty()) + qDeleteAll(m_resources); +} + +QVariant QTextDocumentWithImageResources_1::loadResource(int type, const QUrl &name) +{ + QDeclarativeContext *context = qmlContext(parent()); + QUrl url = context->resolvedUrl(name); + + if (type == QTextDocument::ImageResource) { + QHash<QUrl, QDeclarative1Pixmap *>::Iterator iter = m_resources.find(url); + + if (iter == m_resources.end()) { + QDeclarative1Pixmap *p = new QDeclarative1Pixmap(context->engine(), url); + iter = m_resources.insert(name, p); + + if (p->isLoading()) { + p->connectFinished(this, SLOT(requestFinished())); + outstanding++; + } + } + + QDeclarative1Pixmap *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 QTextDocumentWithImageResources_1::requestFinished() +{ + outstanding--; + if (outstanding == 0) { + QDeclarative1Text *textItem = static_cast<QDeclarative1Text*>(parent()); + QString text = textItem->text(); +#ifndef QT_NO_TEXTHTMLPARSER + setHtml(text); +#else + setPlainText(text); +#endif + QDeclarative1TextPrivate *d = QDeclarative1TextPrivate::get(textItem); + d->updateLayout(); + } +} + +void QTextDocumentWithImageResources_1::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> QTextDocumentWithImageResources_1::errors; + +QDeclarative1TextPrivate::~QDeclarative1TextPrivate() +{ +} + +DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE); + +QString QDeclarative1TextPrivate::elideChar = QString(0x2026); + +QDeclarative1TextPrivate::QDeclarative1TextPrivate() +: color((QRgb)0), style(QDeclarative1Text::Normal), hAlign(QDeclarative1Text::AlignLeft), + vAlign(QDeclarative1Text::AlignTop), elideMode(QDeclarative1Text::ElideNone), + format(QDeclarative1Text::AutoText), wrapMode(QDeclarative1Text::NoWrap), lineHeight(1), + lineHeightMode(QDeclarative1Text::ProportionalHeight), lineCount(1), truncated(false), maximumLineCount(INT_MAX), + maximumLineCountValid(false), imageCacheDirty(true), updateOnComponentComplete(true), richText(false), singleline(false), + cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), hAlignImplicit(true), + rightToLeftText(false), layoutTextElided(false), naturalWidth(0), doc(0) +{ + cacheAllTextAsImage = enableImageCache(); + QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton; + QGraphicsItemPrivate::flags = QGraphicsItemPrivate::flags & ~QGraphicsItem::ItemHasNoContents; +} + +qreal QDeclarative1TextPrivate::implicitWidth() 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 + QDeclarative1TextPrivate *me = const_cast<QDeclarative1TextPrivate*>(this); + me->requireImplicitWidth = true; + me->updateSize(); + } + return mImplicitWidth; +} + +void QDeclarative1TextPrivate::updateLayout() +{ + Q_Q(QDeclarative1Text); + 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 != QDeclarative1Text::StyledText) { + QString tmp = text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + singleline = !tmp.contains(QChar::LineSeparator); + if (singleline && !maximumLineCountValid && elideMode != QDeclarative1Text::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; + QDeclarative1StyledText::parse(text, layout); + } + } + + updateSize(); +} + +void QDeclarative1TextPrivate::updateSize() +{ + Q_Q(QDeclarative1Text); + + 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(); + if (layedOutTextRect.size() != textRect.size()) + q->prepareGeometryChange(); + 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); + + QDeclarative1Text::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QDeclarative1Text::AlignLeft) + horizontalAlignment = QDeclarative1Text::AlignRight; + else if (horizontalAlignment == QDeclarative1Text::AlignRight) + horizontalAlignment = QDeclarative1Text::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 != QDeclarative1Text::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(); + if (dsize != layedOutTextRect.size()) { + q->prepareGeometryChange(); + layedOutTextRect = QRect(QPoint(0,0), dsize); + } + size = QSize(int(doc->idealWidth()),dsize.height()); + } + int yoff = 0; + + if (q->heightValid()) { + if (vAlign == QDeclarative1Text::AlignBottom) + yoff = dy; + else if (vAlign == QDeclarative1Text::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 QDeclarative1TextPrivate::layout QTextLayout in the constraints of the QDeclarative1Text. + + Returns the size of the final text. This can be used to position the text vertically (the text is + already absolutely positioned horizontally). +*/ +QRect QDeclarative1TextPrivate::setupTextLayout() +{ + // ### text layout handling should be profiled and optimized as needed + // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); + Q_Q(QDeclarative1Text); + 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; + + 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==QDeclarative1Text::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 == QDeclarative1Text::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 QDeclarative1TextPrivate::layout QTextLayout. + If \a drawStyle is true, the style color overrides all colors in the document. +*/ +QPixmap QDeclarative1TextPrivate::textLayoutImage(bool drawStyle) +{ + //do layout + 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 QDeclarative1TextPrivate::layout QTextLayout into \a painter at \a pos. If + \a drawStyle is true, the style color overrides all colors in the document. +*/ +void QDeclarative1TextPrivate::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 QDeclarative1TextPrivate::doc QTextDocument. + If \a drawStyle is true, the style color overrides all colors in the document. +*/ +QPixmap QDeclarative1TextPrivate::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 QDeclarative1TextPrivate::invalidateImageCache() +{ + Q_Q(QDeclarative1Text); + + if(cacheAllTextAsImage || style != QDeclarative1Text::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 QDeclarative1TextPrivate::checkImageCache() +{ + if (!imageCacheDirty) + return; + + if (text.isEmpty()) { + + imageCache = QPixmap(); + + } else { + + QPixmap textImage; + QPixmap styledImage; + + if (richText) { + textImage = textDocumentImage(false); + if (style != QDeclarative1Text::Normal) + styledImage = textDocumentImage(true); //### should use styleColor + } else { + textImage = textLayoutImage(false); + if (style != QDeclarative1Text::Normal) + styledImage = textLayoutImage(true); //### should use styleColor + } + + switch (style) { + case QDeclarative1Text::Outline: + imageCache = drawOutline(textImage, styledImage); + break; + case QDeclarative1Text::Sunken: + imageCache = drawOutline(textImage, styledImage, -1); + break; + case QDeclarative1Text::Raised: + imageCache = drawOutline(textImage, styledImage, 1); + break; + default: + imageCache = textImage; + break; + } + + } + + imageCacheDirty = false; +} + +/*! + Ensures the QDeclarative1TextPrivate::doc variable is set to a valid text document +*/ +void QDeclarative1TextPrivate::ensureDoc() +{ + if (!doc) { + Q_Q(QDeclarative1Text); + doc = new QTextDocumentWithImageResources_1(q); + doc->setDocumentMargin(0); + } +} + +/*! + Draw \a styleSource as an outline around \a source and return the new image. +*/ +QPixmap QDeclarative1TextPrivate::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 QDeclarative1TextPrivate::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; +} + +/*! + \qmlclass Text QDeclarative1Text + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The Text item allows you to add formatted text to a scene. + \inherits Item + + Text items can display both plain and rich text. For example, red text with + a specific font and size can be defined like this: + + \qml + Text { + text: "Hello World!" + font.family: "Helvetica" + font.pointSize: 24 + color: "red" + } + \endqml + + Rich text is defined using HTML-style markup: + + \qml + Text { + text: "<b>Hello</b> <i>World!</i>" + } + \endqml + + \image declarative-text.png + + If height and width are not explicitly set, Text will attempt to determine how + much room is needed and set it accordingly. Unless \l wrapMode is set, it will always + prefer width to height (all text will be placed on a single line). + + The \l elide property can alternatively be used to fit a single line of + plain text to a set width. + + Note that the \l{Supported HTML Subset} is limited. Also, if the text contains + HTML img tags that load remote images, the text is reloaded. + + Text provides read-only text. For editable text, see \l TextEdit. + + \sa {declarative/text/fonts}{Fonts example} +*/ +QDeclarative1Text::QDeclarative1Text(QDeclarativeItem *parent) + : QDeclarative1ImplicitSizeItem(*(new QDeclarative1TextPrivate), parent) +{ +} + +QDeclarative1Text::~QDeclarative1Text() +{ +} + +/*! + \qmlproperty bool Text::clip + This property holds whether the text is clipped. + + Note that if the text does not fit in the bounding rectangle it will be abruptly chopped. + + If you want to display potentially long text in a limited space, you probably want to use \c elide instead. +*/ + +/*! + \qmlproperty bool Text::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlsignal Text::onLinkActivated(string link) + + This handler is called when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. + + \snippet doc/src/snippets/declarative/text/onLinkActivated.qml 0 + + The example code will display the text + "The main website is at \l{http://qt.nokia.com}{Nokia Qt DF}." + + Clicking on the highlighted link will output + \tt{http://qt.nokia.com link activated} to the console. +*/ + +/*! + \qmlproperty string Text::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool Text::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration Text::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + Text { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool Text::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool Text::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool Text::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real Text::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int Text::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. + Use \c pointSize to set the size of the font in a device independent manner. +*/ + +/*! + \qmlproperty real Text::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real Text::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration Text::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + Text { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ +QFont QDeclarative1Text::font() const +{ + Q_D(const QDeclarative1Text); + return d->sourceFont; +} + +void QDeclarative1Text::setFont(const QFont &font) +{ + Q_D(QDeclarative1Text); + 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->updateLayout(); + + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty string Text::text + + The text to display. Text supports both plain and rich text strings. + + The item will try to automatically determine whether the text should + be treated as rich text. This determination is made using Qt::mightBeRichText(). +*/ +QString QDeclarative1Text::text() const +{ + Q_D(const QDeclarative1Text); + return d->text; +} + +void QDeclarative1Text::setText(const QString &n) +{ + Q_D(QDeclarative1Text); + 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); +} + + +/*! + \qmlproperty color Text::color + + The text color. + + An example of green text defined using hexadecimal notation: + \qml + Text { + color: "#00FF00" + text: "green text" + } + \endqml + + An example of steel blue text defined using an SVG color name: + \qml + Text { + color: "steelblue" + text: "blue text" + } + \endqml +*/ +QColor QDeclarative1Text::color() const +{ + Q_D(const QDeclarative1Text); + return d->color; +} + +void QDeclarative1Text::setColor(const QColor &color) +{ + Q_D(QDeclarative1Text); + if (d->color == color) + return; + + d->color = color; + d->invalidateImageCache(); + emit colorChanged(d->color); +} + +/*! + \qmlproperty enumeration Text::style + + Set an additional text style. + + Supported text styles are: + \list + \o Text.Normal - the default + \o Text.Outline + \o Text.Raised + \o Text.Sunken + \endlist + + \qml + Row { + Text { font.pointSize: 24; text: "Normal" } + Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" } + Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" } + Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" } + } + \endqml + + \image declarative-textstyle.png +*/ +QDeclarative1Text::TextStyle QDeclarative1Text::style() const +{ + Q_D(const QDeclarative1Text); + return d->style; +} + +void QDeclarative1Text::setStyle(QDeclarative1Text::TextStyle style) +{ + Q_D(QDeclarative1Text); + if (d->style == style) + return; + + // changing to/from Normal requires the boundingRect() to change + if (isComponentComplete() && (d->style == Normal || style == Normal)) + prepareGeometryChange(); + d->style = style; + d->invalidateImageCache(); + emit styleChanged(d->style); +} + +/*! + \qmlproperty color Text::styleColor + + Defines the secondary color used by text styles. + + \c styleColor is used as the outline color for outlined text, and as the + shadow color for raised or sunken text. If no style has been set, it is not + used at all. + + \qml + Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" } + \endqml + + \sa style + */ +QColor QDeclarative1Text::styleColor() const +{ + Q_D(const QDeclarative1Text); + return d->styleColor; +} + +void QDeclarative1Text::setStyleColor(const QColor &color) +{ + Q_D(QDeclarative1Text); + if (d->styleColor == color) + return; + + d->styleColor = color; + d->invalidateImageCache(); + emit styleColorChanged(d->styleColor); +} + + +/*! + \qmlproperty enumeration Text::horizontalAlignment + \qmlproperty enumeration Text::verticalAlignment + \qmlproperty enumeration Text::effectiveHorizontalAlignment + + Sets the horizontal and vertical alignment of the text within the Text items + width and height. By default, the text is vertically aligned to the top. Horizontal + alignment follows the natural alignment of the text, for example text that is read + from left to right will be aligned to the left. + + The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and + \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom + and \c Text.AlignVCenter. + + Note that for a single line of text, the size of the text is the area of the text. In this common case, + all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will + need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to + that of the parent. + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of Text, use the read-only property \c effectiveHorizontalAlignment. +*/ +QDeclarative1Text::HAlignment QDeclarative1Text::hAlign() const +{ + Q_D(const QDeclarative1Text); + return d->hAlign; +} + +void QDeclarative1Text::setHAlign(HAlignment align) +{ + Q_D(QDeclarative1Text); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) + d->updateLayout(); +} + +void QDeclarative1Text::resetHAlign() +{ + Q_D(QDeclarative1Text); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) + d->updateLayout(); +} + +QDeclarative1Text::HAlignment QDeclarative1Text::effectiveHAlign() const +{ + Q_D(const QDeclarative1Text); + QDeclarative1Text::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarative1Text::AlignLeft: + effectiveAlignment = QDeclarative1Text::AlignRight; + break; + case QDeclarative1Text::AlignRight: + effectiveAlignment = QDeclarative1Text::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarative1TextPrivate::setHAlign(QDeclarative1Text::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarative1Text); + if (hAlign != alignment || forceAlign) { + QDeclarative1Text::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + + emit q->horizontalAlignmentChanged(hAlign); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QDeclarative1TextPrivate::determineHorizontalAlignment() +{ + Q_Q(QDeclarative1Text); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QDeclarative1Text::AlignRight : QDeclarative1Text::AlignLeft); + } + return false; +} + +void QDeclarative1TextPrivate::mirrorChange() +{ + Q_Q(QDeclarative1Text); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarative1Text::AlignRight || hAlign == QDeclarative1Text::AlignLeft)) { + updateLayout(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +QTextDocument *QDeclarative1TextPrivate::textDocument() +{ + return doc; +} + +QDeclarative1Text::VAlignment QDeclarative1Text::vAlign() const +{ + Q_D(const QDeclarative1Text); + return d->vAlign; +} + +void QDeclarative1Text::setVAlign(VAlignment align) +{ + Q_D(QDeclarative1Text); + if (d->vAlign == align) + return; + + if (isComponentComplete()) + prepareGeometryChange(); + d->vAlign = align; + emit verticalAlignmentChanged(align); +} + +/*! + \qmlproperty enumeration Text::wrapMode + + Set this property to wrap the text to the Text item's width. The text will only + wrap if an explicit width has been set. wrapMode can be one of: + + \list + \o Text.NoWrap (default) - no wrapping will be performed. If the text contains insufficient newlines, then \l paintedWidth will exceed a set width. + \o Text.WordWrap - wrapping is done on word boundaries only. If a word is too long, \l paintedWidth will exceed a set width. + \o Text.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \o Text.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist +*/ +QDeclarative1Text::WrapMode QDeclarative1Text::wrapMode() const +{ + Q_D(const QDeclarative1Text); + return d->wrapMode; +} + +void QDeclarative1Text::setWrapMode(WrapMode mode) +{ + Q_D(QDeclarative1Text); + if (mode == d->wrapMode) + return; + + d->wrapMode = mode; + d->updateLayout(); + + emit wrapModeChanged(); +} + +/*! + \qmlproperty int Text::lineCount + \since Quick 1.1 + + Returns the number of lines visible in the text item. + + This property is not supported for rich text. + + \sa maximumLineCount +*/ +int QDeclarative1Text::lineCount() const +{ + Q_D(const QDeclarative1Text); + return d->lineCount; +} + +/*! + \qmlproperty bool Text::truncated + \since Quick 1.1 + + Returns true if the text has been truncated due to \l maximumLineCount + or \l elide. + + This property is not supported for rich text. + + \sa maximumLineCount, elide +*/ +bool QDeclarative1Text::truncated() const +{ + Q_D(const QDeclarative1Text); + return d->truncated; +} + +/*! + \qmlproperty int Text::maximumLineCount + \since Quick 1.1 + + Set this property to limit the number of lines that the text item will show. + If elide is set to Text.ElideRight, the text will be elided appropriately. + By default, this is the value of the largest possible integer. + + This property is not supported for rich text. + + \sa lineCount, elide +*/ +int QDeclarative1Text::maximumLineCount() const +{ + Q_D(const QDeclarative1Text); + return d->maximumLineCount; +} + +void QDeclarative1Text::setMaximumLineCount(int lines) +{ + Q_D(QDeclarative1Text); + + d->maximumLineCountValid = lines==INT_MAX ? false : true; + if (d->maximumLineCount != lines) { + d->maximumLineCount = lines; + d->updateLayout(); + emit maximumLineCountChanged(); + } +} + +void QDeclarative1Text::resetMaximumLineCount() +{ + Q_D(QDeclarative1Text); + setMaximumLineCount(INT_MAX); + d->elidePos = QPointF(); + if (d->truncated != false) { + d->truncated = false; + emit truncatedChanged(); + } +} + +/*! + \qmlproperty enumeration Text::textFormat + + The way the text property should be displayed. + + Supported text formats are: + + \list + \o Text.AutoText (default) + \o Text.PlainText + \o Text.RichText + \o Text.StyledText + \endlist + + If the text format is \c Text.AutoText the text element + will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + Text.StyledText is an optimized format supporting some basic text + styling markup, in the style of html 3.2: + + \code + <font size="4" color="#ff0000">font size and color</font> + <b>bold</b> + <i>italic</i> + <br> + > < & + \endcode + + \c Text.StyledText parser is strict, requiring tags to be correctly nested. + + \table + \row + \o + \qml +Column { + Text { + font.pointSize: 24 + text: "<b>Hello</b> <i>World!</i>" + } + Text { + font.pointSize: 24 + textFormat: Text.RichText + text: "<b>Hello</b> <i>World!</i>" + } + Text { + font.pointSize: 24 + textFormat: Text.PlainText + text: "<b>Hello</b> <i>World!</i>" + } +} + \endqml + \o \image declarative-textformat.png + \endtable +*/ +QDeclarative1Text::TextFormat QDeclarative1Text::textFormat() const +{ + Q_D(const QDeclarative1Text); + return d->format; +} + +void QDeclarative1Text::setTextFormat(TextFormat format) +{ + Q_D(QDeclarative1Text); + 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); +} + +/*! + \qmlproperty enumeration Text::elide + + Set this property to elide parts of the text fit to the Text item's width. + The text will only elide if an explicit width has been set. + + This property cannot be used with rich text. + + Eliding can be: + \list + \o Text.ElideNone - the default + \o Text.ElideLeft + \o Text.ElideMiddle + \o Text.ElideRight + \endlist + + If this property is set to Text.ElideRight, it can be used with multiline + text. The text will only elide if maximumLineCount has been set. + + If the text is a multi-length string, and the mode is not \c Text.ElideNone, + the first string that fits will be used, otherwise the last will be elided. + + Multi-length strings are ordered from longest to shortest, separated by the + Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}). +*/ +QDeclarative1Text::TextElideMode QDeclarative1Text::elideMode() const +{ + Q_D(const QDeclarative1Text); + return d->elideMode; +} + +void QDeclarative1Text::setElideMode(QDeclarative1Text::TextElideMode mode) +{ + Q_D(QDeclarative1Text); + if (mode == d->elideMode) + return; + + d->elideMode = mode; + d->updateLayout(); + + emit elideModeChanged(d->elideMode); +} + +/*! \internal */ +QRectF QDeclarative1Text::boundingRect() const +{ + Q_D(const QDeclarative1Text); + + 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 QDeclarative1Text::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QDeclarative1Text); + if ((!d->internalWidthUpdate && newGeometry.width() != oldGeometry.width()) + && (d->wrapMode != QDeclarative1Text::NoWrap + || d->elideMode != QDeclarative1Text::ElideNone + || d->hAlign != QDeclarative1Text::AlignLeft)) { + if ((d->singleline || d->maximumLineCountValid) && d->elideMode != QDeclarative1Text::ElideNone && widthValid()) { + // We need to re-elide + d->updateLayout(); + } else { + // We just need to re-layout + d->updateSize(); + } + } + + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +/*! + \qmlproperty real Text::paintedWidth + + Returns the width of the text, including width past the width + which is covered due to insufficient wrapping if WrapMode is set. +*/ +qreal QDeclarative1Text::paintedWidth() const +{ + Q_D(const QDeclarative1Text); + return d->paintedSize.width(); +} + +/*! + \qmlproperty real Text::paintedHeight + + Returns the height of the text, including height past the height + which is covered due to there being more text than fits in the set height. +*/ +qreal QDeclarative1Text::paintedHeight() const +{ + Q_D(const QDeclarative1Text); + return d->paintedSize.height(); +} + +/*! + \qmlproperty real Text::lineHeight + \since Quick 1.1 + + Sets the line height for the text. + The value can be in pixels or a multiplier depending on lineHeightMode. + + The default value is a multiplier of 1.0. + The line height must be a positive value. +*/ +qreal QDeclarative1Text::lineHeight() const +{ + Q_D(const QDeclarative1Text); + return d->lineHeight; +} + +void QDeclarative1Text::setLineHeight(qreal lineHeight) +{ + Q_D(QDeclarative1Text); + + if ((d->lineHeight == lineHeight) || (lineHeight < 0.0)) + return; + + d->lineHeight = lineHeight; + d->updateLayout(); + emit lineHeightChanged(lineHeight); +} + +/*! + \qmlproperty enumeration Text::lineHeightMode + + This property determines how the line height is specified. + The possible values are: + + \list + \o Text.ProportionalHeight (default) - this sets the spacing proportional to the + line (as a multiplier). For example, set to 2 for double spacing. + \o Text.FixedHeight - this sets the line height to a fixed line height (in pixels). + \endlist +*/ +QDeclarative1Text::LineHeightMode QDeclarative1Text::lineHeightMode() const +{ + Q_D(const QDeclarative1Text); + return d->lineHeightMode; +} + +void QDeclarative1Text::setLineHeightMode(LineHeightMode mode) +{ + Q_D(QDeclarative1Text); + 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 QDeclarative1Text::resourcesLoading() const +{ + Q_D(const QDeclarative1Text); + return d->doc ? d->doc->resourcesLoading() : 0; +} + +/*! \internal */ +void QDeclarative1Text::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) +{ + Q_D(QDeclarative1Text); + + if (d->cacheAllTextAsImage || d->style != Normal) { + d->checkImageCache(); + if (d->imageCache.isNull()) + return; + + bool oldAA = p->testRenderHint(QPainter::Antialiasing); + bool oldSmooth = p->testRenderHint(QPainter::SmoothPixmapTransform); + if (d->smooth) + p->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, d->smooth); + + QRect br = boundingRect().toRect(); + + bool needClip = clip() && (d->imageCache.width() > width() || + d->imageCache.height() > height()); + + if (needClip) + p->drawPixmap(0, 0, width(), height(), d->imageCache, -br.x(), -br.y(), width(), height()); + else + p->drawPixmap(br.x(), br.y(), d->imageCache); + + if (d->smooth) { + p->setRenderHint(QPainter::Antialiasing, oldAA); + p->setRenderHint(QPainter::SmoothPixmapTransform, oldSmooth); + } + } else { + QRectF bounds = boundingRect(); + + bool needClip = clip() && (d->layedOutTextRect.width() > width() || + d->layedOutTextRect.height() > height()); + + if (needClip) { + p->save(); + p->setClipRect(0, 0, width(), height(), Qt::IntersectClip); + } + if (d->richText) { + QAbstractTextDocumentLayout::PaintContext context; + context.palette.setColor(QPalette::Text, d->color); + p->translate(bounds.x(), bounds.y()); + d->doc->documentLayout()->draw(p, context); + p->translate(-bounds.x(), -bounds.y()); + } else { + d->drawTextLayout(p, QPointF(0, bounds.y()), false); + } + + if (needClip) { + p->restore(); + } + } +} + +/*! \internal */ +void QDeclarative1Text::componentComplete() +{ + Q_D(QDeclarative1Text); + QDeclarativeItem::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 QDeclarative1Text::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1Text); + + 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()) + QDeclarativeItem::mousePressEvent(event); + +} + +/*! \internal */ +void QDeclarative1Text::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1Text); + + // ### 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()) + QDeclarativeItem::mouseReleaseEvent(event); +} + + + +QT_END_NAMESPACE + +#include "qdeclarativetext.moc" diff --git a/src/qtquick1/graphicsitems/qdeclarativetext_p.h b/src/qtquick1/graphicsitems/qdeclarativetext_p.h new file mode 100644 index 0000000000..93261fbb24 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetext_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXT_H +#define QDECLARATIVETEXT_H + +#include <QtGui/qtextoption.h> +#include "qdeclarativeimplicitsizeitem_p.h" + +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) +class QDeclarative1TextPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Text : public QDeclarative1ImplicitSizeItem +{ + 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 REVISION 1) + 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 REVISION 1) + Q_PROPERTY(bool truncated READ truncated NOTIFY truncatedChanged REVISION 1) + Q_PROPERTY(int maximumLineCount READ maximumLineCount WRITE setMaximumLineCount NOTIFY maximumLineCountChanged RESET resetMaximumLineCount REVISION 1) + + 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 REVISION 1) + Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged REVISION 1) + +public: + QDeclarative1Text(QDeclarativeItem *parent=0); + ~QDeclarative1Text(); + + enum HAlignment { AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify }; // ### VERSIONING: Only in QtQuick 1.1 + 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); + + void paint(QPainter *, const QStyleOptionGraphicsItem *, QWidget *); + + 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(); + Q_REVISION(1) void lineCountChanged(); + Q_REVISION(1) void truncatedChanged(); + Q_REVISION(1) void maximumLineCountChanged(); + void textFormatChanged(TextFormat textFormat); + void elideModeChanged(TextElideMode mode); + void paintedSizeChanged(); + Q_REVISION(1) void lineHeightChanged(qreal lineHeight); + Q_REVISION(1) void lineHeightModeChanged(LineHeightMode mode); + Q_REVISION(1) void effectiveHorizontalAlignmentChanged(); + +protected: + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +private: + Q_DISABLE_COPY(QDeclarative1Text) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1Text) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Text) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativetext_p_p.h b/src/qtquick1/graphicsitems/qdeclarativetext_p_p.h new file mode 100644 index 0000000000..b6862d8e85 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetext_p_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXT_P_H +#define QDECLARATIVETEXT_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 "qdeclarativeitem.h" +#include "private/qdeclarativeimplicitsizeitem_p_p.h" +#include "QtQuick1/private/qdeclarativetextlayout_p.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <QtGui/qtextlayout.h> + +QT_BEGIN_NAMESPACE + +class QTextLayout; +class QTextDocumentWithImageResources_1; + +class Q_AUTOTEST_EXPORT QDeclarative1TextPrivate : public QDeclarative1ImplicitSizeItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Text) +public: + QDeclarative1TextPrivate(); + + ~QDeclarative1TextPrivate(); + + void updateSize(); + void updateLayout(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarative1Text::HAlignment, bool forceAlign = false); + void mirrorChange(); + QTextDocument *textDocument(); + + QString text; + QFont font; + QFont sourceFont; + QColor color; + QDeclarative1Text::TextStyle style; + QColor styleColor; + QString activeLink; + QDeclarative1Text::HAlignment hAlign; + QDeclarative1Text::VAlignment vAlign; + QDeclarative1Text::TextElideMode elideMode; + QDeclarative1Text::TextFormat format; + QDeclarative1Text::WrapMode wrapMode; + qreal lineHeight; + QDeclarative1Text::LineHeightMode lineHeightMode; + int lineCount; + bool truncated; + int maximumLineCount; + int maximumLineCountValid; + QPointF elidePos; + + static QString elideChar; + + void invalidateImageCache(); + void checkImageCache(); + QPixmap imageCache; + + bool imageCacheDirty:1; + bool updateOnComponentComplete:1; + bool richText:1; + bool singleline:1; + bool cacheAllTextAsImage:1; + bool internalWidthUpdate:1; + bool requireImplicitWidth:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; + bool layoutTextElided:1; + + QRect layedOutTextRect; + QSize paintedSize; + qreal naturalWidth; + virtual qreal implicitWidth() const; + void ensureDoc(); + QPixmap textDocumentImage(bool drawStyle); + QTextDocumentWithImageResources_1 *doc; + + QRect setupTextLayout(); + QPixmap textLayoutImage(bool drawStyle); + void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle); + QDeclarative1TextLayout layout; + + static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource); + static QPixmap drawOutline(const QPixmap &source, const QPixmap &styleSource, int yOffset); + + static inline QDeclarative1TextPrivate *get(QDeclarative1Text *t) { + return t->d_func(); + } +}; + +QT_END_NAMESPACE +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativetextedit.cpp b/src/qtquick1/graphicsitems/qdeclarativetextedit.cpp new file mode 100644 index 0000000000..1645bfd390 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextedit.cpp @@ -0,0 +1,1892 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetextedit_p.h" +#include "private/qdeclarativetextedit_p_p.h" + +#include "private/qdeclarativeevents_p_p.h" +#include <private/qdeclarativeglobal_p.h> +#include <qdeclarativeinfo.h> + +#include <QtCore/qmath.h> + +#include <private/qtextengine_p.h> +#include <QTextLayout> +#include <QTextLine> +#include <QTextDocument> +#include <QTextObject> +#include <QGraphicsSceneMouseEvent> +#include <QDebug> +#include <QPainter> + +#include <private/qtextcontrol_p.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass TextEdit QDeclarative1TextEdit + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The TextEdit item displays multiple lines of editable formatted text. + \inherits Item + + The TextEdit item displays a block of editable, formatted text. + + It can display both plain and rich text. For example: + + \qml +TextEdit { + width: 240 + text: "<b>Hello</b> <i>World!</i>" + font.family: "Helvetica" + font.pointSize: 20 + color: "blue" + focus: true +} + \endqml + + \image declarative-textedit.gif + + Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus. + + Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific + to a look-and-feel. For example, to add flickable scrolling that follows the cursor: + + \snippet snippets/declarative/texteditor.qml 0 + + A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible + scrollbar, or a scrollbar that fades in to show location, etc. + + Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can + be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely + from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord(). + + You can translate between cursor positions (characters from the start of the document) and pixel + points using positionAt() and positionToRectangle(). + + \sa Text, TextInput, {declarative/text/textselection}{Text Selection example} +*/ + +/*! + \qmlsignal TextEdit::onLinkActivated(string link) + \since Quick 1.1 + + This handler is called when the user clicks on a link embedded in the text. + The link must be in rich text or HTML format and the + \a link string provides access to the particular link. +*/ +QDeclarative1TextEdit::QDeclarative1TextEdit(QDeclarativeItem *parent) +: QDeclarative1ImplicitSizePaintedItem(*(new QDeclarative1TextEditPrivate), parent) +{ + Q_D(QDeclarative1TextEdit); + d->init(); +} + +QString QDeclarative1TextEdit::text() const +{ + Q_D(const QDeclarative1TextEdit); + +#ifndef QT_NO_TEXTHTMLPARSER + if (d->richText) + return d->document->toHtml(); + else +#endif + return d->document->toPlainText(); +} + +/*! + \qmlproperty string TextEdit::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool TextEdit::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration TextEdit::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + TextEdit { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool TextEdit::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool TextEdit::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool TextEdit::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real TextEdit::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int TextEdit::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. Use + \l{TextEdit::font.pointSize} to set the size of the font in a + device independent manner. +*/ + +/*! + \qmlproperty real TextEdit::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real TextEdit::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration TextEdit::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ + +/*! + \qmlproperty string TextEdit::text + + The text to display. If the text format is AutoText the text edit will + automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). +*/ +void QDeclarative1TextEdit::setText(const QString &text) +{ + Q_D(QDeclarative1TextEdit); + if (QDeclarative1TextEdit::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(); +} + +/*! + \qmlproperty enumeration TextEdit::textFormat + + The way the text property should be displayed. + + \list + \o TextEdit.AutoText + \o TextEdit.PlainText + \o TextEdit.RichText + \endlist + + The default is TextEdit.AutoText. If the text format is TextEdit.AutoText the text edit + will automatically determine whether the text should be treated as + rich text. This determination is made using Qt::mightBeRichText(). + + \table + \row + \o + \qml +Column { + TextEdit { + font.pointSize: 24 + text: "<b>Hello</b> <i>World!</i>" + } + TextEdit { + font.pointSize: 24 + textFormat: TextEdit.RichText + text: "<b>Hello</b> <i>World!</i>" + } + TextEdit { + font.pointSize: 24 + textFormat: TextEdit.PlainText + text: "<b>Hello</b> <i>World!</i>" + } +} + \endqml + \o \image declarative-textformat.png + \endtable +*/ +QDeclarative1TextEdit::TextFormat QDeclarative1TextEdit::textFormat() const +{ + Q_D(const QDeclarative1TextEdit); + return d->format; +} + +void QDeclarative1TextEdit::setTextFormat(TextFormat format) +{ + Q_D(QDeclarative1TextEdit); + 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 QDeclarative1TextEdit::font() const +{ + Q_D(const QDeclarative1TextEdit); + return d->sourceFont; +} + +void QDeclarative1TextEdit::setFont(const QFont &font) +{ + Q_D(QDeclarative1TextEdit); + 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) { + clearCache(); + d->document->setDefaultFont(d->font); + if(d->cursor){ + d->cursor->setHeight(QFontMetrics(d->font).height()); + moveCursorDelegate(); + } + updateSize(); + update(); + } + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty color TextEdit::color + + The text color. + + \qml + // green text using hexadecimal notation + TextEdit { color: "#00FF00" } + \endqml + + \qml + // steelblue text using SVG color name + TextEdit { color: "steelblue" } + \endqml +*/ +QColor QDeclarative1TextEdit::color() const +{ + Q_D(const QDeclarative1TextEdit); + return d->color; +} + +void QDeclarative1TextEdit::setColor(const QColor &color) +{ + Q_D(QDeclarative1TextEdit); + if (d->color == color) + return; + + clearCache(); + d->color = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Text, color); + d->control->setPalette(pal); + update(); + emit colorChanged(d->color); +} + +/*! + \qmlproperty color TextEdit::selectionColor + + The text highlight color, used behind selections. +*/ +QColor QDeclarative1TextEdit::selectionColor() const +{ + Q_D(const QDeclarative1TextEdit); + return d->selectionColor; +} + +void QDeclarative1TextEdit::setSelectionColor(const QColor &color) +{ + Q_D(QDeclarative1TextEdit); + if (d->selectionColor == color) + return; + + clearCache(); + d->selectionColor = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::Highlight, color); + d->control->setPalette(pal); + update(); + emit selectionColorChanged(d->selectionColor); +} + +/*! + \qmlproperty color TextEdit::selectedTextColor + + The selected text color, used in selections. +*/ +QColor QDeclarative1TextEdit::selectedTextColor() const +{ + Q_D(const QDeclarative1TextEdit); + return d->selectedTextColor; +} + +void QDeclarative1TextEdit::setSelectedTextColor(const QColor &color) +{ + Q_D(QDeclarative1TextEdit); + if (d->selectedTextColor == color) + return; + + clearCache(); + d->selectedTextColor = color; + QPalette pal = d->control->palette(); + pal.setColor(QPalette::HighlightedText, color); + d->control->setPalette(pal); + update(); + emit selectedTextColorChanged(d->selectedTextColor); +} + +/*! + \qmlproperty enumeration TextEdit::horizontalAlignment + \qmlproperty enumeration TextEdit::verticalAlignment + \qmlproperty enumeration TextEdit::effectiveHorizontalAlignment + + Sets the horizontal and vertical alignment of the text within the TextEdit item's + width and height. By default, the text alignment follows the natural alignment + of the text, for example text that is read from left to right will be aligned to + the left. + + Valid values for \c horizontalAlignment are: + \list + \o TextEdit.AlignLeft (default) + \o TextEdit.AlignRight + \o TextEdit.AlignHCenter + \o TextEdit.AlignJustify + \endlist + + Valid values for \c verticalAlignment are: + \list + \o TextEdit.AlignTop (default) + \o TextEdit.AlignBottom + \o TextEdit.AlignVCenter + \endlist + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextEdit, use the read-only property \c effectiveHorizontalAlignment. +*/ +QDeclarative1TextEdit::HAlignment QDeclarative1TextEdit::hAlign() const +{ + Q_D(const QDeclarative1TextEdit); + return d->hAlign; +} + +void QDeclarative1TextEdit::setHAlign(HAlignment align) +{ + Q_D(QDeclarative1TextEdit); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +void QDeclarative1TextEdit::resetHAlign() +{ + Q_D(QDeclarative1TextEdit); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +QDeclarative1TextEdit::HAlignment QDeclarative1TextEdit::effectiveHAlign() const +{ + Q_D(const QDeclarative1TextEdit); + QDeclarative1TextEdit::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarative1TextEdit::AlignLeft: + effectiveAlignment = QDeclarative1TextEdit::AlignRight; + break; + case QDeclarative1TextEdit::AlignRight: + effectiveAlignment = QDeclarative1TextEdit::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarative1TextEditPrivate::setHAlign(QDeclarative1TextEdit::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarative1TextEdit); + if (hAlign != alignment || forceAlign) { + QDeclarative1TextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QDeclarative1TextEditPrivate::determineHorizontalAlignment() +{ + Q_Q(QDeclarative1TextEdit); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QDeclarative1TextEdit::AlignRight : QDeclarative1TextEdit::AlignLeft); + } + return false; +} + +void QDeclarative1TextEditPrivate::mirrorChange() +{ + Q_Q(QDeclarative1TextEdit); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarative1TextEdit::AlignRight || hAlign == QDeclarative1TextEdit::AlignLeft)) { + updateDefaultTextOption(); + q->updateSize(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +QDeclarative1TextEdit::VAlignment QDeclarative1TextEdit::vAlign() const +{ + Q_D(const QDeclarative1TextEdit); + return d->vAlign; +} + +void QDeclarative1TextEdit::setVAlign(QDeclarative1TextEdit::VAlignment alignment) +{ + Q_D(QDeclarative1TextEdit); + if (alignment == d->vAlign) + return; + d->vAlign = alignment; + d->updateDefaultTextOption(); + updateSize(); + moveCursorDelegate(); + emit verticalAlignmentChanged(d->vAlign); +} + +/*! + \qmlproperty enumeration TextEdit::wrapMode + + Set this property to wrap the text to the TextEdit item's width. + The text will only wrap if an explicit width has been set. + + \list + \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width. + \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width. + \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word. + \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word. + \endlist + + The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap. +*/ +QDeclarative1TextEdit::WrapMode QDeclarative1TextEdit::wrapMode() const +{ + Q_D(const QDeclarative1TextEdit); + return d->wrapMode; +} + +void QDeclarative1TextEdit::setWrapMode(WrapMode mode) +{ + Q_D(QDeclarative1TextEdit); + if (mode == d->wrapMode) + return; + d->wrapMode = mode; + d->updateDefaultTextOption(); + updateSize(); + emit wrapModeChanged(); +} + +/*! + \qmlproperty int TextEdit::lineCount + \since Quick 1.1 + + Returns the total number of lines in the textEdit item. +*/ +int QDeclarative1TextEdit::lineCount() const +{ + Q_D(const QDeclarative1TextEdit); + return d->lineCount; +} + +/*! + \qmlproperty real TextEdit::paintedWidth + + Returns the width of the text, including the width past the width + which is covered due to insufficient wrapping if \l wrapMode is set. +*/ +qreal QDeclarative1TextEdit::paintedWidth() const +{ + Q_D(const QDeclarative1TextEdit); + return d->paintedSize.width(); +} + +/*! + \qmlproperty real TextEdit::paintedHeight + + Returns the height of the text, including the height past the height + that is covered if the text does not fit within the set height. +*/ +qreal QDeclarative1TextEdit::paintedHeight() const +{ + Q_D(const QDeclarative1TextEdit); + return d->paintedSize.height(); +} + +/*! + \qmlmethod rectangle TextEdit::positionToRectangle(position) + + Returns the rectangle at the given \a position in the text. The x, y, + and height properties correspond to the cursor that would describe + that position. +*/ +QRectF QDeclarative1TextEdit::positionToRectangle(int pos) const +{ + Q_D(const QDeclarative1TextEdit); + QTextCursor c(d->document); + c.setPosition(pos); + return d->control->cursorRect(c); + +} + +/*! + \qmlmethod int TextEdit::positionAt(int x, int y) + + Returns the text position closest to pixel position (\a x, \a y). + + Position 0 is before the first character, position 1 is after the first character + but before the second, and so on until position \l {text}.length, which is after all characters. +*/ +int QDeclarative1TextEdit::positionAt(int x, int y) const +{ + Q_D(const QDeclarative1TextEdit); + 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 QDeclarative1TextEdit::moveCursorSelection(int pos) +{ + //Note that this is the same as setCursorPosition but with the KeepAnchor flag set + Q_D(QDeclarative1TextEdit); + QTextCursor cursor = d->control->textCursor(); + if (cursor.position() == pos) + return; + cursor.setPosition(pos, QTextCursor::KeepAnchor); + d->control->setTextCursor(cursor); +} + +/*! + \qmlmethod void TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters) + \since Quick 1.1 + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextEdit.SelectCharacters. + + \list + \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified postion and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextEdit.SelectCharacters) + moveCursorSelection(7, TextEdit.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. +*/ +void QDeclarative1TextEdit::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QDeclarative1TextEdit); + 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); +} + +/*! + \qmlproperty bool TextEdit::cursorVisible + If true the text edit shows a cursor. + + This property is set and unset when the text edit gets active focus, but it can also + be set directly (useful, for example, if a KeyProxy might forward keys to it). +*/ +bool QDeclarative1TextEdit::isCursorVisible() const +{ + Q_D(const QDeclarative1TextEdit); + return d->cursorVisible; +} + +void QDeclarative1TextEdit::setCursorVisible(bool on) +{ + Q_D(QDeclarative1TextEdit); + 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); +} + +/*! + \qmlproperty int TextEdit::cursorPosition + The position of the cursor in the TextEdit. +*/ +int QDeclarative1TextEdit::cursorPosition() const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->textCursor().position(); +} + +void QDeclarative1TextEdit::setCursorPosition(int pos) +{ + Q_D(QDeclarative1TextEdit); + 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); +} + +/*! + \qmlproperty Component TextEdit::cursorDelegate + The delegate for the cursor in the TextEdit. + + If you set a cursorDelegate for a TextEdit, this delegate will be used for + drawing the cursor instead of the standard cursor. An instance of the + delegate will be created and managed by the text edit when a cursor is + needed, and the x and y properties of delegate instance will be set so as + to be one pixel before the top left of the current character. + + Note that the root item of the delegate component must be a QDeclarativeItem or + QDeclarativeItem derived item. +*/ +QDeclarativeComponent* QDeclarative1TextEdit::cursorDelegate() const +{ + Q_D(const QDeclarative1TextEdit); + return d->cursorComponent; +} + +void QDeclarative1TextEdit::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QDeclarative1TextEdit); + if(d->cursorComponent){ + if(d->cursor){ + d->control->setCursorWidth(-1); + dirtyCache(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 QDeclarative1TextEdit::loadCursorDelegate() +{ + Q_D(QDeclarative1TextEdit); + if(d->cursorComponent->isLoading()) + return; + d->cursor = qobject_cast<QDeclarativeItem*>(d->cursorComponent->create(qmlContext(this))); + if(d->cursor){ + d->control->setCursorWidth(0); + dirtyCache(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."; + } +} + +/*! + \qmlproperty int TextEdit::selectionStart + + The cursor position before the first character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionEnd, cursorPosition, selectedText +*/ +int QDeclarative1TextEdit::selectionStart() const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->textCursor().selectionStart(); +} + +/*! + \qmlproperty int TextEdit::selectionEnd + + The cursor position after the last character in the current selection. + + This property is read-only. To change the selection, use select(start,end), + selectAll(), or selectWord(). + + \sa selectionStart, cursorPosition, selectedText +*/ +int QDeclarative1TextEdit::selectionEnd() const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->textCursor().selectionEnd(); +} + +/*! + \qmlproperty string TextEdit::selectedText + + This read-only property provides the text currently selected in the + text edit. + + It is equivalent to the following snippet, but is faster and easier + to use. + \code + //myTextEdit is the id of the TextEdit + myTextEdit.text.toString().substring(myTextEdit.selectionStart, + myTextEdit.selectionEnd); + \endcode +*/ +QString QDeclarative1TextEdit::selectedText() const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->textCursor().selectedText(); +} + +/*! + \qmlproperty bool TextEdit::activeFocusOnPress + + Whether the TextEdit should gain active focus on a mouse press. By default this is + set to true. +*/ +bool QDeclarative1TextEdit::focusOnPress() const +{ + Q_D(const QDeclarative1TextEdit); + return d->focusOnPress; +} + +void QDeclarative1TextEdit::setFocusOnPress(bool on) +{ + Q_D(QDeclarative1TextEdit); + if (d->focusOnPress == on) + return; + d->focusOnPress = on; + emit activeFocusOnPressChanged(d->focusOnPress); +} + +/*! + \qmlproperty bool TextEdit::persistentSelection + + Whether the TextEdit should keep the selection visible when it loses active focus to another + item in the scene. By default this is set to true; +*/ +bool QDeclarative1TextEdit::persistentSelection() const +{ + Q_D(const QDeclarative1TextEdit); + return d->persistentSelection; +} + +void QDeclarative1TextEdit::setPersistentSelection(bool on) +{ + Q_D(QDeclarative1TextEdit); + if (d->persistentSelection == on) + return; + d->persistentSelection = on; + emit persistentSelectionChanged(d->persistentSelection); +} + +/* + \qmlproperty real TextEdit::textMargin + + The margin, in pixels, around the text in the TextEdit. +*/ +qreal QDeclarative1TextEdit::textMargin() const +{ + Q_D(const QDeclarative1TextEdit); + return d->textMargin; +} + +void QDeclarative1TextEdit::setTextMargin(qreal margin) +{ + Q_D(QDeclarative1TextEdit); + if (d->textMargin == margin) + return; + d->textMargin = margin; + d->document->setDocumentMargin(d->textMargin); + emit textMarginChanged(d->textMargin); +} + +void QDeclarative1TextEdit::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.width() != oldGeometry.width()) + updateSize(); + QDeclarative1PaintedItem::geometryChanged(newGeometry, oldGeometry); +} + +/*! + Ensures any delayed caching or data loading the class + needs to performed is complete. +*/ +void QDeclarative1TextEdit::componentComplete() +{ + Q_D(QDeclarative1TextEdit); + QDeclarative1PaintedItem::componentComplete(); + if (d->dirty) { + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); + updateSize(); + d->dirty = false; + } +} + +/*! + \qmlproperty bool TextEdit::selectByMouse + + Defaults to false. + + If true, the user can use the mouse to select text in some + platform-specific way. Note that for some platforms this may + not be an appropriate interaction (eg. may conflict with how + the text needs to behave inside a Flickable. +*/ +bool QDeclarative1TextEdit::selectByMouse() const +{ + Q_D(const QDeclarative1TextEdit); + return d->selectByMouse; +} + +void QDeclarative1TextEdit::setSelectByMouse(bool on) +{ + Q_D(QDeclarative1TextEdit); + 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); + } +} + + +/*! + \qmlproperty enum TextEdit::mouseSelectionMode + \since Quick 1.1 + + Specifies how text should be selected using a mouse. + + \list + \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default) + \o TextEdit.SelectWords - The selection is updated with whole words. + \endlist + + This property only applies when \l selectByMouse is true. +*/ + +QDeclarative1TextEdit::SelectionMode QDeclarative1TextEdit::mouseSelectionMode() const +{ + Q_D(const QDeclarative1TextEdit); + return d->mouseSelectionMode; +} + +void QDeclarative1TextEdit::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QDeclarative1TextEdit); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + d->control->setWordSelectionEnabled(mode == SelectWords); + emit mouseSelectionModeChanged(mode); + } +} + +/*! + \qmlproperty bool TextEdit::readOnly + + Whether the user can interact with the TextEdit item. If this + property is set to true the text cannot be edited by user interaction. + + By default this property is false. +*/ +void QDeclarative1TextEdit::setReadOnly(bool r) +{ + Q_D(QDeclarative1TextEdit); + if (r == isReadOnly()) + return; + + setFlag(QGraphicsItem::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 QDeclarative1TextEdit::isReadOnly() const +{ + Q_D(const QDeclarative1TextEdit); + return !(d->control->textInteractionFlags() & Qt::TextEditable); +} + +/*! + Sets how the text edit should interact with user input to the given + \a flags. +*/ +void QDeclarative1TextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags) +{ + Q_D(QDeclarative1TextEdit); + d->control->setTextInteractionFlags(flags); +} + +/*! + Returns the flags specifying how the text edit should interact + with user input. +*/ +Qt::TextInteractionFlags QDeclarative1TextEdit::textInteractionFlags() const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->textInteractionFlags(); +} + +/*! + \qmlproperty rectangle TextEdit::cursorRectangle + + The rectangle where the text cursor is rendered + within the text edit. Read-only. +*/ +QRect QDeclarative1TextEdit::cursorRectangle() const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->cursorRect().toRect().translated(0,d->yoff); +} + + +/*! +\overload +Handles the given \a event. +*/ +bool QDeclarative1TextEdit::event(QEvent *event) +{ + Q_D(QDeclarative1TextEdit); + if (event->type() == QEvent::ShortcutOverride) { + d->control->processEvent(event, QPointF(0, -d->yoff)); + return event->isAccepted(); + } + return QDeclarative1PaintedItem::event(event); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QDeclarative1TextEdit::keyPressEvent(QKeyEvent *event) +{ + Q_D(QDeclarative1TextEdit); + keyPressPreHandler(event); + if (!event->isAccepted()) + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarative1PaintedItem::keyPressEvent(event); +} + +/*! +\overload +Handles the given key \a event. +*/ +void QDeclarative1TextEdit::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QDeclarative1TextEdit); + keyReleasePreHandler(event); + if (!event->isAccepted()) + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarative1PaintedItem::keyReleaseEvent(event); +} + +void QDeclarative1TextEditPrivate::focusChanged(bool hasFocus) +{ + Q_Q(QDeclarative1TextEdit); + q->setCursorVisible(hasFocus && scene && scene->hasFocus()); + QDeclarativeItemPrivate::focusChanged(hasFocus); +} + +/*! + \qmlmethod void TextEdit::deselect() + \since Quick 1.1 + + Removes active text selection. +*/ +void QDeclarative1TextEdit::deselect() +{ + Q_D(QDeclarative1TextEdit); + QTextCursor c = d->control->textCursor(); + c.clearSelection(); + d->control->setTextCursor(c); +} + +/*! + \qmlmethod void TextEdit::selectAll() + + Causes all text to be selected. +*/ +void QDeclarative1TextEdit::selectAll() +{ + Q_D(QDeclarative1TextEdit); + d->control->selectAll(); +} + +/*! + \qmlmethod void TextEdit::selectWord() + + Causes the word closest to the current cursor position to be selected. +*/ +void QDeclarative1TextEdit::selectWord() +{ + Q_D(QDeclarative1TextEdit); + QTextCursor c = d->control->textCursor(); + c.select(QTextCursor::WordUnderCursor); + d->control->setTextCursor(c); +} + +/*! + \qmlmethod void TextEdit::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd +*/ +void QDeclarative1TextEdit::select(int start, int end) +{ + Q_D(QDeclarative1TextEdit); + 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(); +} + +/*! + \qmlmethod void TextEdit::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. +*/ +bool QDeclarative1TextEdit::isRightToLeft(int start, int end) +{ + Q_D(QDeclarative1TextEdit); + 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 +/*! + \qmlmethod TextEdit::cut() + + Moves the currently selected text to the system clipboard. +*/ +void QDeclarative1TextEdit::cut() +{ + Q_D(QDeclarative1TextEdit); + d->control->cut(); +} + +/*! + \qmlmethod TextEdit::copy() + + Copies the currently selected text to the system clipboard. +*/ +void QDeclarative1TextEdit::copy() +{ + Q_D(QDeclarative1TextEdit); + d->control->copy(); +} + +/*! + \qmlmethod TextEdit::paste() + + Replaces the currently selected text by the contents of the system clipboard. +*/ +void QDeclarative1TextEdit::paste() +{ + Q_D(QDeclarative1TextEdit); + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarative1TextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextEdit); + 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()) + QDeclarative1PaintedItem::mousePressEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarative1TextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!d->showInputPanelOnFocus) { // input panel on click + if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) { + if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); + } + } + } + } + d->clickCausedFocus = false; + + if (!event->isAccepted()) + QDeclarative1PaintedItem::mouseReleaseEvent(event); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarative1TextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextEdit); + + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarative1PaintedItem::mouseDoubleClickEvent(event); + +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarative1TextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextEdit); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (!event->isAccepted()) + QDeclarative1PaintedItem::mouseMoveEvent(event); +} + +/*! +\overload +Handles the given input method \a event. +*/ +void QDeclarative1TextEdit::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QDeclarative1TextEdit); + const bool wasComposing = isInputMethodComposing(); + d->control->processEvent(event, QPointF(0, -d->yoff)); + if (wasComposing != isInputMethodComposing()) + emit inputMethodComposingChanged(); +} + +/*! +\overload +Returns the value of the given \a property. +*/ +QVariant QDeclarative1TextEdit::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QDeclarative1TextEdit); + return d->control->inputMethodQuery(property); +} + +/*! +Draws the contents of the text edit using the given \a painter within +the given \a bounds. +*/ +void QDeclarative1TextEdit::drawContents(QPainter *painter, const QRect &bounds) +{ + Q_D(QDeclarative1TextEdit); + + 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 QDeclarative1TextEdit::updateImgCache(const QRectF &rf) +{ + Q_D(const QDeclarative1TextEdit); + 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); + } + } + dirtyCache(r); + emit update(); +} + +/*! + \qmlproperty bool TextEdit::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty bool TextEdit::canPaste + \since QtQuick 1.1 + + Returns true if the TextEdit is writable and the content of the clipboard is + suitable for pasting into the TextEdit. +*/ +bool QDeclarative1TextEdit::canPaste() const +{ + Q_D(const QDeclarative1TextEdit); + return d->canPaste; +} + +/*! + \qmlproperty bool TextEdit::inputMethodComposing + + \since QtQuick 1.1 + + This property holds whether the TextEdit has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextEdit to edit or commit the partial text. This property can be used + to determine when to disable events handlers that may interfere with the + correct operation of an input method. +*/ +bool QDeclarative1TextEdit::isInputMethodComposing() const +{ + Q_D(const QDeclarative1TextEdit); + if (QTextLayout *layout = d->control->textCursor().block().layout()) + return layout->preeditAreaText().length() > 0; + return false; +} + +void QDeclarative1TextEditPrivate::init() +{ + Q_Q(QDeclarative1TextEdit); + + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QGraphicsItem::ItemHasNoContents, false); + q->setFlag(QGraphicsItem::ItemAcceptsInputMethod); + + control = new QTextControl(q); + control->setIgnoreUnusedNavigationEvents(true); + control->setTextInteractionFlags(Qt::TextInteractionFlags(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 QDeclarative1TextEdit::q_textChanged() +{ + Q_D(QDeclarative1TextEdit); + d->text = text(); + d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft(); + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); + updateSize(); + updateTotalLines(); + emit textChanged(d->text); +} + +void QDeclarative1TextEdit::moveCursorDelegate() +{ + Q_D(QDeclarative1TextEdit); + updateMicroFocus(); + emit cursorRectangleChanged(); + if(!d->cursor) + return; + QRectF cursorRect = cursorRectangle(); + d->cursor->setX(cursorRect.x()); + d->cursor->setY(cursorRect.y()); +} + +void QDeclarative1TextEditPrivate::updateSelection() +{ + Q_Q(QDeclarative1TextEdit); + 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 QDeclarative1TextEdit::updateSelectionMarkers() +{ + Q_D(QDeclarative1TextEdit); + 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 QDeclarative1TextEdit::boundingRect() const +{ + Q_D(const QDeclarative1TextEdit); + QRectF r = QDeclarative1PaintedItem::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 QDeclarative1TextEditPrivate::implicitWidth() const +{ + Q_Q(const QDeclarative1TextEdit); + 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<QDeclarative1TextEditPrivate*>(this)->requireImplicitWidth = true; + const_cast<QDeclarative1TextEdit*>(q)->updateSize(); + } + return mImplicitWidth; +} + +//### 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 QDeclarative1TextEdit::updateSize() +{ + Q_D(QDeclarative1TextEdit); + if (isComponentComplete()) { + qreal naturalWidth = d->mImplicitWidth; + // ### 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) { + prepareGeometryChange(); + 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; + } + emit update(); +} + +void QDeclarative1TextEdit::updateTotalLines() +{ + Q_D(QDeclarative1TextEdit); + + 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 QDeclarative1TextEditPrivate::updateDefaultTextOption() +{ + Q_Q(QDeclarative1TextEdit); + QTextOption opt = document->defaultTextOption(); + int oldAlignment = opt.alignment(); + + QDeclarative1TextEdit::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QDeclarative1TextEdit::AlignLeft) + horizontalAlignment = QDeclarative1TextEdit::AlignRight; + else if (horizontalAlignment == QDeclarative1TextEdit::AlignRight) + horizontalAlignment = QDeclarative1TextEdit::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); +} + + +/*! + \qmlmethod void TextEdit::openSoftwareInputPanel() + + Opens software input panels like virtual keyboards for typing, useful for + customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms + the panels are automatically opened when TextEdit element gains active focus. Input panels are + always closed if no editor has active focus. + + You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \code + import QtQuick 1.0 + TextEdit { + id: textEdit + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textEdit.activeFocus) { + textEdit.forceActiveFocus(); + textEdit.openSoftwareInputPanel(); + } else { + textEdit.focus = false; + } + } + onPressAndHold: textEdit.closeSoftwareInputPanel(); + } + } + \endcode +*/ +void QDeclarative1TextEdit::openSoftwareInputPanel() +{ + QEvent event(QEvent::RequestSoftwareInputPanel); + if (qApp) { + if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +/*! + \qmlmethod void TextEdit::closeSoftwareInputPanel() + + Closes a software input panel like a virtual keyboard shown on the screen, useful + for customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms + the panels are automatically opened when TextEdit element gains active focus. Input panels are + always closed if no editor has active focus. + + You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \code + import QtQuick 1.0 + TextEdit { + id: textEdit + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textEdit.activeFocus) { + textEdit.forceActiveFocus(); + textEdit.openSoftwareInputPanel(); + } else { + textEdit.focus = false; + } + } + onPressAndHold: textEdit.closeSoftwareInputPanel(); + } + } + \endcode +*/ +void QDeclarative1TextEdit::closeSoftwareInputPanel() +{ + QEvent event(QEvent::CloseSoftwareInputPanel); + if (qApp) { + if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +void QDeclarative1TextEdit::focusInEvent(QFocusEvent *event) +{ + Q_D(const QDeclarative1TextEdit); + if (d->showInputPanelOnFocus) { + if (d->focusOnPress && !isReadOnly()) { + openSoftwareInputPanel(); + } + } + QDeclarative1PaintedItem::focusInEvent(event); +} + +void QDeclarative1TextEdit::q_canPasteChanged() +{ + Q_D(QDeclarative1TextEdit); + bool old = d->canPaste; + d->canPaste = d->control->canPaste(); + if(old!=d->canPaste) + emit canPasteChanged(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativetextedit_p.h b/src/qtquick1/graphicsitems/qdeclarativetextedit_p.h new file mode 100644 index 0000000000..712d2f079f --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextedit_p.h @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTEDIT_H +#define QDECLARATIVETEXTEDIT_H + +#include "private/qdeclarativetext_p.h" +#include "private/qdeclarativeimplicitsizeitem_p.h" + +#include <QtGui/qtextdocument.h> +#include <QtGui/qtextoption.h> +#include <QtGui/qtextcursor.h> +#include <QtGui/qtextformat.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + + +class QDeclarative1TextEditPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1TextEdit : public QDeclarative1ImplicitSizePaintedItem +{ + 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 REVISION 1) + 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 REVISION 1) + 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 REVISION 1) + Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged REVISION 1) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged REVISION 1) + +public: + QDeclarative1TextEdit(QDeclarativeItem *parent=0); + + enum HAlignment { + AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter, + AlignJustify = Qt::AlignJustify // ### VERSIONING: Only in QtQuick 1.1 + }; + + 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 Q_REVISION(1) 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); + Q_REVISION(1) void mouseSelectionModeChanged(SelectionMode mode); + Q_REVISION(1) void linkActivated(const QString &link); + Q_REVISION(1) void canPasteChanged(); + Q_REVISION(1) void inputMethodComposingChanged(); + Q_REVISION(1) void effectiveHorizontalAlignmentChanged(); + +public Q_SLOTS: + void selectAll(); + void selectWord(); + void select(int start, int end); + Q_REVISION(1) void deselect(); + Q_REVISION(1) 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); + + void drawContents(QPainter *, const QRect &); +private: + Q_DISABLE_COPY(QDeclarative1TextEdit) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1TextEdit) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1TextEdit) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativetextedit_p_p.h b/src/qtquick1/graphicsitems/qdeclarativetextedit_p_p.h new file mode 100644 index 0000000000..b370d5aeaa --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextedit_p_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTEDIT_P_H +#define QDECLARATIVETEXTEDIT_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 "qdeclarativeitem.h" +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +#include <QtDeclarative/qdeclarative.h> + +QT_BEGIN_NAMESPACE + +class QTextLayout; +class QTextDocument; +class QTextControl; +class QDeclarative1TextEditPrivate : public QDeclarative1ImplicitSizePaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1TextEdit) + +public: + QDeclarative1TextEditPrivate() + : color("black"), hAlign(QDeclarative1TextEdit::AlignLeft), vAlign(QDeclarative1TextEdit::AlignTop), + imgDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true), + showInputPanelOnFocus(true), clickCausedFocus(false), persistentSelection(true), requireImplicitWidth(false), + hAlignImplicit(true), rightToLeftText(false), textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), + cursorComponent(0), cursor(0), format(QDeclarative1TextEdit::AutoText), document(0), wrapMode(QDeclarative1TextEdit::NoWrap), + mouseSelectionMode(QDeclarative1TextEdit::SelectCharacters), selectByMouse(false), canPaste(false), + 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(QDeclarative1TextEdit::HAlignment, bool forceAlign = false); + void mirrorChange(); + qreal implicitWidth() const; + void focusChanged(bool); + + QString text; + QFont font; + QFont sourceFont; + QColor color; + QColor selectionColor; + QColor selectedTextColor; + QString style; + QColor styleColor; + QPixmap imgCache; + QPixmap imgStyleCache; + QDeclarative1TextEdit::HAlignment hAlign; + QDeclarative1TextEdit::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 hAlignImplicit:1; + bool rightToLeftText:1; + qreal textMargin; + int lastSelectionStart; + int lastSelectionEnd; + QDeclarativeComponent* cursorComponent; + QDeclarativeItem* cursor; + QDeclarative1TextEdit::TextFormat format; + QTextDocument *document; + QTextControl *control; + QDeclarative1TextEdit::WrapMode wrapMode; + QDeclarative1TextEdit::SelectionMode mouseSelectionMode; + int lineCount; + bool selectByMouse; + bool canPaste; + int yoff; + QSize paintedSize; +}; + +QT_END_NAMESPACE +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativetextinput.cpp b/src/qtquick1/graphicsitems/qdeclarativetextinput.cpp new file mode 100644 index 0000000000..53be1d3dfa --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextinput.cpp @@ -0,0 +1,2016 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetextinput_p.h" +#include "private/qdeclarativetextinput_p_p.h" + +#include <private/qdeclarativeglobal_p.h> +#include <qdeclarativeinfo.h> + +#include <QValidator> +#include <QTextCursor> +#include <QApplication> +#include <QFontMetrics> +#include <QPainter> +#include <QTextBoundaryFinder> +#include <QInputContext> +#include <qstyle.h> + +#ifndef QT_NO_LINEEDIT + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass TextInput QDeclarative1TextInput + \ingroup qml-basic-visual-elements + \since 4.7 + \brief The TextInput item displays an editable line of text. + \inherits Item + + The TextInput element displays a single line of editable plain text. + + TextInput is used to accept a line of text input. Input constraints + can be placed on a TextInput item (for example, through a \l validator or \l inputMask), + and setting \l echoMode to an appropriate value enables TextInput to be used for + a password input field. + + On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled. + If you want such bindings (on any platform), you will need to construct them in QML. + + \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example} +*/ +QDeclarative1TextInput::QDeclarative1TextInput(QDeclarativeItem* parent) + : QDeclarative1ImplicitSizePaintedItem(*(new QDeclarative1TextInputPrivate), parent) +{ + Q_D(QDeclarative1TextInput); + d->init(); +} + +QDeclarative1TextInput::~QDeclarative1TextInput() +{ +} + +/*! + \qmlproperty string TextInput::text + + The text in the TextInput. +*/ + +QString QDeclarative1TextInput::text() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->text(); +} + +void QDeclarative1TextInput::setText(const QString &s) +{ + Q_D(QDeclarative1TextInput); + if(s == text()) + return; + d->control->setText(s); +} + +/*! + \qmlproperty string TextInput::font.family + + Sets the family name of the font. + + The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]". + If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen. + If the family isn't available a family will be set using the font matching algorithm. +*/ + +/*! + \qmlproperty bool TextInput::font.bold + + Sets whether the font weight is bold. +*/ + +/*! + \qmlproperty enumeration TextInput::font.weight + + Sets the font's weight. + + The weight can be one of: + \list + \o Font.Light + \o Font.Normal - the default + \o Font.DemiBold + \o Font.Bold + \o Font.Black + \endlist + + \qml + TextInput { text: "Hello"; font.weight: Font.DemiBold } + \endqml +*/ + +/*! + \qmlproperty bool TextInput::font.italic + + Sets whether the font has an italic style. +*/ + +/*! + \qmlproperty bool TextInput::font.underline + + Sets whether the text is underlined. +*/ + +/*! + \qmlproperty bool TextInput::font.strikeout + + Sets whether the font has a strikeout style. +*/ + +/*! + \qmlproperty real TextInput::font.pointSize + + Sets the font size in points. The point size must be greater than zero. +*/ + +/*! + \qmlproperty int TextInput::font.pixelSize + + Sets the font size in pixels. + + Using this function makes the font device dependent. + Use \c pointSize to set the size of the font in a device independent manner. +*/ + +/*! + \qmlproperty real TextInput::font.letterSpacing + + Sets the letter spacing for the font. + + Letter spacing changes the default spacing between individual letters in the font. + A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing. +*/ + +/*! + \qmlproperty real TextInput::font.wordSpacing + + Sets the word spacing for the font. + + Word spacing changes the default spacing between individual words. + A positive value increases the word spacing by a corresponding amount of pixels, + while a negative value decreases the inter-word spacing accordingly. +*/ + +/*! + \qmlproperty enumeration TextInput::font.capitalization + + Sets the capitalization for the text. + + \list + \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied. + \o Font.AllUppercase - This alters the text to be rendered in all uppercase type. + \o Font.AllLowercase - This alters the text to be rendered in all lowercase type. + \o Font.SmallCaps - This alters the text to be rendered in small-caps type. + \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character. + \endlist + + \qml + TextInput { text: "Hello"; font.capitalization: Font.AllLowercase } + \endqml +*/ + +QFont QDeclarative1TextInput::font() const +{ + Q_D(const QDeclarative1TextInput); + return d->sourceFont; +} + +void QDeclarative1TextInput::setFont(const QFont &font) +{ + Q_D(QDeclarative1TextInput); + 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); + updateSize(); + updateCursorRectangle(); + if(d->cursorItem){ + d->cursorItem->setHeight(QFontMetrics(d->font).height()); + } + } + emit fontChanged(d->sourceFont); +} + +/*! + \qmlproperty color TextInput::color + + The text color. +*/ +QColor QDeclarative1TextInput::color() const +{ + Q_D(const QDeclarative1TextInput); + return d->color; +} + +void QDeclarative1TextInput::setColor(const QColor &c) +{ + Q_D(QDeclarative1TextInput); + if (c != d->color) { + d->color = c; + clearCache(); + update(); + emit colorChanged(c); + } +} + + +/*! + \qmlproperty color TextInput::selectionColor + + The text highlight color, used behind selections. +*/ +QColor QDeclarative1TextInput::selectionColor() const +{ + Q_D(const QDeclarative1TextInput); + return d->selectionColor; +} + +void QDeclarative1TextInput::setSelectionColor(const QColor &color) +{ + Q_D(QDeclarative1TextInput); + 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()) { + clearCache(); + update(); + } + emit selectionColorChanged(color); +} + +/*! + \qmlproperty color TextInput::selectedTextColor + + The highlighted text color, used in selections. +*/ +QColor QDeclarative1TextInput::selectedTextColor() const +{ + Q_D(const QDeclarative1TextInput); + return d->selectedTextColor; +} + +void QDeclarative1TextInput::setSelectedTextColor(const QColor &color) +{ + Q_D(QDeclarative1TextInput); + 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()) { + clearCache(); + update(); + } + emit selectedTextColorChanged(color); +} + +/*! + \qmlproperty enumeration TextInput::horizontalAlignment + \qmlproperty enumeration TextInput::effectiveHorizontalAlignment + + Sets the horizontal alignment of the text within the TextInput item's + width and height. By default, the text alignment follows the natural alignment + of the text, for example text that is read from left to right will be aligned to + the left. + + TextInput does not have vertical alignment, as the natural height is + exactly the height of the single line of text. If you set the height + manually to something larger, TextInput will always be top aligned + vertically. You can use anchors to align it however you want within + another item. + + The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and + \c TextInput.AlignHCenter. + + When using the attached property LayoutMirroring::enabled to mirror application + layouts, the horizontal alignment of text will also be mirrored. However, the property + \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment + of TextInput, use the read-only property \c effectiveHorizontalAlignment. +*/ +QDeclarative1TextInput::HAlignment QDeclarative1TextInput::hAlign() const +{ + Q_D(const QDeclarative1TextInput); + return d->hAlign; +} + +void QDeclarative1TextInput::setHAlign(HAlignment align) +{ + Q_D(QDeclarative1TextInput); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + updateCursorRectangle(); + } +} + +void QDeclarative1TextInput::resetHAlign() +{ + Q_D(QDeclarative1TextInput); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + updateCursorRectangle(); + } +} + +QDeclarative1TextInput::HAlignment QDeclarative1TextInput::effectiveHAlign() const +{ + Q_D(const QDeclarative1TextInput); + QDeclarative1TextInput::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarative1TextInput::AlignLeft: + effectiveAlignment = QDeclarative1TextInput::AlignRight; + break; + case QDeclarative1TextInput::AlignRight: + effectiveAlignment = QDeclarative1TextInput::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarative1TextInputPrivate::setHAlign(QDeclarative1TextInput::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarative1TextInput); + if ((hAlign != alignment || forceAlign) && alignment <= QDeclarative1TextInput::AlignHCenter) { // justify not supported + QDeclarative1TextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QDeclarative1TextInputPrivate::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 ? QDeclarative1TextInput::AlignRight : QDeclarative1TextInput::AlignLeft); + } + return false; +} + +void QDeclarative1TextInputPrivate::mirrorChange() +{ + Q_Q(QDeclarative1TextInput); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarative1TextInput::AlignRight || hAlign == QDeclarative1TextInput::AlignLeft)) { + q->updateCursorRectangle(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +/*! + \qmlproperty bool TextInput::readOnly + + Sets whether user input can modify the contents of the TextInput. + + If readOnly is set to true, then user input will not affect the text + property. Any bindings or attempts to set the text property will still + work. +*/ + +bool QDeclarative1TextInput::isReadOnly() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->isReadOnly(); +} + +void QDeclarative1TextInput::setReadOnly(bool ro) +{ + Q_D(QDeclarative1TextInput); + if (d->control->isReadOnly() == ro) + return; + + setFlag(QGraphicsItem::ItemAcceptsInputMethod, !ro); + d->control->setReadOnly(ro); + + emit readOnlyChanged(ro); +} + +/*! + \qmlproperty int TextInput::maximumLength + The maximum permitted length of the text in the TextInput. + + If the text is too long, it is truncated at the limit. + + By default, this property contains a value of 32767. +*/ +int QDeclarative1TextInput::maxLength() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->maxLength(); +} + +void QDeclarative1TextInput::setMaxLength(int ml) +{ + Q_D(QDeclarative1TextInput); + if (d->control->maxLength() == ml) + return; + + d->control->setMaxLength(ml); + + emit maximumLengthChanged(ml); +} + +/*! + \qmlproperty bool TextInput::cursorVisible + Set to true when the TextInput shows a cursor. + + This property is set and unset when the TextInput gets active focus, so that other + properties can be bound to whether the cursor is currently showing. As it + gets set and unset automatically, when you set the value yourself you must + keep in mind that your value may be overwritten. + + It can be set directly in script, for example if a KeyProxy might + forward keys to it and you desire it to look active when this happens + (but without actually giving it active focus). + + It should not be set directly on the element, like in the below QML, + as the specified value will be overridden an lost on focus changes. + + \code + TextInput { + text: "Text" + cursorVisible: false + } + \endcode + + In the above snippet the cursor will still become visible when the + TextInput gains active focus. +*/ +bool QDeclarative1TextInput::isCursorVisible() const +{ + Q_D(const QDeclarative1TextInput); + return d->cursorVisible; +} + +void QDeclarative1TextInput::setCursorVisible(bool on) +{ + Q_D(QDeclarative1TextInput); + 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); +} + +/*! + \qmlproperty int TextInput::cursorPosition + The position of the cursor in the TextInput. +*/ +int QDeclarative1TextInput::cursorPosition() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->cursor(); +} +void QDeclarative1TextInput::setCursorPosition(int cp) +{ + Q_D(QDeclarative1TextInput); + if (cp < 0 || cp > d->control->text().length()) + return; + d->control->moveCursor(cp); +} + +/*! + Returns a Rect which encompasses the cursor, but which may be larger than is + required. Ignores custom cursor delegates. +*/ +QRect QDeclarative1TextInput::cursorRectangle() const +{ + Q_D(const QDeclarative1TextInput); + 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; +} + +/*! + \qmlproperty int TextInput::selectionStart + + The cursor position before the first character in the current selection. + Setting this and selectionEnd allows you to specify a selection in the + text edit. + + Note that if selectionStart == selectionEnd then there is no current + selection. + + \sa selectionEnd, cursorPosition, selectedText, select() +*/ +int QDeclarative1TextInput::selectionStart() const +{ + Q_D(const QDeclarative1TextInput); + return d->lastSelectionStart; +} + +/*! + \qmlproperty int TextInput::selectionEnd + + The cursor position after the last character in the current selection. + Setting this and selectionStart allows you to specify a selection in the + text edit. + + Note that if selectionStart == selectionEnd then there is no current + selection. + + \sa selectionStart, cursorPosition, selectedText, select() +*/ +int QDeclarative1TextInput::selectionEnd() const +{ + Q_D(const QDeclarative1TextInput); + return d->lastSelectionEnd; +} + +/*! + \qmlmethod void TextInput::select(int start, int end) + + Causes the text from \a start to \a end to be selected. + + If either start or end is out of range, the selection is not changed. + + After calling this, selectionStart will become the lesser + and selectionEnd will become the greater (regardless of the order passed + to this method). + + \sa selectionStart, selectionEnd +*/ +void QDeclarative1TextInput::select(int start, int end) +{ + Q_D(QDeclarative1TextInput); + if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length()) + return; + d->control->setSelection(start, end-start); +} + +/*! + \qmlproperty string TextInput::selectedText + + This read-only property provides the text currently selected in the + text input. + + It is equivalent to the following snippet, but is faster and easier + to use. + + \js + myTextInput.text.toString().substring(myTextInput.selectionStart, + myTextInput.selectionEnd); + \endjs +*/ +QString QDeclarative1TextInput::selectedText() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->selectedText(); +} + +/*! + \qmlproperty bool TextInput::activeFocusOnPress + + Whether the TextInput should gain active focus on a mouse press. By default this is + set to true. +*/ +bool QDeclarative1TextInput::focusOnPress() const +{ + Q_D(const QDeclarative1TextInput); + return d->focusOnPress; +} + +void QDeclarative1TextInput::setFocusOnPress(bool b) +{ + Q_D(QDeclarative1TextInput); + if (d->focusOnPress == b) + return; + + d->focusOnPress = b; + + emit activeFocusOnPressChanged(d->focusOnPress); +} + +/*! + \qmlproperty bool TextInput::autoScroll + + Whether the TextInput should scroll when the text is longer than the width. By default this is + set to true. +*/ +bool QDeclarative1TextInput::autoScroll() const +{ + Q_D(const QDeclarative1TextInput); + return d->autoScroll; +} + +void QDeclarative1TextInput::setAutoScroll(bool b) +{ + Q_D(QDeclarative1TextInput); + if (d->autoScroll == b) + return; + + d->autoScroll = b; + //We need to repaint so that the scrolling is taking into account. + updateSize(true); + updateCursorRectangle(); + emit autoScrollChanged(d->autoScroll); +} + +/*! + \qmlclass IntValidator QIntValidator + \ingroup qml-basic-visual-elements + + This element provides a validator for integer values. + + IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and + will accept locale specific digits, group separators, and positive and negative signs. In + addition, IntValidator is always guaranteed to accept a number formatted according to the "C" + locale. +*/ +/*! + \qmlproperty int IntValidator::top + + This property holds the validator's highest acceptable value. + By default, this property's value is derived from the highest signed integer available (typically 2147483647). +*/ +/*! + \qmlproperty int IntValidator::bottom + + This property holds the validator's lowest acceptable value. + By default, this property's value is derived from the lowest signed integer available (typically -2147483647). +*/ + +/*! + \qmlclass DoubleValidator QDoubleValidator + \ingroup qml-basic-visual-elements + + This element provides a validator for non-integer numbers. +*/ + +/*! + \qmlproperty real DoubleValidator::top + + This property holds the validator's maximum acceptable value. + By default, this property contains a value of infinity. +*/ +/*! + \qmlproperty real DoubleValidator::bottom + + This property holds the validator's minimum acceptable value. + By default, this property contains a value of -infinity. +*/ +/*! + \qmlproperty int DoubleValidator::decimals + + This property holds the validator's maximum number of digits after the decimal point. + By default, this property contains a value of 1000. +*/ +/*! + \qmlproperty enumeration DoubleValidator::notation + This property holds the notation of how a string can describe a number. + + The possible values for this property are: + + \list + \o DoubleValidator.StandardNotation + \o DoubleValidator.ScientificNotation (default) + \endlist + + If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2). +*/ + +/*! + \qmlclass RegExpValidator QRegExpValidator + \ingroup qml-basic-visual-elements + + This element provides a validator, which counts as valid any string which + matches a specified regular expression. +*/ +/*! + \qmlproperty regExp RegExpValidator::regExp + + This property holds the regular expression used for validation. + + Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression + matching "a". + + By default, this property contains a regular expression with the pattern .* that matches any string. +*/ + +/*! + \qmlproperty Validator TextInput::validator + + Allows you to set a validator on the TextInput. When a validator is set + the TextInput will only accept input which leaves the text property in + an acceptable or intermediate state. The accepted signal will only be sent + if the text is in an acceptable state when enter is pressed. + + Currently supported validators are IntValidator, DoubleValidator and + RegExpValidator. An example of using validators is shown below, which allows + input of integers between 11 and 31 into the text input: + + \code + import QtQuick 1.0 + TextInput{ + validator: IntValidator{bottom: 11; top: 31;} + focus: true + } + \endcode + + \sa acceptableInput, inputMask +*/ +#ifndef QT_NO_VALIDATOR +QValidator* QDeclarative1TextInput::validator() const +{ + Q_D(const QDeclarative1TextInput); + //###const cast isn't good, but needed for property system? + return const_cast<QValidator*>(d->control->validator()); +} + +void QDeclarative1TextInput::setValidator(QValidator* v) +{ + Q_D(QDeclarative1TextInput); + 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 + +/*! + \qmlproperty string TextInput::inputMask + + Allows you to set an input mask on the TextInput, restricting the allowable + text inputs. See QLineEdit::inputMask for further details, as the exact + same mask strings are used by TextInput. + + \sa acceptableInput, validator +*/ +QString QDeclarative1TextInput::inputMask() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->inputMask(); +} + +void QDeclarative1TextInput::setInputMask(const QString &im) +{ + Q_D(QDeclarative1TextInput); + if (d->control->inputMask() == im) + return; + + d->control->setInputMask(im); + emit inputMaskChanged(d->control->inputMask()); +} + +/*! + \qmlproperty bool TextInput::acceptableInput + + This property is always true unless a validator or input mask has been set. + If a validator or input mask has been set, this property will only be true + if the current text is acceptable to the validator or input mask as a final + string (not as an intermediate string). +*/ +bool QDeclarative1TextInput::hasAcceptableInput() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->hasAcceptableInput(); +} + +/*! + \qmlsignal TextInput::onAccepted() + + This handler is called when the Return or Enter key is pressed. + Note that if there is a \l validator or \l inputMask set on the text + input, the handler will only be emitted if the input is in an acceptable + state. +*/ + +void QDeclarative1TextInputPrivate::updateInputMethodHints() +{ + Q_Q(QDeclarative1TextInput); + Qt::InputMethodHints hints = inputMethodHints; + uint echo = control->echoMode(); + if (echo == QDeclarative1TextInput::Password || echo == QDeclarative1TextInput::NoEcho) + hints |= Qt::ImhHiddenText; + else if (echo == QDeclarative1TextInput::PasswordEchoOnEdit) + hints &= ~Qt::ImhHiddenText; + if (echo != QDeclarative1TextInput::Normal) { + hints |= Qt::ImhNoAutoUppercase; + hints |= Qt::ImhNoPredictiveText; + } + q->setInputMethodHints(hints); +} + +/*! + \qmlproperty enumeration TextInput::echoMode + + Specifies how the text should be displayed in the TextInput. + \list + \o TextInput.Normal - Displays the text as it is. (Default) + \o TextInput.Password - Displays asterixes instead of characters. + \o TextInput.NoEcho - Displays nothing. + \o TextInput.PasswordEchoOnEdit - Displays all but the current character as asterixes. + \endlist +*/ +QDeclarative1TextInput::EchoMode QDeclarative1TextInput::echoMode() const +{ + Q_D(const QDeclarative1TextInput); + return (QDeclarative1TextInput::EchoMode)d->control->echoMode(); +} + +void QDeclarative1TextInput::setEchoMode(QDeclarative1TextInput::EchoMode echo) +{ + Q_D(QDeclarative1TextInput); + if (echoMode() == echo) + return; + d->control->setEchoMode((uint)echo); + d->updateInputMethodHints(); + q_textChanged(); + emit echoModeChanged(echoMode()); +} + +Qt::InputMethodHints QDeclarative1TextInput::imHints() const +{ + Q_D(const QDeclarative1TextInput); + return d->inputMethodHints; +} + +void QDeclarative1TextInput::setIMHints(Qt::InputMethodHints hints) +{ + Q_D(QDeclarative1TextInput); + if (d->inputMethodHints == hints) + return; + d->inputMethodHints = hints; + d->updateInputMethodHints(); +} + +/*! + \qmlproperty Component TextInput::cursorDelegate + The delegate for the cursor in the TextInput. + + If you set a cursorDelegate for a TextInput, this delegate will be used for + drawing the cursor instead of the standard cursor. An instance of the + delegate will be created and managed by the TextInput when a cursor is + needed, and the x property of delegate instance will be set so as + to be one pixel before the top left of the current character. + + Note that the root item of the delegate component must be a QDeclarativeItem or + QDeclarativeItem derived item. +*/ +QDeclarativeComponent* QDeclarative1TextInput::cursorDelegate() const +{ + Q_D(const QDeclarative1TextInput); + return d->cursorComponent; +} + +void QDeclarative1TextInput::setCursorDelegate(QDeclarativeComponent* c) +{ + Q_D(QDeclarative1TextInput); + if (d->cursorComponent == c) + return; + + d->cursorComponent = c; + if(!c){ + //note that the components are owned by something else + delete d->cursorItem; + }else{ + d->startCreatingCursor(); + } + + emit cursorDelegateChanged(); +} + +void QDeclarative1TextInputPrivate::startCreatingCursor() +{ + Q_Q(QDeclarative1TextInput); + if(cursorComponent->isReady()){ + q->createCursor(); + }else if(cursorComponent->isLoading()){ + q->connect(cursorComponent, SIGNAL(statusChanged(int)), + q, SLOT(createCursor())); + }else {//isError + qmlInfo(q, cursorComponent->errors()) << QDeclarative1TextInput::tr("Could not load cursor delegate"); + } +} + +void QDeclarative1TextInput::createCursor() +{ + Q_D(QDeclarative1TextInput); + 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<QDeclarativeItem*>(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. +} + +/*! + \qmlmethod rect TextInput::positionToRectangle(int pos) + + This function takes a character position and returns the rectangle that the + cursor would occupy, if it was placed at that character position. + + This is similar to setting the cursorPosition, and then querying the cursor + rectangle, but the cursorPosition is not changed. +*/ +QRectF QDeclarative1TextInput::positionToRectangle(int pos) const +{ + Q_D(const QDeclarative1TextInput); + 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 QDeclarative1TextInput::positionAt(int x) const +{ + return positionAt(x, CursorBetweenCharacters); +} + +/*! + \qmlmethod int TextInput::positionAt(int x, CursorPosition position = CursorBetweenCharacters) + \since Quick 1.1 + + This function returns the character position at + x pixels from the left of the textInput. Position 0 is before the + first character, position 1 is after the first character but before the second, + and so on until position text.length, which is after all characters. + + This means that for all x values before the first character this function returns 0, + and for all x values after the last character this function returns text.length. + + The cursor position type specifies how the cursor position should be resolved. + + \list + \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x. + \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x. + \endlist +*/ +int QDeclarative1TextInput::positionAt(int x, CursorPosition position) const +{ + Q_D(const QDeclarative1TextInput); + 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 QDeclarative1TextInputPrivate::focusChanged(bool hasFocus) +{ + Q_Q(QDeclarative1TextInput); + focused = hasFocus; + q->setCursorVisible(hasFocus && scene && scene->hasFocus()); + if(q->echoMode() == QDeclarative1TextInput::PasswordEchoOnEdit && !hasFocus) + control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events + if (!hasFocus) + control->deselect(); + QDeclarativeItemPrivate::focusChanged(hasFocus); +} + +void QDeclarative1TextInput::keyPressEvent(QKeyEvent* ev) +{ + Q_D(QDeclarative1TextInput); + keyPressPreHandler(ev); + if (ev->isAccepted()) + return; + + // 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()) + QDeclarative1PaintedItem::keyPressEvent(ev); +} + +void QDeclarative1TextInput::inputMethodEvent(QInputMethodEvent *ev) +{ + Q_D(QDeclarative1TextInput); + ev->ignore(); + const bool wasComposing = d->control->preeditAreaText().length() > 0; + inputMethodPreHandler(ev); + if (!ev->isAccepted()) { + if (d->control->isReadOnly()) { + ev->ignore(); + } else { + d->control->processInputMethodEvent(ev); + } + } + if (!ev->isAccepted()) + QDeclarative1PaintedItem::inputMethodEvent(ev); + + if (wasComposing != (d->control->preeditAreaText().length() > 0)) + emit inputMethodComposingChanged(); +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarative1TextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextInput); + 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 { + QDeclarative1PaintedItem::mouseDoubleClickEvent(event); + } +} + +void QDeclarative1TextInput::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextInput); + 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 QDeclarative1TextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextInput); + 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 { + QDeclarative1PaintedItem::mouseMoveEvent(event); + } +} + +/*! +\overload +Handles the given mouse \a event. +*/ +void QDeclarative1TextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QDeclarative1TextInput); + 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 (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus); + } + } + } + } + d->clickCausedFocus = false; + d->control->processEvent(event); + if (!event->isAccepted()) + QDeclarative1PaintedItem::mouseReleaseEvent(event); +} + +bool QDeclarative1TextInputPrivate::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; +} + +bool QDeclarative1TextInput::sceneEvent(QEvent *event) +{ + Q_D(QDeclarative1TextInput); + bool rv = QDeclarativeItem::sceneEvent(event); + if (event->type() == QEvent::UngrabMouse) { + d->selectPressed = false; + setKeepMouseGrab(false); + } + return rv; +} + +bool QDeclarative1TextInput::event(QEvent* ev) +{ + Q_D(QDeclarative1TextInput); + //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 = QDeclarative1PaintedItem::event(ev); + return handled; +} + +void QDeclarative1TextInput::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QDeclarative1TextInput); + if (newGeometry.width() != oldGeometry.width()) { + updateSize(); + updateCursorRectangle(); + } + QDeclarative1PaintedItem::geometryChanged(newGeometry, oldGeometry); +} + +int QDeclarative1TextInputPrivate::calculateTextWidth() +{ + return qRound(control->naturalTextWidth()); +} + +void QDeclarative1TextInputPrivate::updateHorizontalScroll() +{ + Q_Q(QDeclarative1TextInput); + const int preeditLength = control->preeditAreaText().length(); + int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); + QRect br(q->boundingRect().toRect()); + int widthUsed = calculateTextWidth(); + + QDeclarative1TextInput::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 QDeclarative1TextInput::AlignRight: + hscroll = q->width() - widthUsed; + break; + case QDeclarative1TextInput::AlignHCenter: + hscroll = (q->width() - widthUsed) / 2; + break; + default: + // Left + hscroll = 0; + break; + } + } +} + +void QDeclarative1TextInput::drawContents(QPainter *p, const QRect &r) +{ + Q_D(QDeclarative1TextInput); + 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(); +} + +/*! +\overload +Returns the value of the given \a property. +*/ +QVariant QDeclarative1TextInput::inputMethodQuery(Qt::InputMethodQuery property) const +{ + Q_D(const QDeclarative1TextInput); + 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(); + } +} + +/*! + \qmlmethod void TextInput::deselect() + \since Quick 1.1 + + Removes active text selection. +*/ +void QDeclarative1TextInput::deselect() +{ + Q_D(QDeclarative1TextInput); + d->control->deselect(); +} + +/*! + \qmlmethod void TextInput::selectAll() + + Causes all text to be selected. +*/ +void QDeclarative1TextInput::selectAll() +{ + Q_D(QDeclarative1TextInput); + d->control->setSelection(0, d->control->text().length()); +} + +/*! + \qmlmethod void TextInput::isRightToLeft(int start, int end) + + Returns true if the natural reading direction of the editor text + found between positions \a start and \a end is right to left. +*/ +bool QDeclarative1TextInput::isRightToLeft(int start, int end) +{ + Q_D(QDeclarative1TextInput); + 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 +/*! + \qmlmethod TextInput::cut() + + Moves the currently selected text to the system clipboard. +*/ +void QDeclarative1TextInput::cut() +{ + Q_D(QDeclarative1TextInput); + d->control->copy(); + d->control->del(); +} + +/*! + \qmlmethod TextInput::copy() + + Copies the currently selected text to the system clipboard. +*/ +void QDeclarative1TextInput::copy() +{ + Q_D(QDeclarative1TextInput); + d->control->copy(); +} + +/*! + \qmlmethod TextInput::paste() + + Replaces the currently selected text by the contents of the system clipboard. +*/ +void QDeclarative1TextInput::paste() +{ + Q_D(QDeclarative1TextInput); + if(!d->control->isReadOnly()) + d->control->paste(); +} +#endif // QT_NO_CLIPBOARD + +/*! + \qmlmethod void TextInput::selectWord() + + Causes the word closest to the current cursor position to be selected. +*/ +void QDeclarative1TextInput::selectWord() +{ + Q_D(QDeclarative1TextInput); + d->control->selectWordAtPos(d->control->cursor()); +} + +/*! + \qmlproperty bool TextInput::smooth + + This property holds whether the text is smoothly scaled or transformed. + + Smooth filtering gives better visual quality, but is slower. If + the item is displayed at its natural size, this property has no visual or + performance effect. + + \note Generally scaling artifacts are only visible if the item is stationary on + the screen. A common pattern when animating an item is to disable smooth + filtering at the beginning of the animation and reenable it at the conclusion. +*/ + +/*! + \qmlproperty string TextInput::passwordCharacter + + This is the character displayed when echoMode is set to Password or + PasswordEchoOnEdit. By default it is an asterisk. + + If this property is set to a string with more than one character, + the first character is used. If the string is empty, the value + is ignored and the property is not set. +*/ +QString QDeclarative1TextInput::passwordCharacter() const +{ + Q_D(const QDeclarative1TextInput); + return QString(d->control->passwordCharacter()); +} + +void QDeclarative1TextInput::setPasswordCharacter(const QString &str) +{ + Q_D(QDeclarative1TextInput); + if(str.length() < 1) + return; + d->control->setPasswordCharacter(str.constData()[0]); + EchoMode echoMode_ = echoMode(); + if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) { + updateSize(); + } + emit passwordCharacterChanged(); +} + +/*! + \qmlproperty string TextInput::displayText + + This is the text displayed in the TextInput. + + If \l echoMode is set to TextInput::Normal, this holds the + same value as the TextInput::text property. Otherwise, + this property holds the text visible to the user, while + the \l text property holds the actual entered text. +*/ +QString QDeclarative1TextInput::displayText() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->displayText(); +} + +/*! + \qmlproperty bool TextInput::selectByMouse + + Defaults to false. + + If true, the user can use the mouse to select text in some + platform-specific way. Note that for some platforms this may + not be an appropriate interaction (eg. may conflict with how + the text needs to behave inside a Flickable. +*/ +bool QDeclarative1TextInput::selectByMouse() const +{ + Q_D(const QDeclarative1TextInput); + return d->selectByMouse; +} + +void QDeclarative1TextInput::setSelectByMouse(bool on) +{ + Q_D(QDeclarative1TextInput); + if (d->selectByMouse != on) { + d->selectByMouse = on; + emit selectByMouseChanged(on); + } +} + +/*! + \qmlproperty enum TextInput::mouseSelectionMode + \since Quick 1.1 + + Specifies how text should be selected using a mouse. + + \list + \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default) + \o TextInput.SelectWords - The selection is updated with whole words. + \endlist + + This property only applies when \l selectByMouse is true. +*/ + +QDeclarative1TextInput::SelectionMode QDeclarative1TextInput::mouseSelectionMode() const +{ + Q_D(const QDeclarative1TextInput); + return d->mouseSelectionMode; +} + +void QDeclarative1TextInput::setMouseSelectionMode(SelectionMode mode) +{ + Q_D(QDeclarative1TextInput); + if (d->mouseSelectionMode != mode) { + d->mouseSelectionMode = mode; + emit mouseSelectionModeChanged(mode); + } +} + +/*! + \qmlproperty bool TextInput::canPaste + \since QtQuick 1.1 + + Returns true if the TextInput is writable and the content of the clipboard is + suitable for pasting into the TextEdit. +*/ +bool QDeclarative1TextInput::canPaste() const +{ + Q_D(const QDeclarative1TextInput); + return d->canPaste; +} + +void QDeclarative1TextInput::moveCursorSelection(int position) +{ + Q_D(QDeclarative1TextInput); + d->control->moveCursor(position, true); +} + +/*! + \qmlmethod void TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters) + \since Quick 1.1 + + Moves the cursor to \a position and updates the selection according to the optional \a mode + parameter. (To only move the cursor, set the \l cursorPosition property.) + + When this method is called it additionally sets either the + selectionStart or the selectionEnd (whichever was at the previous cursor position) + to the specified position. This allows you to easily extend and contract the selected + text range. + + The selection mode specifies whether the selection is updated on a per character or a per word + basis. If not specified the selection mode will default to TextInput.SelectCharacters. + + \list + \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at + the previous cursor position) to the specified position. + \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all + words between the specified postion and the previous cursor position. Words partially in the + range are included. + \endlist + + For example, take this sequence of calls: + + \code + cursorPosition = 5 + moveCursorSelection(9, TextInput.SelectCharacters) + moveCursorSelection(7, TextInput.SelectCharacters) + \endcode + + This moves the cursor to position 5, extend the selection end from 5 to 9 + and then retract the selection end from 9 to 7, leaving the text from position 5 to 7 + selected (the 6th and 7th characters). + + The same sequence with TextInput.SelectWords will extend the selection start to a word boundary + before or on position 5 and extend the selection end to a word boundary on or past position 9. +*/ +void QDeclarative1TextInput::moveCursorSelection(int pos, SelectionMode mode) +{ + Q_D(QDeclarative1TextInput); + + 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); + } + } +} + +/*! + \qmlmethod void TextInput::openSoftwareInputPanel() + + Opens software input panels like virtual keyboards for typing, useful for + customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms + the panels are automatically opened when TextInput element gains active focus. Input panels are + always closed if no editor has active focus. + + . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \qml + import QtQuick 1.0 + TextInput { + id: textInput + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus() + textInput.openSoftwareInputPanel(); + } else { + textInput.focus = false; + } + } + onPressAndHold: textInput.closeSoftwareInputPanel(); + } + } + \endqml +*/ +void QDeclarative1TextInput::openSoftwareInputPanel() +{ + QEvent event(QEvent::RequestSoftwareInputPanel); + if (qApp) { + if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +/*! + \qmlmethod void TextInput::closeSoftwareInputPanel() + + Closes a software input panel like a virtual keyboard shown on the screen, useful + for customizing when you want the input keyboard to be shown and hidden in + your application. + + By default the opening of input panels follows the platform style. On Symbian^1 and + Symbian^3 -based devices the panels are opened by clicking TextInput. On other platforms + the panels are automatically opened when TextInput element gains active focus. Input panels are + always closed if no editor has active focus. + + . You can disable the automatic behavior by setting the property \c activeFocusOnPress to false + and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement + the behavior you want. + + Only relevant on platforms, which provide virtual keyboards. + + \qml + import QtQuick 1.0 + TextInput { + id: textInput + text: "Hello world!" + activeFocusOnPress: false + MouseArea { + anchors.fill: parent + onClicked: { + if (!textInput.activeFocus) { + textInput.forceActiveFocus(); + textInput.openSoftwareInputPanel(); + } else { + textInput.focus = false; + } + } + onPressAndHold: textInput.closeSoftwareInputPanel(); + } + } + \endqml +*/ +void QDeclarative1TextInput::closeSoftwareInputPanel() +{ + QEvent event(QEvent::CloseSoftwareInputPanel); + if (qApp) { + QEvent event(QEvent::CloseSoftwareInputPanel); + if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) { + if (view->scene() && view->scene() == scene()) { + QApplication::sendEvent(view, &event); + } + } + } +} + +void QDeclarative1TextInput::focusInEvent(QFocusEvent *event) +{ + Q_D(const QDeclarative1TextInput); + if (d->showInputPanelOnFocus) { + if (d->focusOnPress && !isReadOnly()) { + openSoftwareInputPanel(); + } + } + QDeclarative1PaintedItem::focusInEvent(event); +} + +/*! + \qmlproperty bool TextInput::inputMethodComposing + + \since QtQuick 1.1 + + This property holds whether the TextInput has partial text input from an + input method. + + While it is composing an input method may rely on mouse or key events from + the TextInput to edit or commit the partial text. This property can be + used to determine when to disable events handlers that may interfere with + the correct operation of an input method. +*/ +bool QDeclarative1TextInput::isInputMethodComposing() const +{ + Q_D(const QDeclarative1TextInput); + return d->control->preeditAreaText().length() > 0; +} + +void QDeclarative1TextInputPrivate::init() +{ + Q_Q(QDeclarative1TextInput); + control->setParent(q);//Now mandatory due to accessibility changes + control->setCursorWidth(1); + control->setPasswordCharacter(QLatin1Char('*')); + q->setSmooth(smooth); + q->setAcceptedMouseButtons(Qt::LeftButton); + q->setFlag(QGraphicsItem::ItemHasNoContents, false); + q->setFlag(QGraphicsItem::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(updateCursorRectangle())); + 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 QDeclarative1TextInput::cursorPosChanged() +{ + Q_D(QDeclarative1TextInput); + updateCursorRectangle(); + emit cursorPositionChanged(); + d->control->resetCursorBlinkTimer(); + + 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 QDeclarative1TextInput::updateCursorRectangle() +{ + Q_D(QDeclarative1TextInput); + d->updateHorizontalScroll(); + updateRect();//TODO: Only update rect between pos's + updateMicroFocus(); + emit cursorRectangleChanged(); + if (d->cursorItem) + d->cursorItem->setX(d->control->cursorToX() - d->hscroll); +} + +void QDeclarative1TextInput::selectionChanged() +{ + Q_D(QDeclarative1TextInput); + 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 QDeclarative1TextInput::q_textChanged() +{ + Q_D(QDeclarative1TextInput); + updateSize(); + d->determineHorizontalAlignment(); + d->updateHorizontalScroll(); + updateMicroFocus(); + emit textChanged(); + emit displayTextChanged(); + if(hasAcceptableInput() != d->oldValidity){ + d->oldValidity = hasAcceptableInput(); + emit acceptableInputChanged(); + } +} + +void QDeclarative1TextInput::updateRect(const QRect &r) +{ + Q_D(QDeclarative1TextInput); + if(r == QRect()) + clearCache(); + else + dirtyCache(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height())); + update(); +} + +QRectF QDeclarative1TextInput::boundingRect() const +{ + Q_D(const QDeclarative1TextInput); + QRectF r = QDeclarative1PaintedItem::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 QDeclarative1TextInput::updateSize(bool needsRedraw) +{ + Q_D(QDeclarative1TextInput); + 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()));//Repaints if changed + if(w==width() && h==height() && needsRedraw){ + clearCache(); + update(); + } +} + +void QDeclarative1TextInput::q_canPasteChanged() +{ + Q_D(QDeclarative1TextInput); + 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 + +#endif // QT_NO_LINEEDIT + diff --git a/src/qtquick1/graphicsitems/qdeclarativetextinput_p.h b/src/qtquick1/graphicsitems/qdeclarativetextinput_p.h new file mode 100644 index 0000000000..562d1bf08a --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextinput_p.h @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTINPUT_H +#define QDECLARATIVETEXTINPUT_H + +#include "private/qdeclarativetext_p.h" +#include "private/qdeclarativeimplicitsizeitem_p.h" + +#include <QGraphicsSceneMouseEvent> +#include <QIntValidator> + +#ifndef QT_NO_LINEEDIT + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QValidator; +class QDeclarative1TextInputPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1TextInput : public QDeclarative1ImplicitSizePaintedItem +{ + 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 REVISION 1) + 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 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 REVISION 1) + Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged REVISION 1) + Q_PROPERTY(bool inputMethodComposing READ isInputMethodComposing NOTIFY inputMethodComposingChanged REVISION 1) + +public: + QDeclarative1TextInput(QDeclarativeItem* parent=0); + ~QDeclarative1TextInput(); + + 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 Q_REVISION(1) int positionAt(int x, CursorPosition position) const; + Q_INVOKABLE QRectF positionToRectangle(int pos) const; + Q_INVOKABLE void moveCursorSelection(int pos); + Q_INVOKABLE Q_REVISION(1) 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 drawContents(QPainter *p,const QRect &r); + 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 cursorRectangleChanged(); + 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); + Q_REVISION(1) void mouseSelectionModeChanged(SelectionMode mode); + Q_REVISION(1) void canPasteChanged(); + Q_REVISION(1) void inputMethodComposingChanged(); + Q_REVISION(1) 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 *); + bool event(QEvent *e); + void focusInEvent(QFocusEvent *event); + +public Q_SLOTS: + void selectAll(); + void selectWord(); + void select(int start, int end); + Q_REVISION(1) void deselect(); + Q_REVISION(1) 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 cursorPosChanged(); + void updateCursorRectangle(); + void updateRect(const QRect &r = QRect()); + void q_canPasteChanged(); + +private: + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarative1TextInput) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1TextInput) +#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 // QT_NO_LINEEDIT + +#endif // QDECLARATIVETEXTINPUT_H diff --git a/src/qtquick1/graphicsitems/qdeclarativetextinput_p_p.h b/src/qtquick1/graphicsitems/qdeclarativetextinput_p_p.h new file mode 100644 index 0000000000..4132db65f3 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextinput_p_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTINPUT_P_H +#define QDECLARATIVETEXTINPUT_P_H + +#include "private/qdeclarativetextinput_p.h" + +#include "private/qdeclarativeimplicitsizeitem_p_p.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <QPointer> + +#include <private/qlinecontrol_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. + +#ifndef QT_NO_LINEEDIT + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QDeclarative1TextInputPrivate : public QDeclarative1ImplicitSizePaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1TextInput) +public: + QDeclarative1TextInputPrivate() : control(new QLineControl), + color((QRgb)0), style(QDeclarative1Text::Normal), + styleColor((QRgb)0), hAlign(QDeclarative1TextInput::AlignLeft), + mouseSelectionMode(QDeclarative1TextInput::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 + } + + ~QDeclarative1TextInputPrivate() + { + } + + int xToPos(int x, QTextLine::CursorPosition betweenOrOn = QTextLine::CursorBetweenCharacters) const + { + Q_Q(const QDeclarative1TextInput); + QRect cr = q->boundingRect().toRect(); + x-= cr.x() - hscroll; + return control->xToPos(x, betweenOrOn); + } + + void init(); + void startCreatingCursor(); + void focusChanged(bool hasFocus); + void updateHorizontalScroll(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarative1TextInput::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; + QDeclarative1Text::TextStyle style; + QColor styleColor; + QDeclarative1TextInput::HAlignment hAlign; + QDeclarative1TextInput::SelectionMode mouseSelectionMode; + Qt::InputMethodHints inputMethodHints; + QPointer<QDeclarativeComponent> cursorComponent; + QPointer<QDeclarativeItem> 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 QDeclarative1TextInputPrivate *get(QDeclarative1TextInput *t) { + return t->d_func(); + } +}; + +QT_END_NAMESPACE + +#endif // QT_NO_LINEEDIT + +#endif + diff --git a/src/qtquick1/graphicsitems/qdeclarativetextlayout.cpp b/src/qtquick1/graphicsitems/qdeclarativetextlayout.cpp new file mode 100644 index 0000000000..454b611783 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextlayout.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativetextlayout_p.h" +#include <private/qstatictext_p.h> +#include <private/qfontengine_p.h> +#include <private/qtextengine_p.h> +#include <private/qpainter_p.h> +#include <private/qpaintengineex_p.h> + +QT_BEGIN_NAMESPACE + +// Defined in qpainter.cpp +extern Q_GUI_EXPORT void qt_draw_decoration_for_glyphs(QPainter *painter, const glyph_t *glyphArray, + const QFixedPoint *positions, int glyphCount, + QFontEngine *fontEngine, const QFont &font, + const QTextCharFormat &charFormat); + + + +class QDeclarative1TextLayoutPrivate +{ +public: + QDeclarative1TextLayoutPrivate() + : cached(false) {} + + QPointF position; + + bool cached; + QVector<QStaticTextItem> items; + QVector<QFixedPoint> positions; + QVector<glyph_t> glyphs; + QVector<QChar> chars; +}; + +namespace { +class DrawTextItemRecorder: public QPaintEngine +{ + public: + DrawTextItemRecorder(bool untransformedCoordinates, bool useBackendOptimizations) + : m_inertText(0), m_dirtyPen(false), m_useBackendOptimizations(useBackendOptimizations), + m_untransformedCoordinates(untransformedCoordinates), m_currentColor(Qt::black) + { + } + + virtual void updateState(const QPaintEngineState &newState) + { + if (newState.state() & QPaintEngine::DirtyPen + && newState.pen().color() != m_currentColor) { + m_dirtyPen = true; + m_currentColor = newState.pen().color(); + } + } + + virtual void drawTextItem(const QPointF &position, const QTextItem &textItem) + { + int glyphOffset = m_inertText->glyphs.size(); // Store offset into glyph pool + int positionOffset = m_inertText->glyphs.size(); // Offset into position pool + int charOffset = m_inertText->chars.size(); + + const QTextItemInt &ti = static_cast<const QTextItemInt &>(textItem); + + bool needFreshCurrentItem = true; + if (!m_inertText->items.isEmpty()) { + QStaticTextItem &last = m_inertText->items[m_inertText->items.count() - 1]; + + if (last.fontEngine() == ti.fontEngine && last.font == ti.font() && + (!m_dirtyPen || last.color == state->pen().color())) { + needFreshCurrentItem = false; + + last.numChars += ti.num_chars; + + } + } + + if (needFreshCurrentItem) { + QStaticTextItem currentItem; + + currentItem.setFontEngine(ti.fontEngine); + currentItem.font = ti.font(); + currentItem.charOffset = charOffset; + currentItem.numChars = ti.num_chars; + currentItem.numGlyphs = 0; + currentItem.glyphOffset = glyphOffset; + currentItem.positionOffset = positionOffset; + currentItem.useBackendOptimizations = m_useBackendOptimizations; + if (m_dirtyPen) + currentItem.color = m_currentColor; + + m_inertText->items.append(currentItem); + } + + QStaticTextItem ¤tItem = m_inertText->items.last(); + + QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); + matrix.translate(position.x(), position.y()); + + QVarLengthArray<glyph_t> glyphs; + QVarLengthArray<QFixedPoint> positions; + ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); + + int size = glyphs.size(); + Q_ASSERT(size == positions.size()); + currentItem.numGlyphs += size; + + m_inertText->glyphs.resize(m_inertText->glyphs.size() + size); + m_inertText->positions.resize(m_inertText->glyphs.size()); + m_inertText->chars.resize(m_inertText->chars.size() + ti.num_chars); + + glyph_t *glyphsDestination = m_inertText->glyphs.data() + glyphOffset; + qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * size); + + QFixedPoint *positionsDestination = m_inertText->positions.data() + positionOffset; + qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * size); + + QChar *charsDestination = m_inertText->chars.data() + charOffset; + qMemCopy(charsDestination, ti.chars, sizeof(QChar) * ti.num_chars); + + } + + virtual void drawPolygon(const QPointF *, int , PolygonDrawMode ) + { + /* intentionally empty */ + } + + virtual bool begin(QPaintDevice *) { return true; } + virtual bool end() { return true; } + virtual void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) {} + virtual Type type() const + { + return User; + } + + void begin(QDeclarative1TextLayoutPrivate *t) { + m_inertText = t; + m_dirtyPen = false; + } + + private: + QDeclarative1TextLayoutPrivate *m_inertText; + + bool m_dirtyPen; + bool m_useBackendOptimizations; + bool m_untransformedCoordinates; + QColor m_currentColor; +}; + +class DrawTextItemDevice: public QPaintDevice +{ + public: + DrawTextItemDevice(bool untransformedCoordinates, bool useBackendOptimizations) + { + m_paintEngine = new DrawTextItemRecorder(untransformedCoordinates, + useBackendOptimizations); + } + + ~DrawTextItemDevice() + { + delete m_paintEngine; + } + + void begin(QDeclarative1TextLayoutPrivate *t) { + m_paintEngine->begin(t); + } + + int metric(PaintDeviceMetric m) const + { + int val; + switch (m) { + case PdmWidth: + case PdmHeight: + case PdmWidthMM: + case PdmHeightMM: + val = 0; + break; + case PdmDpiX: + case PdmPhysicalDpiX: + val = qt_defaultDpiX(); + break; + case PdmDpiY: + case PdmPhysicalDpiY: + val = qt_defaultDpiY(); + break; + case PdmNumColors: + val = 16777216; + break; + case PdmDepth: + val = 24; + break; + default: + val = 0; + qWarning("DrawTextItemDevice::metric: Invalid metric command"); + } + return val; + } + + virtual QPaintEngine *paintEngine() const + { + return m_paintEngine; + } + + private: + DrawTextItemRecorder *m_paintEngine; +}; + +struct InertTextPainter { + InertTextPainter() + : device(true, true), painter(&device) {} + + DrawTextItemDevice device; + QPainter painter; +}; +} + +Q_GLOBAL_STATIC(InertTextPainter, inertTextPainter); + +/*! +\class QDeclarative1TextLayout +\brief The QDeclarative1TextLayout class is a version of QStaticText that works with QTextLayouts. +\internal + +This class is basically a copy of the QStaticText code, but it is adapted to source its text from +QTextLayout. + +It is also considerably faster to create a QDeclarative1TextLayout than a QStaticText because it uses +a single, shared QPainter instance. QStaticText by comparison creates a new QPainter per instance. +As a consequence this means that QDeclarative1TextLayout is not re-enterant. Adding a lock around +the shared painter solves this, and only introduces a minor performance penalty, but is unnecessary +for QDeclarative1TextLayout's current use (QDeclarative1Text is already tied to the GUI thread). +*/ + +QDeclarative1TextLayout::QDeclarative1TextLayout() +: d(0) +{ +} + +QDeclarative1TextLayout::QDeclarative1TextLayout(const QString &text) +: QTextLayout(text), d(0) +{ +} + +QDeclarative1TextLayout::~QDeclarative1TextLayout() +{ + if (d) delete d; +} + +void QDeclarative1TextLayout::beginLayout() +{ + if (d && d->cached) { + d->cached = false; + d->items.clear(); + d->positions.clear(); + d->glyphs.clear(); + d->chars.clear(); + d->position = QPointF(); + } + QTextLayout::beginLayout(); +} + +void QDeclarative1TextLayout::clearLayout() +{ + if (d && d->cached) { + d->cached = false; + d->items.clear(); + d->positions.clear(); + d->glyphs.clear(); + d->chars.clear(); + d->position = QPointF(); + } + QTextLayout::clearLayout(); +} + +void QDeclarative1TextLayout::prepare() +{ + if (!d || !d->cached) { + + if (!d) + d = new QDeclarative1TextLayoutPrivate; + + InertTextPainter *itp = inertTextPainter(); + itp->device.begin(d); + QTextLayout::draw(&itp->painter, QPointF(0, 0)); + + glyph_t *glyphPool = d->glyphs.data(); + QFixedPoint *positionPool = d->positions.data(); + QChar *charPool = d->chars.data(); + + int itemCount = d->items.count(); + for (int ii = 0; ii < itemCount; ++ii) { + QStaticTextItem &item = d->items[ii]; + item.glyphs = glyphPool + item.glyphOffset; + item.glyphPositions = positionPool + item.positionOffset; + item.chars = charPool + item.charOffset; + } + + d->cached = true; + } +} + +void QDeclarative1TextLayout::draw(QPainter *painter, const QPointF &p) +{ + QPainterPrivate *priv = QPainterPrivate::get(painter); + + bool paintEngineSupportsTransformations = priv->extended && + (priv->extended->type() == QPaintEngine::OpenGL2 || + priv->extended->type() == QPaintEngine::OpenVG || + priv->extended->type() == QPaintEngine::OpenGL); + + if (!paintEngineSupportsTransformations || !priv->state->matrix.isAffine()) { + QTextLayout::draw(painter, p); + return; + } + + prepare(); + + int itemCount = d->items.count(); + + if (p != d->position) { + QFixed fx = QFixed::fromReal(p.x()); + QFixed fy = QFixed::fromReal(p.y()); + QFixed oldX = QFixed::fromReal(d->position.x()); + QFixed oldY = QFixed::fromReal(d->position.y()); + for (int item = 0; item < itemCount; ++item) { + QStaticTextItem &textItem = d->items[item]; + + for (int ii = 0; ii < textItem.numGlyphs; ++ii) { + textItem.glyphPositions[ii].x += fx - oldX; + textItem.glyphPositions[ii].y += fy - oldY; + } + textItem.userDataNeedsUpdate = true; + } + + d->position = p; + } + + QPen oldPen = priv->state->pen; + QColor currentColor = oldPen.color(); + for (int ii = 0; ii < itemCount; ++ii) { + QStaticTextItem &item = d->items[ii]; + if (item.color.isValid() && currentColor != item.color) { + painter->setPen(item.color); + currentColor = item.color; + } + priv->extended->drawStaticTextItem(&item); + + qt_draw_decoration_for_glyphs(painter, item.glyphs, item.glyphPositions, + item.numGlyphs, item.fontEngine(), painter->font(), + QTextCharFormat()); + } + if (currentColor != oldPen.color()) + painter->setPen(oldPen); +} + + + +QT_END_NAMESPACE + diff --git a/src/qtquick1/graphicsitems/qdeclarativetextlayout_p.h b/src/qtquick1/graphicsitems/qdeclarativetextlayout_p.h new file mode 100644 index 0000000000..bfedbec958 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetextlayout_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETEXTLAYOUT_P_H +#define QDECLARATIVETEXTLAYOUT_P_H + +#include <QtGui/qtextlayout.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1TextLayoutPrivate; +class QDeclarative1TextLayout : public QTextLayout +{ +public: + QDeclarative1TextLayout(); + QDeclarative1TextLayout(const QString &); + ~QDeclarative1TextLayout(); + + void beginLayout(); + void clearLayout(); + + void prepare(); + void draw(QPainter *, const QPointF & = QPointF()); + +private: + QDeclarative1TextLayoutPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVETEXTLAYOUT_P_H diff --git a/src/qtquick1/graphicsitems/qdeclarativetranslate.cpp b/src/qtquick1/graphicsitems/qdeclarativetranslate.cpp new file mode 100644 index 0000000000..66173efc6c --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetranslate.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "private/qdeclarativetranslate_p.h" +#include <private/qgraphicstransform_p.h> +#include <QDebug> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1TranslatePrivate : public QGraphicsTransformPrivate +{ +public: + QDeclarative1TranslatePrivate() + : x(0), y(0) {} + qreal x; + qreal y; +}; + +/*! + Constructs an empty QDeclarative1Translate object with the given \a parent. +*/ +QDeclarative1Translate::QDeclarative1Translate(QObject *parent) + : QGraphicsTransform(*new QDeclarative1TranslatePrivate, parent) +{ +} + +/*! + Destroys the graphics scale. +*/ +QDeclarative1Translate::~QDeclarative1Translate() +{ +} + +/*! + \property QDeclarative1Translate::x + \brief the horizontal translation. + + The translation can be any real number; the default value is 0.0. + + \sa y +*/ +qreal QDeclarative1Translate::x() const +{ + Q_D(const QDeclarative1Translate); + return d->x; +} +void QDeclarative1Translate::setX(qreal x) +{ + Q_D(QDeclarative1Translate); + if (d->x == x) + return; + d->x = x; + update(); + emit xChanged(); +} + +/*! + \property QDeclarative1Translate::y + \brief the vertical translation. + + The translation can be any real number; the default value is 0.0. + + \sa x +*/ +qreal QDeclarative1Translate::y() const +{ + Q_D(const QDeclarative1Translate); + return d->y; +} +void QDeclarative1Translate::setY(qreal y) +{ + Q_D(QDeclarative1Translate); + if (d->y == y) + return; + d->y = y; + update(); + emit yChanged(); +} + +void QDeclarative1Translate::applyTo(QMatrix4x4 *matrix) const +{ + Q_D(const QDeclarative1Translate); + matrix->translate(d->x, d->y, 0); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/graphicsitems/qdeclarativetranslate_p.h b/src/qtquick1/graphicsitems/qdeclarativetranslate_p.h new file mode 100644 index 0000000000..be5cd74486 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativetranslate_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSLATE_H +#define QDECLARATIVETRANSLATE_H + +#include "qdeclarativeitem.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1TranslatePrivate; + +class Q_AUTOTEST_EXPORT QDeclarative1Translate : public QGraphicsTransform +{ + Q_OBJECT + + Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged) + Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged) + +public: + QDeclarative1Translate(QObject *parent = 0); + ~QDeclarative1Translate(); + + 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(QDeclarative1Translate) + Q_DISABLE_COPY(QDeclarative1Translate) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Translate) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/graphicsitems/qdeclarativevisualitemmodel.cpp b/src/qtquick1/graphicsitems/qdeclarativevisualitemmodel.cpp new file mode 100644 index 0000000000..e712ca1888 --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativevisualitemmodel.cpp @@ -0,0 +1,1429 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativevisualitemmodel_p.h" + +#include "QtQuick1/qdeclarativeitem.h" + +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/private/qdeclarativecontext_p.h> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtQuick1/private/qdeclarativepackage_p.h> +#include <QtQuick1/private/qdeclarativeopenmetaobject_p.h> +#include <QtQuick1/private/qdeclarativelistaccessor_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativedata_p.h> +#include <QtDeclarative/private/qdeclarativepropertycache_p.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +#include <qgraphicsscene.h> +#include <QtDeclarative/private/qlistmodelinterface_p.h> +#include <qhash.h> +#include <qlist.h> +#include <QtDeclarative/private/qmetaobjectbuilder_p.h> +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +QHash<QObject*, QDeclarative1VisualItemModelAttached*> QDeclarative1VisualItemModelAttached::attachedProperties; + + +class QDeclarative1VisualItemModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1VisualItemModel) +public: + QDeclarative1VisualItemModelPrivate() : QObjectPrivate() {} + + static void children_append(QDeclarativeListProperty<QDeclarativeItem> *prop, QDeclarativeItem *item) { + QDeclarative_setParent_noEvent(item, prop->object); + static_cast<QDeclarative1VisualItemModelPrivate *>(prop->data)->children.append(Item(item)); + static_cast<QDeclarative1VisualItemModelPrivate *>(prop->data)->itemAppended(); + static_cast<QDeclarative1VisualItemModelPrivate *>(prop->data)->emitChildrenChanged(); + } + + static int children_count(QDeclarativeListProperty<QDeclarativeItem> *prop) { + return static_cast<QDeclarative1VisualItemModelPrivate *>(prop->data)->children.count(); + } + + static QDeclarativeItem *children_at(QDeclarativeListProperty<QDeclarativeItem> *prop, int index) { + return static_cast<QDeclarative1VisualItemModelPrivate *>(prop->data)->children.at(index).item; + } + + void itemAppended() { + Q_Q(QDeclarative1VisualItemModel); + QDeclarative1VisualItemModelAttached *attached = QDeclarative1VisualItemModelAttached::properties(children.last().item); + attached->setIndex(children.count()-1); + emit q->itemsInserted(children.count()-1, 1); + emit q->countChanged(); + } + + void emitChildrenChanged() { + Q_Q(QDeclarative1VisualItemModel); + emit q->childrenChanged(); + } + + int indexOf(QDeclarativeItem *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + class Item { + public: + Item(QDeclarativeItem *i) : item(i), ref(0) {} + + void addRef() { ++ref; } + bool deref() { return --ref == 0; } + + QDeclarativeItem *item; + int ref; + }; + + QList<Item> children; +}; + + +/*! + \qmlclass VisualItemModel QDeclarative1VisualItemModel + \ingroup qml-working-with-data + \since 4.7 + \brief The VisualItemModel allows items to be provided to a view. + + A VisualItemModel contains the visual items to be used in a view. + When a VisualItemModel is used in a view, the view does not require + a delegate since the VisualItemModel already contains the visual + delegate (items). + + An item can determine its index within the + model via the \l{VisualItemModel::index}{index} attached property. + + The example below places three colored rectangles in a ListView. + \code + import QtQuick 1.0 + + Rectangle { + VisualItemModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode + + \image visualitemmodel.png + + \sa {declarative/modelviews/visualitemmodel}{VisualItemModel example} +*/ +QDeclarative1VisualItemModel::QDeclarative1VisualItemModel(QObject *parent) + : QDeclarative1VisualModel(*(new QDeclarative1VisualItemModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int VisualItemModel::index + This attached property holds the index of this delegate's item within the model. + + It is attached to each instance of the delegate. +*/ + +QDeclarativeListProperty<QDeclarativeItem> QDeclarative1VisualItemModel::children() +{ + Q_D(QDeclarative1VisualItemModel); + return QDeclarativeListProperty<QDeclarativeItem>(this, d, d->children_append, + d->children_count, d->children_at); +} + +/*! + \qmlproperty int VisualItemModel::count + + The number of items in the model. This property is readonly. +*/ +int QDeclarative1VisualItemModel::count() const +{ + Q_D(const QDeclarative1VisualItemModel); + return d->children.count(); +} + +bool QDeclarative1VisualItemModel::isValid() const +{ + return true; +} + +QDeclarativeItem *QDeclarative1VisualItemModel::item(int index, bool) +{ + Q_D(QDeclarative1VisualItemModel); + QDeclarative1VisualItemModelPrivate::Item &item = d->children[index]; + item.addRef(); + return item.item; +} + +QDeclarative1VisualModel::ReleaseFlags QDeclarative1VisualItemModel::release(QDeclarativeItem *item) +{ + Q_D(QDeclarative1VisualItemModel); + int idx = d->indexOf(item); + if (idx >= 0) { + if (d->children[idx].deref()) { + if (item->scene()) + item->scene()->removeItem(item); + QDeclarative_setParent_noEvent(item, this); + } + } + return 0; +} + +bool QDeclarative1VisualItemModel::completePending() const +{ + return false; +} + +void QDeclarative1VisualItemModel::completeItem() +{ + // Nothing to do +} + +QString QDeclarative1VisualItemModel::stringValue(int index, const QString &name) +{ + Q_D(QDeclarative1VisualItemModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QDeclarativeEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); +} + +int QDeclarative1VisualItemModel::indexOf(QDeclarativeItem *item, QObject *) const +{ + Q_D(const QDeclarative1VisualItemModel); + return d->indexOf(item); +} + +QDeclarative1VisualItemModelAttached *QDeclarative1VisualItemModel::qmlAttachedProperties(QObject *obj) +{ + return QDeclarative1VisualItemModelAttached::properties(obj); +} + +//============================================================================ + +class VDMDelegateDataType : public QDeclarative1OpenMetaObjectType +{ +public: + VDMDelegateDataType(const QMetaObject *base, QDeclarativeEngine *engine) : QDeclarative1OpenMetaObjectType(base, engine) {} + + void propertyCreated(int, QMetaPropertyBuilder &prop) { + prop.setWritable(false); + } +}; + +class QDeclarative1VisualDataModelParts; +class QDeclarative1VisualDataModelData; +class QDeclarative1VisualDataModelPrivate : public QObjectPrivate +{ +public: + QDeclarative1VisualDataModelPrivate(QDeclarativeContext *); + + static QDeclarative1VisualDataModelPrivate *get(QDeclarative1VisualDataModel *m) { + return static_cast<QDeclarative1VisualDataModelPrivate *>(QObjectPrivate::get(m)); + } + + QDeclarativeGuard<QListModelInterface> m_listModelInterface; + QDeclarativeGuard<QAbstractItemModel> m_abstractItemModel; + QDeclarativeGuard<QDeclarative1VisualDataModel> 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() == QDeclarative1ListAccessor::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 *, QDeclarative1Package*> m_packaged; + + QDeclarative1VisualDataModelParts *m_parts; + friend class QDeclarative1VisualItemParts; + + VDMDelegateDataType *m_delegateDataType; + friend class QDeclarative1VisualDataModelData; + bool m_metaDataCreated : 1; + bool m_metaDataCacheable : 1; + bool m_delegateValidated : 1; + bool m_completePending : 1; + + QDeclarative1VisualDataModelData *data(QObject *item); + + QVariant m_modelVariant; + QDeclarative1ListAccessor *m_listAccessor; + + QModelIndex m_root; + QList<QByteArray> watchedRoles; + QList<int> watchedRoleIds; +}; + +class QDeclarative1VisualDataModelDataMetaObject : public QDeclarative1OpenMetaObject +{ +public: + QDeclarative1VisualDataModelDataMetaObject(QObject *parent, QDeclarative1OpenMetaObjectType *type) + : QDeclarative1OpenMetaObject(parent, type) {} + + virtual QVariant initialValue(int); + virtual int createProperty(const char *, const char *); + +private: + friend class QDeclarative1VisualDataModelData; +}; + +class QDeclarative1VisualDataModelData : public QObject +{ +Q_OBJECT +public: + QDeclarative1VisualDataModelData(int index, QDeclarative1VisualDataModel *model); + ~QDeclarative1VisualDataModelData(); + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const; + void setIndex(int index); + + int propForRole(int) const; + int modelDataPropertyId() const { + QDeclarative1VisualDataModelPrivate *model = QDeclarative1VisualDataModelPrivate::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 QDeclarative1VisualDataModelDataMetaObject; + int m_index; + QDeclarativeGuard<QDeclarative1VisualDataModel> m_model; + QDeclarative1VisualDataModelDataMetaObject *m_meta; +}; + +int QDeclarative1VisualDataModelData::propForRole(int id) const +{ + QDeclarative1VisualDataModelPrivate *model = QDeclarative1VisualDataModelPrivate::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 QDeclarative1VisualDataModelData::setValue(int id, const QVariant &val) +{ + m_meta->setValue(id, val); +} + +int QDeclarative1VisualDataModelDataMetaObject::createProperty(const char *name, const char *type) +{ + QDeclarative1VisualDataModelData *data = + static_cast<QDeclarative1VisualDataModelData *>(object()); + + if (!data->m_model) + return -1; + + QDeclarative1VisualDataModelPrivate *model = QDeclarative1VisualDataModelPrivate::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() == QDeclarative1ListAccessor::ListProperty) { + model->ensureRoles(); + if (qstrcmp(name,"modelData") == 0) + return QDeclarative1OpenMetaObject::createProperty(name, type); + } + } + return -1; +} + +QVariant QDeclarative1VisualDataModelDataMetaObject::initialValue(int propId) +{ + QDeclarative1VisualDataModelData *data = + static_cast<QDeclarative1VisualDataModelData *>(object()); + + Q_ASSERT(data->m_model); + QDeclarative1VisualDataModelPrivate *model = QDeclarative1VisualDataModelPrivate::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() == QDeclarative1ListAccessor::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(); +} + +QDeclarative1VisualDataModelData::QDeclarative1VisualDataModelData(int index, + QDeclarative1VisualDataModel *model) +: m_index(index), m_model(model), +m_meta(new QDeclarative1VisualDataModelDataMetaObject(this, QDeclarative1VisualDataModelPrivate::get(model)->m_delegateDataType)) +{ + ensureProperties(); +} + +QDeclarative1VisualDataModelData::~QDeclarative1VisualDataModelData() +{ +} + +void QDeclarative1VisualDataModelData::ensureProperties() +{ + QDeclarative1VisualDataModelPrivate *modelPriv = QDeclarative1VisualDataModelPrivate::get(m_model); + if (modelPriv->m_metaDataCacheable) { + if (!modelPriv->m_metaDataCreated) + modelPriv->createMetaData(); + if (modelPriv->m_metaDataCreated) + m_meta->setCached(true); + } +} + +int QDeclarative1VisualDataModelData::index() const +{ + return m_index; +} + +// This is internal only - it should not be set from qml +void QDeclarative1VisualDataModelData::setIndex(int index) +{ + m_index = index; + emit indexChanged(); +} + +//--------------------------------------------------------------------------- + +class QDeclarative1VisualDataModelPartsMetaObject : public QDeclarative1OpenMetaObject +{ +public: + QDeclarative1VisualDataModelPartsMetaObject(QObject *parent) + : QDeclarative1OpenMetaObject(parent) {} + + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual QVariant initialValue(int); +}; + +class QDeclarative1VisualDataModelParts : public QObject +{ +Q_OBJECT +public: + QDeclarative1VisualDataModelParts(QDeclarative1VisualDataModel *parent); + +private: + friend class QDeclarative1VisualDataModelPartsMetaObject; + QDeclarative1VisualDataModel *model; +}; + +void QDeclarative1VisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); +} + +QVariant QDeclarative1VisualDataModelPartsMetaObject::initialValue(int id) +{ + QDeclarative1VisualDataModel *m = new QDeclarative1VisualDataModel; + m->setParent(object()); + m->setPart(QString::fromUtf8(name(id))); + m->setModel(QVariant::fromValue(static_cast<QDeclarative1VisualDataModelParts *>(object())->model)); + + QVariant var = QVariant::fromValue((QObject *)m); + return var; +} + +QDeclarative1VisualDataModelParts::QDeclarative1VisualDataModelParts(QDeclarative1VisualDataModel *parent) +: QObject(parent), model(parent) +{ + new QDeclarative1VisualDataModelPartsMetaObject(this); +} + +QDeclarative1VisualDataModelPrivate::QDeclarative1VisualDataModelPrivate(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) +{ +} + +QDeclarative1VisualDataModelData *QDeclarative1VisualDataModelPrivate::data(QObject *item) +{ + QDeclarative1VisualDataModelData *dataItem = + item->findChild<QDeclarative1VisualDataModelData *>(); + Q_ASSERT(dataItem); + return dataItem; +} + +//--------------------------------------------------------------------------- + +/*! + \qmlclass VisualDataModel QDeclarative1VisualDataModel + \ingroup qml-working-with-data + \brief The VisualDataModel encapsulates a model and delegate + + A VisualDataModel encapsulates a model and the delegate that will + be instantiated for items in the model. + + It is usually not necessary to create VisualDataModel elements. + However, it can be useful for manipulating and accessing the \l modelIndex + when a QAbstractItemModel subclass is used as the + model. Also, VisualDataModel is used together with \l Package to + provide delegates to multiple views. + + The example below illustrates using a VisualDataModel with a ListView. + + \snippet doc/src/snippets/declarative/visualdatamodel.qml 0 +*/ + +QDeclarative1VisualDataModel::QDeclarative1VisualDataModel() +: QDeclarative1VisualModel(*(new QDeclarative1VisualDataModelPrivate(0))) +{ +} + +QDeclarative1VisualDataModel::QDeclarative1VisualDataModel(QDeclarativeContext *ctxt, QObject *parent) +: QDeclarative1VisualModel(*(new QDeclarative1VisualDataModelPrivate(ctxt)), parent) +{ +} + +QDeclarative1VisualDataModel::~QDeclarative1VisualDataModel() +{ + Q_D(QDeclarative1VisualDataModel); + if (d->m_listAccessor) + delete d->m_listAccessor; + if (d->m_delegateDataType) + d->m_delegateDataType->release(); +} + +/*! + \qmlproperty model VisualDataModel::model + This property holds the model providing data for the VisualDataModel. + + The model provides a set of data that is used to create the items + for a view. For large or dynamic datasets the model is usually + provided by a C++ model object. The C++ model object must be a \l + {QAbstractItemModel} subclass or a simple list. + + Models can also be created directly in QML, using a \l{ListModel} or + \l{XmlListModel}. + + \sa {qmlmodels}{Data Models} +*/ +QVariant QDeclarative1VisualDataModel::model() const +{ + Q_D(const QDeclarative1VisualDataModel); + return d->m_modelVariant; +} + +void QDeclarative1VisualDataModel::setModel(const QVariant &model) +{ + Q_D(QDeclarative1VisualDataModel); + 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,QDeclarative1Package*)), + this, SLOT(_q_createdPackage(int,QDeclarative1Package*))); + QObject::disconnect(d->m_visualItemModel, SIGNAL(destroyingPackage(QDeclarative1Package*)), + this, SLOT(_q_destroyingPackage(QDeclarative1Package*))); + 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(&QDeclarative1VisualDataModelData::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<QDeclarative1VisualDataModel *>(model))) { + 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,QDeclarative1Package*)), + this, SLOT(_q_createdPackage(int,QDeclarative1Package*))); + QObject::connect(d->m_visualItemModel, SIGNAL(destroyingPackage(QDeclarative1Package*)), + this, SLOT(_q_destroyingPackage(QDeclarative1Package*))); + return; + } + d->m_listAccessor = new QDeclarative1ListAccessor; + d->m_listAccessor->setList(model, d->m_context?d->m_context->engine():qmlEngine(this)); + if (d->m_listAccessor->type() != QDeclarative1ListAccessor::ListProperty) + d->m_metaDataCacheable = true; + if (d->m_delegate && d->modelCount()) { + emit itemsInserted(0, d->modelCount()); + emit countChanged(); + } +} + +/*! + \qmlproperty Component VisualDataModel::delegate + + The delegate provides a template defining each item instantiated by a view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qmlmodels}{Data Model}. +*/ +QDeclarativeComponent *QDeclarative1VisualDataModel::delegate() const +{ + Q_D(const QDeclarative1VisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->delegate(); + return d->m_delegate; +} + +void QDeclarative1VisualDataModel::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QDeclarative1VisualDataModel); + 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(); + } +} + +/*! + \qmlproperty QModelIndex VisualDataModel::rootIndex + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. \c rootIndex allows the children of + any node in a QAbstractItemModel to be provided by this model. + + This property only affects models of type QAbstractItemModel that + are hierarchical (e.g, a tree model). + + For example, here is a simple interactive file system browser. + When a directory name is clicked, the view's \c rootIndex is set to the + QModelIndex node of the clicked directory, thus updating the view to show + the new directory's contents. + + \c main.cpp: + \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/main.cpp 0 + + \c view.qml: + \snippet doc/src/snippets/declarative/visualdatamodel_rootindex/view.qml 0 + + If the \l model is a QAbstractItemModel subclass, the delegate can also + reference a \c hasModelChildren property (optionally qualified by a + \e model. prefix) that indicates whether the delegate's model item has + any child nodes. + + + \sa modelIndex(), parentModelIndex() +*/ +QVariant QDeclarative1VisualDataModel::rootIndex() const +{ + Q_D(const QDeclarative1VisualDataModel); + return QVariant::fromValue(d->m_root); +} + +void QDeclarative1VisualDataModel::setRootIndex(const QVariant &root) +{ + Q_D(QDeclarative1VisualDataModel); + 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(); + } +} + + +/*! + \qmlmethod QModelIndex VisualDataModel::modelIndex(int index) + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the specified index. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QDeclarative1VisualDataModel::modelIndex(int idx) const +{ + Q_D(const QDeclarative1VisualDataModel); + if (d->m_abstractItemModel) + return QVariant::fromValue(d->m_abstractItemModel->index(idx, 0, d->m_root)); + return QVariant::fromValue(QModelIndex()); +} + +/*! + \qmlmethod QModelIndex VisualDataModel::parentModelIndex() + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the parent of the current rootIndex. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QDeclarative1VisualDataModel::parentModelIndex() const +{ + Q_D(const QDeclarative1VisualDataModel); + if (d->m_abstractItemModel) + return QVariant::fromValue(d->m_abstractItemModel->parent(d->m_root)); + return QVariant::fromValue(QModelIndex()); +} + +QString QDeclarative1VisualDataModel::part() const +{ + Q_D(const QDeclarative1VisualDataModel); + return d->m_part; +} + +void QDeclarative1VisualDataModel::setPart(const QString &part) +{ + Q_D(QDeclarative1VisualDataModel); + d->m_part = part; +} + +int QDeclarative1VisualDataModel::count() const +{ + Q_D(const QDeclarative1VisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->count(); + if (!d->m_delegate) + return 0; + return d->modelCount(); +} + +QDeclarativeItem *QDeclarative1VisualDataModel::item(int index, bool complete) +{ + Q_D(QDeclarative1VisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->item(index, d->m_part.toUtf8(), complete); + return item(index, QByteArray(), complete); +} + +/* + Returns ReleaseStatus flags. +*/ +QDeclarative1VisualDataModel::ReleaseFlags QDeclarative1VisualDataModel::release(QDeclarativeItem *item) +{ + Q_D(QDeclarative1VisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->release(item); + + ReleaseFlags stat = 0; + QObject *obj = item; + bool inPackage = false; + + QHash<QObject*,QDeclarative1Package*>::iterator it = d->m_packaged.find(item); + if (it != d->m_packaged.end()) { + QDeclarative1Package *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<QDeclarative1Package*>(obj)); + } else { + if (item->scene()) + item->scene()->removeItem(item); + } + stat |= Destroyed; + obj->deleteLater(); + } else if (!inPackage) { + stat |= Referenced; + } + + return stat; +} + +/*! + \qmlproperty object VisualDataModel::parts + + The \a parts property selects a VisualDataModel which creates + delegates from the part named. This is used in conjunction with + the \l Package element. + + For example, the code below selects a model which creates + delegates named \e list from a \l Package: + + \code + VisualDataModel { + id: visualModel + delegate: Package { + Item { Package.name: "list" } + } + model: myModel + } + + ListView { + width: 200; height:200 + model: visualModel.parts.list + } + \endcode + + \sa Package +*/ +QObject *QDeclarative1VisualDataModel::parts() +{ + Q_D(QDeclarative1VisualDataModel); + if (!d->m_parts) + d->m_parts = new QDeclarative1VisualDataModelParts(this); + return d->m_parts; +} + +QDeclarativeItem *QDeclarative1VisualDataModel::item(int index, const QByteArray &viewId, bool complete) +{ + Q_D(QDeclarative1VisualDataModel); + 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); + QDeclarative1VisualDataModelData *data = new QDeclarative1VisualDataModelData(index, this); + if ((!d->m_listModelInterface || !d->m_abstractItemModel) && d->m_listAccessor + && d->m_listAccessor->type() == QDeclarative1ListAccessor::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 (QDeclarative1Package *package = qobject_cast<QDeclarative1Package *>(nobj)) + emit createdPackage(index, package); + } else { + delete data; + delete ctxt; + qmlInfo(this, d->m_delegate->errors()) << "Error creating delegate"; + } + } + QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(nobj); + if (!item) { + QDeclarative1Package *package = qobject_cast<QDeclarative1Package *>(nobj); + if (package) { + QObject *o = package->part(QString::fromUtf8(viewId)); + item = qobject_cast<QDeclarativeItem *>(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) << QDeclarative1VisualDataModel::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 QDeclarative1VisualDataModel::completePending() const +{ + Q_D(const QDeclarative1VisualDataModel); + if (d->m_visualItemModel) + return d->m_visualItemModel->completePending(); + return d->m_completePending; +} + +void QDeclarative1VisualDataModel::completeItem() +{ + Q_D(QDeclarative1VisualDataModel); + if (d->m_visualItemModel) { + d->m_visualItemModel->completeItem(); + return; + } + + d->m_delegate->completeCreate(); + d->m_completePending = false; +} + +QString QDeclarative1VisualDataModel::stringValue(int index, const QString &name) +{ + Q_D(QDeclarative1VisualDataModel); + 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 QDeclarative1VisualDataModelData(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 QDeclarative1VisualDataModel::indexOf(QDeclarativeItem *item, QObject *) const +{ + QVariant val = QDeclarativeEngine::contextForObject(item)->contextProperty(QLatin1String("index")); + return val.toInt(); + return -1; +} + +void QDeclarative1VisualDataModel::setWatchedRoles(QList<QByteArray> roles) +{ + Q_D(QDeclarative1VisualDataModel); + d->watchedRoles = roles; + d->watchedRoleIds.clear(); +} + +void QDeclarative1VisualDataModel::_q_itemsChanged(int index, int count, + const QList<int> &roles) +{ + Q_D(QDeclarative1VisualDataModel); + 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,QDeclarative1VisualDataModelPrivate::ObjectRef>::ConstIterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ++iter) { + const int idx = iter.key(); + + if (idx >= index && idx < index+count) { + QDeclarative1VisualDataModelPrivate::ObjectRef objRef = *iter; + QDeclarative1VisualDataModelData *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 QDeclarative1VisualDataModel::_q_itemsInserted(int index, int count) +{ + Q_D(QDeclarative1VisualDataModel); + if (!count) + return; + // XXX - highly inefficient + QHash<int,QDeclarative1VisualDataModelPrivate::ObjectRef> items; + for (QHash<int,QDeclarative1VisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if (iter.key() >= index) { + QDeclarative1VisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() + count; + iter = d->m_cache.erase(iter); + + items.insert(index, objRef); + + QDeclarative1VisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsInserted(index, count); + emit countChanged(); +} + +void QDeclarative1VisualDataModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QDeclarative1VisualDataModel); + if (!count) + return; + // XXX - highly inefficient + QHash<int, QDeclarative1VisualDataModelPrivate::ObjectRef> items; + for (QHash<int, QDeclarative1VisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + if (iter.key() >= index && iter.key() < index + count) { + QDeclarative1VisualDataModelPrivate::ObjectRef objRef = *iter; + iter = d->m_cache.erase(iter); + items.insertMulti(-1, objRef); //XXX perhaps better to maintain separately + QDeclarative1VisualDataModelData *data = d->data(objRef.obj); + data->setIndex(-1); + } else if (iter.key() >= index + count) { + QDeclarative1VisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() - count; + iter = d->m_cache.erase(iter); + items.insert(index, objRef); + QDeclarative1VisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + + d->m_cache.unite(items); + emit itemsRemoved(index, count); + emit countChanged(); +} + +void QDeclarative1VisualDataModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QDeclarative1VisualDataModel); + // XXX - highly inefficient + QHash<int,QDeclarative1VisualDataModelPrivate::ObjectRef> items; + for (QHash<int,QDeclarative1VisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin(); + iter != d->m_cache.end(); ) { + + if (iter.key() >= from && iter.key() < from + count) { + QDeclarative1VisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() - from + to; + iter = d->m_cache.erase(iter); + + items.insert(index, objRef); + + QDeclarative1VisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + for (QHash<int,QDeclarative1VisualDataModelPrivate::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)) { + QDeclarative1VisualDataModelPrivate::ObjectRef objRef = *iter; + int index = iter.key() + diff; + iter = d->m_cache.erase(iter); + + items.insert(index, objRef); + + QDeclarative1VisualDataModelData *data = d->data(objRef.obj); + data->setIndex(index); + } else { + ++iter; + } + } + d->m_cache.unite(items); + + emit itemsMoved(from, to, count); +} + +void QDeclarative1VisualDataModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QDeclarative1VisualDataModel); + if (parent == d->m_root) + _q_itemsInserted(begin, end - begin + 1); +} + +void QDeclarative1VisualDataModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QDeclarative1VisualDataModel); + if (parent == d->m_root) + _q_itemsRemoved(begin, end - begin + 1); +} + +void QDeclarative1VisualDataModel::_q_rowsMoved(const QModelIndex &sourceParent, int sourceStart, int sourceEnd, const QModelIndex &destinationParent, int destinationRow) +{ + Q_D(QDeclarative1VisualDataModel); + 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 QDeclarative1VisualDataModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end) +{ + Q_D(QDeclarative1VisualDataModel); + if (begin.parent() == d->m_root) + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, d->m_roles); +} + +void QDeclarative1VisualDataModel::_q_layoutChanged() +{ + Q_D(QDeclarative1VisualDataModel); + _q_itemsChanged(0, count(), d->m_roles); +} + +void QDeclarative1VisualDataModel::_q_modelReset() +{ + Q_D(QDeclarative1VisualDataModel); + 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 QDeclarative1VisualDataModel::_q_createdPackage(int index, QDeclarative1Package *package) +{ + Q_D(QDeclarative1VisualDataModel); + emit createdItem(index, qobject_cast<QDeclarativeItem*>(package->part(d->m_part))); +} + +void QDeclarative1VisualDataModel::_q_destroyingPackage(QDeclarative1Package *package) +{ + Q_D(QDeclarative1VisualDataModel); + emit destroyingItem(qobject_cast<QDeclarativeItem*>(package->part(d->m_part))); +} + + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QListModelInterface) + +#include <qdeclarativevisualitemmodel.moc> diff --git a/src/qtquick1/graphicsitems/qdeclarativevisualitemmodel_p.h b/src/qtquick1/graphicsitems/qdeclarativevisualitemmodel_p.h new file mode 100644 index 0000000000..e9729d08ce --- /dev/null +++ b/src/qtquick1/graphicsitems/qdeclarativevisualitemmodel_p.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVISUALDATAMODEL_H +#define QDECLARATIVEVISUALDATAMODEL_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 QDeclarativeComponent; +class QDeclarativeItem; +class QDeclarative1Package; +class QDeclarative1VisualDataModelPrivate; + +class Q_AUTOTEST_EXPORT QDeclarative1VisualModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + virtual ~QDeclarative1VisualModel() {} + + enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; + Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) + + virtual int count() const = 0; + virtual bool isValid() const = 0; + virtual QDeclarativeItem *item(int index, bool complete=true) = 0; + virtual ReleaseFlags release(QDeclarativeItem *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(QDeclarativeItem *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, QDeclarativeItem *item); + void destroyingItem(QDeclarativeItem *item); + +protected: + QDeclarative1VisualModel(QObjectPrivate &dd, QObject *parent = 0) + : QObject(dd, parent) {} + +private: + Q_DISABLE_COPY(QDeclarative1VisualModel) +}; + +class QDeclarative1VisualItemModelAttached; +class QDeclarative1VisualItemModelPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1VisualItemModel : public QDeclarative1VisualModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1VisualItemModel) + + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeItem> children READ children NOTIFY childrenChanged DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "children") + +public: + QDeclarative1VisualItemModel(QObject *parent=0); + virtual ~QDeclarative1VisualItemModel() {} + + virtual int count() const; + virtual bool isValid() const; + virtual QDeclarativeItem *item(int index, bool complete=true); + virtual ReleaseFlags release(QDeclarativeItem *item); + virtual bool completePending() const; + virtual void completeItem(); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList<QByteArray>) {} + + virtual int indexOf(QDeclarativeItem *item, QObject *objectContext) const; + + QDeclarativeListProperty<QDeclarativeItem> children(); + + static QDeclarative1VisualItemModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void childrenChanged(); + +private: + Q_DISABLE_COPY(QDeclarative1VisualItemModel) +}; + + +class Q_AUTOTEST_EXPORT QDeclarative1VisualDataModel : public QDeclarative1VisualModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1VisualDataModel) + + 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: + QDeclarative1VisualDataModel(); + QDeclarative1VisualDataModel(QDeclarativeContext *, QObject *parent=0); + virtual ~QDeclarative1VisualDataModel(); + + 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; } + QDeclarativeItem *item(int index, bool complete=true); + QDeclarativeItem *item(int index, const QByteArray &, bool complete=true); + ReleaseFlags release(QDeclarativeItem *item); + bool completePending() const; + void completeItem(); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList<QByteArray> roles); + + int indexOf(QDeclarativeItem *item, QObject *objectContext) const; + + QObject *parts(); + +Q_SIGNALS: + void createdPackage(int index, QDeclarative1Package *package); + void destroyingPackage(QDeclarative1Package *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, QDeclarative1Package *package); + void _q_destroyingPackage(QDeclarative1Package *package); + +private: + Q_DISABLE_COPY(QDeclarative1VisualDataModel) +}; + +class QDeclarative1VisualItemModelAttached : public QObject +{ + Q_OBJECT + +public: + QDeclarative1VisualItemModelAttached(QObject *parent) + : QObject(parent), m_index(0) {} + ~QDeclarative1VisualItemModelAttached() { + 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 QDeclarative1VisualItemModelAttached *properties(QObject *obj) { + QDeclarative1VisualItemModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QDeclarative1VisualItemModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + + static QHash<QObject*, QDeclarative1VisualItemModelAttached*> attachedProperties; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1VisualModel) +QML_DECLARE_TYPE(QDeclarative1VisualItemModel) +QML_DECLARE_TYPEINFO(QDeclarative1VisualItemModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarative1VisualDataModel) + +QT_END_HEADER + +#endif // QDECLARATIVEVISUALDATAMODEL_H diff --git a/src/qtquick1/qtquick1.cpp b/src/qtquick1/qtquick1.cpp new file mode 100644 index 0000000000..b8c0e109ba --- /dev/null +++ b/src/qtquick1/qtquick1.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/private/qdeclarativefastproperties_p.h> +#include <QtQuick1/qdeclarativeitem.h> +#include <QtQuick1/private/qdeclarativeitem_p.h> +#include <QtQuick1/private/qdeclarativeutilmodule_p.h> +#include <QtQuick1/private/qdeclarativeitemsmodule_p.h> +#include "qtquick1_p.h" + +QT_BEGIN_NAMESPACE + +void QDeclarativeQtQuick1Module::defineModule(QDeclarativeQtQuick1Module::Module module) +{ + QDeclarativeFastProperties::instance()->add(&QDeclarativeItem::staticMetaObject, + QDeclarativeItem::staticMetaObject.indexOfProperty("parent"), + QDeclarativeItemPrivate::parentProperty); + if (module == QtQuick1) + qmlRegisterBaseTypes("QtQuick", 1, 0); + else if (module == Qt47) + qmlRegisterBaseTypes("Qt", 4, 7); + QDeclarative1UtilModule::defineModule(module); + QDeclarative1ItemModule::defineModule(module); +} + +QT_END_NAMESPACE + diff --git a/src/qtquick1/qtquick1.pro b/src/qtquick1/qtquick1.pro new file mode 100644 index 0000000000..e3dd0298cd --- /dev/null +++ b/src/qtquick1/qtquick1.pro @@ -0,0 +1,42 @@ +load(qt_module) + +TARGET = QtQuick1 +QPRO_PWD = $$PWD + +CONFIG += module +CONFIG += dll warn_on +MODULE_PRI += ../../modules/qt_qtquick1.pri + +QT += testlib-private declarative script testlib declarative-private core-private gui-private script-private network +DEFINES += QT_NO_URL_CAST_FROM_STRING + +load(qt_module_config) + +# Install qtquick1.prf into the Qt mkspecs so that "CONFIG += qtquick1" +# can be used in customer applications to build against QtQuick 1. +feature.path = $$[QT_INSTALL_DATA]/mkspecs/features +feature.files = $$PWD/features/qtquick1.prf +INSTALLS += feature + +symbian { + DEFINES += QT_MAKEDLL + CONFIG += epocallowdlldata + contains(QT_EDITION, OpenSource) { + TARGET.CAPABILITY = LocalServices NetworkServices ReadUserData UserEnvironment WriteUserData + } else { + TARGET.CAPABILITY = All -Tcb + } +} + +#INCLUDEPATH += $$PWD/QtQuick1 +#INCLUDEPATH += $$PWD + +include(util/util.pri) +include(graphicsitems/graphicsitems.pri) + +HEADERS += qtquick1_p.h +SOURCES += qtquick1.cpp + +DEFINES += QT_NO_OPENTYPE +INCLUDEPATH += $$QT_SOURCE_TREE/src/3rdparty/harfbuzz/src + diff --git a/src/qtquick1/qtquick1_p.h b/src/qtquick1/qtquick1_p.h new file mode 100644 index 0000000000..36dfd1ad05 --- /dev/null +++ b/src/qtquick1/qtquick1_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTQUICK1_H +#define QTQUICK1_H + +#include <qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class Q_DECLARATIVE_EXPORT QDeclarativeQtQuick1Module +{ +public: + enum Module { QtQuick1, Qt47 }; + static void defineModule(Module module); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTQUICK1_H diff --git a/src/qtquick1/util/qdeclarativeanimation.cpp b/src/qtquick1/util/qdeclarativeanimation.cpp new file mode 100644 index 0000000000..3b05429c84 --- /dev/null +++ b/src/qtquick1/util/qdeclarativeanimation.cpp @@ -0,0 +1,2956 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeanimation_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" + +#include "QtQuick1/private/qdeclarativebehavior_p.h" +#include "QtQuick1/private/qdeclarativestateoperations_p.h" +#include "QtDeclarative/private/qdeclarativecontext_p.h" + +#include <QtDeclarative/qdeclarativepropertyvaluesource.h> +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/private/qdeclarativestringconverters_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> +#include <QtDeclarative/private/qdeclarativemetatype_p.h> +#include <QtDeclarative/private/qdeclarativevaluetype_p.h> +#include <QtDeclarative/private/qdeclarativeproperty_p.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +#include <qvariant.h> +#include <qcolor.h> +#include <qfile.h> +#include <QParallelAnimationGroup> +#include <QSequentialAnimationGroup> +#include <QtCore/qset.h> +#include <QtCore/qrect.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> +#include <QtCore/qmath.h> + +#include <private/qvariantanimation_p.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass Animation QDeclarative1AbstractAnimation + \ingroup qml-animation-transition + \since 4.7 + \brief The Animation element is the base of all QML animations. + + The Animation element cannot be used directly in a QML file. It exists + to provide a set of common properties and methods, available across all the + other animation types that inherit from it. Attempting to use the Animation + element directly will result in an error. +*/ + +QDeclarative1AbstractAnimation::QDeclarative1AbstractAnimation(QObject *parent) +: QObject(*(new QDeclarative1AbstractAnimationPrivate), parent) +{ +} + +QDeclarative1AbstractAnimation::~QDeclarative1AbstractAnimation() +{ +} + +QDeclarative1AbstractAnimation::QDeclarative1AbstractAnimation(QDeclarative1AbstractAnimationPrivate &dd, QObject *parent) +: QObject(dd, parent) +{ +} + +/*! + \qmlproperty bool Animation::running + This property holds whether the animation is currently running. + + The \c running property can be set to declaratively control whether or not + an animation is running. The following example will animate a rectangle + whenever the \l MouseArea is pressed. + + \code + Rectangle { + width: 100; height: 100 + NumberAnimation on x { + running: myMouse.pressed + from: 0; to: 100 + } + MouseArea { id: myMouse } + } + \endcode + + Likewise, the \c running property can be read to determine if the animation + is running. In the following example the text element will indicate whether + or not the animation is running. + + \code + NumberAnimation { id: myAnimation } + Text { text: myAnimation.running ? "Animation is running" : "Animation is not running" } + \endcode + + Animations can also be started and stopped imperatively from JavaScript + using the \c start() and \c stop() methods. + + By default, animations are not running. Though, when the animations are assigned to properties, + as property value sources using the \e on syntax, they are set to running by default. +*/ +bool QDeclarative1AbstractAnimation::isRunning() const +{ + Q_D(const QDeclarative1AbstractAnimation); + return d->running; +} + +// the behavior calls this function +void QDeclarative1AbstractAnimation::notifyRunningChanged(bool running) +{ + Q_D(QDeclarative1AbstractAnimation); + if (d->disableUserControl && d->running != running) { + d->running = running; + emit runningChanged(running); + } +} + +//commence is called to start an animation when it is used as a +//simple animation, and not as part of a transition +void QDeclarative1AbstractAnimationPrivate::commence() +{ + Q_Q(QDeclarative1AbstractAnimation); + + QDeclarative1StateActions actions; + QDeclarativeProperties properties; + q->transition(actions, properties, QDeclarative1AbstractAnimation::Forward); + + q->qtAnimation()->start(); + if (q->qtAnimation()->state() != QAbstractAnimation::Running) { + running = false; + emit q->completed(); + } +} + +QDeclarativeProperty QDeclarative1AbstractAnimationPrivate::createProperty(QObject *obj, const QString &str, QObject *infoObj) +{ + QDeclarativeProperty prop(obj, str, qmlContext(infoObj)); + if (!prop.isValid()) { + qmlInfo(infoObj) << QDeclarative1AbstractAnimation::tr("Cannot animate non-existent property \"%1\"").arg(str); + return QDeclarativeProperty(); + } else if (!prop.isWritable()) { + qmlInfo(infoObj) << QDeclarative1AbstractAnimation::tr("Cannot animate read-only property \"%1\"").arg(str); + return QDeclarativeProperty(); + } + return prop; +} + +void QDeclarative1AbstractAnimation::setRunning(bool r) +{ + Q_D(QDeclarative1AbstractAnimation); + if (!d->componentComplete) { + d->running = r; + if (r == false) + d->avoidPropertyValueSourceStart = true; + else if (!d->registered) { + d->registered = true; + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); + } + return; + } + + if (d->running == r) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setRunning() cannot be used on non-root animation nodes."; + return; + } + + d->running = r; + if (d->running) { + bool supressStart = false; + if (d->alwaysRunToEnd && d->loopCount != 1 + && qtAnimation()->state() == QAbstractAnimation::Running) { + //we've restarted before the final loop finished; restore proper loop count + if (d->loopCount == -1) + qtAnimation()->setLoopCount(d->loopCount); + else + qtAnimation()->setLoopCount(qtAnimation()->currentLoop() + d->loopCount); + supressStart = true; //we want the animation to continue, rather than restart + } + + if (!d->connectedTimeLine) { + QObject::connect(qtAnimation(), SIGNAL(finished()), + this, SLOT(timelineComplete())); + d->connectedTimeLine = true; + } + if (!supressStart) + d->commence(); + emit started(); + } else { + if (d->alwaysRunToEnd) { + if (d->loopCount != 1) + qtAnimation()->setLoopCount(qtAnimation()->currentLoop()+1); //finish the current loop + } else + qtAnimation()->stop(); + + emit completed(); + } + + emit runningChanged(d->running); +} + +/*! + \qmlproperty bool Animation::paused + This property holds whether the animation is currently paused. + + The \c paused property can be set to declaratively control whether or not + an animation is paused. + + Animations can also be paused and resumed imperatively from JavaScript + using the \c pause() and \c resume() methods. + + By default, animations are not paused. +*/ +bool QDeclarative1AbstractAnimation::isPaused() const +{ + Q_D(const QDeclarative1AbstractAnimation); + return d->paused; +} + +void QDeclarative1AbstractAnimation::setPaused(bool p) +{ + Q_D(QDeclarative1AbstractAnimation); + if (d->paused == p) + return; + + if (d->group || d->disableUserControl) { + qmlInfo(this) << "setPaused() cannot be used on non-root animation nodes."; + return; + } + + d->paused = p; + if (d->paused) + qtAnimation()->pause(); + else + qtAnimation()->resume(); + + emit pausedChanged(d->paused); +} + +void QDeclarative1AbstractAnimation::classBegin() +{ + Q_D(QDeclarative1AbstractAnimation); + d->componentComplete = false; +} + +void QDeclarative1AbstractAnimation::componentComplete() +{ + Q_D(QDeclarative1AbstractAnimation); + d->componentComplete = true; +} + +void QDeclarative1AbstractAnimation::componentFinalized() +{ + Q_D(QDeclarative1AbstractAnimation); + if (d->running) { + d->running = false; + setRunning(true); + } +} + +/*! + \qmlproperty bool Animation::alwaysRunToEnd + This property holds whether the animation should run to completion when it is stopped. + + If this true the animation will complete its current iteration when it + is stopped - either by setting the \c running property to false, or by + calling the \c stop() method. The \c complete() method is not effected + by this value. + + This behavior is most useful when the \c repeat property is set, as the + animation will finish playing normally but not restart. + + By default, the alwaysRunToEnd property is not set. + + \note alwaysRunToEnd has no effect on animations in a Transition. +*/ +bool QDeclarative1AbstractAnimation::alwaysRunToEnd() const +{ + Q_D(const QDeclarative1AbstractAnimation); + return d->alwaysRunToEnd; +} + +void QDeclarative1AbstractAnimation::setAlwaysRunToEnd(bool f) +{ + Q_D(QDeclarative1AbstractAnimation); + if (d->alwaysRunToEnd == f) + return; + + d->alwaysRunToEnd = f; + emit alwaysRunToEndChanged(f); +} + +/*! + \qmlproperty int Animation::loops + This property holds the number of times the animation should play. + + By default, \c loops is 1: the animation will play through once and then stop. + + If set to Animation.Infinite, the animation will continuously repeat until it is explicitly + stopped - either by setting the \c running property to false, or by calling + the \c stop() method. + + In the following example, the rectangle will spin indefinitely. + + \code + Rectangle { + width: 100; height: 100; color: "green" + RotationAnimation on rotation { + loops: Animation.Infinite + from: 0 + to: 360 + } + } + \endcode +*/ +int QDeclarative1AbstractAnimation::loops() const +{ + Q_D(const QDeclarative1AbstractAnimation); + return d->loopCount; +} + +void QDeclarative1AbstractAnimation::setLoops(int loops) +{ + Q_D(QDeclarative1AbstractAnimation); + if (loops < 0) + loops = -1; + + if (loops == d->loopCount) + return; + + d->loopCount = loops; + qtAnimation()->setLoopCount(loops); + emit loopCountChanged(loops); +} + + +int QDeclarative1AbstractAnimation::currentTime() +{ + return qtAnimation()->currentLoopTime(); +} + +void QDeclarative1AbstractAnimation::setCurrentTime(int time) +{ + qtAnimation()->setCurrentTime(time); +} + +QDeclarative1AnimationGroup *QDeclarative1AbstractAnimation::group() const +{ + Q_D(const QDeclarative1AbstractAnimation); + return d->group; +} + +void QDeclarative1AbstractAnimation::setGroup(QDeclarative1AnimationGroup *g) +{ + Q_D(QDeclarative1AbstractAnimation); + if (d->group == g) + return; + if (d->group) + static_cast<QDeclarative1AnimationGroupPrivate *>(d->group->d_func())->animations.removeAll(this); + + d->group = g; + + if (d->group && !static_cast<QDeclarative1AnimationGroupPrivate *>(d->group->d_func())->animations.contains(this)) + static_cast<QDeclarative1AnimationGroupPrivate *>(d->group->d_func())->animations.append(this); + + //if (g) //if removed from a group, then the group should no longer be the parent + setParent(g); +} + +/*! + \qmlmethod Animation::start() + \brief Starts the animation. + + If the animation is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QDeclarative1AbstractAnimation::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Animation::pause() + \brief Pauses the animation. + + If the animation is already paused, calling this method has no effect. The + \c paused property will be true following a call to \c pause(). +*/ +void QDeclarative1AbstractAnimation::pause() +{ + setPaused(true); +} + +/*! + \qmlmethod Animation::resume() + \brief Resumes a paused animation. + + If the animation is not paused, calling this method has no effect. The + \c paused property will be false following a call to \c resume(). +*/ +void QDeclarative1AbstractAnimation::resume() +{ + setPaused(false); +} + +/*! + \qmlmethod Animation::stop() + \brief Stops the animation. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). + + Normally \c stop() stops the animation immediately, and the animation has + no further influence on property values. In this example animation + \code + Rectangle { + NumberAnimation on x { from: 0; to: 100; duration: 500 } + } + \endcode + was stopped at time 250ms, the \c x property will have a value of 50. + + However, if the \c alwaysRunToEnd property is set, the animation will + continue running until it completes and then stop. The \c running property + will still become false immediately. +*/ +void QDeclarative1AbstractAnimation::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Animation::restart() + \brief Restarts the animation. + + This is a convenience method, and is equivalent to calling \c stop() and + then \c start(). +*/ +void QDeclarative1AbstractAnimation::restart() +{ + stop(); + start(); +} + +/*! + \qmlmethod Animation::complete() + \brief Stops the animation, jumping to the final property values. + + If the animation is not running, calling this method has no effect. The + \c running property will be false following a call to \c complete(). + + Unlike \c stop(), \c complete() immediately fast-forwards the animation to + its end. In the following example, + \code + Rectangle { + NumberAnimation on x { from: 0; to: 100; duration: 500 } + } + \endcode + calling \c stop() at time 250ms will result in the \c x property having + a value of 50, while calling \c complete() will set the \c x property to + 100, exactly as though the animation had played the whole way through. +*/ +void QDeclarative1AbstractAnimation::complete() +{ + if (isRunning()) { + qtAnimation()->setCurrentTime(qtAnimation()->duration()); + } +} + +void QDeclarative1AbstractAnimation::setTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarative1AbstractAnimation); + d->defaultProperty = p; + + if (!d->avoidPropertyValueSourceStart) + setRunning(true); +} + +/* + we rely on setTarget only being called when used as a value source + so this function allows us to do the same thing as setTarget without + that assumption +*/ +void QDeclarative1AbstractAnimation::setDefaultTarget(const QDeclarativeProperty &p) +{ + Q_D(QDeclarative1AbstractAnimation); + d->defaultProperty = p; +} + +/* + don't allow start/stop/pause/resume to be manually invoked, + because something else (like a Behavior) already has control + over the animation. +*/ +void QDeclarative1AbstractAnimation::setDisableUserControl() +{ + Q_D(QDeclarative1AbstractAnimation); + d->disableUserControl = true; +} + +void QDeclarative1AbstractAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(actions); + Q_UNUSED(modified); + Q_UNUSED(direction); +} + +void QDeclarative1AbstractAnimation::timelineComplete() +{ + Q_D(QDeclarative1AbstractAnimation); + setRunning(false); + if (d->alwaysRunToEnd && d->loopCount != 1) { + //restore the proper loopCount for the next run + qtAnimation()->setLoopCount(d->loopCount); + } +} + +/*! + \qmlclass PauseAnimation QDeclarative1PauseAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PauseAnimation element provides a pause for an animation. + + When used in a SequentialAnimation, PauseAnimation is a step when + nothing happens, for a specified duration. + + A 500ms animation sequence, with a 100ms pause between two animations: + \code + SequentialAnimation { + NumberAnimation { ... duration: 200 } + PauseAnimation { duration: 100 } + NumberAnimation { ... duration: 200 } + } + \endcode + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarative1PauseAnimation::QDeclarative1PauseAnimation(QObject *parent) +: QDeclarative1AbstractAnimation(*(new QDeclarative1PauseAnimationPrivate), parent) +{ + Q_D(QDeclarative1PauseAnimation); + d->init(); +} + +QDeclarative1PauseAnimation::~QDeclarative1PauseAnimation() +{ +} + +void QDeclarative1PauseAnimationPrivate::init() +{ + Q_Q(QDeclarative1PauseAnimation); + pa = new QPauseAnimation; + QDeclarative_setParent_noEvent(pa, q); +} + +/*! + \qmlproperty int PauseAnimation::duration + This property holds the duration of the pause in milliseconds + + The default value is 250. +*/ +int QDeclarative1PauseAnimation::duration() const +{ + Q_D(const QDeclarative1PauseAnimation); + return d->pa->duration(); +} + +void QDeclarative1PauseAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarative1PauseAnimation); + if (d->pa->duration() == duration) + return; + d->pa->setDuration(duration); + emit durationChanged(duration); +} + +QAbstractAnimation *QDeclarative1PauseAnimation::qtAnimation() +{ + Q_D(QDeclarative1PauseAnimation); + return d->pa; +} + +/*! + \qmlclass ColorAnimation QDeclarative1ColorAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The ColorAnimation element animates changes in color values. + + ColorAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a color value changes. + + Here is a ColorAnimation applied to the \c color property of a \l Rectangle + as a property value source. It animates the \c color property's value from + its current value to a value of "red", over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/coloranimation.qml 0 + + Like any other animation element, a ColorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + For convenience, when a ColorAnimation is used in a \l Transition, it will + animate any \c color properties that have been modified during the state + change. If a \l{PropertyAnimation::}{property} or + \l{PropertyAnimation::}{properties} are explicitly set for the animation, + then those are used instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarative1ColorAnimation::QDeclarative1ColorAnimation(QObject *parent) +: QDeclarative1PropertyAnimation(parent) +{ + Q_D(QDeclarative1PropertyAnimation); + d->interpolatorType = QMetaType::QColor; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarative1ColorAnimation::~QDeclarative1ColorAnimation() +{ +} + +/*! + \qmlproperty color ColorAnimation::from + This property holds the color value at which the animation should begin. + + For example, the following animation is not applied until a color value + has reached "#c0c0c0": + + \qml + Item { + states: [ + // States are defined here... + ] + + transition: Transition { + NumberAnimation { from: "#c0c0c0"; duration: 2000 } + } + } + \endqml + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarative1ColorAnimation::from() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->from.value<QColor>(); +} + +void QDeclarative1ColorAnimation::setFrom(const QColor &f) +{ + QDeclarative1PropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty color ColorAnimation::to + + This property holds the color value at which the animation should end. + + If the ColorAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QColor QDeclarative1ColorAnimation::to() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->to.value<QColor>(); +} + +void QDeclarative1ColorAnimation::setTo(const QColor &t) +{ + QDeclarative1PropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass ScriptAction QDeclarative1ScriptAction + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ScriptAction element allows scripts to be run during an animation. + + ScriptAction can be used to run a script at a specific point in an animation. + + \qml + SequentialAnimation { + NumberAnimation { + // ... + } + ScriptAction { script: doSomething(); } + NumberAnimation { + // ... + } + } + \endqml + + When used as part of a Transition, you can also target a specific + StateChangeScript to run using the \c scriptName property. + + \snippet doc/src/snippets/declarative/states/statechangescript.qml state and transition + + \sa StateChangeScript +*/ +QDeclarative1ScriptAction::QDeclarative1ScriptAction(QObject *parent) + :QDeclarative1AbstractAnimation(*(new QDeclarative1ScriptActionPrivate), parent) +{ + Q_D(QDeclarative1ScriptAction); + d->init(); +} + +QDeclarative1ScriptAction::~QDeclarative1ScriptAction() +{ +} + +void QDeclarative1ScriptActionPrivate::init() +{ + Q_Q(QDeclarative1ScriptAction); + rsa = new QActionAnimation_1(&proxy); + QDeclarative_setParent_noEvent(rsa, q); +} + +/*! + \qmlproperty script ScriptAction::script + This property holds the script to run. +*/ +QDeclarativeScriptString QDeclarative1ScriptAction::script() const +{ + Q_D(const QDeclarative1ScriptAction); + return d->script; +} + +void QDeclarative1ScriptAction::setScript(const QDeclarativeScriptString &script) +{ + Q_D(QDeclarative1ScriptAction); + d->script = script; +} + +/*! + \qmlproperty string ScriptAction::scriptName + This property holds the the name of the StateChangeScript to run. + + This property is only valid when ScriptAction is used as part of a transition. + If both script and scriptName are set, scriptName will be used. + + \note When using scriptName in a reversible transition, the script will only + be run when the transition is being run forwards. +*/ +QString QDeclarative1ScriptAction::stateChangeScriptName() const +{ + Q_D(const QDeclarative1ScriptAction); + return d->name; +} + +void QDeclarative1ScriptAction::setStateChangeScriptName(const QString &name) +{ + Q_D(QDeclarative1ScriptAction); + d->name = name; +} + +void QDeclarative1ScriptActionPrivate::execute() +{ + Q_Q(QDeclarative1ScriptAction); + if (hasRunScriptScript && reversing) + return; + + QDeclarativeScriptString scriptStr = hasRunScriptScript ? runScriptScript : script; + + const QString &str = scriptStr.script(); + if (!str.isEmpty()) { + QDeclarativeExpression expr(scriptStr.context(), scriptStr.scopeObject(), str); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expr.setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(q) << expr.error(); + } +} + +void QDeclarative1ScriptAction::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1ScriptAction); + Q_UNUSED(modified); + + d->hasRunScriptScript = false; + d->reversing = (direction == Backward); + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarative1Action &action = actions[ii]; + + if (action.event && action.event->typeName() == QLatin1String("StateChangeScript") + && static_cast<QDeclarative1StateChangeScript*>(action.event)->name() == d->name) { + d->runScriptScript = static_cast<QDeclarative1StateChangeScript*>(action.event)->script(); + d->hasRunScriptScript = true; + action.actionDone = true; + break; //only match one (names should be unique) + } + } +} + +QAbstractAnimation *QDeclarative1ScriptAction::qtAnimation() +{ + Q_D(QDeclarative1ScriptAction); + return d->rsa; +} + + + +/*! + \qmlclass PropertyAction QDeclarative1PropertyAction + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PropertyAction element allows immediate property changes during animation. + + PropertyAction is used to specify an immediate property change during an + animation. The property change is not animated. + + It is useful for setting non-animated property values during an animation. + + For example, here is a SequentialAnimation that sets the image's + \l {Image::}{smooth} property to \c true, animates the width of the image, + then sets \l {Image::}{smooth} back to \c false: + + \snippet doc/src/snippets/declarative/propertyaction.qml standalone + + PropertyAction is also useful for setting the exact point at which a property + change should occur during a \l Transition. For example, if PropertyChanges + was used in a \l State to rotate an item around a particular + \l {Item::}{transformOrigin}, it might be implemented like this: + + \snippet doc/src/snippets/declarative/propertyaction.qml transition + + However, with this code, the \c transformOrigin is not set until \e after + the animation, as a \l State is taken to define the values at the \e end of + a transition. The animation would rotate at the default \c transformOrigin, + then jump to \c Item.BottomRight. To fix this, insert a PropertyAction + before the RotationAnimation begins: + + \snippet doc/src/snippets/declarative/propertyaction-sequential.qml sequential + + This immediately sets the \c transformOrigin property to the value defined + in the end state of the \l Transition (i.e. the value defined in the + PropertyAction object) so that the rotation animation begins with the + correct transform origin. + + \sa {QML Animation and Transitions}, QtDeclarative +*/ +QDeclarative1PropertyAction::QDeclarative1PropertyAction(QObject *parent) +: QDeclarative1AbstractAnimation(*(new QDeclarative1PropertyActionPrivate), parent) +{ + Q_D(QDeclarative1PropertyAction); + d->init(); +} + +QDeclarative1PropertyAction::~QDeclarative1PropertyAction() +{ +} + +void QDeclarative1PropertyActionPrivate::init() +{ + Q_Q(QDeclarative1PropertyAction); + spa = new QActionAnimation_1; + QDeclarative_setParent_noEvent(spa, q); +} + +QObject *QDeclarative1PropertyAction::target() const +{ + Q_D(const QDeclarative1PropertyAction); + return d->target; +} + +void QDeclarative1PropertyAction::setTarget(QObject *o) +{ + Q_D(QDeclarative1PropertyAction); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarative1PropertyAction::property() const +{ + Q_D(const QDeclarative1PropertyAction); + return d->propertyName; +} + +void QDeclarative1PropertyAction::setProperty(const QString &n) +{ + Q_D(QDeclarative1PropertyAction); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +/*! + \qmlproperty Object PropertyAction::target + \qmlproperty list<Object> PropertyAction::targets + \qmlproperty string PropertyAction::property + \qmlproperty string PropertyAction::properties + + These properties determine the items and their properties that are + affected by this action. + + The details of how these properties are interpreted in different situations + is covered in the \l{PropertyAnimation::properties}{corresponding} PropertyAnimation + documentation. + + \sa exclude +*/ +QString QDeclarative1PropertyAction::properties() const +{ + Q_D(const QDeclarative1PropertyAction); + return d->properties; +} + +void QDeclarative1PropertyAction::setProperties(const QString &p) +{ + Q_D(QDeclarative1PropertyAction); + if (d->properties == p) + return; + d->properties = p; + emit propertiesChanged(p); +} + +QDeclarativeListProperty<QObject> QDeclarative1PropertyAction::targets() +{ + Q_D(QDeclarative1PropertyAction); + return QDeclarativeListProperty<QObject>(this, d->targets); +} + +/*! + \qmlproperty list<Object> PropertyAction::exclude + This property holds the objects that should not be affected by this action. + + \sa targets +*/ +QDeclarativeListProperty<QObject> QDeclarative1PropertyAction::exclude() +{ + Q_D(QDeclarative1PropertyAction); + return QDeclarativeListProperty<QObject>(this, d->exclude); +} + +/*! + \qmlproperty any PropertyAction::value + This property holds the value to be set on the property. + + If the PropertyAction is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QVariant QDeclarative1PropertyAction::value() const +{ + Q_D(const QDeclarative1PropertyAction); + return d->value; +} + +void QDeclarative1PropertyAction::setValue(const QVariant &v) +{ + Q_D(QDeclarative1PropertyAction); + if (d->value.isNull || d->value != v) { + d->value = v; + emit valueChanged(v); + } +} + +QAbstractAnimation *QDeclarative1PropertyAction::qtAnimation() +{ + Q_D(QDeclarative1PropertyAction); + return d->spa; +} + +void QDeclarative1PropertyAction::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1PropertyAction); + Q_UNUSED(direction); + + struct QDeclarative1SetPropertyAnimationAction : public QAbstractAnimationAction + { + QDeclarative1StateActions actions; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarative1Action &action = actions.at(ii); + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + }; + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList<QObject*> targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + QDeclarative1SetPropertyAnimationAction *data = new QDeclarative1SetPropertyAnimationAction; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->value.isValid()) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarative1Action myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + myAction.toValue = d->value; + QDeclarative1PropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarative1Action &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } + } + } + } + } + + if (!hasExplicit) + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarative1Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)))) { + QDeclarative1Action myAction = action; + + if (d->value.isValid()) + myAction.toValue = d->value; + QDeclarative1PropertyAnimationPrivate::convertVariant(myAction.toValue, myAction.property.propertyType()); + + modified << action.property; + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + if (data->actions.count()) { + d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + } else { + delete data; + } +} + +/*! + \qmlclass NumberAnimation QDeclarative1NumberAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The NumberAnimation element animates changes in qreal-type values. + + NumberAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a numerical value changes. + + Here is a NumberAnimation applied to the \c x property of a \l Rectangle + as a property value source. It animates the \c x value from its current + value to a value of 50, over 1000 milliseconds: + + \snippet doc/src/snippets/declarative/numberanimation.qml 0 + + Like any other animation element, a NumberAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + Note that NumberAnimation may not animate smoothly if there are irregular + changes in the number value that it is tracking. If this is the case, use + SmoothedAnimation instead. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarative1NumberAnimation::QDeclarative1NumberAnimation(QObject *parent) +: QDeclarative1PropertyAnimation(parent) +{ + init(); +} + +QDeclarative1NumberAnimation::QDeclarative1NumberAnimation(QDeclarative1PropertyAnimationPrivate &dd, QObject *parent) +: QDeclarative1PropertyAnimation(dd, parent) +{ + init(); +} + +QDeclarative1NumberAnimation::~QDeclarative1NumberAnimation() +{ +} + +void QDeclarative1NumberAnimation::init() +{ + Q_D(QDeclarative1PropertyAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); +} + +/*! + \qmlproperty real NumberAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c x value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + NumberAnimation { properties: "x"; from: 100; duration: 200 } + } + } + \endqml + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ + +qreal QDeclarative1NumberAnimation::from() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->from.toReal(); +} + +void QDeclarative1NumberAnimation::setFrom(qreal f) +{ + QDeclarative1PropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real NumberAnimation::to + This property holds the end value for the animation. + + If the NumberAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarative1NumberAnimation::to() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->to.toReal(); +} + +void QDeclarative1NumberAnimation::setTo(qreal t) +{ + QDeclarative1PropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass Vector3dAnimation QDeclarative1Vector3dAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The Vector3dAnimation element animates changes in QVector3d values. + + Vector3dAnimation is a specialized PropertyAnimation that defines an + animation to be applied when a Vector3d value changes. + + Like any other animation element, a Vector3dAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarative1Vector3dAnimation::QDeclarative1Vector3dAnimation(QObject *parent) +: QDeclarative1PropertyAnimation(parent) +{ + Q_D(QDeclarative1PropertyAnimation); + d->interpolatorType = QMetaType::QVector3D; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultToInterpolatorType = true; +} + +QDeclarative1Vector3dAnimation::~QDeclarative1Vector3dAnimation() +{ +} + +/*! + \qmlproperty real Vector3dAnimation::from + This property holds the starting value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarative1Vector3dAnimation::from() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->from.value<QVector3D>(); +} + +void QDeclarative1Vector3dAnimation::setFrom(QVector3D f) +{ + QDeclarative1PropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real Vector3dAnimation::to + This property holds the end value for the animation. + + If the Vector3dAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVector3D QDeclarative1Vector3dAnimation::to() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->to.value<QVector3D>(); +} + +void QDeclarative1Vector3dAnimation::setTo(QVector3D t) +{ + QDeclarative1PropertyAnimation::setTo(t); +} + + + +/*! + \qmlclass RotationAnimation QDeclarative1RotationAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits PropertyAnimation + \brief The RotationAnimation element animates changes in rotation values. + + RotationAnimation is a specialized PropertyAnimation that gives control + over the direction of rotation during an animation. + + By default, it rotates in the direction + of the numerical change; a rotation from 0 to 240 will rotate 240 degrees + clockwise, while a rotation from 240 to 0 will rotate 240 degrees + counterclockwise. The \l direction property can be set to specify the + direction in which the rotation should occur. + + In the following example we use RotationAnimation to animate the rotation + between states via the shortest path: + + \snippet doc/src/snippets/declarative/rotationanimation.qml 0 + + Notice the RotationAnimation did not need to set a \l target + value. As a convenience, when used in a transition, RotationAnimation will rotate all + properties named "rotation" or "angle". You can override this by providing + your own properties via \l {PropertyAnimation::properties}{properties} or + \l {PropertyAnimation::property}{property}. + + Also, note the \l Rectangle will be rotated around its default + \l {Item::}{transformOrigin} (which is \c Item.Center). To use a different + transform origin, set the origin in the PropertyChanges object and apply + the change at the start of the animation using PropertyAction. See the + PropertyAction documentation for more details. + + Like any other animation element, a RotationAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QVariant _q_interpolateShortestRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 180.0){ + newt -= 360.0; + diff -= 360.0; + } + while(diff < -180.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateClockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff < 0.0){ + newt += 360.0; + diff += 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QVariant _q_interpolateCounterclockwiseRotation(qreal &f, qreal &t, qreal progress) +{ + qreal newt = t; + qreal diff = t-f; + while(diff > 0.0){ + newt -= 360.0; + diff -= 360.0; + } + return QVariant(f + (newt - f) * progress); +} + +QDeclarative1RotationAnimation::QDeclarative1RotationAnimation(QObject *parent) +: QDeclarative1PropertyAnimation(*(new QDeclarative1RotationAnimationPrivate), parent) +{ + Q_D(QDeclarative1RotationAnimation); + d->interpolatorType = QMetaType::QReal; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + d->defaultProperties = QLatin1String("rotation,angle"); +} + +QDeclarative1RotationAnimation::~QDeclarative1RotationAnimation() +{ +} + +/*! + \qmlproperty real RotationAnimation::from + This property holds the starting value for the animation. + + For example, the following animation is not applied until the \c angle value + has reached 100: + + \qml + Item { + states: [ + // ... + ] + + transition: Transition { + RotationAnimation { properties: "angle"; from: 100; duration: 2000 } + } + } + \endqml + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarative1RotationAnimation::from() const +{ + Q_D(const QDeclarative1RotationAnimation); + return d->from.toReal(); +} + +void QDeclarative1RotationAnimation::setFrom(qreal f) +{ + QDeclarative1PropertyAnimation::setFrom(f); +} + +/*! + \qmlproperty real RotationAnimation::to + This property holds the end value for the animation.. + + If the RotationAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +qreal QDeclarative1RotationAnimation::to() const +{ + Q_D(const QDeclarative1RotationAnimation); + return d->to.toReal(); +} + +void QDeclarative1RotationAnimation::setTo(qreal t) +{ + QDeclarative1PropertyAnimation::setTo(t); +} + +/*! + \qmlproperty enumeration RotationAnimation::direction + This property holds the direction of the rotation. + + Possible values are: + + \list + \o RotationAnimation.Numerical (default) - Rotate by linearly interpolating between the two numbers. + A rotation from 10 to 350 will rotate 340 degrees clockwise. + \o RotationAnimation.Clockwise - Rotate clockwise between the two values + \o RotationAnimation.Counterclockwise - Rotate counterclockwise between the two values + \o RotationAnimation.Shortest - Rotate in the direction that produces the shortest animation path. + A rotation from 10 to 350 will rotate 20 degrees counterclockwise. + \endlist +*/ +QDeclarative1RotationAnimation::RotationDirection QDeclarative1RotationAnimation::direction() const +{ + Q_D(const QDeclarative1RotationAnimation); + return d->direction; +} + +void QDeclarative1RotationAnimation::setDirection(QDeclarative1RotationAnimation::RotationDirection direction) +{ + Q_D(QDeclarative1RotationAnimation); + if (d->direction == direction) + return; + + d->direction = direction; + switch(d->direction) { + case Clockwise: + d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&_q_interpolateClockwiseRotation); + break; + case Counterclockwise: + d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&_q_interpolateCounterclockwiseRotation); + break; + case Shortest: + d->interpolator = reinterpret_cast<QVariantAnimation::Interpolator>(&_q_interpolateShortestRotation); + break; + default: + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); + break; + } + + emit directionChanged(); +} + + + +QDeclarative1AnimationGroup::QDeclarative1AnimationGroup(QObject *parent) +: QDeclarative1AbstractAnimation(*(new QDeclarative1AnimationGroupPrivate), parent) +{ +} + +QDeclarative1AnimationGroup::QDeclarative1AnimationGroup(QDeclarative1AnimationGroupPrivate &dd, QObject *parent) + : QDeclarative1AbstractAnimation(dd, parent) +{ +} + +void QDeclarative1AnimationGroupPrivate::append_animation(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list, QDeclarative1AbstractAnimation *a) +{ + QDeclarative1AnimationGroup *q = qobject_cast<QDeclarative1AnimationGroup *>(list->object); + if (q) { + a->setGroup(q); + // This is an optimization for the parenting that already occurs via addAnimation + QDeclarative_setParent_noEvent(a->qtAnimation(), q->d_func()->ag); + q->d_func()->ag->addAnimation(a->qtAnimation()); + } +} + +void QDeclarative1AnimationGroupPrivate::clear_animation(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list) +{ + QDeclarative1AnimationGroup *q = qobject_cast<QDeclarative1AnimationGroup *>(list->object); + if (q) { + while (q->d_func()->animations.count()) { + QDeclarative1AbstractAnimation *firstAnim = q->d_func()->animations.at(0); + QDeclarative_setParent_noEvent(firstAnim->qtAnimation(), 0); + q->d_func()->ag->removeAnimation(firstAnim->qtAnimation()); + firstAnim->setGroup(0); + } + } +} + +QDeclarative1AnimationGroup::~QDeclarative1AnimationGroup() +{ +} + +QDeclarativeListProperty<QDeclarative1AbstractAnimation> QDeclarative1AnimationGroup::animations() +{ + Q_D(QDeclarative1AnimationGroup); + QDeclarativeListProperty<QDeclarative1AbstractAnimation> list(this, d->animations); + list.append = &QDeclarative1AnimationGroupPrivate::append_animation; + list.clear = &QDeclarative1AnimationGroupPrivate::clear_animation; + return list; +} + +/*! + \qmlclass SequentialAnimation QDeclarative1SequentialAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The SequentialAnimation element allows animations to be run sequentially. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following example runs two number animations in a sequence. The \l Rectangle + animates to a \c x position of 50, then to a \c y position of 50. + + \snippet doc/src/snippets/declarative/sequentialanimation.qml 0 + + Animations defined within a \l Transition are automatically run in parallel, + so SequentialAnimation can be used to enclose the animations in a \l Transition + if this is the preferred behavior. + + Like any other animation element, a SequentialAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa ParallelAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarative1SequentialAnimation::QDeclarative1SequentialAnimation(QObject *parent) : + QDeclarative1AnimationGroup(parent) +{ + Q_D(QDeclarative1AnimationGroup); + d->ag = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarative1SequentialAnimation::~QDeclarative1SequentialAnimation() +{ +} + +QAbstractAnimation *QDeclarative1SequentialAnimation::qtAnimation() +{ + Q_D(QDeclarative1AnimationGroup); + return d->ag; +} + +void QDeclarative1SequentialAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1AnimationGroup); + + int inc = 1; + int from = 0; + if (direction == Backward) { + inc = -1; + from = d->animations.count() - 1; + } + + bool valid = d->defaultProperty.isValid(); + for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) { + if (valid) + d->animations.at(ii)->setDefaultTarget(d->defaultProperty); + d->animations.at(ii)->transition(actions, modified, direction); + } +} + + + +/*! + \qmlclass ParallelAnimation QDeclarative1ParallelAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ParallelAnimation element allows animations to be run in parallel. + + The SequentialAnimation and ParallelAnimation elements allow multiple + animations to be run together. Animations defined in a SequentialAnimation + are run one after the other, while animations defined in a ParallelAnimation + are run at the same time. + + The following animation runs two number animations in parallel. The \l Rectangle + moves to (50,50) by animating its \c x and \c y properties at the same time. + + \snippet doc/src/snippets/declarative/parallelanimation.qml 0 + + Like any other animation element, a ParallelAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \note Once an animation has been grouped into a SequentialAnimation or + ParallelAnimation, it cannot be individually started and stopped; the + SequentialAnimation or ParallelAnimation must be started and stopped as a group. + + \sa SequentialAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarative1ParallelAnimation::QDeclarative1ParallelAnimation(QObject *parent) : + QDeclarative1AnimationGroup(parent) +{ + Q_D(QDeclarative1AnimationGroup); + d->ag = new QParallelAnimationGroup; + QDeclarative_setParent_noEvent(d->ag, this); +} + +QDeclarative1ParallelAnimation::~QDeclarative1ParallelAnimation() +{ +} + +QAbstractAnimation *QDeclarative1ParallelAnimation::qtAnimation() +{ + Q_D(QDeclarative1AnimationGroup); + return d->ag; +} + +void QDeclarative1ParallelAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1AnimationGroup); + 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); + } +} + + + +//convert a variant from string type to another animatable type +void QDeclarative1PropertyAnimationPrivate::convertVariant(QVariant &variant, int type) +{ + if (variant.userType() != QVariant::String) { + variant.convert((QVariant::Type)type); + return; + } + + switch (type) { + case QVariant::Rect: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString()).toRect()); + break; + } + case QVariant::RectF: { + variant.setValue(QDeclarativeStringConverters::rectFFromString(variant.toString())); + break; + } + case QVariant::Point: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString()).toPoint()); + break; + } + case QVariant::PointF: { + variant.setValue(QDeclarativeStringConverters::pointFFromString(variant.toString())); + break; + } + case QVariant::Size: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString()).toSize()); + break; + } + case QVariant::SizeF: { + variant.setValue(QDeclarativeStringConverters::sizeFFromString(variant.toString())); + break; + } + case QVariant::Color: { + variant.setValue(QDeclarativeStringConverters::colorFromString(variant.toString())); + break; + } + case QVariant::Vector3D: { + variant.setValue(QDeclarativeStringConverters::vector3DFromString(variant.toString())); + break; + } + default: + if (QDeclarativeValueTypeFactory::isValueType((uint)type)) { + variant.convert((QVariant::Type)type); + } else { + QDeclarativeMetaType::StringConverter converter = QDeclarativeMetaType::customStringConverter(type); + if (converter) + variant = converter(variant.toString()); + } + break; + } +} + +/*! + \qmlclass PropertyAnimation QDeclarative1PropertyAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The PropertyAnimation element animates changes in property values. + + PropertyAnimation provides a way to animate changes to a property's value. + + It can be used to define animations in a number of ways: + + \list + \o In a \l Transition + + For example, to animate any objects that have changed their \c x or \c y properties + as a result of a state change, using an \c InOutQuad easing curve: + + \snippet doc/src/snippets/declarative/propertyanimation.qml transition + + + \o In a \l Behavior + + For example, to animate all changes to a rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml behavior + + + \o As a property value source + + For example, to repeatedly animate the rectangle's \c x property: + + \snippet doc/src/snippets/declarative/propertyanimation.qml propertyvaluesource + + + \o In a signal handler + + For example, to fade out \c theObject when clicked: + \qml + MouseArea { + anchors.fill: theObject + onClicked: PropertyAnimation { target: theObject; property: "opacity"; to: 0 } + } + \endqml + + \o Standalone + + For example, to animate \c rect's \c width property over 500ms, from its current width to 30: + + \snippet doc/src/snippets/declarative/propertyanimation.qml standalone + + \endlist + + Depending on how the animation is used, the set of properties normally used will be + different. For more information see the individual property documentation, as well + as the \l{QML Animation and Transitions} introduction. + + Note that PropertyAnimation inherits the abstract \l Animation element. + This includes additional properties and methods for controlling the animation. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarative1PropertyAnimation::QDeclarative1PropertyAnimation(QObject *parent) +: QDeclarative1AbstractAnimation(*(new QDeclarative1PropertyAnimationPrivate), parent) +{ + Q_D(QDeclarative1PropertyAnimation); + d->init(); +} + +QDeclarative1PropertyAnimation::QDeclarative1PropertyAnimation(QDeclarative1PropertyAnimationPrivate &dd, QObject *parent) +: QDeclarative1AbstractAnimation(dd, parent) +{ + Q_D(QDeclarative1PropertyAnimation); + d->init(); +} + +QDeclarative1PropertyAnimation::~QDeclarative1PropertyAnimation() +{ +} + +void QDeclarative1PropertyAnimationPrivate::init() +{ + Q_Q(QDeclarative1PropertyAnimation); + va = new QDeclarative1BulkValueAnimator; + QDeclarative_setParent_noEvent(va, q); +} + +/*! + \qmlproperty int PropertyAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarative1PropertyAnimation::duration() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->va->duration(); +} + +void QDeclarative1PropertyAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarative1PropertyAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty real PropertyAnimation::from + This property holds the starting value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the starting state of the + \l Transition, or the current value of the property at the moment the + \l Behavior is triggered. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarative1PropertyAnimation::from() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->from; +} + +void QDeclarative1PropertyAnimation::setFrom(const QVariant &f) +{ + Q_D(QDeclarative1PropertyAnimation); + if (d->fromIsDefined && f == d->from) + return; + d->from = f; + d->fromIsDefined = f.isValid(); + emit fromChanged(f); +} + +/*! + \qmlproperty real PropertyAnimation::to + This property holds the end value for the animation. + + If the PropertyAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. + + \sa {QML Animation and Transitions} +*/ +QVariant QDeclarative1PropertyAnimation::to() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->to; +} + +void QDeclarative1PropertyAnimation::setTo(const QVariant &t) +{ + Q_D(QDeclarative1PropertyAnimation); + if (d->toIsDefined && t == d->to) + return; + d->to = t; + d->toIsDefined = t.isValid(); + emit toChanged(t); +} + +/*! + \qmlproperty enumeration PropertyAnimation::easing.type + \qmlproperty real PropertyAnimation::easing.amplitude + \qmlproperty real PropertyAnimation::easing.overshoot + \qmlproperty real PropertyAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot (more details provided after the table). The default easing curve is + \c Easing.Linear. + + \qml + PropertyAnimation { properties: "y"; easing.type: Easing.InOutElastic; easing.amplitude: 2.0; easing.period: 1.5 } + \endqml + + Available types are: + + \table + \row + \o \c Easing.Linear + \o Easing curve for a linear (t) function: velocity is constant. + \o \inlineimage qeasingcurve-linear.png + \row + \o \c Easing.InQuad + \o Easing curve for a quadratic (t^2) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquad.png + \row + \o \c Easing.OutQuad + \o Easing curve for a quadratic (t^2) function: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outquad.png + \row + \o \c Easing.InOutQuad + \o Easing curve for a quadratic (t^2) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquad.png + \row + \o \c Easing.OutInQuad + \o Easing curve for a quadratic (t^2) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquad.png + \row + \o \c Easing.InCubic + \o Easing curve for a cubic (t^3) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incubic.png + \row + \o \c Easing.OutCubic + \o Easing curve for a cubic (t^3) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcubic.png + \row + \o \c Easing.InOutCubic + \o Easing curve for a cubic (t^3) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcubic.png + \row + \o \c Easing.OutInCubic + \o Easing curve for a cubic (t^3) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincubic.png + \row + \o \c Easing.InQuart + \o Easing curve for a quartic (t^4) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquart.png + \row + \o \c Easing.OutQuart + \o Easing curve for a quartic (t^4) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquart.png + \row + \o \c Easing.InOutQuart + \o Easing curve for a quartic (t^4) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquart.png + \row + \o \c Easing.OutInQuart + \o Easing curve for a quartic (t^4) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquart.png + \row + \o \c Easing.InQuint + \o Easing curve for a quintic (t^5) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inquint.png + \row + \o \c Easing.OutQuint + \o Easing curve for a quintic (t^5) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outquint.png + \row + \o \c Easing.InOutQuint + \o Easing curve for a quintic (t^5) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutquint.png + \row + \o \c Easing.OutInQuint + \o Easing curve for a quintic (t^5) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinquint.png + \row + \o \c Easing.InSine + \o Easing curve for a sinusoidal (sin(t)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-insine.png + \row + \o \c Easing.OutSine + \o Easing curve for a sinusoidal (sin(t)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outsine.png + \row + \o \c Easing.InOutSine + \o Easing curve for a sinusoidal (sin(t)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutsine.png + \row + \o \c Easing.OutInSine + \o Easing curve for a sinusoidal (sin(t)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinsine.png + \row + \o \c Easing.InExpo + \o Easing curve for an exponential (2^t) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inexpo.png + \row + \o \c Easing.OutExpo + \o Easing curve for an exponential (2^t) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outexpo.png + \row + \o \c Easing.InOutExpo + \o Easing curve for an exponential (2^t) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutexpo.png + \row + \o \c Easing.OutInExpo + \o Easing curve for an exponential (2^t) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinexpo.png + \row + \o \c Easing.InCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-incirc.png + \row + \o \c Easing.OutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outcirc.png + \row + \o \c Easing.InOutCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutcirc.png + \row + \o \c Easing.OutInCirc + \o Easing curve for a circular (sqrt(1-t^2)) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outincirc.png + \row + \o \c Easing.InElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: accelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-inelastic.png + \row + \o \c Easing.OutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: decelerating from zero velocity. + \br The peak amplitude can be set with the \e amplitude parameter, and the period of decay by the \e period parameter. + \o \inlineimage qeasingcurve-outelastic.png + \row + \o \c Easing.InOutElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutelastic.png + \row + \o \c Easing.OutInElastic + \o Easing curve for an elastic (exponentially decaying sine wave) function: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinelastic.png + \row + \o \c Easing.InBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inback.png + \row + \o \c Easing.OutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing out: decelerating to zero velocity. + \o \inlineimage qeasingcurve-outback.png + \row + \o \c Easing.InOutBack + \o Easing curve for a back (overshooting cubic function: (s+1)*t^3 - s*t^2) easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutback.png + \row + \o \c Easing.OutInBack + \o Easing curve for a back (overshooting cubic easing: (s+1)*t^3 - s*t^2) easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinback.png + \row + \o \c Easing.InBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: accelerating from zero velocity. + \o \inlineimage qeasingcurve-inbounce.png + \row + \o \c Easing.OutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function: decelerating from zero velocity. + \o \inlineimage qeasingcurve-outbounce.png + \row + \o \c Easing.InOutBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing in/out: acceleration until halfway, then deceleration. + \o \inlineimage qeasingcurve-inoutbounce.png + \row + \o \c Easing.OutInBounce + \o Easing curve for a bounce (exponentially decaying parabolic bounce) function easing out/in: deceleration until halfway, then acceleration. + \o \inlineimage qeasingcurve-outinbounce.png + \endtable + + \c easing.amplitude is only applicable for bounce and elastic curves (curves of type + \c Easing.InBounce, \c Easing.OutBounce, \c Easing.InOutBounce, \c Easing.OutInBounce, \c Easing.InElastic, + \c Easing.OutElastic, \c Easing.InOutElastic or \c Easing.OutInElastic). + + \c easing.overshoot is only applicable if \c easing.type is: \c Easing.InBack, \c Easing.OutBack, + \c Easing.InOutBack or \c Easing.OutInBack. + + \c easing.period is only applicable if easing.type is: \c Easing.InElastic, \c Easing.OutElastic, + \c Easing.InOutElastic or \c Easing.OutInElastic. + + See the \l {declarative/animation/easing}{easing} example for a demonstration of + the different easing settings. +*/ +QEasingCurve QDeclarative1PropertyAnimation::easing() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->va->easingCurve(); +} + +void QDeclarative1PropertyAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarative1PropertyAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +QObject *QDeclarative1PropertyAnimation::target() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->target; +} + +void QDeclarative1PropertyAnimation::setTarget(QObject *o) +{ + Q_D(QDeclarative1PropertyAnimation); + if (d->target == o) + return; + d->target = o; + emit targetChanged(); +} + +QString QDeclarative1PropertyAnimation::property() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->propertyName; +} + +void QDeclarative1PropertyAnimation::setProperty(const QString &n) +{ + Q_D(QDeclarative1PropertyAnimation); + if (d->propertyName == n) + return; + d->propertyName = n; + emit propertyChanged(); +} + +QString QDeclarative1PropertyAnimation::properties() const +{ + Q_D(const QDeclarative1PropertyAnimation); + return d->properties; +} + +void QDeclarative1PropertyAnimation::setProperties(const QString &prop) +{ + Q_D(QDeclarative1PropertyAnimation); + if (d->properties == prop) + return; + + d->properties = prop; + emit propertiesChanged(prop); +} + +/*! + \qmlproperty string PropertyAnimation::properties + \qmlproperty list<Object> PropertyAnimation::targets + \qmlproperty string PropertyAnimation::property + \qmlproperty Object PropertyAnimation::target + + These properties are used as a set to determine which properties should be animated. + The singular and plural forms are functionally identical, e.g. + \qml + NumberAnimation { target: theItem; property: "x"; to: 500 } + \endqml + has the same meaning as + \qml + NumberAnimation { targets: theItem; properties: "x"; to: 500 } + \endqml + The singular forms are slightly optimized, so if you do have only a single target/property + to animate you should try to use them. + + The \c targets property allows multiple targets to be set. For example, this animates the + \c x property of both \c itemA and \c itemB: + + \qml + NumberAnimation { targets: [itemA, itemB]; properties: "x"; to: 500 } + \endqml + + In many cases these properties do not need to be explicitly specified, as they can be + inferred from the animation framework: + + \table 80% + \row + \o Value Source / Behavior + \o When an animation is used as a value source or in a Behavior, the default target and property + name to be animated can both be inferred. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + NumberAnimation on x { to: 500; loops: Animation.Infinite } //animate theRect's x property + Behavior on y { NumberAnimation {} } //animate theRect's y property + } + \endqml + \row + \o Transition + \o When used in a transition, a property animation is assumed to match \e all targets + but \e no properties. In practice, that means you need to specify at least the properties + in order for the animation to do anything. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + Item { id: uselessItem } + states: State { + name: "state1" + PropertyChanges { target: theRect; x: 200; y: 200; z: 4 } + PropertyChanges { target: uselessItem; x: 10; y: 10; z: 2 } + } + transitions: Transition { + //animate both theRect's and uselessItem's x and y to their final values + NumberAnimation { properties: "x,y" } + + //animate theRect's z to its final value + NumberAnimation { target: theRect; property: "z" } + } + } + \endqml + \row + \o Standalone + \o When an animation is used standalone, both the target and property need to be + explicitly specified. + \qml + Rectangle { + id: theRect + width: 100; height: 100 + color: Qt.rgba(0,0,1) + //need to explicitly specify target and property + NumberAnimation { id: theAnim; target: theRect; property: "x"; to: 500 } + MouseArea { + anchors.fill: parent + onClicked: theAnim.start() + } + } + \endqml + \endtable + + As seen in the above example, properties is specified as a comma-separated string of property names to animate. + + \sa exclude, {QML Animation and Transitions} +*/ +QDeclarativeListProperty<QObject> QDeclarative1PropertyAnimation::targets() +{ + Q_D(QDeclarative1PropertyAnimation); + return QDeclarativeListProperty<QObject>(this, d->targets); +} + +/*! + \qmlproperty list<Object> PropertyAnimation::exclude + This property holds the items not to be affected by this animation. + \sa PropertyAnimation::targets +*/ +QDeclarativeListProperty<QObject> QDeclarative1PropertyAnimation::exclude() +{ + Q_D(QDeclarative1PropertyAnimation); + return QDeclarativeListProperty<QObject>(this, d->exclude); +} + +QAbstractAnimation *QDeclarative1PropertyAnimation::qtAnimation() +{ + Q_D(QDeclarative1PropertyAnimation); + return d->va; +} + +void QDeclarative1AnimationPropertyUpdater::setValue(qreal v) +{ + bool deleted = false; + wasDeleted = &deleted; + if (reverse) //QVariantAnimation sends us 1->0 when reversed, but we are expecting 0->1 + v = 1 - v; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarative1Action &action = actions[ii]; + + if (v == 1.) + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + else { + if (!fromSourced && !fromDefined) { + action.fromValue = action.property.read(); + if (interpolatorType) + QDeclarative1PropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType); + } + if (!interpolatorType) { + int propType = action.property.propertyType(); + if (!prevInterpolatorType || prevInterpolatorType != propType) { + prevInterpolatorType = propType; + interpolator = QVariantAnimationPrivate::getInterpolator(prevInterpolatorType); + } + } + if (interpolator) + QDeclarativePropertyPrivate::write(action.property, interpolator(action.fromValue.constData(), action.toValue.constData(), v), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + if (deleted) + return; + } + wasDeleted = 0; + fromSourced = true; +} + +void QDeclarative1PropertyAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1PropertyAnimation); + + QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(',')); + for (int ii = 0; ii < props.count(); ++ii) + props[ii] = props.at(ii).trimmed(); + if (!d->propertyName.isEmpty()) + props << d->propertyName; + + QList<QObject*> targets = d->targets; + if (d->target) + targets.append(d->target); + + bool hasSelectors = !props.isEmpty() || !targets.isEmpty() || !d->exclude.isEmpty(); + bool useType = (props.isEmpty() && d->defaultToInterpolatorType) ? true : false; + + if (d->defaultProperty.isValid() && !hasSelectors) { + props << d->defaultProperty.name(); + targets << d->defaultProperty.object(); + } + + if (props.isEmpty() && !d->defaultProperties.isEmpty()) { + props << d->defaultProperties.split(QLatin1Char(',')); + } + + QDeclarative1AnimationPropertyUpdater *data = new QDeclarative1AnimationPropertyUpdater; + data->interpolatorType = d->interpolatorType; + data->interpolator = d->interpolator; + data->reverse = direction == Backward ? true : false; + data->fromSourced = false; + data->fromDefined = d->fromIsDefined; + + bool hasExplicit = false; + //an explicit animation has been specified + if (d->toIsDefined) { + for (int i = 0; i < props.count(); ++i) { + for (int j = 0; j < targets.count(); ++j) { + QDeclarative1Action myAction; + myAction.property = d->createProperty(targets.at(j), props.at(i), this); + if (myAction.property.isValid()) { + if (d->fromIsDefined) { + myAction.fromValue = d->from; + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + } + myAction.toValue = d->to; + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + data->actions << myAction; + hasExplicit = true; + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarative1Action &action = actions[ii]; + if (action.property.object() == myAction.property.object() && + myAction.property.name() == action.property.name()) { + modified << action.property; + break; //### any chance there could be multiples? + } + } + } + } + } + } + + if (!hasExplicit) + for (int ii = 0; ii < actions.count(); ++ii) { + QDeclarative1Action &action = actions[ii]; + + QObject *obj = action.property.object(); + QString propertyName = action.property.name(); + QObject *sObj = action.specifiedObject; + QString sPropertyName = action.specifiedProperty; + bool same = (obj == sObj); + + if ((targets.isEmpty() || targets.contains(obj) || (!same && targets.contains(sObj))) && + (!d->exclude.contains(obj)) && (same || (!d->exclude.contains(sObj))) && + (props.contains(propertyName) || (!same && props.contains(sPropertyName)) + || (useType && action.property.propertyType() == d->interpolatorType))) { + QDeclarative1Action myAction = action; + + if (d->fromIsDefined) + myAction.fromValue = d->from; + else + myAction.fromValue = QVariant(); + if (d->toIsDefined) + myAction.toValue = d->to; + + d->convertVariant(myAction.fromValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType()); + + modified << action.property; + + data->actions << myAction; + action.fromValue = myAction.toValue; + } + } + + 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); + d->actions = &data->actions; + } else { + delete data; + d->va->setFromSourcedValue(0); //clear previous data + d->va->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); //clear previous data + d->actions = 0; + } +} + +/*! + \qmlclass ParentAnimation QDeclarative1ParentAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The ParentAnimation element animates changes in parent values. + + ParentAnimation is used to animate a parent change for an \l Item. + + For example, the following ParentChange changes \c blueRect to become + a child of \c redRect when it is clicked. The inclusion of the + ParentAnimation, which defines a NumberAnimation to be applied during + the transition, ensures the item animates smoothly as it moves to + its new parent: + + \snippet doc/src/snippets/declarative/parentanimation.qml 0 + + A ParentAnimation can contain any number of animations. These animations will + be run in parallel; to run them sequentially, define them within a + SequentialAnimation. + + In some cases, such as when reparenting between items with clipping enabled, it is useful + to animate the parent change via another item that does not have clipping + enabled. Such an item can be set using the \l via property. + + For convenience, when a ParentAnimation is used in a \l Transition, it will + animate any ParentChange that has occurred during the state change. + This can be overridden by setting a specific target item using the + \l target property. + + Like any other animation element, a ParentAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ +QDeclarative1ParentAnimation::QDeclarative1ParentAnimation(QObject *parent) + : QDeclarative1AnimationGroup(*(new QDeclarative1ParentAnimationPrivate), parent) +{ + Q_D(QDeclarative1ParentAnimation); + d->topLevelGroup = new QSequentialAnimationGroup; + QDeclarative_setParent_noEvent(d->topLevelGroup, this); + + d->startAction = new QActionAnimation_1; + 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_1; + QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup); + d->topLevelGroup->addAnimation(d->endAction); +} + +QDeclarative1ParentAnimation::~QDeclarative1ParentAnimation() +{ +} + +/*! + \qmlproperty Item ParentAnimation::target + The item to reparent. + + When used in a transition, if no target is specified, all + ParentChange occurrences are animated by the ParentAnimation. +*/ +QDeclarativeItem *QDeclarative1ParentAnimation::target() const +{ + Q_D(const QDeclarative1ParentAnimation); + return d->target; +} + +void QDeclarative1ParentAnimation::setTarget(QDeclarativeItem *target) +{ + Q_D(QDeclarative1ParentAnimation); + if (target == d->target) + return; + + d->target = target; + emit targetChanged(); +} + +/*! + \qmlproperty Item ParentAnimation::newParent + The new parent to animate to. + + If the ParentAnimation is defined within a \l Transition or \l Behavior, + this value defaults to the value defined in the end state of the + \l Transition, or the value of the property change that triggered the + \l Behavior. +*/ +QDeclarativeItem *QDeclarative1ParentAnimation::newParent() const +{ + Q_D(const QDeclarative1ParentAnimation); + return d->newParent; +} + +void QDeclarative1ParentAnimation::setNewParent(QDeclarativeItem *newParent) +{ + Q_D(QDeclarative1ParentAnimation); + if (newParent == d->newParent) + return; + + d->newParent = newParent; + emit newParentChanged(); +} + +/*! + \qmlproperty Item ParentAnimation::via + The item to reparent via. This provides a way to do an unclipped animation + when both the old parent and new parent are clipped. + + \qml + ParentAnimation { + target: myItem + via: topLevelItem + // ... + } + \endqml +*/ +QDeclarativeItem *QDeclarative1ParentAnimation::via() const +{ + Q_D(const QDeclarative1ParentAnimation); + return d->via; +} + +void QDeclarative1ParentAnimation::setVia(QDeclarativeItem *via) +{ + Q_D(QDeclarative1ParentAnimation); + if (via == d->via) + return; + + d->via = via; + emit viaChanged(); +} + +//### mirrors same-named function in QDeclarativeItem +QPointF QDeclarative1ParentAnimationPrivate::computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const +{ + switch(origin) { + default: + case QDeclarativeItem::TopLeft: + return QPointF(0, 0); + case QDeclarativeItem::Top: + return QPointF(width / 2., 0); + case QDeclarativeItem::TopRight: + return QPointF(width, 0); + case QDeclarativeItem::Left: + return QPointF(0, height / 2.); + case QDeclarativeItem::Center: + return QPointF(width / 2., height / 2.); + case QDeclarativeItem::Right: + return QPointF(width, height / 2.); + case QDeclarativeItem::BottomLeft: + return QPointF(0, height); + case QDeclarativeItem::Bottom: + return QPointF(width / 2., height); + case QDeclarativeItem::BottomRight: + return QPointF(width, height); + } +} + +void QDeclarative1ParentAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1ParentAnimation); + + struct QDeclarative1ParentAnimationData : public QAbstractAnimationAction + { + QDeclarative1ParentAnimationData() {} + ~QDeclarative1ParentAnimationData() { qDeleteAll(pc); } + + QDeclarative1StateActions actions; + //### reverse should probably apply on a per-action basis + bool reverse; + QList<QDeclarative1ParentChange *> pc; + virtual void doAction() + { + for (int ii = 0; ii < actions.count(); ++ii) { + const QDeclarative1Action &action = actions.at(ii); + if (reverse) + action.event->reverse(); + else + action.event->execute(); + } + } + }; + + QDeclarative1ParentAnimationData *data = new QDeclarative1ParentAnimationData; + QDeclarative1ParentAnimationData *viaData = new QDeclarative1ParentAnimationData; + + bool hasExplicit = false; + if (d->target && d->newParent) { + data->reverse = false; + QDeclarative1Action myAction; + QDeclarative1ParentChange *pc = new QDeclarative1ParentChange; + 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; + QDeclarative1Action myVAction; + QDeclarative1ParentChange *vpc = new QDeclarative1ParentChange; + 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) { + QDeclarative1Action &action = actions[i]; + if (action.event && action.event->typeName() == QLatin1String("ParentChange") + && (!d->target || static_cast<QDeclarative1ParentChange*>(action.event)->object() == d->target)) { + + QDeclarative1ParentChange *pc = static_cast<QDeclarative1ParentChange*>(action.event); + QDeclarative1Action myAction = action; + data->reverse = action.reverseEvent; + + //### this logic differs from PropertyAnimation + // (probably a result of modified vs. done) + if (d->newParent) { + QDeclarative1ParentChange *epc = new QDeclarative1ParentChange; + epc->setObject(static_cast<QDeclarative1ParentChange*>(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; + QDeclarative1Action myAction; + QDeclarative1ParentChange *vpc = new QDeclarative1ParentChange; + vpc->setObject(pc->object()); + vpc->setParent(d->via); + myAction.event = vpc; + viaData->pc << vpc; + viaData->actions << myAction; + QDeclarative1Action dummyAction; + QDeclarative1Action &xAction = pc->xIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarative1Action &yAction = pc->yIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarative1Action &sAction = pc->scaleIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarative1Action &rAction = pc->rotationIsSet() && i < actions.size()-1 ? actions[++i] : dummyAction; + QDeclarativeItem *target = pc->object(); + QDeclarativeItem *targetParent = action.reverseEvent ? pc->originalParent() : pc->parent(); + + //### this mirrors the logic in QDeclarative1ParentChange. + bool ok; + const QTransform &transform = targetParent->itemTransform(d->via, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(this) << QDeclarative1ParentAnimation::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) << QDeclarative1ParentAnimation::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) << QDeclarative1ParentAnimation::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) << QDeclarative1ParentAnimation::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() != QDeclarativeItem::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 == QDeclarative1AbstractAnimation::Forward) { + d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation_1::DeleteWhenStopped); + d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation_1::DeleteWhenStopped); + } else { + d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation_1::DeleteWhenStopped); + d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation_1::DeleteWhenStopped); + } + if (!d->via) + delete viaData; + } 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 *QDeclarative1ParentAnimation::qtAnimation() +{ + Q_D(QDeclarative1ParentAnimation); + return d->topLevelGroup; +} + +/*! + \qmlclass AnchorAnimation QDeclarative1AnchorAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits Animation + \brief The AnchorAnimation element animates changes in anchor values. + + AnchorAnimation is used to animate an anchor change. + + In the following snippet we animate the addition of a right anchor to a \l Rectangle: + + \snippet doc/src/snippets/declarative/anchoranimation.qml 0 + + For convenience, when an AnchorAnimation is used in a \l Transition, it will + animate any AnchorChanges that have occurred during the state change. + This can be overridden by setting a specific target item using the + \l target property. + + Like any other animation element, an AnchorAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa {QML Animation and Transitions}, AnchorChanges +*/ + +QDeclarative1AnchorAnimation::QDeclarative1AnchorAnimation(QObject *parent) +: QDeclarative1AbstractAnimation(*(new QDeclarative1AnchorAnimationPrivate), parent) +{ + Q_D(QDeclarative1AnchorAnimation); + d->va = new QDeclarative1BulkValueAnimator; + QDeclarative_setParent_noEvent(d->va, this); +} + +QDeclarative1AnchorAnimation::~QDeclarative1AnchorAnimation() +{ +} + +QAbstractAnimation *QDeclarative1AnchorAnimation::qtAnimation() +{ + Q_D(QDeclarative1AnchorAnimation); + return d->va; +} + +/*! + \qmlproperty list<Item> AnchorAnimation::targets + The items to reanchor. + + If no targets are specified all AnchorChanges will be + animated by the AnchorAnimation. +*/ +QDeclarativeListProperty<QDeclarativeItem> QDeclarative1AnchorAnimation::targets() +{ + Q_D(QDeclarative1AnchorAnimation); + return QDeclarativeListProperty<QDeclarativeItem>(this, d->targets); +} + +/*! + \qmlproperty int AnchorAnimation::duration + This property holds the duration of the animation, in milliseconds. + + The default value is 250. +*/ +int QDeclarative1AnchorAnimation::duration() const +{ + Q_D(const QDeclarative1AnchorAnimation); + return d->va->duration(); +} + +void QDeclarative1AnchorAnimation::setDuration(int duration) +{ + if (duration < 0) { + qmlInfo(this) << tr("Cannot set a duration of < 0"); + return; + } + + Q_D(QDeclarative1AnchorAnimation); + if (d->va->duration() == duration) + return; + d->va->setDuration(duration); + emit durationChanged(duration); +} + +/*! + \qmlproperty enumeration AnchorAnimation::easing.type + \qmlproperty real AnchorAnimation::easing.amplitude + \qmlproperty real AnchorAnimation::easing.overshoot + \qmlproperty real AnchorAnimation::easing.period + \brief the easing curve used for the animation. + + To specify an easing curve you need to specify at least the type. For some curves you can also specify + amplitude, period and/or overshoot. The default easing curve is + Linear. + + \qml + AnchorAnimation { easing.type: Easing.InOutQuad } + \endqml + + See the \l{PropertyAnimation::easing.type} documentation for information + about the different types of easing curves. +*/ + +QEasingCurve QDeclarative1AnchorAnimation::easing() const +{ + Q_D(const QDeclarative1AnchorAnimation); + return d->va->easingCurve(); +} + +void QDeclarative1AnchorAnimation::setEasing(const QEasingCurve &e) +{ + Q_D(QDeclarative1AnchorAnimation); + if (d->va->easingCurve() == e) + return; + + d->va->setEasingCurve(e); + emit easingChanged(e); +} + +void QDeclarative1AnchorAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_UNUSED(modified); + Q_D(QDeclarative1AnchorAnimation); + QDeclarative1AnimationPropertyUpdater *data = new QDeclarative1AnimationPropertyUpdater; + 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) { + QDeclarative1Action &action = actions[ii]; + if (action.event && action.event->typeName() == QLatin1String("AnchorChanges") + && (d->targets.isEmpty() || d->targets.contains(static_cast<QDeclarative1AnchorChanges*>(action.event)->object()))) { + data->actions << static_cast<QDeclarative1AnchorChanges*>(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; + } +} + +QDeclarative1ScriptActionPrivate::QDeclarative1ScriptActionPrivate() + : QDeclarative1AbstractAnimationPrivate(), hasRunScriptScript(false), reversing(false), proxy(this), rsa(0) {} + + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativeanimation_p.h b/src/qtquick1/util/qdeclarativeanimation_p.h new file mode 100644 index 0000000000..decb9a339a --- /dev/null +++ b/src/qtquick1/util/qdeclarativeanimation_p.h @@ -0,0 +1,528 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_H +#define QDECLARATIVEANIMATION_H + +#include "QtQuick1/private/qdeclarativetransition_p.h" +#include "QtQuick1/private/qdeclarativestate_p.h" +#include <QtGui/qvector3d.h> + +#include <QtDeclarative/qdeclarativepropertyvaluesource.h> +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativescriptstring.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qeasingcurve.h> +#include <QtCore/QAbstractAnimation> +#include <QtGui/qcolor.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeItem; +class QDeclarative1AbstractAnimationPrivate; +class QDeclarative1AnimationGroup; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1AbstractAnimation : public QObject, public QDeclarativePropertyValueSource, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1AbstractAnimation) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_INTERFACES(QDeclarativePropertyValueSource) + Q_ENUMS(Loops) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool paused READ isPaused WRITE setPaused NOTIFY pausedChanged) + Q_PROPERTY(bool alwaysRunToEnd READ alwaysRunToEnd WRITE setAlwaysRunToEnd NOTIFY alwaysRunToEndChanged) + Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopCountChanged) + Q_CLASSINFO("DefaultMethod", "start()") + +public: + QDeclarative1AbstractAnimation(QObject *parent=0); + virtual ~QDeclarative1AbstractAnimation(); + + enum Loops { Infinite = -2 }; + + bool isRunning() const; + void setRunning(bool); + bool isPaused() const; + void setPaused(bool); + bool alwaysRunToEnd() const; + void setAlwaysRunToEnd(bool); + + int loops() const; + void setLoops(int); + + int currentTime(); + void setCurrentTime(int); + + QDeclarative1AnimationGroup *group() const; + void setGroup(QDeclarative1AnimationGroup *); + + void setDefaultTarget(const QDeclarativeProperty &); + void setDisableUserControl(); + + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void started(); + void completed(); + void runningChanged(bool); + void pausedChanged(bool); + void alwaysRunToEndChanged(bool); + void loopCountChanged(int); + +public Q_SLOTS: + void restart(); + void start(); + void pause(); + void resume(); + void stop(); + void complete(); + +protected: + QDeclarative1AbstractAnimation(QDeclarative1AbstractAnimationPrivate &dd, QObject *parent); + +public: + enum TransitionDirection { Forward, Backward }; + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation() = 0; + +private Q_SLOTS: + void timelineComplete(); + void componentFinalized(); +private: + virtual void setTarget(const QDeclarativeProperty &); + void notifyRunningChanged(bool running); + friend class QDeclarative1Behavior; + + +}; + +class QDeclarative1PauseAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1PauseAnimation : public QDeclarative1AbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PauseAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + +public: + QDeclarative1PauseAnimation(QObject *parent=0); + virtual ~QDeclarative1PauseAnimation(); + + int duration() const; + void setDuration(int); + +Q_SIGNALS: + void durationChanged(int); + +protected: + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarative1ScriptActionPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1ScriptAction : public QDeclarative1AbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1ScriptAction) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString scriptName READ stateChangeScriptName WRITE setStateChangeScriptName) + +public: + QDeclarative1ScriptAction(QObject *parent=0); + virtual ~QDeclarative1ScriptAction(); + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString stateChangeScriptName() const; + void setStateChangeScriptName(const QString &); + +protected: + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarative1PropertyActionPrivate; +class QDeclarative1PropertyAction : public QDeclarative1AbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PropertyAction) + + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty<QObject> targets READ targets) + Q_PROPERTY(QDeclarativeListProperty<QObject> exclude READ exclude) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + QDeclarative1PropertyAction(QObject *parent=0); + virtual ~QDeclarative1PropertyAction(); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty<QObject> targets(); + QDeclarativeListProperty<QObject> exclude(); + + QVariant value() const; + void setValue(const QVariant &); + +Q_SIGNALS: + void valueChanged(const QVariant &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); + +protected: + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarative1PropertyAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1PropertyAnimation : public QDeclarative1AbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PropertyAnimation) + + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QVariant from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QVariant to READ to WRITE setTo NOTIFY toChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QString property READ property WRITE setProperty NOTIFY propertyChanged) + Q_PROPERTY(QString properties READ properties WRITE setProperties NOTIFY propertiesChanged) + Q_PROPERTY(QDeclarativeListProperty<QObject> targets READ targets) + Q_PROPERTY(QDeclarativeListProperty<QObject> exclude READ exclude) + +public: + QDeclarative1PropertyAnimation(QObject *parent=0); + virtual ~QDeclarative1PropertyAnimation(); + + virtual int duration() const; + virtual void setDuration(int); + + QVariant from() const; + void setFrom(const QVariant &); + + QVariant to() const; + void setTo(const QVariant &); + + QEasingCurve easing() const; + void setEasing(const QEasingCurve &); + + QObject *target() const; + void setTarget(QObject *); + + QString property() const; + void setProperty(const QString &); + + QString properties() const; + void setProperties(const QString &); + + QDeclarativeListProperty<QObject> targets(); + QDeclarativeListProperty<QObject> exclude(); + +protected: + QDeclarative1PropertyAnimation(QDeclarative1PropertyAnimationPrivate &dd, QObject *parent); + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void durationChanged(int); + void fromChanged(QVariant); + void toChanged(QVariant); + void easingChanged(const QEasingCurve &); + void propertiesChanged(const QString &); + void targetChanged(); + void propertyChanged(); +}; + +class Q_AUTOTEST_EXPORT QDeclarative1ColorAnimation : public QDeclarative1PropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PropertyAnimation) + Q_PROPERTY(QColor from READ from WRITE setFrom) + Q_PROPERTY(QColor to READ to WRITE setTo) + +public: + QDeclarative1ColorAnimation(QObject *parent=0); + virtual ~QDeclarative1ColorAnimation(); + + QColor from() const; + void setFrom(const QColor &); + + QColor to() const; + void setTo(const QColor &); +}; + +class Q_AUTOTEST_EXPORT QDeclarative1NumberAnimation : public QDeclarative1PropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PropertyAnimation) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + +public: + QDeclarative1NumberAnimation(QObject *parent=0); + virtual ~QDeclarative1NumberAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + +protected: + QDeclarative1NumberAnimation(QDeclarative1PropertyAnimationPrivate &dd, QObject *parent); + +private: + void init(); +}; + +class Q_AUTOTEST_EXPORT QDeclarative1Vector3dAnimation : public QDeclarative1PropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PropertyAnimation) + + Q_PROPERTY(QVector3D from READ from WRITE setFrom) + Q_PROPERTY(QVector3D to READ to WRITE setTo) + +public: + QDeclarative1Vector3dAnimation(QObject *parent=0); + virtual ~QDeclarative1Vector3dAnimation(); + + QVector3D from() const; + void setFrom(QVector3D); + + QVector3D to() const; + void setTo(QVector3D); +}; + +class QDeclarative1RotationAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1RotationAnimation : public QDeclarative1PropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1RotationAnimation) + Q_ENUMS(RotationDirection) + + Q_PROPERTY(qreal from READ from WRITE setFrom) + Q_PROPERTY(qreal to READ to WRITE setTo) + Q_PROPERTY(RotationDirection direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + QDeclarative1RotationAnimation(QObject *parent=0); + virtual ~QDeclarative1RotationAnimation(); + + qreal from() const; + void setFrom(qreal); + + qreal to() const; + void setTo(qreal); + + enum RotationDirection { Numerical, Shortest, Clockwise, Counterclockwise }; + RotationDirection direction() const; + void setDirection(RotationDirection direction); + +Q_SIGNALS: + void directionChanged(); +}; + +class QDeclarative1AnimationGroupPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1AnimationGroup : public QDeclarative1AbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1AnimationGroup) + + Q_CLASSINFO("DefaultProperty", "animations") + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1AbstractAnimation> animations READ animations) + +public: + QDeclarative1AnimationGroup(QObject *parent); + virtual ~QDeclarative1AnimationGroup(); + + QDeclarativeListProperty<QDeclarative1AbstractAnimation> animations(); + friend class QDeclarative1AbstractAnimation; + +protected: + QDeclarative1AnimationGroup(QDeclarative1AnimationGroupPrivate &dd, QObject *parent); +}; + +class QDeclarative1SequentialAnimation : public QDeclarative1AnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1AnimationGroup) + +public: + QDeclarative1SequentialAnimation(QObject *parent=0); + virtual ~QDeclarative1SequentialAnimation(); + +protected: + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarative1ParallelAnimation : public QDeclarative1AnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1AnimationGroup) + +public: + QDeclarative1ParallelAnimation(QObject *parent=0); + virtual ~QDeclarative1ParallelAnimation(); + +protected: + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarative1ParentAnimationPrivate; +class QDeclarative1ParentAnimation : public QDeclarative1AnimationGroup +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1ParentAnimation) + + Q_PROPERTY(QDeclarativeItem *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(QDeclarativeItem *newParent READ newParent WRITE setNewParent NOTIFY newParentChanged) + Q_PROPERTY(QDeclarativeItem *via READ via WRITE setVia NOTIFY viaChanged) + +public: + QDeclarative1ParentAnimation(QObject *parent=0); + virtual ~QDeclarative1ParentAnimation(); + + QDeclarativeItem *target() const; + void setTarget(QDeclarativeItem *); + + QDeclarativeItem *newParent() const; + void setNewParent(QDeclarativeItem *); + + QDeclarativeItem *via() const; + void setVia(QDeclarativeItem *); + +Q_SIGNALS: + void targetChanged(); + void newParentChanged(); + void viaChanged(); + +protected: + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +class QDeclarative1AnchorAnimationPrivate; +class QDeclarative1AnchorAnimation : public QDeclarative1AbstractAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1AnchorAnimation) + Q_PROPERTY(QDeclarativeListProperty<QDeclarativeItem> targets READ targets) + Q_PROPERTY(int duration READ duration WRITE setDuration NOTIFY durationChanged) + Q_PROPERTY(QEasingCurve easing READ easing WRITE setEasing NOTIFY easingChanged) + +public: + QDeclarative1AnchorAnimation(QObject *parent=0); + virtual ~QDeclarative1AnchorAnimation(); + + QDeclarativeListProperty<QDeclarativeItem> 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(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + virtual QAbstractAnimation *qtAnimation(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1AbstractAnimation) +QML_DECLARE_TYPE(QDeclarative1PauseAnimation) +QML_DECLARE_TYPE(QDeclarative1ScriptAction) +QML_DECLARE_TYPE(QDeclarative1PropertyAction) +QML_DECLARE_TYPE(QDeclarative1PropertyAnimation) +QML_DECLARE_TYPE(QDeclarative1ColorAnimation) +QML_DECLARE_TYPE(QDeclarative1NumberAnimation) +QML_DECLARE_TYPE(QDeclarative1SequentialAnimation) +QML_DECLARE_TYPE(QDeclarative1ParallelAnimation) +QML_DECLARE_TYPE(QDeclarative1Vector3dAnimation) +QML_DECLARE_TYPE(QDeclarative1RotationAnimation) +QML_DECLARE_TYPE(QDeclarative1ParentAnimation) +QML_DECLARE_TYPE(QDeclarative1AnchorAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVEANIMATION_H diff --git a/src/qtquick1/util/qdeclarativeanimation_p_p.h b/src/qtquick1/util/qdeclarativeanimation_p_p.h new file mode 100644 index 0000000000..e528a927ef --- /dev/null +++ b/src/qtquick1/util/qdeclarativeanimation_p_p.h @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEANIMATION_P_H +#define QDECLARATIVEANIMATION_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 "QtQuick1/private/qdeclarativeanimation_p.h" + +#include "QtDeclarative/private/qdeclarativenullablevalue_p_p.h" +#include "QtQuick1/private/qdeclarativetimeline_p_p.h" + +#include <QtDeclarative/qdeclarative.h> +#include <QtQuick1/qdeclarativeitem.h> +#include <QtDeclarative/qdeclarativecontext.h> + +#include <QtCore/QPauseAnimation> +#include <QtCore/QVariantAnimation> +#include <QtCore/QAnimationGroup> +#include <QtGui/QColor> +#include <QDebug> + +#include <private/qobject_p.h> +#include <private/qvariantanimation_p.h> + +QT_BEGIN_NAMESPACE + +//interface for classes that provide animation actions for QActionAnimation_1 +class QAbstractAnimationAction +{ +public: + virtual ~QAbstractAnimationAction() {} + virtual void doAction() = 0; +}; + +//templated animation action +//allows us to specify an action that calls a function of a class. +//(so that class doesn't have to inherit QDeclarative1AbstractAnimationAction) +template<class T, void (T::*method)()> +class QAnimationActionProxy_1 : public QAbstractAnimationAction +{ +public: + QAnimationActionProxy_1(T *p) : m_p(p) {} + virtual void doAction() { (m_p->*method)(); } + +private: + T *m_p; +}; + +//performs an action of type QAbstractAnimationAction +class Q_AUTOTEST_EXPORT QActionAnimation_1 : public QAbstractAnimation +{ + Q_OBJECT +public: + QActionAnimation_1(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {} + QActionAnimation_1(QAbstractAnimationAction *action, QObject *parent = 0) + : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped) {} + ~QActionAnimation_1() { if (policy == DeleteWhenStopped) { delete animAction; animAction = 0; } } + virtual int duration() const { return 0; } + void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animAction; + animAction = action; + policy = p; + } +protected: + virtual void updateCurrentTime(int) {} + + virtual void updateState(State newState, State /*oldState*/) + { + if (newState == Running) { + if (animAction) { + animAction->doAction(); + if (state() == Stopped && policy == DeleteWhenStopped) { + delete animAction; + animAction = 0; + } + } + } + } + +private: + QAbstractAnimationAction *animAction; + DeletionPolicy policy; +}; + +class QDeclarative1BulkValueUpdater +{ +public: + virtual ~QDeclarative1BulkValueUpdater() {} + virtual void setValue(qreal value) = 0; +}; + +//animates QDeclarative1BulkValueUpdater (assumes start and end values will be reals or compatible) +class Q_AUTOTEST_EXPORT QDeclarative1BulkValueAnimator : public QVariantAnimation +{ + Q_OBJECT +public: + QDeclarative1BulkValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {} + ~QDeclarative1BulkValueAnimator() { if (policy == DeleteWhenStopped) { delete animValue; animValue = 0; } } + void setAnimValue(QDeclarative1BulkValueUpdater *value, DeletionPolicy p) + { + if (state() == Running) + stop(); + if (policy == DeleteWhenStopped) + delete animValue; + animValue = value; + policy = p; + } + void setFromSourcedValue(bool *value) + { + fromSourced = value; + } +protected: + virtual void updateCurrentValue(const QVariant &value) + { + if (state() == QAbstractAnimation::Stopped) + return; + + if (animValue) + animValue->setValue(value.toReal()); + } + virtual void updateState(State newState, State oldState) + { + QVariantAnimation::updateState(newState, oldState); + if (newState == Running) { + //check for new from every loop + if (fromSourced) + *fromSourced = false; + } + } + +private: + QDeclarative1BulkValueUpdater *animValue; + bool *fromSourced; + DeletionPolicy policy; +}; + +//an animation that just gives a tick +template<class T, void (T::*method)(int)> +class QTickAnimationProxy_1 : public QAbstractAnimation +{ + //Q_OBJECT //doesn't work with templating +public: + QTickAnimationProxy_1(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {} + virtual int duration() const { return -1; } +protected: + virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); } + +private: + T *m_p; +}; + +class QDeclarative1AbstractAnimationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1AbstractAnimation) +public: + QDeclarative1AbstractAnimationPrivate() + : running(false), paused(false), alwaysRunToEnd(false), + connectedTimeLine(false), componentComplete(true), + avoidPropertyValueSourceStart(false), disableUserControl(false), + registered(false), loopCount(1), group(0) {} + + bool running:1; + bool paused:1; + bool alwaysRunToEnd:1; + bool connectedTimeLine:1; + bool componentComplete:1; + bool avoidPropertyValueSourceStart:1; + bool disableUserControl:1; + bool registered:1; + + int loopCount; + + void commence(); + + QDeclarativeProperty defaultProperty; + + QDeclarative1AnimationGroup *group; + + static QDeclarativeProperty createProperty(QObject *obj, const QString &str, QObject *infoObj); +}; + +class QDeclarative1PauseAnimationPrivate : public QDeclarative1AbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1PauseAnimation) +public: + QDeclarative1PauseAnimationPrivate() + : QDeclarative1AbstractAnimationPrivate(), pa(0) {} + + void init(); + + QPauseAnimation *pa; +}; + +class QDeclarative1ScriptActionPrivate : public QDeclarative1AbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ScriptAction) +public: + QDeclarative1ScriptActionPrivate(); + + void init(); + + QDeclarativeScriptString script; + QString name; + QDeclarativeScriptString runScriptScript; + bool hasRunScriptScript; + bool reversing; + + void execute(); + + QAnimationActionProxy_1<QDeclarative1ScriptActionPrivate, + &QDeclarative1ScriptActionPrivate::execute> proxy; + QActionAnimation_1 *rsa; +}; + +class QDeclarative1PropertyActionPrivate : public QDeclarative1AbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1PropertyAction) +public: + QDeclarative1PropertyActionPrivate() + : QDeclarative1AbstractAnimationPrivate(), target(0), spa(0) {} + + void init(); + + QObject *target; + QString propertyName; + QString properties; + QList<QObject *> targets; + QList<QObject *> exclude; + + QDeclarativeNullableValue<QVariant> value; + + QActionAnimation_1 *spa; +}; + +class QDeclarative1AnimationGroupPrivate : public QDeclarative1AbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1AnimationGroup) +public: + QDeclarative1AnimationGroupPrivate() + : QDeclarative1AbstractAnimationPrivate(), ag(0) {} + + static void append_animation(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list, QDeclarative1AbstractAnimation *role); + static void clear_animation(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list); + QList<QDeclarative1AbstractAnimation *> animations; + QAnimationGroup *ag; +}; + +class QDeclarative1PropertyAnimationPrivate : public QDeclarative1AbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1PropertyAnimation) +public: + QDeclarative1PropertyAnimationPrivate() + : QDeclarative1AbstractAnimationPrivate(), target(0), fromSourced(false), fromIsDefined(false), toIsDefined(false), + rangeIsSet(false), defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), va(0), actions(0) {} + + void init(); + + QVariant from; + QVariant to; + + QObject *target; + QString propertyName; + QString properties; + QList<QObject *> targets; + QList<QObject *> exclude; + QString defaultProperties; + + bool fromSourced; + bool fromIsDefined:1; + bool toIsDefined:1; + bool rangeIsSet:1; + bool defaultToInterpolatorType:1; + int interpolatorType; + QVariantAnimation::Interpolator interpolator; + + QDeclarative1BulkValueAnimator *va; + + // for animations that don't use the QDeclarative1BulkValueAnimator + QDeclarative1StateActions *actions; + + static QVariant interpolateVariant(const QVariant &from, const QVariant &to, qreal progress); + static void convertVariant(QVariant &variant, int type); +}; + +class QDeclarative1RotationAnimationPrivate : public QDeclarative1PropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1RotationAnimation) +public: + QDeclarative1RotationAnimationPrivate() : direction(QDeclarative1RotationAnimation::Numerical) {} + + QDeclarative1RotationAnimation::RotationDirection direction; +}; + +class QDeclarative1ParentAnimationPrivate : public QDeclarative1AnimationGroupPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ParentAnimation) +public: + QDeclarative1ParentAnimationPrivate() + : QDeclarative1AnimationGroupPrivate(), target(0), newParent(0), + via(0), topLevelGroup(0), startAction(0), endAction(0) {} + + QDeclarativeItem *target; + QDeclarativeItem *newParent; + QDeclarativeItem *via; + + QSequentialAnimationGroup *topLevelGroup; + QActionAnimation_1 *startAction; + QActionAnimation_1 *endAction; + + QPointF computeTransformOrigin(QDeclarativeItem::TransformOrigin origin, qreal width, qreal height) const; +}; + +class QDeclarative1AnchorAnimationPrivate : public QDeclarative1AbstractAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1AnchorAnimation) +public: + QDeclarative1AnchorAnimationPrivate() : rangeIsSet(false), va(0), + interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {} + + bool rangeIsSet; + QDeclarative1BulkValueAnimator *va; + QVariantAnimation::Interpolator interpolator; + QList<QDeclarativeItem*> targets; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1AnimationPropertyUpdater : public QDeclarative1BulkValueUpdater +{ +public: + QDeclarative1StateActions actions; + int interpolatorType; //for Number/ColorAnimation + int prevInterpolatorType; //for generic + QVariantAnimation::Interpolator interpolator; + bool reverse; + bool fromSourced; + bool fromDefined; + bool *wasDeleted; + QDeclarative1AnimationPropertyUpdater() : prevInterpolatorType(0), wasDeleted(0) {} + ~QDeclarative1AnimationPropertyUpdater() { if (wasDeleted) *wasDeleted = true; } + void setValue(qreal v); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEANIMATION_P_H diff --git a/src/qtquick1/util/qdeclarativeapplication.cpp b/src/qtquick1/util/qdeclarativeapplication.cpp new file mode 100644 index 0000000000..3cc572f27b --- /dev/null +++ b/src/qtquick1/util/qdeclarativeapplication.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeapplication_p.h" +#include <private/qobject_p.h> +#include <QtGui/QApplication> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1ApplicationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Application) +public: + QDeclarative1ApplicationPrivate() : active(QApplication::activeWindow() != 0), + layoutDirection(QApplication::layoutDirection()) {} + bool active; + Qt::LayoutDirection layoutDirection; +}; + +/* + This object and its properties are documented as part of the Qt object, + in qdeclarativengine.cpp +*/ + +QDeclarative1Application::QDeclarative1Application(QObject *parent) : QObject(*new QDeclarative1ApplicationPrivate(), parent) +{ + if (qApp) + qApp->installEventFilter(this); +} + +QDeclarative1Application::~QDeclarative1Application() +{ +} + +bool QDeclarative1Application::active() const +{ + Q_D(const QDeclarative1Application); + return d->active; +} + +Qt::LayoutDirection QDeclarative1Application::layoutDirection() const +{ + Q_D(const QDeclarative1Application); + return d->layoutDirection; +} + +bool QDeclarative1Application::eventFilter(QObject *obj, QEvent *event) +{ + Q_UNUSED(obj) + Q_D(QDeclarative1Application); + if (event->type() == QEvent::ApplicationActivate + || event->type() == QEvent::ApplicationDeactivate) { + bool active = d->active; + if (event->type() == QEvent::ApplicationActivate) + active = true; + else if (event->type() == QEvent::ApplicationDeactivate) + active = false; + + if (d->active != active) { + d->active = active; + emit activeChanged(); + } + } + if (event->type() == QEvent::LayoutDirectionChange) { + Qt::LayoutDirection direction = QApplication::layoutDirection(); + if (d->layoutDirection != direction) { + d->layoutDirection = direction; + emit layoutDirectionChanged(); + } + } + return false; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativeapplication_p.h b/src/qtquick1/util/qdeclarativeapplication_p.h new file mode 100644 index 0000000000..4a588c2940 --- /dev/null +++ b/src/qtquick1/util/qdeclarativeapplication_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEAPPLICATION_P_H +#define QDECLARATIVEAPPLICATION_P_H + +#include <QtCore/QObject> +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1ApplicationPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Application : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection NOTIFY layoutDirectionChanged) + +public: + explicit QDeclarative1Application(QObject *parent = 0); + virtual ~QDeclarative1Application(); + bool active() const; + Qt::LayoutDirection layoutDirection() const; + +protected: + bool eventFilter(QObject *obj, QEvent *event); + +Q_SIGNALS: + void activeChanged(); + void layoutDirectionChanged(); + +private: + Q_DISABLE_COPY(QDeclarative1Application) + Q_DECLARE_PRIVATE(QDeclarative1Application) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Application) + +QT_END_HEADER + +#endif // QDECLARATIVEAPPLICATION_P_H diff --git a/src/qtquick1/util/qdeclarativebehavior.cpp b/src/qtquick1/util/qdeclarativebehavior.cpp new file mode 100644 index 0000000000..203f141721 --- /dev/null +++ b/src/qtquick1/util/qdeclarativebehavior.cpp @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativebehavior_p.h" + +#include "QtQuick1/private/qdeclarativeanimation_p.h" +#include "QtQuick1/private/qdeclarativetransition_p.h" + +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativeproperty_p.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1BehaviorPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Behavior) +public: + QDeclarative1BehaviorPrivate() : animation(0), enabled(true), finalized(false) + , blockRunningChanged(false) {} + + QDeclarativeProperty property; + QVariant currentValue; + QVariant targetValue; + QDeclarativeGuard<QDeclarative1AbstractAnimation> animation; + bool enabled; + bool finalized; + bool blockRunningChanged; +}; + +/*! + \qmlclass Behavior QDeclarative1Behavior + \ingroup qml-animation-transition + \since 4.7 + \brief The Behavior element allows you to specify a default animation for a property change. + + A Behavior defines the default animation to be applied whenever a + particular property value changes. + + For example, the following Behavior defines a NumberAnimation to be run + whenever the \l Rectangle's \c width value changes. When the MouseArea + is clicked, the \c width is changed, triggering the behavior's animation: + + \snippet doc/src/snippets/declarative/behavior.qml 0 + + Note that a property cannot have more than one assigned Behavior. To provide + multiple animations within a Behavior, use ParallelAnimation or + SequentialAnimation. + + If a \l{QML States}{state change} has a \l Transition that matches the same property as a + Behavior, the \l Transition animation overrides the Behavior for that + state change. For general advice on using Behaviors to animate state changes, see + \l{Using QML Behaviors with States}. + + \sa {QML Animation and Transitions}, {declarative/animation/behaviors}{Behavior example}, QtDeclarative +*/ + + +QDeclarative1Behavior::QDeclarative1Behavior(QObject *parent) + : QObject(*(new QDeclarative1BehaviorPrivate), parent) +{ +} + +QDeclarative1Behavior::~QDeclarative1Behavior() +{ +} + +/*! + \qmlproperty Animation Behavior::animation + \default + + This property holds the animation to run when the behavior is triggered. +*/ + +QDeclarative1AbstractAnimation *QDeclarative1Behavior::animation() +{ + Q_D(QDeclarative1Behavior); + return d->animation; +} + +void QDeclarative1Behavior::setAnimation(QDeclarative1AbstractAnimation *animation) +{ + Q_D(QDeclarative1Behavior); + if (d->animation) { + qmlInfo(this) << tr("Cannot change the animation assigned to a Behavior."); + return; + } + + d->animation = animation; + if (d->animation) { + d->animation->setDefaultTarget(d->property); + d->animation->setDisableUserControl(); + connect(d->animation->qtAnimation(), + SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), + this, + SLOT(qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State))); + } +} + + +void QDeclarative1Behavior::qtAnimationStateChanged(QAbstractAnimation::State newState,QAbstractAnimation::State) +{ + Q_D(QDeclarative1Behavior); + if (!d->blockRunningChanged) + d->animation->notifyRunningChanged(newState == QAbstractAnimation::Running); +} + + +/*! + \qmlproperty bool Behavior::enabled + + This property holds whether the behavior will be triggered when the tracked + property changes value. + + By default a Behavior is enabled. +*/ + +bool QDeclarative1Behavior::enabled() const +{ + Q_D(const QDeclarative1Behavior); + return d->enabled; +} + +void QDeclarative1Behavior::setEnabled(bool enabled) +{ + Q_D(QDeclarative1Behavior); + if (d->enabled == enabled) + return; + d->enabled = enabled; + emit enabledChanged(); +} + +void QDeclarative1Behavior::write(const QVariant &value) +{ + Q_D(QDeclarative1Behavior); + qmlExecuteDeferred(this); + if (!d->animation || !d->enabled || !d->finalized) { + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + d->targetValue = value; + return; + } + + if (d->animation->isRunning() && value == d->targetValue) + return; + + d->currentValue = d->property.read(); + d->targetValue = value; + + if (d->animation->qtAnimation()->duration() != -1 + && d->animation->qtAnimation()->state() != QAbstractAnimation::Stopped) { + d->blockRunningChanged = true; + d->animation->qtAnimation()->stop(); + } + + QDeclarative1StateOperation::ActionList actions; + QDeclarative1Action action; + action.property = d->property; + action.fromValue = d->currentValue; + action.toValue = value; + actions << action; + + QList<QDeclarativeProperty> after; + d->animation->transition(actions, after, QDeclarative1AbstractAnimation::Forward); + d->animation->qtAnimation()->start(); + d->blockRunningChanged = false; + if (!after.contains(d->property)) + QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QDeclarative1Behavior::setTarget(const QDeclarativeProperty &property) +{ + Q_D(QDeclarative1Behavior); + d->property = property; + d->currentValue = property.read(); + if (d->animation) + d->animation->setDefaultTarget(property); + + QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this)); + engPriv->registerFinalizedParserStatusObject(this, this->metaObject()->indexOfSlot("componentFinalized()")); +} + +void QDeclarative1Behavior::componentFinalized() +{ + Q_D(QDeclarative1Behavior); + d->finalized = true; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativebehavior_p.h b/src/qtquick1/util/qdeclarativebehavior_p.h new file mode 100644 index 0000000000..76792bc438 --- /dev/null +++ b/src/qtquick1/util/qdeclarativebehavior_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBEHAVIOR_H +#define QDECLARATIVEBEHAVIOR_H + +#include "QtQuick1/private/qdeclarativestate_p.h" + +#include <QtDeclarative/qdeclarativepropertyvaluesource.h> +#include <QtDeclarative/qdeclarativepropertyvalueinterceptor.h> +#include <QtDeclarative/qdeclarative.h> +#include <QtCore/QAbstractAnimation> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1AbstractAnimation; +class QDeclarative1BehaviorPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Behavior : public QObject, public QDeclarativePropertyValueInterceptor +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1Behavior) + + Q_INTERFACES(QDeclarativePropertyValueInterceptor) + Q_CLASSINFO("DefaultProperty", "animation") + Q_PROPERTY(QDeclarative1AbstractAnimation *animation READ animation WRITE setAnimation) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_CLASSINFO("DeferredPropertyNames", "animation") + +public: + QDeclarative1Behavior(QObject *parent=0); + ~QDeclarative1Behavior(); + + virtual void setTarget(const QDeclarativeProperty &); + virtual void write(const QVariant &value); + + QDeclarative1AbstractAnimation *animation(); + void setAnimation(QDeclarative1AbstractAnimation *); + + bool enabled() const; + void setEnabled(bool enabled); + +Q_SIGNALS: + void enabledChanged(); + +private Q_SLOTS: + void componentFinalized(); + void qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Behavior) + +QT_END_HEADER + +#endif // QDECLARATIVEBEHAVIOR_H diff --git a/src/qtquick1/util/qdeclarativebind.cpp b/src/qtquick1/util/qdeclarativebind.cpp new file mode 100644 index 0000000000..bf0802b1d1 --- /dev/null +++ b/src/qtquick1/util/qdeclarativebind.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativebind_p.h" + +#include "QtDeclarative/private/qdeclarativenullablevalue_p_p.h" + +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/qdeclarativeproperty.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptcontext.h> +#include <QtScript/qscriptengine.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1BindPrivate : public QObjectPrivate +{ +public: + QDeclarative1BindPrivate() : when(true), componentComplete(true), obj(0) {} + + bool when : 1; + bool componentComplete : 1; + QObject *obj; + QString prop; + QDeclarativeNullableValue<QVariant> value; +}; + + +/*! + \qmlclass Binding QDeclarative1Bind + \ingroup qml-working-with-data + \since 4.7 + \brief The Binding element allows arbitrary property bindings to be created. + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Binding + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Binding to update the enteredText property + like this. + \code + TextEdit { id: myTextField; text: "Please type here..." } + Binding { target: app; property: "enteredText"; value: myTextField.text } + \endcode + Whenever the text in the TextEdit is updated, the C++ property will be + updated also. + + If the binding target or binding property is changed, the bound value is + immediately pushed onto the new target. + + \sa QtDeclarative +*/ +QDeclarative1Bind::QDeclarative1Bind(QObject *parent) + : QObject(*(new QDeclarative1BindPrivate), parent) +{ +} + +QDeclarative1Bind::~QDeclarative1Bind() +{ +} + +/*! + \qmlproperty bool Binding::when + + This property holds when the binding is active. + This should be set to an expression that evaluates to true when you want the binding to be active. + + \code + Binding { + target: contactName; property: 'text' + value: name; when: list.ListView.isCurrentItem + } + \endcode +*/ +bool QDeclarative1Bind::when() const +{ + Q_D(const QDeclarative1Bind); + return d->when; +} + +void QDeclarative1Bind::setWhen(bool v) +{ + Q_D(QDeclarative1Bind); + d->when = v; + eval(); +} + +/*! + \qmlproperty Object Binding::target + + The object to be updated. +*/ +QObject *QDeclarative1Bind::object() +{ + Q_D(const QDeclarative1Bind); + return d->obj; +} + +void QDeclarative1Bind::setObject(QObject *obj) +{ + Q_D(QDeclarative1Bind); + d->obj = obj; + eval(); +} + +/*! + \qmlproperty string Binding::property + + The property to be updated. +*/ +QString QDeclarative1Bind::property() const +{ + Q_D(const QDeclarative1Bind); + return d->prop; +} + +void QDeclarative1Bind::setProperty(const QString &p) +{ + Q_D(QDeclarative1Bind); + d->prop = p; + eval(); +} + +/*! + \qmlproperty any Binding::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. +*/ +QVariant QDeclarative1Bind::value() const +{ + Q_D(const QDeclarative1Bind); + return d->value.value; +} + +void QDeclarative1Bind::setValue(const QVariant &v) +{ + Q_D(QDeclarative1Bind); + d->value.value = v; + d->value.isNull = false; + eval(); +} + +void QDeclarative1Bind::classBegin() +{ + Q_D(QDeclarative1Bind); + d->componentComplete = false; +} + +void QDeclarative1Bind::componentComplete() +{ + Q_D(QDeclarative1Bind); + d->componentComplete = true; + eval(); +} + +void QDeclarative1Bind::eval() +{ + Q_D(QDeclarative1Bind); + if (!d->obj || d->value.isNull || !d->when || !d->componentComplete) + return; + + QDeclarativeProperty prop(d->obj, d->prop); + prop.write(d->value.value); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativebind_p.h b/src/qtquick1/util/qdeclarativebind_p.h new file mode 100644 index 0000000000..d8296455c7 --- /dev/null +++ b/src/qtquick1/util/qdeclarativebind_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEBIND_H +#define QDECLARATIVEBIND_H + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1BindPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Bind : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1Bind) + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QString property READ property WRITE setProperty) + Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(bool when READ when WRITE setWhen) + +public: + QDeclarative1Bind(QObject *parent=0); + ~QDeclarative1Bind(); + + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +protected: + virtual void classBegin(); + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Bind) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/util/qdeclarativeconnections.cpp b/src/qtquick1/util/qdeclarativeconnections.cpp new file mode 100644 index 0000000000..d1d92356ac --- /dev/null +++ b/src/qtquick1/util/qdeclarativeconnections.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeconnections_p.h" + +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/private/qdeclarativeproperty_p.h> +#include <QtDeclarative/private/qdeclarativeboundsignal_p.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/private/qdeclarativecontext_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstringlist.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1ConnectionsPrivate : public QObjectPrivate +{ +public: + QDeclarative1ConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + + QList<QDeclarativeBoundSignal*> boundsignals; + QObject *target; + + bool targetSet; + bool ignoreUnknownSignals; + bool componentcomplete; + + QByteArray data; +}; + +/*! + \qmlclass Connections QDeclarative1Connections + \ingroup qml-utility-elements + \since 4.7 + \brief A Connections element describes generalized connections to signals. + + A Connections object creates a connection to a QML signal. + + When connecting to signals in QML, the usual way is to create an + "on<Signal>" handler that reacts when a signal is received, like this: + + \qml + MouseArea { + onClicked: { foo(parameters) } + } + \endqml + + However, it is not possible to connect to a signal in this way in some + cases, such as when: + + \list + \i Multiple connections to the same signal are required + \i Creating connections outside the scope of the signal sender + \i Connecting to targets not defined in QML + \endlist + + When any of these are needed, the Connections element can be used instead. + + For example, the above code can be changed to use a Connections object, + like this: + + \qml + MouseArea { + Connections { + onClicked: foo(parameters) + } + } + \endqml + + More generally, the Connections object can be a child of some object other than + the sender of the signal: + + \qml + MouseArea { + id: area + } + // ... + \endqml + \qml + Connections { + target: area + onClicked: foo(parameters) + } + \endqml + + \sa QtDeclarative +*/ +QDeclarative1Connections::QDeclarative1Connections(QObject *parent) : + QObject(*(new QDeclarative1ConnectionsPrivate), parent) +{ +} + +QDeclarative1Connections::~QDeclarative1Connections() +{ +} + +/*! + \qmlproperty Object Connections::target + This property holds the object that sends the signal. + + If this property is not set, the \c target defaults to the parent of the Connection. + + If set to null, no connection is made and any signal handlers are ignored + until the target is not null. +*/ +QObject *QDeclarative1Connections::target() const +{ + Q_D(const QDeclarative1Connections); + return d->targetSet ? d->target : parent(); +} + +void QDeclarative1Connections::setTarget(QObject *obj) +{ + Q_D(QDeclarative1Connections); + d->targetSet = true; // even if setting to 0, it is *set* + if (d->target == obj) + return; + foreach (QDeclarativeBoundSignal *s, d->boundsignals) { + // It is possible that target is being changed due to one of our signal + // handlers -> use deleteLater(). + if (s->isEvaluating()) + s->deleteLater(); + else + delete s; + } + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); +} + +/*! + \qmlproperty bool Connections::ignoreUnknownSignals + + Normally, a connection to a non-existent signal produces runtime errors. + + If this property is set to \c true, such errors are ignored. + This is useful if you intend to connect to different types of objects, handling + a different set of signals for each object. +*/ +bool QDeclarative1Connections::ignoreUnknownSignals() const +{ + Q_D(const QDeclarative1Connections); + return d->ignoreUnknownSignals; +} + +void QDeclarative1Connections::setIgnoreUnknownSignals(bool ignore) +{ + Q_D(QDeclarative1Connections); + d->ignoreUnknownSignals = ignore; +} + + + +QByteArray +QDeclarative1ConnectionsParser::compile(const QList<QDeclarativeCustomParserProperty> &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = QString::fromUtf8(props.at(ii).name()); + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QDeclarative1Connections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList<QVariant> values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + error(props.at(ii), QDeclarative1Connections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) { + error(props.at(ii), QDeclarative1Connections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(value); + if (v.isScript()) { + ds << propName; + ds << v.asScript(); + } else { + error(props.at(ii), QDeclarative1Connections::tr("Connections: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QDeclarative1ConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarative1ConnectionsPrivate *p = + static_cast<QDeclarative1ConnectionsPrivate *>(QObjectPrivate::get(object)); + p->data = data; +} + + +void QDeclarative1Connections::connectSignals() +{ + Q_D(QDeclarative1Connections); + if (!d->componentcomplete || (d->targetSet && !target())) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QString script; + ds >> script; + QDeclarativeProperty prop(target(), propName); + if (prop.isValid() && (prop.type() & QDeclarativeProperty::SignalProperty)) { + QDeclarativeBoundSignal *signal = + new QDeclarativeBoundSignal(target(), prop.method(), this); + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(this), 0, script); + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + signal->setExpression(expression); + d->boundsignals += signal; + } else { + if (!d->ignoreUnknownSignals) + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } + } +} + +void QDeclarative1Connections::classBegin() +{ + Q_D(QDeclarative1Connections); + d->componentcomplete=false; +} + +void QDeclarative1Connections::componentComplete() +{ + Q_D(QDeclarative1Connections); + d->componentcomplete=true; + connectSignals(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativeconnections_p.h b/src/qtquick1/util/qdeclarativeconnections_p.h new file mode 100644 index 0000000000..6d6112bf98 --- /dev/null +++ b/src/qtquick1/util/qdeclarativeconnections_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONNECTIONS_H +#define QDECLARATIVECONNECTIONS_H + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativescriptstring.h> +#include <QtDeclarative/private/qdeclarativecustomparser_p.h> + +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeBoundSignal; +class QDeclarativeContext; +class QDeclarative1ConnectionsPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1Connections : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1Connections) + + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) + +public: + QDeclarative1Connections(QObject *parent=0); + ~QDeclarative1Connections(); + + QObject *target() const; + void setTarget(QObject *); + + bool ignoreUnknownSignals() const; + void setIgnoreUnknownSignals(bool ignore); + +Q_SIGNALS: + void targetChanged(); + +private: + void connectSignals(); + void classBegin(); + void componentComplete(); +}; + +class QDeclarative1ConnectionsParser : public QDeclarativeCustomParser +{ +public: + virtual QByteArray compile(const QList<QDeclarativeCustomParserProperty> &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Connections) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/util/qdeclarativefontloader.cpp b/src/qtquick1/util/qdeclarativefontloader.cpp new file mode 100644 index 0000000000..38f7fc6121 --- /dev/null +++ b/src/qtquick1/util/qdeclarativefontloader.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativefontloader_p.h" + +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/qdeclarativeengine.h> + +#include <QStringList> +#include <QUrl> +#include <QDebug> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QFontDatabase> + +#include <private/qobject_p.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +QT_BEGIN_NAMESPACE + + + +#define FONTLOADER_MAXIMUM_REDIRECT_RECURSION 16 + +class QDeclarative1FontObject : public QObject +{ +Q_OBJECT + +public: + QDeclarative1FontObject(int _id); + + void download(const QUrl &url, QNetworkAccessManager *manager); + +Q_SIGNALS: + void fontDownloaded(const QString&, QDeclarative1FontLoader::Status); + +private Q_SLOTS: + void replyFinished(); + +public: + int id; + +private: + QNetworkReply *reply; + int redirectCount; + + Q_DISABLE_COPY(QDeclarative1FontObject) +}; + +QDeclarative1FontObject::QDeclarative1FontObject(int _id = -1) + : QObject(0), id(_id), reply(0), redirectCount(0) {} + + +void QDeclarative1FontObject::download(const QUrl &url, QNetworkAccessManager *manager) +{ + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + reply = manager->get(req); + QObject::connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); +} + +void QDeclarative1FontObject::replyFinished() +{ + if (reply) { + redirectCount++; + if (redirectCount < FONTLOADER_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkAccessManager *manager = reply->manager(); + reply->deleteLater(); + reply = 0; + download(url, manager); + return; + } + } + redirectCount = 0; + + if (!reply->error()) { + id = QFontDatabase::addApplicationFontFromData(reply->readAll()); + if (id != -1) + emit fontDownloaded(QFontDatabase::applicationFontFamilies(id).at(0), QDeclarative1FontLoader::Ready); + else + emit fontDownloaded(QString(), QDeclarative1FontLoader::Error); + } else { + emit fontDownloaded(QString(), QDeclarative1FontLoader::Error); + } + reply->deleteLater(); + reply = 0; + } +} + + +class QDeclarative1FontLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1FontLoader) + +public: + QDeclarative1FontLoaderPrivate() : status(QDeclarative1FontLoader::Null) {} + + QUrl url; + QString name; + QDeclarative1FontLoader::Status status; + static QHash<QUrl, QDeclarative1FontObject*> fonts; +}; + +QHash<QUrl, QDeclarative1FontObject*> QDeclarative1FontLoaderPrivate::fonts; + +/*! + \qmlclass FontLoader QDeclarative1FontLoader + \ingroup qml-utility-elements + \since 4.7 + \brief The FontLoader element allows fonts to be loaded by name or URL. + + The FontLoader element is used to load fonts by name or URL. + + The \l status indicates when the font has been loaded, which is useful + for fonts loaded from remote sources. + + For example: + \qml + import QtQuick 1.0 + + Column { + FontLoader { id: fixedFont; name: "Courier" } + FontLoader { id: webFont; source: "http://www.mysite.com/myfont.ttf" } + + Text { text: "Fixed-size font"; font.family: fixedFont.name } + Text { text: "Fancy font"; font.family: webFont.name } + } + \endqml + + \sa {declarative/text/fonts}{Fonts example} +*/ +QDeclarative1FontLoader::QDeclarative1FontLoader(QObject *parent) + : QObject(*(new QDeclarative1FontLoaderPrivate), parent) +{ +} + +QDeclarative1FontLoader::~QDeclarative1FontLoader() +{ +} + +/*! + \qmlproperty url FontLoader::source + The url of the font to load. +*/ +QUrl QDeclarative1FontLoader::source() const +{ + Q_D(const QDeclarative1FontLoader); + return d->url; +} + +void QDeclarative1FontLoader::setSource(const QUrl &url) +{ + Q_D(QDeclarative1FontLoader); + if (url == d->url) + return; + d->url = qmlContext(this)->resolvedUrl(url); + emit sourceChanged(); + +#ifndef QT_NO_LOCALFILE_OPTIMIZED_QML + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(d->url); + if (!localFile.isEmpty()) { + if (!d->fonts.contains(d->url)) { + int id = QFontDatabase::addApplicationFont(localFile); + if (id != -1) { + updateFontInfo(QFontDatabase::applicationFontFamilies(id).at(0), Ready); + QDeclarative1FontObject *fo = new QDeclarative1FontObject(id); + d->fonts[d->url] = fo; + } else { + updateFontInfo(QString(), Error); + } + } else { + updateFontInfo(QFontDatabase::applicationFontFamilies(d->fonts[d->url]->id).at(0), Ready); + } + } else +#endif + { + if (!d->fonts.contains(d->url)) { + QDeclarative1FontObject *fo = new QDeclarative1FontObject; + d->fonts[d->url] = fo; + fo->download(d->url, qmlEngine(this)->networkAccessManager()); + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarative1FontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarative1FontLoader::Status))); + } else { + QDeclarative1FontObject *fo = d->fonts[d->url]; + if (fo->id == -1) { + d->status = Loading; + emit statusChanged(); + QObject::connect(fo, SIGNAL(fontDownloaded(QString,QDeclarative1FontLoader::Status)), + this, SLOT(updateFontInfo(QString,QDeclarative1FontLoader::Status))); + } + else + updateFontInfo(QFontDatabase::applicationFontFamilies(fo->id).at(0), Ready); + } + } +} + +void QDeclarative1FontLoader::updateFontInfo(const QString& name, QDeclarative1FontLoader::Status status) +{ + Q_D(QDeclarative1FontLoader); + + if (name != d->name) { + d->name = name; + emit nameChanged(); + } + if (status != d->status) { + if (status == Error) + qmlInfo(this) << "Cannot load font: \"" << d->url.toString() << "\""; + d->status = status; + emit statusChanged(); + } +} + +/*! + \qmlproperty string FontLoader::name + + This property holds the name of the font family. + It is set automatically when a font is loaded using the \c url property. + + Use this to set the \c font.family property of a \c Text item. + + Example: + \qml + Item { + width: 200; height: 50 + + FontLoader { + id: webFont + source: "http://www.mysite.com/myfont.ttf" + } + Text { + text: "Fancy font" + font.family: webFont.name + } + } + \endqml +*/ +QString QDeclarative1FontLoader::name() const +{ + Q_D(const QDeclarative1FontLoader); + return d->name; +} + +void QDeclarative1FontLoader::setName(const QString &name) +{ + Q_D(QDeclarative1FontLoader); + if (d->name == name) + return; + d->name = name; + emit nameChanged(); + d->status = Ready; + emit statusChanged(); +} + +/*! + \qmlproperty enumeration FontLoader::status + + This property holds the status of font loading. It can be one of: + \list + \o FontLoader.Null - no font has been set + \o FontLoader.Ready - the font has been loaded + \o FontLoader.Loading - the font is currently being loaded + \o FontLoader.Error - an error occurred while loading the font + \endlist + + Use this status to provide an update or respond to the status change in some way. + For example, you could: + + \list + \o Trigger a state change: + \qml + State { name: 'loaded'; when: loader.status == FontLoader.Ready } + \endqml + + \o Implement an \c onStatusChanged signal handler: + \qml + FontLoader { + id: loader + onStatusChanged: if (loader.status == FontLoader.Ready) console.log('Loaded') + } + \endqml + + \o Bind to the status value: + \qml + Text { text: loader.status == FontLoader.Ready ? 'Loaded' : 'Not loaded' } + \endqml + \endlist +*/ +QDeclarative1FontLoader::Status QDeclarative1FontLoader::status() const +{ + Q_D(const QDeclarative1FontLoader); + return d->status; +} + + + +QT_END_NAMESPACE + +#include <qdeclarativefontloader.moc> diff --git a/src/qtquick1/util/qdeclarativefontloader_p.h b/src/qtquick1/util/qdeclarativefontloader_p.h new file mode 100644 index 0000000000..76fa8e350f --- /dev/null +++ b/src/qtquick1/util/qdeclarativefontloader_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEFONTLOADER_H +#define QDECLARATIVEFONTLOADER_H + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1FontLoaderPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1FontLoader : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1FontLoader) + Q_ENUMS(Status) + + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + +public: + enum Status { Null = 0, Ready, Loading, Error }; + + QDeclarative1FontLoader(QObject *parent = 0); + ~QDeclarative1FontLoader(); + + QUrl source() const; + void setSource(const QUrl &url); + + QString name() const; + void setName(const QString &name); + + Status status() const; + +private Q_SLOTS: + void updateFontInfo(const QString&, QDeclarative1FontLoader::Status); + +Q_SIGNALS: + void sourceChanged(); + void nameChanged(); + void statusChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1FontLoader) + +QT_END_HEADER + +#endif // QDECLARATIVEFONTLOADER_H + diff --git a/src/qtquick1/util/qdeclarativelistaccessor.cpp b/src/qtquick1/util/qdeclarativelistaccessor.cpp new file mode 100644 index 0000000000..215fe670e5 --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistaccessor.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativelistaccessor_p.h" + +#include <QtDeclarative/private/qdeclarativemetatype_p.h> + +#include <QtCore/qstringlist.h> +#include <QtCore/qdebug.h> + +// ### Remove me +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +QT_BEGIN_NAMESPACE + + + +QDeclarative1ListAccessor::QDeclarative1ListAccessor() +: m_type(Invalid) +{ +} + +QDeclarative1ListAccessor::~QDeclarative1ListAccessor() +{ +} + +QVariant QDeclarative1ListAccessor::list() const +{ + return d; +} + +void QDeclarative1ListAccessor::setList(const QVariant &v, QDeclarativeEngine *engine) +{ + d = v; + + QDeclarativeEnginePrivate *enginePrivate = engine?QDeclarativeEnginePrivate::get(engine):0; + + if (!d.isValid()) { + m_type = Invalid; + } else if (d.userType() == QVariant::StringList) { + m_type = StringList; + } else if (d.userType() == QMetaType::QVariantList) { + m_type = VariantList; + } else if (d.canConvert(QVariant::Int)) { + m_type = Integer; + } else if ((!enginePrivate && QDeclarativeMetaType::isQObject(d.userType())) || + (enginePrivate && enginePrivate->isQObject(d.userType()))) { + QObject *data = enginePrivate?enginePrivate->toQObject(v):QDeclarativeMetaType::toQObject(v); + d = QVariant::fromValue(data); + m_type = Instance; + } else if (d.userType() == qMetaTypeId<QDeclarativeListReference>()) { + m_type = ListProperty; + } else { + m_type = Instance; + } +} + +int QDeclarative1ListAccessor::count() const +{ + switch(m_type) { + case StringList: + return qvariant_cast<QStringList>(d).count(); + case VariantList: + return qvariant_cast<QVariantList>(d).count(); + case ListProperty: + return ((QDeclarativeListReference *)d.constData())->count(); + case Instance: + return 1; + case Integer: + return d.toInt(); + default: + case Invalid: + return 0; + } +} + +QVariant QDeclarative1ListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(m_type) { + case StringList: + return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx)); + case VariantList: + return qvariant_cast<QVariantList>(d).at(idx); + case ListProperty: + return QVariant::fromValue(((QDeclarativeListReference *)d.constData())->at(idx)); + case Instance: + return d; + case Integer: + return QVariant(idx); + default: + case Invalid: + return QVariant(); + } +} + +bool QDeclarative1ListAccessor::isValid() const +{ + return m_type != Invalid; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativelistaccessor_p.h b/src/qtquick1/util/qdeclarativelistaccessor_p.h new file mode 100644 index 0000000000..6372deec23 --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistaccessor_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTACCESSOR_H +#define QDECLARATIVELISTACCESSOR_H + +#include <QtCore/QVariant> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; + +QT_MODULE(Declarative) + +class Q_AUTOTEST_EXPORT QDeclarative1ListAccessor +{ +public: + QDeclarative1ListAccessor(); + ~QDeclarative1ListAccessor(); + + QVariant list() const; + void setList(const QVariant &, QDeclarativeEngine * = 0); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; + Type type() const { return m_type; } + +private: + Type m_type; + QVariant d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVELISTACCESSOR_H diff --git a/src/qtquick1/util/qdeclarativelistmodel.cpp b/src/qtquick1/util/qdeclarativelistmodel.cpp new file mode 100644 index 0000000000..f9c9ded9b0 --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistmodel.cpp @@ -0,0 +1,1631 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativelistmodel_p_p.h" +#include "QtQuick1/private/qdeclarativelistmodelworkeragent_p.h" +#include "QtQuick1/private/qdeclarativeopenmetaobject_p.h" + +#include <QtDeclarative/private/qdeclarativecustomparser_p.h> +#include <QtDeclarative/private/qdeclarativeparser_p.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qstack.h> +#include <QXmlStreamReader> +#include <QtScript/qscriptvalueiterator.h> + +Q_DECLARE_METATYPE(QListModelInterface *) + +QT_BEGIN_NAMESPACE + + + +template<typename T> +void qdeclarativelistmodel_move(int from, int to, int n, T *items) +{ + if (n == 1) { + items->move(from, to); + } else { + T replaced; + int i=0; + typename T::ConstIterator it=items->begin(); it += from+n; + for (; i<to-from; ++i,++it) + replaced.append(*it); + i=0; + it=items->begin(); it += from; + for (; i<n; ++i,++it) + replaced.append(*it); + typename T::ConstIterator f=replaced.begin(); + typename T::Iterator t=items->begin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } +} + +QDeclarative1ListModelParser::ListInstruction *QDeclarative1ListModelParser::ListModelData::instructions() const +{ + return (QDeclarative1ListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmlclass ListModel QDeclarative1ListModel + \ingroup qml-working-with-data + \since 4.7 + \brief The ListModel element defines a free-form list data source. + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet doc/src/snippets/declarative/listmodel.qml 0 + + \clearfloat + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet doc/src/snippets/declarative/listmodel-simple.qml 0 + \dots 8 + \snippet doc/src/snippets/declarative/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet doc/src/snippets/declarative/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate + + \clearfloat + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external + thread will not be reflected in the list model in the main thread. + + \section1 Restrictions + + If a list model is to be accessed from a WorkerScript, it cannot + contain list-type data. So, the following model cannot be used from a WorkerScript + because of the list contained in the "attributes" property: + + \code + ListModel { + id: fruitModel + ListElement { + name: "Apple" + cost: 2.45 + attributes: [ + ListElement { description: "Core" }, + ListElement { description: "Deciduous" } + ] + } + } + \endcode + + In addition, the WorkerScript cannot add list-type data to the model. + + \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative +*/ + + +/* + A ListModel internally uses either a NestedListModel_1 or FlatListModel_1. + + A NestedListModel_1 can contain lists of ListElements (which + when retrieved from get() is accessible as a list model within the list + model) whereas a FlatListModel_1 cannot. + + ListModel uses a NestedListModel_1 to begin with, and if the model is later + used from a WorkerScript, it changes to use a FlatListModel_1 instead. This + is because ModelNode (which abstracts the nested list model data) needs + access to the declarative engine and script engine, which cannot be + safely used from outside of the main thread. +*/ + +QDeclarative1ListModel::QDeclarative1ListModel(QObject *parent) +: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel_1(this)), m_flat(0) +{ +} + +QDeclarative1ListModel::QDeclarative1ListModel(const QDeclarative1ListModel *orig, QDeclarative1ListModelWorkerAgent *parent) +: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0) +{ + m_flat = new FlatListModel_1(this); + m_flat->m_parentAgent = parent; + + if (orig->m_flat) { + m_flat->m_roles = orig->m_flat->m_roles; + m_flat->m_strings = orig->m_flat->m_strings; + m_flat->m_values = orig->m_flat->m_values; + + m_flat->m_nodeData.reserve(m_flat->m_values.count()); + for (int i=0; i<m_flat->m_values.count(); i++) + m_flat->m_nodeData << 0; + } +} + +QDeclarative1ListModel::~QDeclarative1ListModel() +{ + if (m_agent) + m_agent->release(); + + delete m_nested; + delete m_flat; +} + +bool QDeclarative1ListModel::flatten() +{ + if (m_flat) + return true; + + QList<int> roles = m_nested->roles(); + + QList<QHash<int, QVariant> > values; + bool hasNested = false; + for (int i=0; i<m_nested->count(); i++) { + values.append(m_nested->data(i, roles, &hasNested)); + if (hasNested) + return false; + } + + FlatListModel_1 *flat = new FlatListModel_1(this); + flat->m_values = values; + + for (int i=0; i<roles.count(); i++) { + QString s = m_nested->toString(roles[i]); + flat->m_roles.insert(roles[i], s); + flat->m_strings.insert(s, roles[i]); + } + + flat->m_nodeData.reserve(flat->m_values.count()); + for (int i=0; i<flat->m_values.count(); i++) + flat->m_nodeData << 0; + + m_flat = flat; + delete m_nested; + m_nested = 0; + return true; +} + +bool QDeclarative1ListModel::inWorkerThread() const +{ + return m_flat && m_flat->m_parentAgent; +} + +QDeclarative1ListModelWorkerAgent *QDeclarative1ListModel::agent() +{ + if (m_agent) + return m_agent; + + if (!flatten()) { + qmlInfo(this) << "List contains list-type data and cannot be used from a worker script"; + return 0; + } + + m_agent = new QDeclarative1ListModelWorkerAgent(this); + return m_agent; +} + +QList<int> QDeclarative1ListModel::roles() const +{ + return m_flat ? m_flat->roles() : m_nested->roles(); +} + +QString QDeclarative1ListModel::toString(int role) const +{ + return m_flat ? m_flat->toString(role) : m_nested->toString(role); +} + +QVariant QDeclarative1ListModel::data(int index, int role) const +{ + if (index >= count() || index < 0) + return QVariant(); + + return m_flat ? m_flat->data(index, role) : m_nested->data(index, role); +} + +/*! + \qmlproperty int ListModel::count + The number of data entries in the model. +*/ +int QDeclarative1ListModel::count() const +{ + return m_flat ? m_flat->count() : m_nested->count(); +} + +/*! + \qmlmethod ListModel::clear() + + Deletes all content from the model. + + \sa append() remove() +*/ +void QDeclarative1ListModel::clear() +{ + int cleared = count(); + if (m_flat) + m_flat->clear(); + else + m_nested->clear(); + + if (!inWorkerThread()) { + emit itemsRemoved(0, cleared); + emit countChanged(); + } +} + +QDeclarative1ListModel *ModelNode::model(const NestedListModel_1 *model) +{ + if (!modelCache) { + modelCache = new QDeclarative1ListModel; + QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel)); + modelCache->m_nested->_root = this; // ListModel defaults to nestable model + + for (int i=0; i<values.count(); ++i) { + ModelNode *subNode = qvariant_cast<ModelNode *>(values.at(i)); + if (subNode) + subNode->m_model = modelCache->m_nested; + } + } + return modelCache; +} + +ModelObject_1 *ModelNode::object(const NestedListModel_1 *model) +{ + if (!objectCache) { + objectCache = new ModelObject_1(this, + const_cast<NestedListModel_1*>(model), + QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel))); + QHash<QString, ModelNode *>::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it)); + } + objectCache->setNodeUpdatesEnabled(true); + } + return objectCache; +} + +/*! + \qmlmethod ListModel::remove(int index) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QDeclarative1ListModel::remove(int index) +{ + if (index < 0 || index >= count()) { + qmlInfo(this) << tr("remove: index %1 out of range").arg(index); + return; + } + + if (m_flat) + m_flat->remove(index); + else + m_nested->remove(index); + + if (!inWorkerThread()) { + emit itemsRemoved(index, 1); + emit countChanged(); + } +} + +/*! + \qmlmethod ListModel::insert(int index, jsobject dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set() append() +*/ +void QDeclarative1ListModel::insert(int index, const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("insert: value is not an object"); + return; + } + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap); + if (ok && !inWorkerThread()) { + emit itemsInserted(index, 1); + emit countChanged(); + } +} + +/*! + \qmlmethod ListModel::move(int from, int to, int n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + fruitModel.move(0, fruitModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QDeclarative1ListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + int origfrom = from; + int origto = to; + int orign = n; + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + if (m_flat) + m_flat->move(from, to, n); + else + m_nested->move(from, to, n); + + if (!inWorkerThread()) + emit itemsMoved(origfrom, origto, orign); +} + +/*! + \qmlmethod ListModel::append(jsobject dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + fruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set() remove() +*/ +void QDeclarative1ListModel::append(const QScriptValue& valuemap) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("append: value is not an object"); + return; + } + + insert(count(), valuemap); +} + +/*! + \qmlmethod object ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + fruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + fruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QScriptValue QDeclarative1ListModel::get(int index) const +{ + // the internal flat/nested class checks for bad index + return m_flat ? m_flat->get(index) : m_nested->get(index); +} + +/*! + \qmlmethod ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QDeclarative1ListModel::set(int index, const QScriptValue& valuemap) +{ + QList<int> roles; + set(index, valuemap, &roles); + if (!roles.isEmpty() && !inWorkerThread()) + emit itemsChanged(index, 1, roles); +} + +void QDeclarative1ListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles) +{ + if (!valuemap.isObject() || valuemap.isArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (index == count()) { + append(valuemap); + } else { + if (m_flat) + m_flat->set(index, valuemap, roles); + else + m_nested->set(index, valuemap, roles); + } +} + +/*! + \qmlmethod ListModel::setProperty(int index, string property, variant value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + fruitModel.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QDeclarative1ListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + QList<int> roles; + setProperty(index, property, value, &roles); + if (!roles.isEmpty() && !inWorkerThread()) + emit itemsChanged(index, 1, roles); +} + +void QDeclarative1ListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_flat) + m_flat->setProperty(index, property, value, roles); + else + m_nested->setProperty(index, property, value, roles); +} + +/*! + \qmlmethod ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QDeclarative1ListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QDeclarative1ListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QDeclarative1ListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data) +{ + QList<QVariant> values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + QDeclarativeCustomParserNode node = + qvariant_cast<QDeclarativeCustomParserNode>(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QDeclarative1ListElement::staticMetaObject) { + error(node, QDeclarative1ListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList<QDeclarativeCustomParserProperty> props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QDeclarativeCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QDeclarative1ListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == "id") { + error(nodeProp, QDeclarative1ListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QDeclarativeParser::Variant variant = + qvariant_cast<QDeclarativeParser::Variant>(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + int v = evaluateEnum(script); + if (v<0) { + if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) { + d[0] = char(QDeclarativeParser::Variant::String); + d += script.mid(12,script.length()-14); + } else { + error(prop, QDeclarative1ListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QDeclarativeParser::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QDeclarative1ListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps) +{ + QList<ListInstruction> instr; + QByteArray data; + listElementTypeName = QByteArray(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QDeclarativeCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QDeclarative1ListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name()))); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QDeclarative1ListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QDeclarative1ListModel *rv = static_cast<QDeclarative1ListModel *>(obj); + + ModelNode *root = new ModelNode(rv->m_nested); + rv->m_nested->_root = root; + QStack<ModelNode *> nodes; + nodes << root; + + bool processingSet = false; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode(rv->m_nested); + n->values << QVariant::fromValue(n2); + nodes.push(n2); + if (processingSet) + n->isArray = true; + } + break; + + case ListInstruction::Pop: + nodes.pop(); + break; + + case ListInstruction::Value: + { + ModelNode *n = nodes.top(); + switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) { + case QDeclarativeParser::Variant::Invalid: + n->isArray = true; + break; + case QDeclarativeParser::Variant::Boolean: + n->values.append(bool(data[1 + instr.dataIdx])); + break; + case QDeclarativeParser::Variant::Number: + n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble()); + break; + case QDeclarativeParser::Variant::String: + n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx)); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + processingSet = false; + } + break; + + case ListInstruction::Set: + { + ModelNode *n = nodes.top(); + ModelNode *n2 = new ModelNode(rv->m_nested); + n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2); + nodes.push(n2); + processingSet = true; + } + break; + } + } + + ModelNode *rootNode = rv->m_nested->_root; + for (int i=0; i<rootNode->values.count(); ++i) { + ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]); + node->listIndex = i; + node->updateListIndexes(); + } +} + +bool QDeclarative1ListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i<s.length()-1; i++) { + if (!s[i].isSpace()) + return false; + } + return true; + } + return false; +} + + +/*! + \qmlclass ListElement QDeclarative1ListElement + \ingroup qml-working-with-data + \since 4.7 + \brief The ListElement element defines a data item in a ListModel. + + List elements are defined inside ListModel definitions, and represent items in a + list that will be displayed using ListView or \l Repeater items. + + List elements are defined like other QML elements except that they contain + a collection of \e role definitions instead of properties. Using the same + syntax as property definitions, roles both define how the data is accessed + and include the data itself. + + The names used for roles must begin with a lower-case letter and should be + common to all elements in a given model. Values must be simple constants; either + strings (quoted and optionally within a call to QT_TR_NOOP), boolean values + (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). + + \section1 Referencing Roles + + The role names are used by delegates to obtain data from list elements. + Each role name is accessible in the delegate's scope, and refers to the + corresponding role in the current element. Where a role name would be + ambiguous to use, it can be accessed via the \l{ListView::}{model} + property (e.g., \c{model.cost} instead of \c{cost}). + + \section1 Example Usage + + The following model defines a series of list elements, each of which + contain "name" and "cost" roles and their associated values. + + \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model + + The delegate obtains the name and cost for each element by simply referring + to \c name and \c cost: + + \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view + + \sa ListModel +*/ + +FlatListModel_1::FlatListModel_1(QDeclarative1ListModel *base) + : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0) +{ +} + +FlatListModel_1::~FlatListModel_1() +{ + qDeleteAll(m_nodeData); +} + +QVariant FlatListModel_1::data(int index, int role) const +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + if (m_values[index].contains(role)) + return m_values[index][role]; + return QVariant(); +} + +QList<int> FlatListModel_1::roles() const +{ + return m_roles.keys(); +} + +QString FlatListModel_1::toString(int role) const +{ + if (m_roles.contains(role)) + return m_roles[role]; + return QString(); +} + +int FlatListModel_1::count() const +{ + return m_values.count(); +} + +void FlatListModel_1::clear() +{ + m_values.clear(); + + qDeleteAll(m_nodeData); + m_nodeData.clear(); +} + +void FlatListModel_1::remove(int index) +{ + m_values.removeAt(index); + removedNode(index); +} + +bool FlatListModel_1::insert(int index, const QScriptValue &value) +{ + Q_ASSERT(index >= 0 && index <= m_values.count()); + + QHash<int, QVariant> row; + if (!addValue(value, &row, 0)) + return false; + + m_values.insert(index, row); + insertedNode(index); + + return true; +} + +QScriptValue FlatListModel_1::get(int index) const +{ + QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel)); + + if (!scriptEngine) + return 0; + + if (index < 0 || index >= m_values.count()) + return scriptEngine->undefinedValue(); + + FlatListModel_1 *that = const_cast<FlatListModel_1*>(this); + if (!m_scriptClass) + that->m_scriptClass = new FlatListScriptClass_1(that, scriptEngine); + + FlatNodeData_1 *data = m_nodeData.value(index); + if (!data) { + data = new FlatNodeData_1(index); + that->m_nodeData.replace(index, data); + } + + return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data)); +} + +void FlatListModel_1::set(int index, const QScriptValue &value, QList<int> *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash<int, QVariant> row = m_values[index]; + if (addValue(value, &row, roles)) + m_values[index] = row; +} + +void FlatListModel_1::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) +{ + Q_ASSERT(index >= 0 && index < m_values.count()); + + QHash<QString, int>::Iterator iter = m_strings.find(property); + int role; + if (iter == m_strings.end()) { + role = m_roles.count(); + m_roles.insert(role, property); + m_strings.insert(property, role); + } else { + role = iter.value(); + } + + if (m_values[index][role] != value) { + roles->append(role); + m_values[index][role] = value; + } +} + +void FlatListModel_1::move(int from, int to, int n) +{ + qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values); + moveNodes(from, to, n); +} + +bool FlatListModel_1::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles) +{ + QScriptValueIterator it(value); + while (it.hasNext()) { + it.next(); + QScriptValue value = it.value(); + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return false; + } + + QString name = it.name(); + QVariant v = it.value().toVariant(); + + QHash<QString, int>::Iterator iter = m_strings.find(name); + if (iter == m_strings.end()) { + int role = m_roles.count(); + m_roles.insert(role, name); + iter = m_strings.insert(name, role); + if (roles) + roles->append(role); + } else { + int role = iter.value(); + if (roles && row->contains(role) && row->value(role) != v) + roles->append(role); + } + row->insert(*iter, v); + } + return true; +} + +void FlatListModel_1::insertedNode(int index) +{ + if (index >= 0 && index <= m_values.count()) { + m_nodeData.insert(index, 0); + + for (int i=index + 1; i<m_nodeData.count(); i++) { + if (m_nodeData[i]) + m_nodeData[i]->index = i; + } + } +} + +void FlatListModel_1::removedNode(int index) +{ + if (index >= 0 && index < m_nodeData.count()) { + delete m_nodeData.takeAt(index); + + for (int i=index; i<m_nodeData.count(); i++) { + if (m_nodeData[i]) + m_nodeData[i]->index = i; + } + } +} + +void FlatListModel_1::moveNodes(int from, int to, int n) +{ + if (!m_listModel->canMove(from, to, n)) + return; + + qdeclarativelistmodel_move<QList<FlatNodeData_1 *> >(from, to, n, &m_nodeData); + + for (int i=from; i<from + (to-from); i++) { + if (m_nodeData[i]) + m_nodeData[i]->index = i; + } +} + + + +FlatNodeData_1::~FlatNodeData_1() +{ + for (QSet<FlatNodeObjectData *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) { + FlatNodeObjectData *data = *iter; + data->nodeData = 0; + } +} + +void FlatNodeData_1::addData(FlatNodeObjectData *data) +{ + objects.insert(data); +} + +void FlatNodeData_1::removeData(FlatNodeObjectData *data) +{ + objects.remove(data); +} + + +FlatListScriptClass_1::FlatListScriptClass_1(FlatListModel_1 *model, QScriptEngine *seng) + : QScriptDeclarativeClass(seng), + m_model(model) +{ +} + +QScriptDeclarativeClass::Value FlatListScriptClass_1::property(Object *obj, const Identifier &name) +{ + FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj); + if (!objData->nodeData) // item at this index has been deleted + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); + + int index = objData->nodeData->index; + QString propName = toString(name); + int role = m_model->m_strings.value(propName, -1); + + if (role >= 0 && index >=0 ) { + const QHash<int, QVariant> &row = m_model->m_values[index]; + QScriptValue sv = engine()->toScriptValue<QVariant>(row[role]); + return QScriptDeclarativeClass::Value(engine(), sv); + } + + return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue()); +} + +void FlatListScriptClass_1::setProperty(Object *obj, const Identifier &name, const QScriptValue &value) +{ + if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) { + qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script"; + return; + } + + FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj); + if (!objData->nodeData) // item at this index has been deleted + return; + + int index = objData->nodeData->index; + QString propName = toString(name); + + int role = m_model->m_strings.value(propName, -1); + if (role >= 0 && index >= 0) { + QHash<int, QVariant> &row = m_model->m_values[index]; + row[role] = value.toVariant(); + + QList<int> roles; + roles << role; + if (m_model->m_parentAgent) { + // This is the list in the worker thread, so tell the agent to + // emit itemsChanged() later + m_model->m_parentAgent->changedData(index, 1, roles); + } else { + // This is the list in the main thread, so emit itemsChanged() + emit m_model->m_listModel->itemsChanged(index, 1, roles); + } + } +} + +QScriptClass::QueryFlags FlatListScriptClass_1::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags) +{ + return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess); +} + +bool FlatListScriptClass_1::compare(Object *obj1, Object *obj2) +{ + FlatNodeObjectData *data1 = static_cast<FlatNodeObjectData*>(obj1); + FlatNodeObjectData *data2 = static_cast<FlatNodeObjectData*>(obj2); + + if (!data1->nodeData || !data2->nodeData) + return false; + + return data1->nodeData->index == data2->nodeData->index; +} + + + +NestedListModel_1::NestedListModel_1(QDeclarative1ListModel *base) + : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false) +{ +} + +NestedListModel_1::~NestedListModel_1() +{ + if (m_ownsRoot) + delete _root; +} + +QVariant NestedListModel_1::valueForNode(ModelNode *node, bool *hasNested) const +{ + QObject *rv = 0; + if (hasNested) + *hasNested = false; + + if (node->isArray) { + // List + rv = node->model(this); + if (hasNested) + *hasNested = true; + } else { + if (!node->properties.isEmpty()) { + // Object + rv = node->object(this); + } else if (node->values.count() == 0) { + // Invalid + return QVariant(); + } else if (node->values.count() == 1) { + // Value + QVariant &var = node->values[0]; + ModelNode *valueNode = qvariant_cast<ModelNode *>(var); + if (valueNode) { + if (!valueNode->properties.isEmpty()) + rv = valueNode->object(this); + else + rv = valueNode->model(this); + } else { + return var; + } + } + } + + if (rv) { + return QVariant::fromValue(rv); + } else { + return QVariant(); + } +} + +QHash<int,QVariant> NestedListModel_1::data(int index, const QList<int> &roles, bool *hasNested) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QHash<int, QVariant> rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return rv; + + for (int ii = 0; ii < roles.count(); ++ii) { + const QString &roleString = roleStrings.at(roles.at(ii)); + + QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv.insert(roles.at(ii), valueForNode(row, hasNested)); + } + } + + return rv; +} + +QVariant NestedListModel_1::data(int index, int role) const +{ + Q_ASSERT(_root && index >= 0 && index < _root->values.count()); + checkRoles(); + QVariant rv; + if (roleStrings.count() < role) + return rv; + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return rv; + + const QString &roleString = roleStrings.at(role); + + QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString); + if (iter != node->properties.end()) { + ModelNode *row = *iter; + rv = valueForNode(row); + } + + return rv; +} + +int NestedListModel_1::count() const +{ + if (!_root) return 0; + return _root->values.count(); +} + +void NestedListModel_1::clear() +{ + if (_root) + _root->clear(); +} + +void NestedListModel_1::remove(int index) +{ + if (!_root) + return; + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + _root->values.removeAt(index); + if (node) + delete node; +} + +bool NestedListModel_1::insert(int index, const QScriptValue& valuemap) +{ + if (!_root) { + _root = new ModelNode(this); + m_ownsRoot = true; + } + + ModelNode *mn = new ModelNode(this); + mn->listIndex = index; + mn->setObjectValue(valuemap); + _root->values.insert(index,QVariant::fromValue(mn)); + return true; +} + +void NestedListModel_1::move(int from, int to, int n) +{ + if (!_root) + return; + qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values); +} + +QScriptValue NestedListModel_1::get(int index) const +{ + QDeclarativeEngine *eng = qmlEngine(m_listModel); + if (!eng) + return 0; + + if (index < 0 || index >= count()) { + QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng); + if (seng) + return seng->undefinedValue(); + return 0; + } + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + if (!node) + return 0; + + return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng); +} + +void NestedListModel_1::set(int index, const QScriptValue& valuemap, QList<int> *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + bool emitItemsChanged = node->setObjectValue(valuemap); + if (!emitItemsChanged) + return; + + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + int r = roleStrings.indexOf(it.name()); + if (r < 0) { + r = roleStrings.count(); + roleStrings << it.name(); + } + roles->append(r); + } +} + +void NestedListModel_1::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles) +{ + Q_ASSERT(index >=0 && index < count()); + + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index)); + bool emitItemsChanged = node->setProperty(property, value); + if (!emitItemsChanged) + return; + + int r = roleStrings.indexOf(property); + if (r < 0) { + r = roleStrings.count(); + roleStrings << property; + } + roles->append(r); +} + +void NestedListModel_1::checkRoles() const +{ + if (_rolesOk || !_root) + return; + + for (int i = 0; i<_root->values.count(); ++i) { + ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i)); + if (node) { + foreach (const QString &role, node->properties.keys()) { + if (!roleStrings.contains(role)) + roleStrings.append(role); + } + } + } + + _rolesOk = true; +} + +QList<int> NestedListModel_1::roles() const +{ + checkRoles(); + QList<int> rv; + for (int ii = 0; ii < roleStrings.count(); ++ii) + rv << ii; + return rv; +} + +QString NestedListModel_1::toString(int role) const +{ + checkRoles(); + if (role < roleStrings.count()) + return roleStrings.at(role); + else + return QString(); +} + + +ModelNode::ModelNode(NestedListModel_1 *model) +: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1) +{ +} + +ModelNode::~ModelNode() +{ + clear(); + if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; } + if (objectCache) { delete objectCache; objectCache = 0; } +} + +void ModelNode::clear() +{ + ModelNode *node; + for (int ii = 0; ii < values.count(); ++ii) { + node = qvariant_cast<ModelNode *>(values.at(ii)); + if (node) { delete node; node = 0; } + } + values.clear(); + + qDeleteAll(properties.values()); + properties.clear(); +} + +bool ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) +{ + bool emitItemsChanged = false; + + QScriptValueIterator it(valuemap); + while (it.hasNext()) { + it.next(); + ModelNode *prev = properties.value(it.name()); + ModelNode *value = new ModelNode(m_model); + QScriptValue v = it.value(); + + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + if (writeToCache && objectCache) + objectCache->setValue(it.name().toUtf8(), QVariant::fromValue(value->model(m_model))); + emitItemsChanged = true; // for now, too inefficient to check whether list and sublists have changed + } else { + value->values << v.toVariant(); + if (writeToCache && objectCache) + objectCache->setValue(it.name().toUtf8(), value->values.last()); + if (!emitItemsChanged && prev && prev->values.count() == 1 + && prev->values[0] != value->values.last()) { + emitItemsChanged = true; + } + } + if (properties.contains(it.name())) + delete properties[it.name()]; + properties.insert(it.name(), value); + } + return emitItemsChanged; +} + +void ModelNode::setListValue(const QScriptValue& valuelist) { + values.clear(); + int size = valuelist.property(QLatin1String("length")).toInt32(); + for (int i=0; i<size; i++) { + ModelNode *value = new ModelNode(m_model); + QScriptValue v = valuelist.property(i); + if (v.isArray()) { + value->isArray = true; + value->setListValue(v); + } else if (v.isObject()) { + value->listIndex = i; + value->setObjectValue(v); + } else { + value->listIndex = i; + value->values << v.toVariant(); + } + values.append(QVariant::fromValue(value)); + } +} + +bool ModelNode::setProperty(const QString& prop, const QVariant& val) { + QHash<QString, ModelNode *>::const_iterator it = properties.find(prop); + bool emitItemsChanged = false; + if (it != properties.end()) { + if (val != (*it)->values[0]) + emitItemsChanged = true; + (*it)->values[0] = val; + } else { + ModelNode *n = new ModelNode(m_model); + n->values << val; + properties.insert(prop,n); + } + if (objectCache) + objectCache->setValue(prop.toUtf8(), val); + return emitItemsChanged; +} + +void ModelNode::updateListIndexes() +{ + for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) { + ModelNode *node = iter.value(); + if (node->isArray) { + for (int i=0; i<node->values.count(); ++i) { + ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i)); + if (subNode) + subNode->listIndex = i; + } + } + node->updateListIndexes(); + } +} + +/* + Need to call this to emit itemsChanged() for modifications outside of set() + and setProperty(), i.e. if an item returned from get() is modified +*/ +void ModelNode::changedProperty(const QString &name) const +{ + if (listIndex < 0) + return; + + m_model->checkRoles(); + QList<int> roles; + int role = m_model->roleStrings.indexOf(name); + if (role < 0) + roles = m_model->roles(); + else + roles << role; + emit m_model->m_listModel->itemsChanged(listIndex, 1, roles); +} + +void ModelNode::dump(ModelNode *node, int ind) +{ + QByteArray indentBa(ind * 4, ' '); + const char *indent = indentBa.constData(); + + for (int ii = 0; ii < node->values.count(); ++ii) { + ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii)); + if (subNode) { + qWarning().nospace() << indent << "Sub-node " << ii; + dump(subNode, ind + 1); + } else { + qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString(); + } + } + + for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) { + qWarning().nospace() << indent << "Property " << iter.key() << ':'; + dump(iter.value(), ind + 1); + } +} + +ModelObject_1::ModelObject_1(ModelNode *node, NestedListModel_1 *model, QScriptEngine *seng) + : m_model(model), + m_node(node), + m_meta(new ModelNodeMetaObject_1(seng, this)) +{ +} + +void ModelObject_1::setValue(const QByteArray &name, const QVariant &val) +{ + m_meta->setValue(name, val); + //setProperty(name.constData(), val); +} + +void ModelObject_1::setNodeUpdatesEnabled(bool enable) +{ + m_meta->m_enabled = enable; +} + + +ModelNodeMetaObject_1::ModelNodeMetaObject_1(QScriptEngine *seng, ModelObject_1 *object) + : QDeclarative1OpenMetaObject(object), + m_enabled(false), + m_seng(seng), + m_obj(object) +{ +} + +void ModelNodeMetaObject_1::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + QScriptValue sv = m_seng->newObject(); + sv.setProperty(propName, m_seng->newVariant(value)); + bool changed = m_obj->m_node->setObjectValue(sv, false); + if (changed) + m_obj->m_node->changedProperty(propName); +} + + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativelistmodel_p.h b/src/qtquick1/util/qdeclarativelistmodel_p.h new file mode 100644 index 0000000000..21398f7213 --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistmodel_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_H +#define QDECLARATIVELISTMODEL_H + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/private/qdeclarativecustomparser_p.h> + +#include <QtCore/QObject> +#include <QtCore/QStringList> +#include <QtCore/QHash> +#include <QtCore/QList> +#include <QtCore/QVariant> +#include <QtQuick1/private/qlistmodelinterface_p.h> +#include <QtScript/qscriptvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class FlatListModel_1; +class NestedListModel_1; +class QDeclarative1ListModelWorkerAgent; +struct ModelNode; +class FlatListScriptClass_1; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1ListModel : public QListModelInterface +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + QDeclarative1ListModel(QObject *parent=0); + ~QDeclarative1ListModel(); + + virtual QList<int> roles() const; + virtual QString toString(int role) const; + virtual int count() const; + virtual QVariant data(int index, int role) const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QScriptValue&); + Q_INVOKABLE void insert(int index, const QScriptValue&); + Q_INVOKABLE QScriptValue get(int index) const; + Q_INVOKABLE void set(int index, const QScriptValue&); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QDeclarative1ListModelWorkerAgent *agent(); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QDeclarative1ListModelParser; + friend class QDeclarative1ListModelWorkerAgent; + friend class FlatListModel_1; + friend class FlatListScriptClass_1; + friend struct ModelNode; + + // Constructs a flat list model for a worker agent + QDeclarative1ListModel(const QDeclarative1ListModel *orig, QDeclarative1ListModelWorkerAgent *parent); + + void set(int index, const QScriptValue&, QList<int> *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles); + + bool flatten(); + bool inWorkerThread() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QDeclarative1ListModelWorkerAgent *m_agent; + NestedListModel_1 *m_nested; + FlatListModel_1 *m_flat; +}; + +// ### FIXME +class QDeclarative1ListElement : public QObject +{ +Q_OBJECT +}; + +class QDeclarative1ListModelParser : public QDeclarativeCustomParser +{ +public: + QByteArray compile(const QList<QDeclarativeCustomParserProperty> &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QByteArray listElementTypeName; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1ListModel) +QML_DECLARE_TYPE(QDeclarative1ListElement) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_H diff --git a/src/qtquick1/util/qdeclarativelistmodel_p_p.h b/src/qtquick1/util/qdeclarativelistmodel_p_p.h new file mode 100644 index 0000000000..e34f6d850d --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistmodel_p_p.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODEL_P_P_H +#define QDECLARATIVELISTMODEL_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 <QtDeclarative/private/qdeclarativeengine_p.h> +#include "QtQuick1/private/qdeclarativelistmodel_p.h" +#include "QtQuick1/private/qdeclarativeopenmetaobject_p.h" +#include <QtDeclarative/qdeclarative.h> + +#include <QtScript/private/qscriptdeclarativeclass_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QScriptEngine; + +QT_MODULE(Declarative) + +class QDeclarative1OpenMetaObject; +class QDeclarative1ListModelWorkerAgent; +struct ModelNode; +class FlatListScriptClass_1; +class FlatNodeData_1; + +class FlatListModel_1 +{ +public: + FlatListModel_1(QDeclarative1ListModel *base); + ~FlatListModel_1(); + + QVariant data(int index, int role) const; + + QList<int> roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList<int> *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles); + void move(int from, int to, int count); + +private: + friend class QDeclarative1ListModelWorkerAgent; + friend class QDeclarative1ListModel; + friend class FlatListScriptClass_1; + friend class FlatNodeData_1; + + bool addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles); + void insertedNode(int index); + void removedNode(int index); + void moveNodes(int from, int to, int n); + + QScriptEngine *m_scriptEngine; + QHash<int, QString> m_roles; + QHash<QString, int> m_strings; + QList<QHash<int, QVariant> > m_values; + QDeclarative1ListModel *m_listModel; + + FlatListScriptClass_1 *m_scriptClass; + QList<FlatNodeData_1 *> m_nodeData; + QDeclarative1ListModelWorkerAgent *m_parentAgent; +}; + + +/* + Created when get() is called on a FlatListModel_1. This allows changes to the + object returned by get() to be tracked, and passed onto the model. +*/ +class FlatListScriptClass_1 : public QScriptDeclarativeClass +{ +public: + FlatListScriptClass_1(FlatListModel_1 *model, QScriptEngine *seng); + + Value property(Object *, const Identifier &); + void setProperty(Object *, const Identifier &name, const QScriptValue &); + QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags flags); + bool compare(Object *, Object *); + +private: + FlatListModel_1 *m_model; +}; + +/* + FlatNodeData_1 and FlatNodeObjectData allow objects returned by get() to still + point to the correct list index if move(), insert() or remove() are called. +*/ +struct FlatNodeObjectData; +class FlatNodeData_1 +{ +public: + FlatNodeData_1(int i) + : index(i) {} + + ~FlatNodeData_1(); + + void addData(FlatNodeObjectData *data); + void removeData(FlatNodeObjectData *data); + + int index; + +private: + QSet<FlatNodeObjectData*> objects; +}; + +struct FlatNodeObjectData : public QScriptDeclarativeClass::Object +{ + FlatNodeObjectData(FlatNodeData_1 *data) : nodeData(data) { + nodeData->addData(this); + } + + ~FlatNodeObjectData() { + if (nodeData) + nodeData->removeData(this); + } + + FlatNodeData_1 *nodeData; +}; + + + +class NestedListModel_1 +{ +public: + NestedListModel_1(QDeclarative1ListModel *base); + ~NestedListModel_1(); + + QHash<int,QVariant> data(int index, const QList<int> &roles, bool *hasNested = 0) const; + QVariant data(int index, int role) const; + + QList<int> roles() const; + QString toString(int role) const; + + int count() const; + void clear(); + void remove(int index); + bool insert(int index, const QScriptValue&); + QScriptValue get(int index) const; + void set(int index, const QScriptValue&, QList<int> *roles); + void setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles); + void move(int from, int to, int count); + + QVariant valueForNode(ModelNode *, bool *hasNested = 0) const; + void checkRoles() const; + + ModelNode *_root; + bool m_ownsRoot; + QDeclarative1ListModel *m_listModel; + +private: + friend struct ModelNode; + mutable QStringList roleStrings; + mutable bool _rolesOk; +}; + + +class ModelNodeMetaObject_1; +class ModelObject_1 : public QObject +{ + Q_OBJECT +public: + ModelObject_1(ModelNode *node, NestedListModel_1 *model, QScriptEngine *seng); + void setValue(const QByteArray &name, const QVariant &val); + void setNodeUpdatesEnabled(bool enable); + + NestedListModel_1 *m_model; + ModelNode *m_node; + +private: + ModelNodeMetaObject_1 *m_meta; +}; + +class ModelNodeMetaObject_1 : public QDeclarative1OpenMetaObject +{ +public: + ModelNodeMetaObject_1(QScriptEngine *seng, ModelObject_1 *object); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + QScriptEngine *m_seng; + ModelObject_1 *m_obj; +}; + + +/* + A ModelNode is created for each item in a NestedListModel_1. +*/ +struct ModelNode +{ + ModelNode(NestedListModel_1 *model); + ~ModelNode(); + + QList<QVariant> values; + QHash<QString, ModelNode *> properties; + + void clear(); + + QDeclarative1ListModel *model(const NestedListModel_1 *model); + ModelObject_1 *object(const NestedListModel_1 *model); + + bool setObjectValue(const QScriptValue& valuemap, bool writeToCache = true); + void setListValue(const QScriptValue& valuelist); + bool setProperty(const QString& prop, const QVariant& val); + void changedProperty(const QString &name) const; + void updateListIndexes(); + static void dump(ModelNode *node, int ind); + + QDeclarative1ListModel *modelCache; + ModelObject_1 *objectCache; + bool isArray; + + NestedListModel_1 *m_model; + int listIndex; // only used for top-level nodes within a list +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ModelNode *) + +QT_END_HEADER + +#endif // QDECLARATIVELISTMODEL_P_P_H + diff --git a/src/qtquick1/util/qdeclarativelistmodelworkeragent.cpp b/src/qtquick1/util/qdeclarativelistmodelworkeragent.cpp new file mode 100644 index 0000000000..903f700ee3 --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistmodelworkeragent.cpp @@ -0,0 +1,282 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativelistmodelworkeragent_p.h" +#include "QtQuick1/private/qdeclarativelistmodel_p_p.h" +#include <QtDeclarative/private/qdeclarativedata_p.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QtCore/qcoreevent.h> +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdebug.h> + + +QT_BEGIN_NAMESPACE + + + + +void QDeclarative1ListModelWorkerAgent::Data::clearChange() +{ + changes.clear(); +} + +void QDeclarative1ListModelWorkerAgent::Data::insertChange(int index, int count) +{ + Change c = { Change::Inserted, index, count, 0, QList<int>() }; + changes << c; +} + +void QDeclarative1ListModelWorkerAgent::Data::removeChange(int index, int count) +{ + Change c = { Change::Removed, index, count, 0, QList<int>() }; + changes << c; +} + +void QDeclarative1ListModelWorkerAgent::Data::moveChange(int index, int count, int to) +{ + Change c = { Change::Moved, index, count, to, QList<int>() }; + changes << c; +} + +void QDeclarative1ListModelWorkerAgent::Data::changedChange(int index, int count, const QList<int> &roles) +{ + Change c = { Change::Changed, index, count, 0, roles }; + changes << c; +} + +QDeclarative1ListModelWorkerAgent::QDeclarative1ListModelWorkerAgent(QDeclarative1ListModel *model) + : m_engine(0), + m_ref(1), + m_orig(model), + m_copy(new QDeclarative1ListModel(model, this)) +{ +} + +QDeclarative1ListModelWorkerAgent::~QDeclarative1ListModelWorkerAgent() +{ +} + +void QDeclarative1ListModelWorkerAgent::setScriptEngine(QScriptEngine *eng) +{ + m_engine = eng; + if (m_copy->m_flat) + m_copy->m_flat->m_scriptEngine = eng; +} + +QScriptEngine *QDeclarative1ListModelWorkerAgent::scriptEngine() const +{ + return m_engine; +} + +void QDeclarative1ListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QDeclarative1ListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + delete this; +} + +int QDeclarative1ListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QDeclarative1ListModelWorkerAgent::clear() +{ + data.clearChange(); + data.removeChange(0, m_copy->count()); + m_copy->clear(); +} + +void QDeclarative1ListModelWorkerAgent::remove(int index) +{ + int count = m_copy->count(); + m_copy->remove(index); + + if (m_copy->count() != count) + data.removeChange(index, 1); +} + +void QDeclarative1ListModelWorkerAgent::append(const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->append(value); + + if (m_copy->count() != count) + data.insertChange(m_copy->count() - 1, 1); +} + +void QDeclarative1ListModelWorkerAgent::insert(int index, const QScriptValue &value) +{ + int count = m_copy->count(); + m_copy->insert(index, value); + + if (m_copy->count() != count) + data.insertChange(index, 1); +} + +QScriptValue QDeclarative1ListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QDeclarative1ListModelWorkerAgent::set(int index, const QScriptValue &value) +{ + QList<int> roles; + m_copy->set(index, value, &roles); + if (!roles.isEmpty()) + data.changedChange(index, 1, roles); +} + +void QDeclarative1ListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + QList<int> roles; + m_copy->setProperty(index, property, value, &roles); + if (!roles.isEmpty()) + data.changedChange(index, 1, roles); +} + +void QDeclarative1ListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); + data.moveChange(from, to, count); +} + +void QDeclarative1ListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +void QDeclarative1ListModelWorkerAgent::changedData(int index, int count, const QList<int> &roles) +{ + data.changedChange(index, count, roles); +} + +bool QDeclarative1ListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + QMutexLocker locker(&mutex); + Sync *s = static_cast<Sync *>(e); + + const QList<Change> &changes = s->data.changes; + + if (m_copy) { + bool cc = m_orig->count() != s->list->count(); + + FlatListModel_1 *orig = m_orig->m_flat; + FlatListModel_1 *copy = s->list->m_flat; + if (!orig || !copy) { + syncDone.wakeAll(); + return QObject::event(e); + } + + orig->m_roles = copy->m_roles; + orig->m_strings = copy->m_strings; + orig->m_values = copy->m_values; + + // update the orig->m_nodeData list + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + orig->insertedNode(change.index); + break; + case Change::Removed: + orig->removedNode(change.index); + break; + case Change::Moved: + orig->moveNodes(change.index, change.to, change.count); + break; + case Change::Changed: + break; + } + } + + syncDone.wakeAll(); + locker.unlock(); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + switch (change.type) { + case Change::Inserted: + emit m_orig->itemsInserted(change.index, change.count); + break; + case Change::Removed: + emit m_orig->itemsRemoved(change.index, change.count); + break; + case Change::Moved: + emit m_orig->itemsMoved(change.index, change.to, change.count); + break; + case Change::Changed: + emit m_orig->itemsChanged(change.index, change.count, change.roles); + break; + } + } + + if (cc) + emit m_orig->countChanged(); + } else { + syncDone.wakeAll(); + } + } + + return QObject::event(e); +} + + + +QT_END_NAMESPACE + diff --git a/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h b/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h new file mode 100644 index 0000000000..f0979c4b31 --- /dev/null +++ b/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTMODELWORKERAGENT_P_H +#define QDECLARATIVELISTMODELWORKERAGENT_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 <QtDeclarative/qdeclarative.h> + +#include <QtScript/qscriptvalue.h> +#include <QtGui/qevent.h> +#include <QMutex> +#include <QWaitCondition> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1ListModel; +class FlatListScriptClass_1; + +class QDeclarative1ListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QDeclarative1ListModelWorkerAgent(QDeclarative1ListModel *); + ~QDeclarative1ListModelWorkerAgent(); + + void setScriptEngine(QScriptEngine *eng); + QScriptEngine *scriptEngine() const; + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(int index); + Q_INVOKABLE void append(const QScriptValue &); + Q_INVOKABLE void insert(int index, const QScriptValue&); + Q_INVOKABLE QScriptValue get(int index) const; + Q_INVOKABLE void set(int index, const QScriptValue &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QDeclarative1ListModelWorkerAgent *_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; + } + + QDeclarative1ListModelWorkerAgent *a; + }; +protected: + virtual bool event(QEvent *); + +private: + friend class QDeclarative1WorkerScriptEnginePrivate; + friend class FlatListScriptClass_1; + QScriptEngine *m_engine; + + struct Change { + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QList<int> roles; + }; + + struct Data { + QList<Change> changes; + + void clearChange(); + void insertChange(int index, int count); + void removeChange(int index, int count); + void moveChange(int index, int count, int to); + void changedChange(int index, int count, const QList<int> &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QDeclarative1ListModel *list; + }; + + void changedData(int index, int count, const QList<int> &roles); + + QAtomicInt m_ref; + QDeclarative1ListModel *m_orig; + QDeclarative1ListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarative1ListModelWorkerAgent::VariantRef) + +QT_END_HEADER + +#endif // QDECLARATIVEWORKERSCRIPT_P_H + diff --git a/src/qtquick1/util/qdeclarativeopenmetaobject.cpp b/src/qtquick1/util/qdeclarativeopenmetaobject.cpp new file mode 100644 index 0000000000..b4864a9a7c --- /dev/null +++ b/src/qtquick1/util/qdeclarativeopenmetaobject.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeopenmetaobject_p.h" +#include "QtDeclarative/private/qdeclarativepropertycache_p.h" +#include "QtDeclarative/private/qdeclarativedata_p.h" +#include <QtDeclarative/private/qmetaobjectbuilder_p.h> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + + + + +class QDeclarative1OpenMetaObjectTypePrivate +{ +public: + QDeclarative1OpenMetaObjectTypePrivate() : mem(0), cache(0), engine(0) {} + + void init(const QMetaObject *metaObj); + + int propertyOffset; + int signalOffset; + QHash<QByteArray, int> names; + QMetaObjectBuilder mob; + QMetaObject *mem; + QDeclarativePropertyCache *cache; + QDeclarativeEngine *engine; + QSet<QDeclarative1OpenMetaObject*> referers; +}; + +QDeclarative1OpenMetaObjectType::QDeclarative1OpenMetaObjectType(const QMetaObject *base, QDeclarativeEngine *engine) + : d(new QDeclarative1OpenMetaObjectTypePrivate) +{ + d->engine = engine; + d->init(base); +} + +QDeclarative1OpenMetaObjectType::~QDeclarative1OpenMetaObjectType() +{ + if (d->mem) + qFree(d->mem); + if (d->cache) + d->cache->release(); + delete d; +} + + +int QDeclarative1OpenMetaObjectType::propertyOffset() const +{ + return d->propertyOffset; +} + +int QDeclarative1OpenMetaObjectType::signalOffset() const +{ + return d->signalOffset; +} + +int QDeclarative1OpenMetaObjectType::createProperty(const QByteArray &name) +{ + int id = d->mob.propertyCount(); + d->mob.addSignal("__" + QByteArray::number(id) + "()"); + QMetaPropertyBuilder build = d->mob.addProperty(name, "QVariant", id); + propertyCreated(id, build); + qFree(d->mem); + d->mem = d->mob.toMetaObject(); + d->names.insert(name, id); + QSet<QDeclarative1OpenMetaObject*>::iterator it = d->referers.begin(); + while (it != d->referers.end()) { + QDeclarative1OpenMetaObject *omo = *it; + *static_cast<QMetaObject *>(omo) = *d->mem; + if (d->cache) + d->cache->update(d->engine, omo); + ++it; + } + + return d->propertyOffset + id; +} + +void QDeclarative1OpenMetaObjectType::propertyCreated(int id, QMetaPropertyBuilder &builder) +{ + if (d->referers.count()) + (*d->referers.begin())->propertyCreated(id, builder); +} + +void QDeclarative1OpenMetaObjectTypePrivate::init(const QMetaObject *metaObj) +{ + if (!mem) { + mob.setSuperClass(metaObj); + mob.setClassName(metaObj->className()); + mob.setFlags(QMetaObjectBuilder::DynamicMetaObject); + + mem = mob.toMetaObject(); + + propertyOffset = mem->propertyOffset(); + signalOffset = mem->methodOffset(); + } +} + +//---------------------------------------------------------------------------- + +class QDeclarative1OpenMetaObjectPrivate +{ +public: + QDeclarative1OpenMetaObjectPrivate(QDeclarative1OpenMetaObject *_q) + : q(_q), parent(0), type(0), cacheProperties(false) {} + + inline QVariant &getData(int idx) { + while (data.count() <= idx) + data << QPair<QVariant, bool>(QVariant(), false); + QPair<QVariant, bool> &prop = data[idx]; + if (!prop.second) { + prop.first = q->initialValue(idx); + prop.second = true; + } + return prop.first; + } + + inline void writeData(int idx, const QVariant &value) { + while (data.count() <= idx) + data << QPair<QVariant, bool>(QVariant(), false); + QPair<QVariant, bool> &prop = data[idx]; + prop.first = value; + prop.second = true; + } + + inline bool hasData(int idx) const { + if (idx >= data.count()) + return false; + return data[idx].second; + } + + bool autoCreate; + QDeclarative1OpenMetaObject *q; + QAbstractDynamicMetaObject *parent; + QList<QPair<QVariant, bool> > data; + QObject *object; + QDeclarative1OpenMetaObjectType *type; + bool cacheProperties; +}; + +QDeclarative1OpenMetaObject::QDeclarative1OpenMetaObject(QObject *obj, bool automatic) +: d(new QDeclarative1OpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = new QDeclarative1OpenMetaObjectType(obj->metaObject(), 0); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); + *static_cast<QMetaObject *>(this) = *d->type->d->mem; + op->metaObject = this; +} + +QDeclarative1OpenMetaObject::QDeclarative1OpenMetaObject(QObject *obj, QDeclarative1OpenMetaObjectType *type, bool automatic) +: d(new QDeclarative1OpenMetaObjectPrivate(this)) +{ + d->autoCreate = automatic; + d->object = obj; + + d->type = type; + d->type->addref(); + d->type->d->referers.insert(this); + + QObjectPrivate *op = QObjectPrivate::get(obj); + d->parent = static_cast<QAbstractDynamicMetaObject *>(op->metaObject); + *static_cast<QMetaObject *>(this) = *d->type->d->mem; + op->metaObject = this; +} + +QDeclarative1OpenMetaObject::~QDeclarative1OpenMetaObject() +{ + if (d->parent) + delete d->parent; + d->type->d->referers.remove(this); + d->type->release(); + delete d; +} + +QDeclarative1OpenMetaObjectType *QDeclarative1OpenMetaObject::type() const +{ + return d->type; +} + +int QDeclarative1OpenMetaObject::metaCall(QMetaObject::Call c, int id, void **a) +{ + if (( c == QMetaObject::ReadProperty || c == QMetaObject::WriteProperty) + && id >= d->type->d->propertyOffset) { + int propId = id - d->type->d->propertyOffset; + if (c == QMetaObject::ReadProperty) { + propertyRead(propId); + *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId); + } else if (c == QMetaObject::WriteProperty) { + if (propId <= d->data.count() || d->data[propId].first != *reinterpret_cast<QVariant *>(a[0])) { + propertyWrite(propId); + d->writeData(propId, *reinterpret_cast<QVariant *>(a[0])); + propertyWritten(propId); + activate(d->object, d->type->d->signalOffset + propId, 0); + } + } + return -1; + } else { + if (d->parent) + return d->parent->metaCall(c, id, a); + else + return d->object->qt_metacall(c, id, a); + } +} + +QAbstractDynamicMetaObject *QDeclarative1OpenMetaObject::parent() const +{ + return d->parent; +} + +QVariant QDeclarative1OpenMetaObject::value(int id) const +{ + return d->getData(id); +} + +void QDeclarative1OpenMetaObject::setValue(int id, const QVariant &value) +{ + d->writeData(id, value); + activate(d->object, id + d->type->d->signalOffset, 0); +} + +QVariant QDeclarative1OpenMetaObject::value(const QByteArray &name) const +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + if (iter == d->type->d->names.end()) + return QVariant(); + + return d->getData(*iter); +} + +QVariant &QDeclarative1OpenMetaObject::operator[](const QByteArray &name) +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + Q_ASSERT(iter != d->type->d->names.end()); + + return d->getData(*iter); +} + +QVariant &QDeclarative1OpenMetaObject::operator[](int id) +{ + return d->getData(id); +} + +void QDeclarative1OpenMetaObject::setValue(const QByteArray &name, const QVariant &val) +{ + QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.find(name); + + int id = -1; + if (iter == d->type->d->names.end()) { + id = createProperty(name.constData(), "") - d->type->d->propertyOffset; + } else { + id = *iter; + } + + if (id >= 0) { + QVariant &dataVal = d->getData(id); + if (dataVal == val) + return; + + dataVal = val; + activate(d->object, id + d->type->d->signalOffset, 0); + } +} + +// returns true if this value has been initialized by a call to either value() or setValue() +bool QDeclarative1OpenMetaObject::hasValue(int id) const +{ + return d->hasData(id); +} + +void QDeclarative1OpenMetaObject::setCached(bool c) +{ + if (c == d->cacheProperties || !d->type->d->engine) + return; + + d->cacheProperties = c; + + QDeclarativeData *qmldata = QDeclarativeData::get(d->object, true); + if (d->cacheProperties) { + if (!d->type->d->cache) + d->type->d->cache = new QDeclarativePropertyCache(d->type->d->engine, this); + qmldata->propertyCache = d->type->d->cache; + d->type->d->cache->addref(); + } else { + if (d->type->d->cache) + d->type->d->cache->release(); + qmldata->propertyCache = 0; + } +} + + +int QDeclarative1OpenMetaObject::createProperty(const char *name, const char *) +{ + if (d->autoCreate) + return d->type->createProperty(name); + else + return -1; +} + +void QDeclarative1OpenMetaObject::propertyRead(int) +{ +} + +void QDeclarative1OpenMetaObject::propertyWrite(int) +{ +} + +void QDeclarative1OpenMetaObject::propertyWritten(int) +{ +} + +void QDeclarative1OpenMetaObject::propertyCreated(int, QMetaPropertyBuilder &) +{ +} + +QVariant QDeclarative1OpenMetaObject::initialValue(int) +{ + return QVariant(); +} + +int QDeclarative1OpenMetaObject::count() const +{ + return d->type->d->names.count(); +} + +QByteArray QDeclarative1OpenMetaObject::name(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < d->type->d->names.count()); + + return d->type->d->mob.property(idx).name(); +} + +QObject *QDeclarative1OpenMetaObject::object() const +{ + return d->object; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativeopenmetaobject_p.h b/src/qtquick1/util/qdeclarativeopenmetaobject_p.h new file mode 100644 index 0000000000..3ee6230e71 --- /dev/null +++ b/src/qtquick1/util/qdeclarativeopenmetaobject_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEOPENMETAOBJECT_H +#define QDECLARATIVEOPENMETAOBJECT_H + +#include <QtCore/QMetaObject> +#include <QtCore/QObject> + +#include <QtDeclarative/private/qdeclarativerefcount_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> +#include <QtCore/private/qobject_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; +class QMetaPropertyBuilder; + +QT_MODULE(Declarative) + +class QDeclarative1OpenMetaObjectTypePrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1OpenMetaObjectType : public QDeclarativeRefCount +{ +public: + QDeclarative1OpenMetaObjectType(const QMetaObject *base, QDeclarativeEngine *engine); + ~QDeclarative1OpenMetaObjectType(); + + int createProperty(const QByteArray &name); + + int propertyOffset() const; + int signalOffset() const; + +protected: + virtual void propertyCreated(int, QMetaPropertyBuilder &); + +private: + QDeclarative1OpenMetaObjectTypePrivate *d; + friend class QDeclarative1OpenMetaObject; + friend class QDeclarative1OpenMetaObjectPrivate; +}; + +class QDeclarative1OpenMetaObjectPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1OpenMetaObject : public QAbstractDynamicMetaObject +{ +public: + QDeclarative1OpenMetaObject(QObject *, bool = true); + QDeclarative1OpenMetaObject(QObject *, QDeclarative1OpenMetaObjectType *, bool = true); + ~QDeclarative1OpenMetaObject(); + + QVariant value(const QByteArray &) const; + void setValue(const QByteArray &, const QVariant &); + QVariant value(int) const; + void setValue(int, const QVariant &); + QVariant &operator[](const QByteArray &); + QVariant &operator[](int); + bool hasValue(int) const; + + int count() const; + QByteArray name(int) const; + + QObject *object() const; + virtual QVariant initialValue(int); + + // Be careful - once setCached(true) is called createProperty() is no + // longer automatically called for new properties. + void setCached(bool); + + QDeclarative1OpenMetaObjectType *type() const; + +protected: + virtual int metaCall(QMetaObject::Call _c, int _id, void **_a); + virtual int createProperty(const char *, const char *); + + virtual void propertyRead(int); + virtual void propertyWrite(int); + virtual void propertyWritten(int); + virtual void propertyCreated(int, QMetaPropertyBuilder &); + + QAbstractDynamicMetaObject *parent() const; + +private: + QDeclarative1OpenMetaObjectPrivate *d; + friend class QDeclarative1OpenMetaObjectType; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEOPENMETAOBJECT_H diff --git a/src/qtquick1/util/qdeclarativepackage.cpp b/src/qtquick1/util/qdeclarativepackage.cpp new file mode 100644 index 0000000000..989a780f6b --- /dev/null +++ b/src/qtquick1/util/qdeclarativepackage.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepackage_p.h" + +#include <private/qobject_p.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass Package QDeclarative1Package + \ingroup qml-working-with-data + \brief Package provides a collection of named items. + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third element in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet examples/declarative/modelviews/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet examples/declarative/modelviews/package/view.qml 0 + + \sa {declarative/modelviews/package}{Package example}, {demos/declarative/photoviewer}{Photo Viewer demo}, QtDeclarative +*/ + +/*! + \qmlattachedproperty string Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QDeclarative1PackagePrivate : public QObjectPrivate +{ +public: + QDeclarative1PackagePrivate() {} + + struct DataGuard : public QDeclarativeGuard<QObject> + { + DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QDeclarativeGuard<QObject>&)*this = obj; } + QList<DataGuard> *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList<DataGuard> dataList; + static void data_append(QDeclarativeListProperty<QObject> *prop, QObject *o) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QDeclarativeListProperty<QObject> *prop) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + list->clear(); + } + static QObject *data_at(QDeclarativeListProperty<QObject> *prop, int index) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + return list->at(index); + } + static int data_count(QDeclarativeListProperty<QObject> *prop) { + QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); + return list->count(); + } +}; + +QHash<QObject *, QDeclarative1PackageAttached *> QDeclarative1PackageAttached::attached; + +QDeclarative1PackageAttached::QDeclarative1PackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QDeclarative1PackageAttached::~QDeclarative1PackageAttached() +{ + attached.remove(parent()); +} + +QString QDeclarative1PackageAttached::name() const +{ + return _name; +} + +void QDeclarative1PackageAttached::setName(const QString &n) +{ + _name = n; +} + +QDeclarative1Package::QDeclarative1Package(QObject *parent) + : QObject(*(new QDeclarative1PackagePrivate), parent) +{ +} + +QDeclarative1Package::~QDeclarative1Package() +{ + Q_D(QDeclarative1Package); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + obj->setParent(this); + } +} + +QDeclarativeListProperty<QObject> QDeclarative1Package::data() +{ + Q_D(QDeclarative1Package); + return QDeclarativeListProperty<QObject>(this, &d->dataList, QDeclarative1PackagePrivate::data_append, + QDeclarative1PackagePrivate::data_count, + QDeclarative1PackagePrivate::data_at, + QDeclarative1PackagePrivate::data_clear); +} + +bool QDeclarative1Package::hasPart(const QString &name) +{ + Q_D(QDeclarative1Package); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarative1PackageAttached *a = QDeclarative1PackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QDeclarative1Package::part(const QString &name) +{ + Q_D(QDeclarative1Package); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QDeclarative1PackageAttached *a = QDeclarative1PackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QDeclarative1PackageAttached *QDeclarative1Package::qmlAttachedProperties(QObject *o) +{ + return new QDeclarative1PackageAttached(o); +} + + + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativepackage_p.h b/src/qtquick1/util/qdeclarativepackage_p.h new file mode 100644 index 0000000000..33991c9809 --- /dev/null +++ b/src/qtquick1/util/qdeclarativepackage_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPACKAGE_H +#define QDECLARATIVEPACKAGE_H + +#include <QtDeclarative/qdeclarative.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1PackagePrivate; +class QDeclarative1PackageAttached; +class Q_AUTOTEST_EXPORT QDeclarative1Package : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1Package) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QDeclarativeListProperty<QObject> data READ data SCRIPTABLE false) + +public: + QDeclarative1Package(QObject *parent=0); + virtual ~QDeclarative1Package(); + + QDeclarativeListProperty<QObject> data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QDeclarative1PackageAttached *qmlAttachedProperties(QObject *); +}; + +class QDeclarative1PackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QDeclarative1PackageAttached(QObject *parent); + virtual ~QDeclarative1PackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash<QObject *, QDeclarative1PackageAttached *> attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Package) +QML_DECLARE_TYPEINFO(QDeclarative1Package, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QDECLARATIVEPACKAGE_H diff --git a/src/qtquick1/util/qdeclarativepixmapcache.cpp b/src/qtquick1/util/qdeclarativepixmapcache.cpp new file mode 100644 index 0000000000..0b2e16c2ff --- /dev/null +++ b/src/qtquick1/util/qdeclarativepixmapcache.cpp @@ -0,0 +1,1084 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepixmapcache_p.h" +#include "QtDeclarative/qdeclarativenetworkaccessmanagerfactory.h" +#include "QtDeclarative/qdeclarativeimageprovider.h" + +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +#include <QCoreApplication> +#include <QImageReader> +#include <QHash> +#include <QNetworkReply> +#include <QPixmapCache> +#include <QFile> +#include <QThread> +#include <QMutex> +#include <QMutexLocker> +#include <QWaitCondition> +#include <QBuffer> +#include <QWaitCondition> +#include <QtCore/qdebug.h> +#include <private/qobject_p.h> +#include <QSslError> + +#define IMAGEREQUEST_MAX_REQUEST_COUNT 8 +#define IMAGEREQUEST_MAX_REDIRECT_RECURSION 16 +#define CACHE_EXPIRE_TIME 30 +#define CACHE_REMOVAL_FRACTION 4 + +QT_BEGIN_NAMESPACE + + + +// The cache limit describes the maximum "junk" in the cache. +// These are the same defaults as QPixmapCache +#if defined(Q_WS_QWS) || defined(Q_WS_WINCE) +static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded +#else +static int cache_limit = 10240 * 1024; // 10 MB cache limit for desktop +#endif + +class QDeclarative1PixmapReader; +class QDeclarative1PixmapData; +class QDeclarative1PixmapReply : public QObject +{ + Q_OBJECT +public: + enum ReadError { NoError, Loading, Decoding }; + + QDeclarative1PixmapReply(QDeclarative1PixmapData *); + ~QDeclarative1PixmapReply(); + + QDeclarative1PixmapData *data; + QDeclarative1PixmapReader *reader; + QSize requestSize; + + bool loading; + int redirectCount; + + class Event : public QEvent { + public: + Event(ReadError, const QString &, const QSize &, const QImage &); + + ReadError error; + QString errorString; + QSize implicitSize; + QImage image; + }; + void postReply(ReadError, const QString &, const QSize &, const QImage &); + + +Q_SIGNALS: + void finished(); + void downloadProgress(qint64, qint64); + +protected: + bool event(QEvent *event); + +private: + Q_DISABLE_COPY(QDeclarative1PixmapReply) + +public: + static int finishedIndex; + static int downloadProgressIndex; +}; + +class QDeclarative1PixmapReaderThreadObject : public QObject { + Q_OBJECT +public: + QDeclarative1PixmapReaderThreadObject(QDeclarative1PixmapReader *); + void processJobs(); + virtual bool event(QEvent *e); +private slots: + void networkRequestDone(); +private: + QDeclarative1PixmapReader *reader; +}; + +class QDeclarative1PixmapData; +class QDeclarative1PixmapReader : public QThread +{ + Q_OBJECT +public: + QDeclarative1PixmapReader(QDeclarativeEngine *eng); + ~QDeclarative1PixmapReader(); + + QDeclarative1PixmapReply *getImage(QDeclarative1PixmapData *); + void cancel(QDeclarative1PixmapReply *rep); + + static QDeclarative1PixmapReader *instance(QDeclarativeEngine *engine); + +protected: + void run(); + +private: + friend class QDeclarative1PixmapReaderThreadObject; + void processJobs(); + void processJob(QDeclarative1PixmapReply *, const QUrl &, const QSize &); + void networkRequestDone(QNetworkReply *); + + QList<QDeclarative1PixmapReply*> jobs; + QList<QDeclarative1PixmapReply*> cancelled; + QDeclarativeEngine *engine; + QObject *eventLoopQuitHack; + + QMutex mutex; + QDeclarative1PixmapReaderThreadObject *threadObject; + QWaitCondition waitCondition; + + QNetworkAccessManager *networkAccessManager(); + QNetworkAccessManager *accessManager; + + QHash<QNetworkReply*,QDeclarative1PixmapReply*> replies; + + static int replyDownloadProgress; + static int replyFinished; + static int downloadProgress; + static int threadNetworkRequestDone; + static QHash<QDeclarativeEngine *,QDeclarative1PixmapReader*> readers; + static QMutex readerMutex; +}; + +class QDeclarative1PixmapData +{ +public: + QDeclarative1PixmapData(const QUrl &u, const QSize &s, const QString &e) + : refCount(1), inCache(false), pixmapStatus(QDeclarative1Pixmap::Error), + url(u), errorString(e), requestSize(s), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + QDeclarative1PixmapData(const QUrl &u, const QSize &r) + : refCount(1), inCache(false), pixmapStatus(QDeclarative1Pixmap::Loading), + url(u), requestSize(r), reply(0), prevUnreferenced(0), prevUnreferencedPtr(0), + nextUnreferenced(0) + { + } + + QDeclarative1PixmapData(const QUrl &u, const QPixmap &p, const QSize &s, const QSize &r) + : refCount(1), inCache(false), privatePixmap(false), pixmapStatus(QDeclarative1Pixmap::Ready), + url(u), pixmap(p), implicitSize(s), requestSize(r), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + QDeclarative1PixmapData(const QPixmap &p) + : refCount(1), inCache(false), privatePixmap(true), pixmapStatus(QDeclarative1Pixmap::Ready), + pixmap(p), implicitSize(p.size()), requestSize(p.size()), reply(0), prevUnreferenced(0), + prevUnreferencedPtr(0), nextUnreferenced(0) + { + } + + int cost() const; + void addref(); + void release(); + void addToCache(); + void removeFromCache(); + + uint refCount; + + bool inCache:1; + bool privatePixmap:1; + + QDeclarative1Pixmap::Status pixmapStatus; + QUrl url; + QString errorString; + QPixmap pixmap; + QSize implicitSize; + QSize requestSize; + + QDeclarative1PixmapReply *reply; + + QDeclarative1PixmapData *prevUnreferenced; + QDeclarative1PixmapData**prevUnreferencedPtr; + QDeclarative1PixmapData *nextUnreferenced; +}; + +int QDeclarative1PixmapReply::finishedIndex = -1; +int QDeclarative1PixmapReply::downloadProgressIndex = -1; + +// XXX +QHash<QDeclarativeEngine *,QDeclarative1PixmapReader*> QDeclarative1PixmapReader::readers; +QMutex QDeclarative1PixmapReader::readerMutex; + +int QDeclarative1PixmapReader::replyDownloadProgress = -1; +int QDeclarative1PixmapReader::replyFinished = -1; +int QDeclarative1PixmapReader::downloadProgress = -1; +int QDeclarative1PixmapReader::threadNetworkRequestDone = -1; + + +void QDeclarative1PixmapReply::postReply(ReadError error, const QString &errorString, + const QSize &implicitSize, const QImage &image) +{ + loading = false; + QCoreApplication::postEvent(this, new Event(error, errorString, implicitSize, image)); +} + +QDeclarative1PixmapReply::Event::Event(ReadError e, const QString &s, const QSize &iSize, const QImage &i) +: QEvent(QEvent::User), error(e), errorString(s), implicitSize(iSize), image(i) +{ +} + +QNetworkAccessManager *QDeclarative1PixmapReader::networkAccessManager() +{ + if (!accessManager) { + Q_ASSERT(threadObject); + accessManager = QDeclarativeEnginePrivate::get(engine)->createNetworkAccessManager(threadObject); + } + return accessManager; +} + +static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *errorString, QSize *impsize, + const QSize &requestSize) +{ + QImageReader imgio(dev); + + bool force_scale = false; + if (url.path().endsWith(QLatin1String(".svg"),Qt::CaseInsensitive)) { + imgio.setFormat("svg"); // QSvgPlugin::capabilities bug QTBUG-9053 + force_scale = true; + } + + bool scaled = false; + if (requestSize.width() > 0 || requestSize.height() > 0) { + QSize s = imgio.size(); + if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + if (requestSize.height() <= 0) + s.setHeight(s.height()*requestSize.width()/s.width()); + s.setWidth(requestSize.width()); scaled = true; + } + if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + if (requestSize.width() <= 0) + s.setWidth(s.width()*requestSize.height()/s.height()); + s.setHeight(requestSize.height()); scaled = true; + } + if (scaled) { imgio.setScaledSize(s); } + } + + if (impsize) + *impsize = imgio.size(); + + if (imgio.read(image)) { + if (impsize && impsize->width() < 0) + *impsize = image->size(); + return true; + } else { + if (errorString) + *errorString = QDeclarative1Pixmap::tr("Error decoding: %1: %2").arg(url.toString()) + .arg(imgio.errorString()); + return false; + } +} + +QDeclarative1PixmapReader::QDeclarative1PixmapReader(QDeclarativeEngine *eng) +: QThread(eng), engine(eng), threadObject(0), accessManager(0) +{ + eventLoopQuitHack = new QObject; + eventLoopQuitHack->moveToThread(this); + connect(eventLoopQuitHack, SIGNAL(destroyed(QObject*)), SLOT(quit()), Qt::DirectConnection); + start(QThread::IdlePriority); +} + +QDeclarative1PixmapReader::~QDeclarative1PixmapReader() +{ + readerMutex.lock(); + readers.remove(engine); + readerMutex.unlock(); + + eventLoopQuitHack->deleteLater(); + wait(); +} + +void QDeclarative1PixmapReader::networkRequestDone(QNetworkReply *reply) +{ + QDeclarative1PixmapReply *job = replies.take(reply); + + if (job) { + job->redirectCount++; + if (job->redirectCount < IMAGEREQUEST_MAX_REDIRECT_RECURSION) { + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = reply->url().resolved(redirect.toUrl()); + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + + reply->deleteLater(); + reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, job, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + + replies.insert(reply, job); + return; + } + } + + QImage image; + QDeclarative1PixmapReply::ReadError error = QDeclarative1PixmapReply::NoError; + QString errorString; + QSize readSize; + if (reply->error()) { + error = QDeclarative1PixmapReply::Loading; + errorString = reply->errorString(); + } else { + QByteArray all = reply->readAll(); + QBuffer buff(&all); + buff.open(QIODevice::ReadOnly); + if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize)) { + error = QDeclarative1PixmapReply::Decoding; + } + } + // send completion event to the QDeclarative1PixmapReply + mutex.lock(); + if (!cancelled.contains(job)) job->postReply(error, errorString, readSize, image); + mutex.unlock(); + } + reply->deleteLater(); + + // kick off event loop again incase we have dropped below max request count + threadObject->processJobs(); +} + +QDeclarative1PixmapReaderThreadObject::QDeclarative1PixmapReaderThreadObject(QDeclarative1PixmapReader *i) +: reader(i) +{ +} + +void QDeclarative1PixmapReaderThreadObject::processJobs() +{ + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); +} + +bool QDeclarative1PixmapReaderThreadObject::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + reader->processJobs(); + return true; + } else { + return QObject::event(e); + } +} + +void QDeclarative1PixmapReaderThreadObject::networkRequestDone() +{ + QNetworkReply *reply = static_cast<QNetworkReply *>(sender()); + reader->networkRequestDone(reply); +} + +void QDeclarative1PixmapReader::processJobs() +{ + QMutexLocker locker(&mutex); + + while (true) { + if (cancelled.isEmpty() && (jobs.isEmpty() || replies.count() >= IMAGEREQUEST_MAX_REQUEST_COUNT)) + return; // Nothing else to do + + // Clean cancelled jobs + if (cancelled.count()) { + for (int i = 0; i < cancelled.count(); ++i) { + QDeclarative1PixmapReply *job = cancelled.at(i); + QNetworkReply *reply = replies.key(job, 0); + if (reply && reply->isRunning()) { + // cancel any jobs already started + replies.remove(reply); + reply->close(); + } + // deleteLater, since not owned by this thread + job->deleteLater(); + } + cancelled.clear(); + } + + if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { + QDeclarative1PixmapReply *runningJob = jobs.takeLast(); + runningJob->loading = true; + + QUrl url = runningJob->data->url; + QSize requestSize = runningJob->data->requestSize; + locker.unlock(); + processJob(runningJob, url, requestSize); + locker.relock(); + } + } +} + +void QDeclarative1PixmapReader::processJob(QDeclarative1PixmapReply *runningJob, const QUrl &url, + const QSize &requestSize) +{ + // fetch + if (url.scheme() == QLatin1String("image")) { + // Use QmlImageProvider + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + + QDeclarative1PixmapReply::ReadError errorCode = QDeclarative1PixmapReply::NoError; + QString errorStr; + if (image.isNull()) { + errorCode = QDeclarative1PixmapReply::Loading; + errorStr = QDeclarative1Pixmap::tr("Failed to get image from provider: %1").arg(url.toString()); + } + + mutex.lock(); + if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + QString lf = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (!lf.isEmpty()) { + // Image is local - load/decode immediately + QImage image; + QDeclarative1PixmapReply::ReadError errorCode = QDeclarative1PixmapReply::NoError; + QString errorStr; + QFile f(lf); + QSize readSize; + if (f.open(QIODevice::ReadOnly)) { + if (!readImage(url, &f, &image, &errorStr, &readSize, requestSize)) + errorCode = QDeclarative1PixmapReply::Loading; + } else { + errorStr = QDeclarative1Pixmap::tr("Cannot open: %1").arg(url.toString()); + errorCode = QDeclarative1PixmapReply::Loading; + } + mutex.lock(); + if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); + mutex.unlock(); + } else { + // Network resource + QNetworkRequest req(url); + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + QNetworkReply *reply = networkAccessManager()->get(req); + + QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); + QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); + + replies.insert(reply, runningJob); + } + } +} + +QDeclarative1PixmapReader *QDeclarative1PixmapReader::instance(QDeclarativeEngine *engine) +{ + readerMutex.lock(); + QDeclarative1PixmapReader *reader = readers.value(engine); + if (!reader) { + reader = new QDeclarative1PixmapReader(engine); + readers.insert(engine, reader); + } + readerMutex.unlock(); + + return reader; +} + +QDeclarative1PixmapReply *QDeclarative1PixmapReader::getImage(QDeclarative1PixmapData *data) +{ + mutex.lock(); + QDeclarative1PixmapReply *reply = new QDeclarative1PixmapReply(data); + reply->reader = this; + jobs.append(reply); + // XXX + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + return reply; +} + +void QDeclarative1PixmapReader::cancel(QDeclarative1PixmapReply *reply) +{ + mutex.lock(); + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + // XXX + if (threadObject) threadObject->processJobs(); + } else { + jobs.removeAll(reply); + delete reply; + } + mutex.unlock(); +} + +void QDeclarative1PixmapReader::run() +{ + if (replyDownloadProgress == -1) { + const QMetaObject *nr = &QNetworkReply::staticMetaObject; + const QMetaObject *pr = &QDeclarative1PixmapReply::staticMetaObject; + const QMetaObject *ir = &QDeclarative1PixmapReaderThreadObject::staticMetaObject; + replyDownloadProgress = nr->indexOfSignal("downloadProgress(qint64,qint64)"); + replyFinished = nr->indexOfSignal("finished()"); + downloadProgress = pr->indexOfSignal("downloadProgress(qint64,qint64)"); + threadNetworkRequestDone = ir->indexOfSlot("networkRequestDone()"); + } + + mutex.lock(); + threadObject = new QDeclarative1PixmapReaderThreadObject(this); + mutex.unlock(); + + processJobs(); + exec(); + + delete threadObject; + threadObject = 0; +} + +class QDeclarative1PixmapKey +{ +public: + const QUrl *url; + const QSize *size; +}; + +inline bool operator==(const QDeclarative1PixmapKey &lhs, const QDeclarative1PixmapKey &rhs) +{ + return *lhs.size == *rhs.size && *lhs.url == *rhs.url; +} + +inline uint qHash(const QDeclarative1PixmapKey &key) +{ + return qHash(*key.url) ^ key.size->width() ^ key.size->height(); +} + +class QDeclarative1PixmapStore : public QObject +{ + Q_OBJECT +public: + QDeclarative1PixmapStore(); + + void unreferencePixmap(QDeclarative1PixmapData *); + void referencePixmap(QDeclarative1PixmapData *); + +protected: + virtual void timerEvent(QTimerEvent *); + +public: + QHash<QDeclarative1PixmapKey, QDeclarative1PixmapData *> m_cache; + +private: + void shrinkCache(int remove); + + QDeclarative1PixmapData *m_unreferencedPixmaps; + QDeclarative1PixmapData *m_lastUnreferencedPixmap; + + int m_unreferencedCost; + int m_timerId; +}; +Q_GLOBAL_STATIC(QDeclarative1PixmapStore, pixmapStore); + +QDeclarative1PixmapStore::QDeclarative1PixmapStore() +: m_unreferencedPixmaps(0), m_lastUnreferencedPixmap(0), m_unreferencedCost(0), m_timerId(-1) +{ +} + +void QDeclarative1PixmapStore::unreferencePixmap(QDeclarative1PixmapData *data) +{ + Q_ASSERT(data->prevUnreferenced == 0); + Q_ASSERT(data->prevUnreferencedPtr == 0); + Q_ASSERT(data->nextUnreferenced == 0); + + data->nextUnreferenced = m_unreferencedPixmaps; + data->prevUnreferencedPtr = &m_unreferencedPixmaps; + + m_unreferencedPixmaps = data; + if (m_unreferencedPixmaps->nextUnreferenced) { + m_unreferencedPixmaps->nextUnreferenced->prevUnreferenced = m_unreferencedPixmaps; + m_unreferencedPixmaps->nextUnreferenced->prevUnreferencedPtr = &m_unreferencedPixmaps->nextUnreferenced; + } + + if (!m_lastUnreferencedPixmap) + m_lastUnreferencedPixmap = data; + + m_unreferencedCost += data->cost(); + + shrinkCache(-1); // Shrink the cache incase it has become larger than cache_limit + + if (m_timerId == -1 && m_unreferencedPixmaps) + m_timerId = startTimer(CACHE_EXPIRE_TIME * 1000); +} + +void QDeclarative1PixmapStore::referencePixmap(QDeclarative1PixmapData *data) +{ + Q_ASSERT(data->prevUnreferencedPtr); + + *data->prevUnreferencedPtr = data->nextUnreferenced; + if (data->nextUnreferenced) { + data->nextUnreferenced->prevUnreferencedPtr = data->prevUnreferencedPtr; + data->nextUnreferenced->prevUnreferenced = data->prevUnreferenced; + } + if (m_lastUnreferencedPixmap == data) + m_lastUnreferencedPixmap = data->prevUnreferenced; + + data->nextUnreferenced = 0; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + m_unreferencedCost -= data->cost(); +} + +void QDeclarative1PixmapStore::shrinkCache(int remove) +{ + while ((remove > 0 || m_unreferencedCost > cache_limit) && m_lastUnreferencedPixmap) { + QDeclarative1PixmapData *data = m_lastUnreferencedPixmap; + Q_ASSERT(data->nextUnreferenced == 0); + + *data->prevUnreferencedPtr = 0; + m_lastUnreferencedPixmap = data->prevUnreferenced; + data->prevUnreferencedPtr = 0; + data->prevUnreferenced = 0; + + remove -= data->cost(); + m_unreferencedCost -= data->cost(); + data->removeFromCache(); + delete data; + } +} + +void QDeclarative1PixmapStore::timerEvent(QTimerEvent *) +{ + int removalCost = m_unreferencedCost / CACHE_REMOVAL_FRACTION; + + shrinkCache(removalCost); + + if (m_unreferencedPixmaps == 0) { + killTimer(m_timerId); + m_timerId = -1; + } +} + +QDeclarative1PixmapReply::QDeclarative1PixmapReply(QDeclarative1PixmapData *d) +: data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0) +{ + if (finishedIndex == -1) { + finishedIndex = QDeclarative1PixmapReply::staticMetaObject.indexOfSignal("finished()"); + downloadProgressIndex = QDeclarative1PixmapReply::staticMetaObject.indexOfSignal("downloadProgress(qint64,qint64)"); + } +} + +QDeclarative1PixmapReply::~QDeclarative1PixmapReply() +{ +} + +bool QDeclarative1PixmapReply::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + + if (data) { + Event *de = static_cast<Event *>(event); + data->pixmapStatus = (de->error == NoError) ? QDeclarative1Pixmap::Ready : QDeclarative1Pixmap::Error; + + if (data->pixmapStatus == QDeclarative1Pixmap::Ready) { + data->pixmap = QPixmap::fromImage(de->image); + data->implicitSize = de->implicitSize; + } else { + data->errorString = de->errorString; + data->removeFromCache(); // We don't continue to cache error'd pixmaps + } + + data->reply = 0; + emit finished(); + } + + delete this; + return true; + } else { + return QObject::event(event); + } +} + +int QDeclarative1PixmapData::cost() const +{ + return (pixmap.width() * pixmap.height() * pixmap.depth()) / 8; +} + +void QDeclarative1PixmapData::addref() +{ + ++refCount; + if (prevUnreferencedPtr) + pixmapStore()->referencePixmap(this); +} + +void QDeclarative1PixmapData::release() +{ + Q_ASSERT(refCount > 0); + --refCount; + + if (refCount == 0) { + if (reply) { + reply->reader->cancel(reply); + reply = 0; + } + + if (pixmapStatus == QDeclarative1Pixmap::Ready) { + pixmapStore()->unreferencePixmap(this); + } else { + removeFromCache(); + delete this; + } + } +} + +void QDeclarative1PixmapData::addToCache() +{ + if (!inCache) { + QDeclarative1PixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.insert(key, this); + inCache = true; + } +} + +void QDeclarative1PixmapData::removeFromCache() +{ + if (inCache) { + QDeclarative1PixmapKey key = { &url, &requestSize }; + pixmapStore()->m_cache.remove(key); + inCache = false; + } +} + +static QDeclarative1PixmapData* createPixmapDataSync(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, bool *ok) +{ + if (url.scheme() == QLatin1String("image")) { + QSize readSize; + QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); + QDeclarativeImageProvider::ImageType imageType = ep->getImageProviderType(url); + + switch (imageType) { + case QDeclarativeImageProvider::Image: + { + QImage image = ep->getImageFromProvider(url, &readSize, requestSize); + if (!image.isNull()) { + *ok = true; + return new QDeclarative1PixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } + case QDeclarativeImageProvider::Pixmap: + { + QPixmap pixmap = ep->getPixmapFromProvider(url, &readSize, requestSize); + if (!pixmap.isNull()) { + *ok = true; + return new QDeclarative1PixmapData(url, pixmap, readSize, requestSize); + } + } + } + + // no matching provider, or provider has bad image type, or provider returned null image + return new QDeclarative1PixmapData(url, requestSize, + QDeclarative1Pixmap::tr("Failed to get image from provider: %1").arg(url.toString())); + } + + QString localFile = QDeclarativeEnginePrivate::urlToLocalFileOrQrc(url); + if (localFile.isEmpty()) + return 0; + + QFile f(localFile); + QSize readSize; + QString errorString; + + if (f.open(QIODevice::ReadOnly)) { + QImage image; + if (readImage(url, &f, &image, &errorString, &readSize, requestSize)) { + *ok = true; + return new QDeclarative1PixmapData(url, QPixmap::fromImage(image), readSize, requestSize); + } + } else { + errorString = QDeclarative1Pixmap::tr("Cannot open: %1").arg(url.toString()); + } + return new QDeclarative1PixmapData(url, requestSize, errorString); +} + + +struct QDeclarative1PixmapNull { + QUrl url; + QPixmap pixmap; + QSize size; +}; +Q_GLOBAL_STATIC(QDeclarative1PixmapNull, nullPixmap); + +QDeclarative1Pixmap::QDeclarative1Pixmap() +: d(0) +{ +} + +QDeclarative1Pixmap::QDeclarative1Pixmap(QDeclarativeEngine *engine, const QUrl &url) +: d(0) +{ + load(engine, url); +} + +QDeclarative1Pixmap::QDeclarative1Pixmap(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +: d(0) +{ + load(engine, url, size); +} + +QDeclarative1Pixmap::~QDeclarative1Pixmap() +{ + if (d) { + d->release(); + d = 0; + } +} + +bool QDeclarative1Pixmap::isNull() const +{ + return d == 0; +} + +bool QDeclarative1Pixmap::isReady() const +{ + return status() == Ready; +} + +bool QDeclarative1Pixmap::isError() const +{ + return status() == Error; +} + +bool QDeclarative1Pixmap::isLoading() const +{ + return status() == Loading; +} + +QString QDeclarative1Pixmap::error() const +{ + if (d) + return d->errorString; + else + return QString(); +} + +QDeclarative1Pixmap::Status QDeclarative1Pixmap::status() const +{ + if (d) + return d->pixmapStatus; + else + return Null; +} + +const QUrl &QDeclarative1Pixmap::url() const +{ + if (d) + return d->url; + else + return nullPixmap()->url; +} + +const QSize &QDeclarative1Pixmap::implicitSize() const +{ + if (d) + return d->implicitSize; + else + return nullPixmap()->size; +} + +const QSize &QDeclarative1Pixmap::requestSize() const +{ + if (d) + return d->requestSize; + else + return nullPixmap()->size; +} + +const QPixmap &QDeclarative1Pixmap::pixmap() const +{ + if (d) + return d->pixmap; + else + return nullPixmap()->pixmap; +} + +void QDeclarative1Pixmap::setPixmap(const QPixmap &p) +{ + clear(); + + if (!p.isNull()) + d = new QDeclarative1PixmapData(p); +} + +int QDeclarative1Pixmap::width() const +{ + if (d) + return d->pixmap.width(); + else + return 0; +} + +int QDeclarative1Pixmap::height() const +{ + if (d) + return d->pixmap.height(); + else + return 0; +} + +QRect QDeclarative1Pixmap::rect() const +{ + if (d) + return d->pixmap.rect(); + else + return QRect(); +} + +void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url) +{ + load(engine, url, QSize(), QDeclarative1Pixmap::Cache); +} + +void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url, QDeclarative1Pixmap::Options options) +{ + load(engine, url, QSize(), options); +} + +void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &size) +{ + load(engine, url, size, QDeclarative1Pixmap::Cache); +} + +void QDeclarative1Pixmap::load(QDeclarativeEngine *engine, const QUrl &url, const QSize &requestSize, QDeclarative1Pixmap::Options options) +{ + if (d) { d->release(); d = 0; } + + QDeclarative1PixmapKey key = { &url, &requestSize }; + QDeclarative1PixmapStore *store = pixmapStore(); + + QHash<QDeclarative1PixmapKey, QDeclarative1PixmapData *>::Iterator iter = store->m_cache.find(key); + + if (iter == store->m_cache.end()) { + if (options & QDeclarative1Pixmap::Asynchronous) { + // pixmaps can only be loaded synchronously + if (url.scheme() == QLatin1String("image") + && QDeclarativeEnginePrivate::get(engine)->getImageProviderType(url) == QDeclarativeImageProvider::Pixmap) { + options &= ~QDeclarative1Pixmap::Asynchronous; + } + } + + if (!(options & QDeclarative1Pixmap::Asynchronous)) { + bool ok = false; + d = createPixmapDataSync(engine, url, requestSize, &ok); + if (ok) { + if (options & QDeclarative1Pixmap::Cache) + d->addToCache(); + return; + } + if (d) // loadable, but encountered error while loading + return; + } + + if (!engine) + return; + + QDeclarative1PixmapReader *reader = QDeclarative1PixmapReader::instance(engine); + + d = new QDeclarative1PixmapData(url, requestSize); + if (options & QDeclarative1Pixmap::Cache) + d->addToCache(); + + d->reply = reader->getImage(d); + } else { + d = *iter; + d->addref(); + } +} + +void QDeclarative1Pixmap::clear() +{ + if (d) { + d->release(); + d = 0; + } +} + +void QDeclarative1Pixmap::clear(QObject *obj) +{ + if (d) { + if (d->reply) + QObject::disconnect(d->reply, 0, obj, 0); + d->release(); + d = 0; + } +} + +bool QDeclarative1Pixmap::connectFinished(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarative1Pixmap: connectFinished() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(finished()), object, method); +} + +bool QDeclarative1Pixmap::connectFinished(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarative1Pixmap: connectFinished() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarative1PixmapReply::finishedIndex, object, method); +} + +bool QDeclarative1Pixmap::connectDownloadProgress(QObject *object, const char *method) +{ + if (!d || !d->reply) { + qWarning("QDeclarative1Pixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), object, method); +} + +bool QDeclarative1Pixmap::connectDownloadProgress(QObject *object, int method) +{ + if (!d || !d->reply) { + qWarning("QDeclarative1Pixmap: connectDownloadProgress() called when not loading."); + return false; + } + + return QMetaObject::connect(d->reply, QDeclarative1PixmapReply::downloadProgressIndex, object, method); +} + + + +QT_END_NAMESPACE + +#include <qdeclarativepixmapcache.moc> diff --git a/src/qtquick1/util/qdeclarativepixmapcache_p.h b/src/qtquick1/util/qdeclarativepixmapcache_p.h new file mode 100644 index 0000000000..f0a7770be0 --- /dev/null +++ b/src/qtquick1/util/qdeclarativepixmapcache_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPIXMAPCACHE_H +#define QDECLARATIVEPIXMAPCACHE_H + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qstring.h> +#include <QtGui/qpixmap.h> +#include <QtCore/qurl.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeEngine; + +QT_MODULE(Declarative) + +class QDeclarative1PixmapData; +class Q_DECLARATIVE_EXPORT QDeclarative1Pixmap +{ + Q_DECLARE_TR_FUNCTIONS(QDeclarative1Pixmap) +public: + QDeclarative1Pixmap(); + QDeclarative1Pixmap(QDeclarativeEngine *, const QUrl &); + QDeclarative1Pixmap(QDeclarativeEngine *, const QUrl &, const QSize &); + ~QDeclarative1Pixmap(); + + enum Status { Null, Ready, Error, Loading }; + + enum Option { + Asynchronous = 0x00000001, + Cache = 0x00000002 + }; + Q_DECLARE_FLAGS(Options, Option) + + bool isNull() const; + bool isReady() const; + bool isError() const; + bool isLoading() const; + + Status status() const; + QString error() const; + const QUrl &url() const; + const QSize &implicitSize() const; + const QSize &requestSize() const; + const QPixmap &pixmap() const; + void setPixmap(const QPixmap &); + + QRect rect() const; + int width() const; + int height() const; + inline operator const QPixmap &() const; + + void load(QDeclarativeEngine *, const QUrl &); + void load(QDeclarativeEngine *, const QUrl &, QDeclarative1Pixmap::Options options); + void load(QDeclarativeEngine *, const QUrl &, const QSize &); + void load(QDeclarativeEngine *, const QUrl &, const QSize &, QDeclarative1Pixmap::Options options); + + void clear(); + void clear(QObject *); + + bool connectFinished(QObject *, const char *); + bool connectFinished(QObject *, int); + bool connectDownloadProgress(QObject *, const char *); + bool connectDownloadProgress(QObject *, int); + +private: + Q_DISABLE_COPY(QDeclarative1Pixmap) + QDeclarative1PixmapData *d; +}; + +inline QDeclarative1Pixmap::operator const QPixmap &() const +{ + return pixmap(); +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QDeclarative1Pixmap::Options) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEPIXMAPCACHE_H diff --git a/src/qtquick1/util/qdeclarativepropertychanges.cpp b/src/qtquick1/util/qdeclarativepropertychanges.cpp new file mode 100644 index 0000000000..c9a512503b --- /dev/null +++ b/src/qtquick1/util/qdeclarativepropertychanges.cpp @@ -0,0 +1,801 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativepropertychanges_p.h" + +#include "QtQuick1/private/qdeclarativeopenmetaobject_p.h" +#include "QtDeclarative/private/qdeclarativerewrite_p.h" +#include "QtDeclarative/private/qdeclarativeengine_p.h" +#include "QtDeclarative/private/qdeclarativecompiler_p.h" + +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativecustomparser_p.h> +#include <QtDeclarative/private/qdeclarativeparser_p.h> +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/private/qdeclarativebinding_p.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> +#include <QtDeclarative/private/qdeclarativeproperty_p.h> +#include <QtDeclarative/private/qdeclarativecontext_p.h> +#include <QtQuick1/private/qdeclarativestate_p_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass PropertyChanges QDeclarative1PropertyChanges + \ingroup qml-state-elements + \since 4.7 + \brief The PropertyChanges element describes new property bindings or values for a state. + + PropertyChanges is used to define the property values or bindings in a + \l State. This enables an item's property values to be changed when it + \l {QML States}{changes between states}. + + To create a PropertyChanges object, specify the \l target item whose + properties are to be modified, and define the new property values or + bindings. For example: + + \snippet doc/src/snippets/declarative/propertychanges.qml import + \codeline + \snippet doc/src/snippets/declarative/propertychanges.qml 0 + + When the mouse is pressed, the \l Rectangle changes to the \e resized + state. In this state, the PropertyChanges object sets the rectangle's + color to blue and the \c height value to that of \c container.height. + + Note this automatically binds \c rect.height to \c container.height + in the \e resized state. If a property binding should not be + established, and the height should just be set to the value of + \c container.height at the time of the state change, set the \l explicit + property to \c true. + + A PropertyChanges object can also override the default signal handler + for an object to implement a signal handler specific to the new state: + + \qml + PropertyChanges { + target: myMouseArea + onClicked: doSomethingDifferent() + } + \endqml + + \note PropertyChanges can be used to change anchor margins, but not other anchor + values; use AnchorChanges for this instead. Similarly, to change an \l Item's + \l {Item::}{parent} value, use ParentChanges instead. + + + \section2 Resetting property values + + The \c undefined value can be used to reset the property value for a state. + In the following example, when \c theText changes to the \e widerText + state, its \c width property is reset, giving the text its natural width + and displaying the whole string on a single line. + + \snippet doc/src/snippets/declarative/propertychanges.qml reset + + + \section2 Immediate property changes in transitions + + When \l{QML Animation and Transitions}{Transitions} are used to animate + state changes, they animate properties from their values in the current + state to those defined in the new state (as defined by PropertyChanges + objects). However, it is sometimes desirable to set a property value + \e immediately during a \l Transition, without animation; in these cases, + the PropertyAction element can be used to force an immediate property + change. + + See the PropertyAction documentation for more details. + + \sa {declarative/animation/states}{states example}, {qmlstate}{States}, QtDeclarative +*/ + +/*! + \qmlproperty Object PropertyChanges::target + This property holds the object which contains the properties to be changed. +*/ + +class QDeclarative1ReplaceSignalHandler : public QDeclarative1ActionEvent +{ +public: + QDeclarative1ReplaceSignalHandler() : expression(0), reverseExpression(0), + rewindExpression(0), ownedExpression(0) {} + ~QDeclarative1ReplaceSignalHandler() { + delete ownedExpression; + } + + virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); } + + QDeclarativeProperty property; + QDeclarativeExpression *expression; + QDeclarativeExpression *reverseExpression; + QDeclarativeExpression *rewindExpression; + QDeclarativeGuard<QDeclarativeExpression> ownedExpression; + + virtual void execute(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression); + if (ownedExpression == expression) + ownedExpression = 0; + } + + virtual bool isReversable() { return true; } + virtual void reverse(Reason) { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression); + if (ownedExpression == reverseExpression) + ownedExpression = 0; + } + + virtual void saveOriginals() { + saveCurrentValues(); + reverseExpression = rewindExpression; + } + + /*virtual void copyOriginals(QDeclarative1ActionEvent *other) + { + QDeclarative1ReplaceSignalHandler *rsh = static_cast<QDeclarative1ReplaceSignalHandler*>(other); + saveCurrentValues(); + if (rsh == this) + return; + reverseExpression = rsh->reverseExpression; + if (rsh->ownedExpression == reverseExpression) { + ownedExpression = rsh->ownedExpression; + rsh->ownedExpression = 0; + } + }*/ + + virtual void rewind() { + ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression); + if (ownedExpression == rewindExpression) + ownedExpression = 0; + } + virtual void saveCurrentValues() { + rewindExpression = QDeclarativePropertyPrivate::signalExpression(property); + } + + virtual bool override(QDeclarative1ActionEvent*other) { + if (other == this) + return true; + if (other->typeName() != typeName()) + return false; + if (static_cast<QDeclarative1ReplaceSignalHandler*>(other)->property == property) + return true; + return false; + } +}; + + +class QDeclarative1PropertyChangesPrivate : public QDeclarative1StateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1PropertyChanges) +public: + QDeclarative1PropertyChangesPrivate() : decoded(true), restore(true), + isExplicit(false) {} + + QDeclarativeGuard<QObject> object; + QByteArray data; + + bool decoded : 1; + bool restore : 1; + bool isExplicit : 1; + + void decode(); + + class ExpressionChange { + public: + ExpressionChange(const QString &_name, + QDeclarativeBinding::Identifier _id, + QDeclarativeExpression *_expr) + : name(_name), id(_id), expression(_expr) {} + QString name; + QDeclarativeBinding::Identifier id; + QDeclarativeExpression *expression; + }; + + QList<QPair<QString, QVariant> > properties; + QList<ExpressionChange> expressions; + QList<QDeclarative1ReplaceSignalHandler*> signalReplacements; + + QDeclarativeProperty property(const QString &); +}; + +void +QDeclarative1PropertyChangesParser::compileList(QList<QPair<QByteArray, QVariant> > &list, + const QByteArray &pre, + const QDeclarativeCustomParserProperty &prop) +{ + QByteArray propName = pre + prop.name(); + + QList<QVariant> values = prop.assignedValues(); + for (int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + error(qvariant_cast<QDeclarativeCustomParserNode>(value), + QDeclarative1PropertyChanges::tr("PropertyChanges does not support creating state-specific objects.")); + continue; + } else if(value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) { + + QDeclarativeCustomParserProperty prop = + qvariant_cast<QDeclarativeCustomParserProperty>(value); + QByteArray pre = propName + '.'; + compileList(list, pre, prop); + + } else { + list << qMakePair(propName, value); + } + } +} + +QByteArray +QDeclarative1PropertyChangesParser::compile(const QList<QDeclarativeCustomParserProperty> &props) +{ + QList<QPair<QByteArray, QVariant> > data; + for(int ii = 0; ii < props.count(); ++ii) + compileList(data, QByteArray(), props.at(ii)); + + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + ds << data.count(); + for(int ii = 0; ii < data.count(); ++ii) { + QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(data.at(ii).second); + QVariant var; + bool isScript = v.isScript(); + QDeclarativeBinding::Identifier id = 0; + switch(v.type()) { + case QDeclarativeParser::Variant::Boolean: + var = QVariant(v.asBoolean()); + break; + case QDeclarativeParser::Variant::Number: + var = QVariant(v.asNumber()); + break; + case QDeclarativeParser::Variant::String: + var = QVariant(v.asString()); + break; + case QDeclarativeParser::Variant::Invalid: + case QDeclarativeParser::Variant::Script: + var = QVariant(v.asScript()); + { + // Pre-rewrite the expression + QString expression = v.asScript(); + id = rewriteBinding(expression, data.at(ii).first); //### recreates the AST, which is slow + } + break; + } + + ds << QString::fromUtf8(data.at(ii).first) << isScript << var; + if (isScript) + ds << id; + } + + return rv; +} + +void QDeclarative1PropertyChangesPrivate::decode() +{ + Q_Q(QDeclarative1PropertyChanges); + if (decoded) + return; + + QDataStream ds(&data, QIODevice::ReadOnly); + + int count; + ds >> count; + for (int ii = 0; ii < count; ++ii) { + QString name; + bool isScript; + QVariant data; + QDeclarativeBinding::Identifier id = QDeclarativeBinding::Invalid; + ds >> name; + ds >> isScript; + ds >> data; + if (isScript) + ds >> id; + + QDeclarativeProperty prop = property(name); //### better way to check for signal property? + if (prop.type() & QDeclarativeProperty::SignalProperty) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + QDeclarative1ReplaceSignalHandler *handler = new QDeclarative1ReplaceSignalHandler; + handler->property = prop; + handler->expression = expression; + signalReplacements << handler; + } else if (isScript) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString()); + QDeclarativeData *ddata = QDeclarativeData::get(q); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expressions << ExpressionChange(name, id, expression); + } else { + properties << qMakePair(name, data); + } + } + + decoded = true; + data.clear(); +} + +void QDeclarative1PropertyChangesParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarative1PropertyChangesPrivate *p = + static_cast<QDeclarative1PropertyChangesPrivate *>(QObjectPrivate::get(object)); + p->data = data; + p->decoded = false; +} + +QDeclarative1PropertyChanges::QDeclarative1PropertyChanges() +: QDeclarative1StateOperation(*(new QDeclarative1PropertyChangesPrivate)) +{ +} + +QDeclarative1PropertyChanges::~QDeclarative1PropertyChanges() +{ + Q_D(QDeclarative1PropertyChanges); + for(int ii = 0; ii < d->expressions.count(); ++ii) + delete d->expressions.at(ii).expression; + for(int ii = 0; ii < d->signalReplacements.count(); ++ii) + delete d->signalReplacements.at(ii); +} + +QObject *QDeclarative1PropertyChanges::object() const +{ + Q_D(const QDeclarative1PropertyChanges); + return d->object; +} + +void QDeclarative1PropertyChanges::setObject(QObject *o) +{ + Q_D(QDeclarative1PropertyChanges); + d->object = o; +} + +/*! + \qmlproperty bool PropertyChanges::restoreEntryValues + + This property holds whether the previous values should be restored when + leaving the state. + + The default value is \c true. Setting this value to \c false creates a + temporary state that has permanent effects on property values. +*/ +bool QDeclarative1PropertyChanges::restoreEntryValues() const +{ + Q_D(const QDeclarative1PropertyChanges); + return d->restore; +} + +void QDeclarative1PropertyChanges::setRestoreEntryValues(bool v) +{ + Q_D(QDeclarative1PropertyChanges); + d->restore = v; +} + +QDeclarativeProperty +QDeclarative1PropertyChangesPrivate::property(const QString &property) +{ + Q_Q(QDeclarative1PropertyChanges); + QDeclarativeProperty prop(object, property, qmlContext(q)); + if (!prop.isValid()) { + qmlInfo(q) << QDeclarative1PropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property); + return QDeclarativeProperty(); + } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) { + qmlInfo(q) << QDeclarative1PropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(property); + return QDeclarativeProperty(); + } + return prop; +} + +QDeclarative1PropertyChanges::ActionList QDeclarative1PropertyChanges::actions() +{ + Q_D(QDeclarative1PropertyChanges); + + d->decode(); + + ActionList list; + + for (int ii = 0; ii < d->properties.count(); ++ii) { + + QDeclarative1Action a(d->object, d->properties.at(ii).first, + qmlContext(this), d->properties.at(ii).second); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + list << a; + } + } + + for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { + + QDeclarative1ReplaceSignalHandler *handler = d->signalReplacements.at(ii); + + if (handler->property.isValid()) { + QDeclarative1Action a; + a.event = handler; + list << a; + } + } + + for (int ii = 0; ii < d->expressions.count(); ++ii) { + + const QString &property = d->expressions.at(ii).name; + QDeclarativeProperty prop = d->property(property); + + if (prop.isValid()) { + QDeclarative1Action a; + a.restore = restoreEntryValues(); + a.property = prop; + a.fromValue = a.property.read(); + a.specifiedObject = d->object; + a.specifiedProperty = property; + + if (d->isExplicit) { + a.toValue = d->expressions.at(ii).expression->evaluate(); + } else { + QDeclarativeExpression *e = d->expressions.at(ii).expression; + + QDeclarativeBinding::Identifier id = d->expressions.at(ii).id; + QDeclarativeBinding *newBinding = id != QDeclarativeBinding::Invalid ? QDeclarativeBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0; + if (!newBinding) { + newBinding = new QDeclarativeBinding(e->expression(), object(), qmlContext(this)); + newBinding->setSourceLocation(e->sourceFile(), e->lineNumber()); + } + newBinding->setTarget(prop); + a.toBinding = newBinding; + a.deletableToBinding = true; + } + + list << a; + } + } + + return list; +} + +/*! + \qmlproperty bool PropertyChanges::explicit + + If explicit is set to true, any potential bindings will be interpreted as + once-off assignments that occur when the state is entered. + + In the following example, the addition of explicit prevents \c myItem.width from + being bound to \c parent.width. Instead, it is assigned the value of \c parent.width + at the time of the state change. + \qml + PropertyChanges { + target: myItem + explicit: true + width: parent.width + } + \endqml + + By default, explicit is false. +*/ +bool QDeclarative1PropertyChanges::isExplicit() const +{ + Q_D(const QDeclarative1PropertyChanges); + return d->isExplicit; +} + +void QDeclarative1PropertyChanges::setIsExplicit(bool e) +{ + Q_D(QDeclarative1PropertyChanges); + d->isExplicit = e; +} + +bool QDeclarative1PropertyChanges::containsValue(const QString &name) const +{ + Q_D(const QDeclarative1PropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return true; + } + } + + return false; +} + +bool QDeclarative1PropertyChanges::containsExpression(const QString &name) const +{ + Q_D(const QDeclarative1PropertyChanges); + typedef QDeclarative1PropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return true; + } + } + + return false; +} + +bool QDeclarative1PropertyChanges::containsProperty(const QString &name) const +{ + return containsValue(name) || containsExpression(name); +} + +void QDeclarative1PropertyChanges::changeValue(const QString &name, const QVariant &value) +{ + Q_D(QDeclarative1PropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarative1PropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + d->property(name).write(value); + } + + d->properties.append(PropertyEntry(name, value)); + return; + } + } + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + entry.second = value; + if (state() && state()->isStateActive()) + d->property(name).write(value); + return; + } + } + + QDeclarative1Action action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + action.toValue = value; + + propertyIterator.insert(PropertyEntry(name, value)); + if (state() && state()->isStateActive()) { + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + d->property(name).write(value); + } +} + +void QDeclarative1PropertyChanges::changeExpression(const QString &name, const QString &expression) +{ + Q_D(QDeclarative1PropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarative1PropertyChangesPrivate::ExpressionChange ExpressionEntry; + + bool hadValue = false; + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + hadValue = true; + break; + } + } + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + entry.expression->setExpression(expression); + if (state() && state()->isStateActive()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(d->property(name), 0); + oldBinding->destroy(); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + return; + } + } + + QDeclarativeExpression *newExpression = new QDeclarativeExpression(qmlContext(this), d->object, expression); + expressionIterator.insert(ExpressionEntry(name, QDeclarativeBinding::Invalid, newExpression)); + + if (state() && state()->isStateActive()) { + if (hadValue) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name)); + if (oldBinding) { + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + state()->changeBindingInRevertList(object(), name, oldBinding); + } + + QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } else { + QDeclarative1Action action; + action.restore = restoreEntryValues(); + action.property = d->property(name); + action.fromValue = action.property.read(); + action.specifiedObject = object(); + action.specifiedProperty = name; + + + if (d->isExplicit) { + action.toValue = newExpression->evaluate(); + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(newExpression->expression(), object(), qmlContext(this)); + newBinding->setTarget(d->property(name)); + action.toBinding = newBinding; + action.deletableToBinding = true; + + state()->addEntryToRevertList(action); + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property); + if (oldBinding) + oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + + QDeclarativePropertyPrivate::setBinding(action.property, newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor); + } + } + } + // what about the signal handler? +} + +QVariant QDeclarative1PropertyChanges::property(const QString &name) const +{ + Q_D(const QDeclarative1PropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarative1PropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return QVariant(entry.expression->expression()); + } + } + + return QVariant(); +} + +void QDeclarative1PropertyChanges::removeProperty(const QString &name) +{ + Q_D(QDeclarative1PropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + typedef QDeclarative1PropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + expressionIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } + + QMutableListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + propertyIterator.remove(); + state()->removeEntryFromRevertList(object(), name); + return; + } + } +} + +QVariant QDeclarative1PropertyChanges::value(const QString &name) const +{ + Q_D(const QDeclarative1PropertyChanges); + typedef QPair<QString, QVariant> PropertyEntry; + + QListIterator<PropertyEntry> propertyIterator(d->properties); + while (propertyIterator.hasNext()) { + const PropertyEntry &entry = propertyIterator.next(); + if (entry.first == name) { + return entry.second; + } + } + + return QVariant(); +} + +QString QDeclarative1PropertyChanges::expression(const QString &name) const +{ + Q_D(const QDeclarative1PropertyChanges); + typedef QDeclarative1PropertyChangesPrivate::ExpressionChange ExpressionEntry; + + QListIterator<ExpressionEntry> expressionIterator(d->expressions); + while (expressionIterator.hasNext()) { + const ExpressionEntry &entry = expressionIterator.next(); + if (entry.name == name) { + return entry.expression->expression(); + } + } + + return QString(); +} + +void QDeclarative1PropertyChanges::detachFromState() +{ + if (state()) + state()->removeAllEntriesFromRevertList(object()); +} + +void QDeclarative1PropertyChanges::attachToState() +{ + if (state()) + state()->addEntriesToRevertList(actions()); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativepropertychanges_p.h b/src/qtquick1/util/qdeclarativepropertychanges_p.h new file mode 100644 index 0000000000..30609d3c2d --- /dev/null +++ b/src/qtquick1/util/qdeclarativepropertychanges_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPROPERTYCHANGES_H +#define QDECLARATIVEPROPERTYCHANGES_H + +#include "QtQuick1/private/qdeclarativestateoperations_p.h" +#include <QtDeclarative/private/qdeclarativecustomparser_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1PropertyChangesPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1PropertyChanges : public QDeclarative1StateOperation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1PropertyChanges) + + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(bool restoreEntryValues READ restoreEntryValues WRITE setRestoreEntryValues) + Q_PROPERTY(bool explicit READ isExplicit WRITE setIsExplicit) +public: + QDeclarative1PropertyChanges(); + ~QDeclarative1PropertyChanges(); + + QObject *object() const; + void setObject(QObject *); + + bool restoreEntryValues() const; + void setRestoreEntryValues(bool); + + bool isExplicit() const; + void setIsExplicit(bool); + + virtual ActionList actions(); + + bool containsProperty(const QString &name) const; + bool containsValue(const QString &name) const; + bool containsExpression(const QString &name) const; + void changeValue(const QString &name, const QVariant &value); + void changeExpression(const QString &name, const QString &expression); + void removeProperty(const QString &name); + QVariant value(const QString &name) const; + QString expression(const QString &name) const; + + void detachFromState(); + void attachToState(); + + QVariant property(const QString &name) const; +}; + +class QDeclarative1PropertyChangesParser : public QDeclarativeCustomParser +{ +public: + QDeclarative1PropertyChangesParser() + : QDeclarativeCustomParser(AcceptsAttachedProperties) {} + + void compileList(QList<QPair<QByteArray, QVariant> > &list, const QByteArray &pre, const QDeclarativeCustomParserProperty &prop); + + virtual QByteArray compile(const QList<QDeclarativeCustomParserProperty> &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1PropertyChanges) + +QT_END_HEADER + +#endif // QDECLARATIVEPROPERTYCHANGES_H diff --git a/src/qtquick1/util/qdeclarativesmoothedanimation.cpp b/src/qtquick1/util/qdeclarativesmoothedanimation.cpp new file mode 100644 index 0000000000..d94c132a56 --- /dev/null +++ b/src/qtquick1/util/qdeclarativesmoothedanimation.cpp @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativesmoothedanimation_p.h" +#include "QtQuick1/private/qdeclarativesmoothedanimation_p_p.h" + +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" + +#include <QtDeclarative/qdeclarativeproperty.h> +#include "QtDeclarative/private/qdeclarativeproperty_p.h" + +#include "QtDeclarative/private/qdeclarativeglobal_p.h" + +#include <QtCore/qdebug.h> + +#include <math.h> + +#define DELAY_STOP_TIMER_INTERVAL 32 + +QT_BEGIN_NAMESPACE + + + +QSmoothedAnimation_1::QSmoothedAnimation_1(QObject *parent) + : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1), + reversingMode(QDeclarative1SmoothedAnimation::Eased), initialVelocity(0), + trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0) +{ + delayedStopTimer.setInterval(DELAY_STOP_TIMER_INTERVAL); + delayedStopTimer.setSingleShot(true); + connect(&delayedStopTimer, SIGNAL(timeout()), this, SLOT(stop())); +} + +void QSmoothedAnimation_1::restart() +{ + initialVelocity = trackVelocity; + if (state() != QAbstractAnimation::Running) + start(); + else + init(); +} + +void QSmoothedAnimation_1::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/) +{ + if (newState == QAbstractAnimation::Running) + init(); +} + +void QSmoothedAnimation_1::delayedStop() +{ + if (!delayedStopTimer.isActive()) + delayedStopTimer.start(); +} + +int QSmoothedAnimation_1::duration() const +{ + return -1; +} + +bool QSmoothedAnimation_1::recalc() +{ + s = to - initialValue; + vi = initialVelocity; + + s = (invert? -1.0: 1.0) * s; + + if (userDuration > 0 && velocity > 0) { + tf = s / velocity; + if (tf > (userDuration / 1000.)) tf = (userDuration / 1000.); + } else if (userDuration > 0) { + tf = userDuration / 1000.; + } else if (velocity > 0) { + tf = s / velocity; + } else { + return false; + } + + finalDuration = ceil(tf * 1000.0); + + if (maximumEasingTime == 0) { + a = 0; + d = 0; + tp = 0; + td = tf; + vp = velocity; + sp = 0; + sd = s; + } else if (maximumEasingTime != -1 && tf > (maximumEasingTime / 1000.)) { + qreal met = maximumEasingTime / 1000.; + td = tf - met; + + qreal c1 = td; + qreal c2 = (tf - td) * vi - tf * velocity; + qreal c3 = -0.5 * (tf - td) * vi * vi; + + qreal vp1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + + vp = vp1; + a = vp / met; + d = a; + tp = (vp - vi) / a; + sp = vi * tp + 0.5 * a * tp * tp; + sd = sp + (td - tp) * vp; + } else { + qreal c1 = 0.25 * tf * tf; + qreal c2 = 0.5 * vi * tf - s; + qreal c3 = -0.25 * vi * vi; + + qreal a1 = (-c2 + sqrt(c2 * c2 - 4 * c1 * c3)) / (2. * c1); + + qreal tp1 = 0.5 * tf - 0.5 * vi / a1; + qreal vp1 = a1 * tp1 + vi; + + qreal sp1 = 0.5 * a1 * tp1 * tp1 + vi * tp1; + + a = a1; + d = a1; + tp = tp1; + td = tp1; + vp = vp1; + sp = sp1; + sd = sp1; + } + return true; +} + +qreal QSmoothedAnimation_1::easeFollow(qreal time_seconds) +{ + qreal value; + if (time_seconds < tp) { + trackVelocity = vi + time_seconds * a; + value = 0.5 * a * time_seconds * time_seconds + vi * time_seconds; + } else if (time_seconds < td) { + time_seconds -= tp; + trackVelocity = vp; + value = sp + time_seconds * vp; + } else if (time_seconds < tf) { + time_seconds -= td; + trackVelocity = vp - time_seconds * a; + value = sd - 0.5 * d * time_seconds * time_seconds + vp * time_seconds; + } else { + trackVelocity = 0; + value = s; + delayedStop(); + } + + // to normalize 's' between [0..1], divide 'value' by 's' + return value; +} + +void QSmoothedAnimation_1::updateCurrentTime(int t) +{ + qreal time_seconds = qreal(t - lastTime) / 1000.; + + qreal value = easeFollow(time_seconds); + value *= (invert? -1.0: 1.0); + QDeclarativePropertyPrivate::write(target, initialValue + value, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); +} + +void QSmoothedAnimation_1::init() +{ + if (velocity == 0) { + stop(); + return; + } + + if (delayedStopTimer.isActive()) + delayedStopTimer.stop(); + + initialValue = target.read().toReal(); + lastTime = this->currentTime(); + + if (to == initialValue) { + stop(); + return; + } + + bool hasReversed = trackVelocity != 0. && + ((!invert) == ((initialValue - to) > 0)); + + if (hasReversed) { + switch (reversingMode) { + default: + case QDeclarative1SmoothedAnimation::Eased: + initialVelocity = -trackVelocity; + break; + case QDeclarative1SmoothedAnimation::Sync: + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + trackVelocity = 0; + stop(); + return; + case QDeclarative1SmoothedAnimation::Immediate: + initialVelocity = 0; + break; + } + } + + trackVelocity = initialVelocity; + + invert = (to < initialValue); + + if (!recalc()) { + QDeclarativePropertyPrivate::write(target, to, + QDeclarativePropertyPrivate::BypassInterceptor + | QDeclarativePropertyPrivate::DontRemoveBinding); + stop(); + return; + } +} + +/*! + \qmlclass SmoothedAnimation QDeclarative1SmoothedAnimation + \ingroup qml-animation-transition + \since 4.7 + \inherits NumberAnimation + \brief The SmoothedAnimation element allows a property to smoothly track a value. + + A SmoothedAnimation animates a property's value to a set target value + using an ease in/out quad easing curve. When the target value changes, + the easing curves used to animate between the old and new target values + are smoothly spliced together to create a smooth movement to the new + target value that maintains the current velocity. + + The follow example shows one \l Rectangle tracking the position of another + using SmoothedAnimation. The green rectangle's \c x and \c y values are + bound to those of the red rectangle. Whenever these values change, the + green rectangle smoothly animates to its new position: + + \snippet doc/src/snippets/declarative/smoothedanimation.qml 0 + + A SmoothedAnimation can be configured by setting the \l velocity at which the + animation should occur, or the \l duration that the animation should take. + If both the \l velocity and \l duration are specified, the one that results in + the quickest animation is chosen for each change in the target value. + + For example, animating from 0 to 800 will take 4 seconds if a velocity + of 200 is set, will take 8 seconds with a duration of 8000 set, and will + take 4 seconds with both a velocity of 200 and a duration of 8000 set. + Animating from 0 to 20000 will take 10 seconds if a velocity of 200 is set, + will take 8 seconds with a duration of 8000 set, and will take 8 seconds + with both a velocity of 200 and a duration of 8000 set. + + The default velocity of SmoothedAnimation is 200 units/second. Note that if the range of the + value being animated is small, then the velocity will need to be adjusted + appropriately. For example, the opacity of an item ranges from 0 - 1.0. + To enable a smooth animation in this range the velocity will need to be + set to a value such as 0.5 units/second. Animating from 0 to 1.0 with a velocity + of 0.5 will take 2000 ms to complete. + + Like any other animation element, a SmoothedAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SpringAnimation, NumberAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example} +*/ + +QDeclarative1SmoothedAnimation::QDeclarative1SmoothedAnimation(QObject *parent) +: QDeclarative1NumberAnimation(*(new QDeclarative1SmoothedAnimationPrivate), parent) +{ +} + +QDeclarative1SmoothedAnimation::~QDeclarative1SmoothedAnimation() +{ +} + +QDeclarative1SmoothedAnimationPrivate::QDeclarative1SmoothedAnimationPrivate() + : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation_1) +{ + Q_Q(QDeclarative1SmoothedAnimation); + QDeclarative_setParent_noEvent(wrapperGroup, q); + QDeclarative_setParent_noEvent(anim, q); +} + +void QDeclarative1SmoothedAnimationPrivate::updateRunningAnimations() +{ + foreach(QSmoothedAnimation_1* ease, activeAnimations.values()){ + ease->maximumEasingTime = anim->maximumEasingTime; + ease->reversingMode = anim->reversingMode; + ease->velocity = anim->velocity; + ease->userDuration = anim->userDuration; + ease->init(); + } +} + +QAbstractAnimation* QDeclarative1SmoothedAnimation::qtAnimation() +{ + Q_D(QDeclarative1SmoothedAnimation); + return d->wrapperGroup; +} + +void QDeclarative1SmoothedAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1SmoothedAnimation); + QDeclarative1NumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + QSet<QAbstractAnimation*> anims; + for (int i = 0; i < d->actions->size(); i++) { + QSmoothedAnimation_1 *ease; + bool needsRestart; + if (!d->activeAnimations.contains((*d->actions)[i].property)) { + ease = new QSmoothedAnimation_1(); + d->wrapperGroup->addAnimation(ease); + d->activeAnimations.insert((*d->actions)[i].property, ease); + needsRestart = false; + } else { + ease = d->activeAnimations.value((*d->actions)[i].property); + needsRestart = true; + } + ease->target = (*d->actions)[i].property; + ease->to = (*d->actions)[i].toValue.toReal(); + + // copying public members from main value holder animation + ease->maximumEasingTime = d->anim->maximumEasingTime; + ease->reversingMode = d->anim->reversingMode; + ease->velocity = d->anim->velocity; + ease->userDuration = d->anim->userDuration; + + ease->initialVelocity = ease->trackVelocity; + + if (needsRestart) + ease->init(); + anims.insert(ease); + } + + for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) { + if (!anims.contains(d->wrapperGroup->animationAt(i))) { + QSmoothedAnimation_1 *ease = static_cast<QSmoothedAnimation_1*>(d->wrapperGroup->animationAt(i)); + d->activeAnimations.remove(ease->target); + d->wrapperGroup->takeAnimation(i); + delete ease; + } + } +} + +/*! + \qmlproperty enumeration SmoothedAnimation::reversingMode + + Sets how the SmoothedAnimation behaves if an animation direction is reversed. + + Possible values are: + + \list + \o SmoothedAnimation.Eased (default) - the animation will smoothly decelerate, and then reverse direction + \o SmoothedAnimation.Immediate - the animation will immediately begin accelerating in the reverse direction, beginning with a velocity of 0 + \o SmoothedAnimation.Sync - the property is immediately set to the target value + \endlist +*/ +QDeclarative1SmoothedAnimation::ReversingMode QDeclarative1SmoothedAnimation::reversingMode() const +{ + Q_D(const QDeclarative1SmoothedAnimation); + return (QDeclarative1SmoothedAnimation::ReversingMode) d->anim->reversingMode; +} + +void QDeclarative1SmoothedAnimation::setReversingMode(ReversingMode m) +{ + Q_D(QDeclarative1SmoothedAnimation); + if (d->anim->reversingMode == m) + return; + + d->anim->reversingMode = m; + emit reversingModeChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int SmoothedAnimation::duration + + This property holds the animation duration, in msecs, used when tracking the source. + + Setting this to -1 (the default) disables the duration value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +int QDeclarative1SmoothedAnimation::duration() const +{ + Q_D(const QDeclarative1SmoothedAnimation); + return d->anim->userDuration; +} + +void QDeclarative1SmoothedAnimation::setDuration(int duration) +{ + Q_D(QDeclarative1SmoothedAnimation); + if (duration != -1) + QDeclarative1NumberAnimation::setDuration(duration); + if(duration == d->anim->userDuration) + return; + d->anim->userDuration = duration; + d->updateRunningAnimations(); +} + +qreal QDeclarative1SmoothedAnimation::velocity() const +{ + Q_D(const QDeclarative1SmoothedAnimation); + return d->anim->velocity; +} + +/*! + \qmlproperty real SmoothedAnimation::velocity + + This property holds the average velocity allowed when tracking the 'to' value. + + The default velocity of SmoothedAnimation is 200 units/second. + + Setting this to -1 disables the velocity value. + + If the velocity value and the duration value are both enabled, then the animation will + use whichever gives the shorter duration. +*/ +void QDeclarative1SmoothedAnimation::setVelocity(qreal v) +{ + Q_D(QDeclarative1SmoothedAnimation); + if (d->anim->velocity == v) + return; + + d->anim->velocity = v; + emit velocityChanged(); + d->updateRunningAnimations(); +} + +/*! + \qmlproperty int SmoothedAnimation::maximumEasingTime + + This property specifies the maximum time, in msecs, any "eases" during the follow should take. + Setting this property causes the velocity to "level out" after at a time. Setting + a negative value reverts to the normal mode of easing over the entire animation + duration. + + The default value is -1. +*/ +int QDeclarative1SmoothedAnimation::maximumEasingTime() const +{ + Q_D(const QDeclarative1SmoothedAnimation); + return d->anim->maximumEasingTime; +} + +void QDeclarative1SmoothedAnimation::setMaximumEasingTime(int v) +{ + Q_D(QDeclarative1SmoothedAnimation); + if(v == d->anim->maximumEasingTime) + return; + d->anim->maximumEasingTime = v; + emit maximumEasingTimeChanged(); + d->updateRunningAnimations(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativesmoothedanimation_p.h b/src/qtquick1/util/qdeclarativesmoothedanimation_p.h new file mode 100644 index 0000000000..89946179da --- /dev/null +++ b/src/qtquick1/util/qdeclarativesmoothedanimation_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_H +#define QDECLARATIVESMOOTHEDANIMATION_H + +#include <QtDeclarative/qdeclarative.h> +#include "QtQuick1/private/qdeclarativeanimation_p.h" + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeProperty; + +QT_MODULE(Declarative) + +class QDeclarative1SmoothedAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1SmoothedAnimation : public QDeclarative1NumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1SmoothedAnimation) + Q_ENUMS(ReversingMode) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity NOTIFY velocityChanged) + Q_PROPERTY(ReversingMode reversingMode READ reversingMode WRITE setReversingMode NOTIFY reversingModeChanged) + Q_PROPERTY(qreal maximumEasingTime READ maximumEasingTime WRITE setMaximumEasingTime NOTIFY maximumEasingTimeChanged) + +public: + enum ReversingMode { Eased, Immediate, Sync }; + + QDeclarative1SmoothedAnimation(QObject *parent = 0); + ~QDeclarative1SmoothedAnimation(); + + ReversingMode reversingMode() const; + void setReversingMode(ReversingMode); + + virtual int duration() const; + virtual void setDuration(int); + + qreal velocity() const; + void setVelocity(qreal); + + int maximumEasingTime() const; + void setMaximumEasingTime(int); + + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + QAbstractAnimation* qtAnimation(); + +Q_SIGNALS: + void velocityChanged(); + void reversingModeChanged(); + void maximumEasingTimeChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1SmoothedAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESMOOTHEDANIMATION_H diff --git a/src/qtquick1/util/qdeclarativesmoothedanimation_p_p.h b/src/qtquick1/util/qdeclarativesmoothedanimation_p_p.h new file mode 100644 index 0000000000..b31a931873 --- /dev/null +++ b/src/qtquick1/util/qdeclarativesmoothedanimation_p_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESMOOTHEDANIMATION_P_H +#define QDECLARATIVESMOOTHEDANIMATION_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 "QtQuick1/private/qdeclarativesmoothedanimation_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p.h" + +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" + +#include <qparallelanimationgroup.h> + +#include <QtCore/private/qobject_p.h> +#include <QTimer> + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QSmoothedAnimation_1 : public QAbstractAnimation +{ +public: + QSmoothedAnimation_1(QObject *parent=0); + + qreal to; + qreal velocity; + int userDuration; + + int maximumEasingTime; + QDeclarative1SmoothedAnimation::ReversingMode reversingMode; + + qreal initialVelocity; + qreal trackVelocity; + + QDeclarativeProperty target; + + int duration() const; + void restart(); + void init(); + +protected: + virtual void updateCurrentTime(int); + virtual void updateState(QAbstractAnimation::State, QAbstractAnimation::State); + +private: + qreal easeFollow(qreal); + qreal initialValue; + + bool invert; + + int finalDuration; + + // Parameters for use in updateCurrentTime() + qreal a; // Acceleration + qreal d; // Deceleration + qreal tf; // Total time + qreal tp; // Time at which peak velocity occurs + qreal td; // Time at which decelleration begins + qreal vp; // Velocity at tp + qreal sp; // Displacement at tp + qreal sd; // Displacement at td + qreal vi; // "Normalized" initialvelocity + qreal s; // Total s + + int lastTime; + + bool recalc(); + void delayedStop(); + + QTimer delayedStopTimer; +}; + +class QDeclarative1SmoothedAnimationPrivate : public QDeclarative1PropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1SmoothedAnimation) +public: + QDeclarative1SmoothedAnimationPrivate(); + void updateRunningAnimations(); + + QParallelAnimationGroup *wrapperGroup; + QSmoothedAnimation_1 *anim; + QHash<QDeclarativeProperty, QSmoothedAnimation_1*> activeAnimations; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESMOOTHEDANIMATION_P_H diff --git a/src/qtquick1/util/qdeclarativespringanimation.cpp b/src/qtquick1/util/qdeclarativespringanimation.cpp new file mode 100644 index 0000000000..8afa5e7840 --- /dev/null +++ b/src/qtquick1/util/qdeclarativespringanimation.cpp @@ -0,0 +1,466 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativespringanimation_p.h" + +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" +#include <QtDeclarative/private/qdeclarativeproperty_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +#include <limits.h> +#include <math.h> + +QT_BEGIN_NAMESPACE + + + + +class QDeclarative1SpringAnimationPrivate : public QDeclarative1PropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1SpringAnimation) +public: + + + struct SpringAnimation { + SpringAnimation() + : currentValue(0), to(0), velocity(0), start(0), duration(0) {} + qreal currentValue; + qreal to; + qreal velocity; + int start; + int duration; + }; + QHash<QDeclarativeProperty, SpringAnimation> activeAnimations; + + qreal maxVelocity; + qreal velocityms; + int lastTime; + qreal mass; + qreal spring; + qreal damping; + qreal epsilon; + qreal modulus; + + bool useMass : 1; + bool haveModulus : 1; + + enum Mode { + Track, + Velocity, + Spring + }; + Mode mode; + + QDeclarative1SpringAnimationPrivate() + : maxVelocity(0), velocityms(0), lastTime(0) + , mass(1.0), spring(0.), damping(0.), epsilon(0.01) + , modulus(0.0), useMass(false), haveModulus(false) + , mode(Track), clock(0) + { } + + void tick(int time); + bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed); + void updateMode(); + + typedef QTickAnimationProxy_1<QDeclarative1SpringAnimationPrivate, &QDeclarative1SpringAnimationPrivate::tick> Clock; + Clock *clock; +}; + +void QDeclarative1SpringAnimationPrivate::tick(int time) +{ + if (mode == Track) { + clock->stop(); + return; + } + int elapsed = time - lastTime; + if (!elapsed) + return; + + if (mode == Spring) { + if (elapsed < 16) // capped at 62fps. + return; + int count = elapsed / 16; + lastTime = time - (elapsed - count * 16); + } else { + lastTime = time; + } + + QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations); + while (it.hasNext()) { + it.next(); + if (animate(it.key(), it.value(), elapsed)) + it.remove(); + } + + if (activeAnimations.isEmpty()) + clock->stop(); +} + +bool QDeclarative1SpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed) +{ + qreal srcVal = animation.to; + + bool stop = false; + + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + srcVal = fmod(srcVal, modulus); + } + if (mode == Spring) { + // Real men solve the spring DEs using RK4. + // We'll do something much simpler which gives a result that looks fine. + int count = elapsed / 16; + for (int i = 0; i < count; ++i) { + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (useMass) + animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass; + else + animation.velocity = animation.velocity + spring * diff - damping * animation.velocity; + if (maxVelocity > 0.) { + // limit velocity + if (animation.velocity > maxVelocity) + animation.velocity = maxVelocity; + else if (animation.velocity < -maxVelocity) + animation.velocity = -maxVelocity; + } + animation.currentValue += animation.velocity * 16.0 / 1000.0; + if (haveModulus) { + animation.currentValue = fmod(animation.currentValue, modulus); + if (animation.currentValue < 0.0) + animation.currentValue += modulus; + } + } + if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) { + animation.velocity = 0.0; + animation.currentValue = srcVal; + stop = true; + } + } else { + qreal moveBy = elapsed * velocityms; + qreal diff = srcVal - animation.currentValue; + if (haveModulus && qAbs(diff) > modulus / 2) { + if (diff < 0) + diff += modulus; + else + diff -= modulus; + } + if (diff > 0) { + animation.currentValue += moveBy; + if (haveModulus) + animation.currentValue = fmod(animation.currentValue, modulus); + } else { + animation.currentValue -= moveBy; + if (haveModulus && animation.currentValue < 0.0) + animation.currentValue = fmod(animation.currentValue, modulus) + modulus; + } + if (lastTime - animation.start >= animation.duration) { + animation.currentValue = animation.to; + stop = true; + } + } + + qreal old_to = animation.to; + + QDeclarativePropertyPrivate::write(property, animation.currentValue, + QDeclarativePropertyPrivate::BypassInterceptor | + QDeclarativePropertyPrivate::DontRemoveBinding); + + return (stop && old_to == animation.to); // do not stop if we got restarted +} + +void QDeclarative1SpringAnimationPrivate::updateMode() +{ + if (spring == 0. && maxVelocity == 0.) + mode = Track; + else if (spring > 0.) + mode = Spring; + else { + mode = Velocity; + QHash<QDeclarativeProperty, SpringAnimation>::iterator it; + for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) { + SpringAnimation &animation = *it; + animation.start = lastTime; + qreal dist = qAbs(animation.currentValue - animation.to); + if (haveModulus && dist > modulus / 2) + dist = modulus - fmod(dist, modulus); + animation.duration = dist / velocityms; + } + } +} + +/*! + \qmlclass SpringAnimation QDeclarative1SpringAnimation + \ingroup qml-animation-transition + \inherits NumberAnimation + \since 4.7 + + \brief The SpringAnimation element allows a property to track a value in a spring-like motion. + + SpringAnimation mimics the oscillatory behavior of a spring, with the appropriate \l spring constant to + control the acceleration and the \l damping to control how quickly the effect dies away. + + You can also limit the maximum \l velocity of the animation. + + The following \l Rectangle moves to the position of the mouse using a + SpringAnimation when the mouse is clicked. The use of the \l Behavior + on the \c x and \c y values indicates that whenever these values are + changed, a SpringAnimation should be applied. + + \snippet doc/src/snippets/declarative/springanimation.qml 0 + + Like any other animation element, a SpringAnimation can be applied in a + number of ways, including transitions, behaviors and property value + sources. The \l {QML Animation and Transitions} documentation shows a + variety of methods for creating animations. + + \sa SmoothedAnimation, {QML Animation and Transitions}, {declarative/animation/basics}{Animation basics example}, {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarative1SpringAnimation::QDeclarative1SpringAnimation(QObject *parent) +: QDeclarative1NumberAnimation(*(new QDeclarative1SpringAnimationPrivate),parent) +{ + Q_D(QDeclarative1SpringAnimation); + d->clock = new QDeclarative1SpringAnimationPrivate::Clock(d, this); +} + +QDeclarative1SpringAnimation::~QDeclarative1SpringAnimation() +{ +} + +/*! + \qmlproperty real SpringAnimation::velocity + + This property holds the maximum velocity allowed when tracking the source. + + The default value is 0 (no maximum velocity). +*/ + +qreal QDeclarative1SpringAnimation::velocity() const +{ + Q_D(const QDeclarative1SpringAnimation); + return d->maxVelocity; +} + +void QDeclarative1SpringAnimation::setVelocity(qreal velocity) +{ + Q_D(QDeclarative1SpringAnimation); + d->maxVelocity = velocity; + d->velocityms = velocity / 1000.0; + d->updateMode(); +} + +/*! + \qmlproperty real SpringAnimation::spring + + This property describes how strongly the target is pulled towards the + source. The default value is 0 (that is, the spring-like motion is disabled). + + The useful value range is 0 - 5.0. + + When this property is set and the \l velocity value is greater than 0, + the \l velocity limits the maximum speed. +*/ +qreal QDeclarative1SpringAnimation::spring() const +{ + Q_D(const QDeclarative1SpringAnimation); + return d->spring; +} + +void QDeclarative1SpringAnimation::setSpring(qreal spring) +{ + Q_D(QDeclarative1SpringAnimation); + d->spring = spring; + d->updateMode(); +} + +/*! + \qmlproperty real SpringAnimation::damping + This property holds the spring damping value. + + This value describes how quickly the spring-like motion comes to rest. + The default value is 0. + + The useful value range is 0 - 1.0. The lower the value, the faster it + comes to rest. +*/ +qreal QDeclarative1SpringAnimation::damping() const +{ + Q_D(const QDeclarative1SpringAnimation); + return d->damping; +} + +void QDeclarative1SpringAnimation::setDamping(qreal damping) +{ + Q_D(QDeclarative1SpringAnimation); + if (damping > 1.) + damping = 1.; + + d->damping = damping; +} + + +/*! + \qmlproperty real SpringAnimation::epsilon + This property holds the spring epsilon. + + The epsilon is the rate and amount of change in the value which is close enough + to 0 to be considered equal to zero. This will depend on the usage of the value. + For pixel positions, 0.25 would suffice. For scale, 0.005 will suffice. + + The default is 0.01. Tuning this value can provide small performance improvements. +*/ +qreal QDeclarative1SpringAnimation::epsilon() const +{ + Q_D(const QDeclarative1SpringAnimation); + return d->epsilon; +} + +void QDeclarative1SpringAnimation::setEpsilon(qreal epsilon) +{ + Q_D(QDeclarative1SpringAnimation); + d->epsilon = epsilon; +} + +/*! + \qmlproperty real SpringAnimation::modulus + This property holds the modulus value. The default value is 0. + + Setting a \a modulus forces the target value to "wrap around" at the modulus. + For example, setting the modulus to 360 will cause a value of 370 to wrap around to 10. +*/ +qreal QDeclarative1SpringAnimation::modulus() const +{ + Q_D(const QDeclarative1SpringAnimation); + return d->modulus; +} + +void QDeclarative1SpringAnimation::setModulus(qreal modulus) +{ + Q_D(QDeclarative1SpringAnimation); + if (d->modulus != modulus) { + d->haveModulus = modulus != 0.0; + d->modulus = modulus; + d->updateMode(); + emit modulusChanged(); + } +} + +/*! + \qmlproperty real SpringAnimation::mass + This property holds the "mass" of the property being moved. + + The value is 1.0 by default. + + A greater mass causes slower movement and a greater spring-like + motion when an item comes to rest. +*/ +qreal QDeclarative1SpringAnimation::mass() const +{ + Q_D(const QDeclarative1SpringAnimation); + return d->mass; +} + +void QDeclarative1SpringAnimation::setMass(qreal mass) +{ + Q_D(QDeclarative1SpringAnimation); + if (d->mass != mass && mass > 0.0) { + d->useMass = mass != 1.0; + d->mass = mass; + emit massChanged(); + } +} + +void QDeclarative1SpringAnimation::transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction) +{ + Q_D(QDeclarative1SpringAnimation); + Q_UNUSED(direction); + + if (d->clock->state() != QAbstractAnimation::Running) { + d->lastTime = 0; + } + + QDeclarative1NumberAnimation::transition(actions, modified, direction); + + if (!d->actions) + return; + + if (!d->actions->isEmpty()) { + for (int i = 0; i < d->actions->size(); ++i) { + const QDeclarativeProperty &property = d->actions->at(i).property; + QDeclarative1SpringAnimationPrivate::SpringAnimation &animation + = d->activeAnimations[property]; + animation.to = d->actions->at(i).toValue.toReal(); + animation.start = d->lastTime; + if (d->fromIsDefined) + animation.currentValue = d->actions->at(i).fromValue.toReal(); + else + animation.currentValue = property.read().toReal(); + if (d->mode == QDeclarative1SpringAnimationPrivate::Velocity) { + qreal dist = qAbs(animation.currentValue - animation.to); + if (d->haveModulus && dist > d->modulus / 2) + dist = d->modulus - fmod(dist, d->modulus); + animation.duration = dist / d->velocityms; + } + } + } +} + + +QAbstractAnimation *QDeclarative1SpringAnimation::qtAnimation() +{ + Q_D(QDeclarative1SpringAnimation); + return d->clock; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativespringanimation_p.h b/src/qtquick1/util/qdeclarativespringanimation_p.h new file mode 100644 index 0000000000..2eb3f3772c --- /dev/null +++ b/src/qtquick1/util/qdeclarativespringanimation_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESPRINGANIMATION_H +#define QDECLARATIVESPRINGANIMATION_H + +#include <QtDeclarative/qdeclarative.h> +#include "QtQuick1/private/qdeclarativeanimation_p.h" + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1SpringAnimationPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1SpringAnimation : public QDeclarative1NumberAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1SpringAnimation) + Q_INTERFACES(QDeclarativePropertyValueSource) + + Q_PROPERTY(qreal velocity READ velocity WRITE setVelocity) + Q_PROPERTY(qreal spring READ spring WRITE setSpring) + Q_PROPERTY(qreal damping READ damping WRITE setDamping) + Q_PROPERTY(qreal epsilon READ epsilon WRITE setEpsilon) + Q_PROPERTY(qreal modulus READ modulus WRITE setModulus NOTIFY modulusChanged) + Q_PROPERTY(qreal mass READ mass WRITE setMass NOTIFY massChanged) + +public: + QDeclarative1SpringAnimation(QObject *parent=0); + ~QDeclarative1SpringAnimation(); + + qreal velocity() const; + void setVelocity(qreal velocity); + + qreal spring() const; + void setSpring(qreal spring); + + qreal damping() const; + void setDamping(qreal damping); + + qreal epsilon() const; + void setEpsilon(qreal epsilon); + + qreal mass() const; + void setMass(qreal modulus); + + qreal modulus() const; + void setModulus(qreal modulus); + + virtual void transition(QDeclarative1StateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); + +protected: + virtual QAbstractAnimation *qtAnimation(); + +Q_SIGNALS: + void modulusChanged(); + void massChanged(); + void syncChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1SpringAnimation) + +QT_END_HEADER + +#endif // QDECLARATIVESPRINGANIMATION_H diff --git a/src/qtquick1/util/qdeclarativestate.cpp b/src/qtquick1/util/qdeclarativestate.cpp new file mode 100644 index 0000000000..de6fcd0d86 --- /dev/null +++ b/src/qtquick1/util/qdeclarativestate.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativestate_p_p.h" +#include "QtQuick1/private/qdeclarativestate_p.h" + +#include "QtQuick1/private/qdeclarativetransition_p.h" +#include "QtQuick1/private/qdeclarativestategroup_p.h" +#include "QtQuick1/private/qdeclarativestateoperations_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" + +#include <QtDeclarative/private/qdeclarativebinding_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + + + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +QDeclarative1Action::QDeclarative1Action() +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0), + specifiedObject(0) +{ +} + +QDeclarative1Action::QDeclarative1Action(QObject *target, const QString &propertyName, + const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, qmlEngine(target)), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + +QDeclarative1Action::QDeclarative1Action(QObject *target, const QString &propertyName, + QDeclarativeContext *context, const QVariant &value) +: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), + property(target, propertyName, context), toValue(value), + fromBinding(0), event(0), + specifiedObject(target), specifiedProperty(propertyName) +{ + if (property.isValid()) + fromValue = property.read(); +} + + +QDeclarative1ActionEvent::~QDeclarative1ActionEvent() +{ +} + +QString QDeclarative1ActionEvent::typeName() const +{ + return QString(); +} + +void QDeclarative1ActionEvent::execute(Reason) +{ +} + +bool QDeclarative1ActionEvent::isReversable() +{ + return false; +} + +void QDeclarative1ActionEvent::reverse(Reason) +{ +} + +bool QDeclarative1ActionEvent::changesBindings() +{ + return false; +} + +void QDeclarative1ActionEvent::clearBindings() +{ +} + +bool QDeclarative1ActionEvent::override(QDeclarative1ActionEvent *other) +{ + Q_UNUSED(other); + return false; +} + +QDeclarative1StateOperation::QDeclarative1StateOperation(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + \qmlclass State QDeclarative1State + \ingroup qml-state-elements + \since 4.7 + \brief The State element defines configurations of objects and properties. + + A \e state is a set of batched changes from the default configuration. + + All items have a default state that defines the default configuration of objects + and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to + allow items to switch between different configurations. These configurations + can, for example, be used to apply different sets of property values or execute + different scripts. + + The following example displays a single \l Rectangle. In the default state, the rectangle + is colored black. In the "clicked" state, a PropertyChanges element changes the + rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state + between the default state and the "clicked" state, thus toggling the color of the + rectangle between black and red. + + \snippet doc/src/snippets/declarative/state.qml 0 + + Notice the default state is referred to using an empty string (""). + + States are commonly used together with \l{QML Animation and Transitions}{Transitions} to provide + animations when state changes occur. + + \note Setting the state of an object from within another state of the same object is + not allowed. + + \sa {declarative/animation/states}{states example}, {qmlstates}{States}, + {QML Animation and Transitions}{Transitions}, QtDeclarative +*/ +QDeclarative1State::QDeclarative1State(QObject *parent) +: QObject(*(new QDeclarative1StatePrivate), parent) +{ + Q_D(QDeclarative1State); + d->transitionManager.setState(this); +} + +QDeclarative1State::~QDeclarative1State() +{ + Q_D(QDeclarative1State); + if (d->group) + d->group->removeState(this); +} + +/*! + \qmlproperty string State::name + This property holds the name of the state. + + Each state should have a unique name within its item. +*/ +QString QDeclarative1State::name() const +{ + Q_D(const QDeclarative1State); + return d->name; +} + +void QDeclarative1State::setName(const QString &n) +{ + Q_D(QDeclarative1State); + d->name = n; + d->named = true; +} + +bool QDeclarative1State::isNamed() const +{ + Q_D(const QDeclarative1State); + return d->named; +} + +bool QDeclarative1State::isWhenKnown() const +{ + Q_D(const QDeclarative1State); + return d->when != 0; +} + +/*! + \qmlproperty bool State::when + This property holds when the state should be applied. + + This should be set to an expression that evaluates to \c true when you want the state to + be applied. For example, the following \l Rectangle changes in and out of the "hidden" + state when the \l MouseArea is pressed: + + \snippet doc/src/snippets/declarative/state-when.qml 0 + + If multiple states in a group have \c when clauses that evaluate to \c true + at the same time, the first matching state will be applied. For example, in + the following snippet \c state1 will always be selected rather than + \c state2 when sharedCondition becomes \c true. + \qml + Item { + states: [ + State { name: "state1"; when: sharedCondition }, + State { name: "state2"; when: sharedCondition } + ] + // ... + } + \endqml +*/ +QDeclarativeBinding *QDeclarative1State::when() const +{ + Q_D(const QDeclarative1State); + return d->when; +} + +void QDeclarative1State::setWhen(QDeclarativeBinding *when) +{ + Q_D(QDeclarative1State); + d->when = when; + if (d->group) + d->group->updateAutoState(); +} + +/*! + \qmlproperty string State::extend + This property holds the state that this state extends. + + When a state extends another state, it inherits all the changes of that state. + + The state being extended is treated as the base state in regards to + the changes specified by the extending state. +*/ +QString QDeclarative1State::extends() const +{ + Q_D(const QDeclarative1State); + return d->extends; +} + +void QDeclarative1State::setExtends(const QString &extends) +{ + Q_D(QDeclarative1State); + d->extends = extends; +} + +/*! + \qmlproperty list<Change> State::changes + This property holds the changes to apply for this state + \default + + By default these changes are applied against the default state. If the state + extends another state, then the changes are applied against the state being + extended. +*/ +QDeclarativeListProperty<QDeclarative1StateOperation> QDeclarative1State::changes() +{ + Q_D(QDeclarative1State); + return QDeclarativeListProperty<QDeclarative1StateOperation>(this, &d->operations, QDeclarative1StatePrivate::operations_append, + QDeclarative1StatePrivate::operations_count, QDeclarative1StatePrivate::operations_at, + QDeclarative1StatePrivate::operations_clear); +} + +int QDeclarative1State::operationCount() const +{ + Q_D(const QDeclarative1State); + return d->operations.count(); +} + +QDeclarative1StateOperation *QDeclarative1State::operationAt(int index) const +{ + Q_D(const QDeclarative1State); + return d->operations.at(index); +} + +QDeclarative1State &QDeclarative1State::operator<<(QDeclarative1StateOperation *op) +{ + Q_D(QDeclarative1State); + d->operations.append(QDeclarative1StatePrivate::OperationGuard(op, &d->operations)); + return *this; +} + +void QDeclarative1StatePrivate::complete() +{ + Q_Q(QDeclarative1State); + + for (int ii = 0; ii < reverting.count(); ++ii) { + for (int jj = 0; jj < revertList.count(); ++jj) { + if (revertList.at(jj).property() == reverting.at(ii)) { + revertList.removeAt(jj); + break; + } + } + } + reverting.clear(); + + emit q->completed(); +} + +// Generate a list of actions for this state. This includes coelescing state +// actions that this state "extends" +QDeclarative1StateOperation::ActionList +QDeclarative1StatePrivate::generateActionList(QDeclarative1StateGroup *group) const +{ + QDeclarative1StateOperation::ActionList applyList; + if (inState) + return applyList; + + // Prevent "extends" recursion + inState = true; + + if (!extends.isEmpty()) { + QList<QDeclarative1State *> states = group->states(); + for (int ii = 0; ii < states.count(); ++ii) + if (states.at(ii)->name() == extends) { + qmlExecuteDeferred(states.at(ii)); + applyList = static_cast<QDeclarative1StatePrivate*>(states.at(ii)->d_func())->generateActionList(group); + } + } + + foreach(QDeclarative1StateOperation *op, operations) + applyList << op->actions(); + + inState = false; + return applyList; +} + +QDeclarative1StateGroup *QDeclarative1State::stateGroup() const +{ + Q_D(const QDeclarative1State); + return d->group; +} + +void QDeclarative1State::setStateGroup(QDeclarative1StateGroup *group) +{ + Q_D(QDeclarative1State); + d->group = group; +} + +void QDeclarative1State::cancel() +{ + Q_D(QDeclarative1State); + d->transitionManager.cancel(); +} + +void QDeclarative1Action::deleteFromBinding() +{ + if (fromBinding) { + QDeclarativePropertyPrivate::setBinding(property, 0); + fromBinding->destroy(); + fromBinding = 0; + } +} + +bool QDeclarative1State::containsPropertyInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarative1State); + + if (isStateActive()) { + QListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return true; + } + } + + return false; +} + +bool QDeclarative1State::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue) +{ + Q_D(QDeclarative1State); + + if (isStateActive()) { + QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + simpleAction.setValue(revertValue); + return true; + } + } + } + + return false; +} + +bool QDeclarative1State::changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding) +{ + Q_D(QDeclarative1State); + + if (isStateActive()) { + QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) { + if (simpleAction.binding()) + simpleAction.binding()->destroy(); + + simpleAction.setBinding(binding); + return true; + } + } + } + + return false; +} + +bool QDeclarative1State::removeEntryFromRevertList(QObject *target, const QString &name) +{ + Q_D(QDeclarative1State); + + if (isStateActive()) { + QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target && simpleAction.property().name() == name) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + return true; + } + } + } + + return false; +} + +void QDeclarative1State::addEntryToRevertList(const QDeclarative1Action &action) +{ + Q_D(QDeclarative1State); + + QDeclarative1SimpleAction simpleAction(action); + + d->revertList.append(simpleAction); +} + +void QDeclarative1State::removeAllEntriesFromRevertList(QObject *target) +{ + Q_D(QDeclarative1State); + + if (isStateActive()) { + QMutableListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.property().object() == target) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) { + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + oldBinding->destroy(); + } + + simpleAction.property().write(simpleAction.value()); + if (simpleAction.binding()) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding()); + + revertListIterator.remove(); + } + } + } +} + +void QDeclarative1State::addEntriesToRevertList(const QList<QDeclarative1Action> &actionList) +{ + Q_D(QDeclarative1State); + if (isStateActive()) { + QList<QDeclarative1SimpleAction> simpleActionList; + + QListIterator<QDeclarative1Action> actionListIterator(actionList); + while(actionListIterator.hasNext()) { + const QDeclarative1Action &action = actionListIterator.next(); + QDeclarative1SimpleAction simpleAction(action); + action.property.write(action.toValue); + if (!action.toBinding.isNull()) { + QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property()); + if (oldBinding) + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0); + QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding); + } + + simpleActionList.append(simpleAction); + } + + d->revertList.append(simpleActionList); + } +} + +QVariant QDeclarative1State::valueInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarative1State); + + if (isStateActive()) { + QListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.value(); + } + } + + return QVariant(); +} + +QDeclarativeAbstractBinding *QDeclarative1State::bindingInRevertList(QObject *target, const QString &name) const +{ + Q_D(const QDeclarative1State); + + if (isStateActive()) { + QListIterator<QDeclarative1SimpleAction> revertListIterator(d->revertList); + + while (revertListIterator.hasNext()) { + const QDeclarative1SimpleAction &simpleAction = revertListIterator.next(); + if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) + return simpleAction.binding(); + } + } + + return 0; +} + +bool QDeclarative1State::isStateActive() const +{ + return stateGroup() && stateGroup()->state() == name(); +} + +void QDeclarative1State::apply(QDeclarative1StateGroup *group, QDeclarative1Transition *trans, QDeclarative1State *revert) +{ + Q_D(QDeclarative1State); + + qmlExecuteDeferred(this); + + cancel(); + if (revert) + revert->cancel(); + d->revertList.clear(); + d->reverting.clear(); + + if (revert) { + QDeclarative1StatePrivate *revertPrivate = + static_cast<QDeclarative1StatePrivate*>(revert->d_func()); + d->revertList = revertPrivate->revertList; + revertPrivate->revertList.clear(); + } + + // List of actions caused by this state + QDeclarative1StateOperation::ActionList applyList = d->generateActionList(group); + + // List of actions that need to be reverted to roll back (just) this state + QDeclarative1StatePrivate::SimpleActionList additionalReverts; + // First add the reverse of all the applyList actions + for (int ii = 0; ii < applyList.count(); ++ii) { + QDeclarative1Action &action = applyList[ii]; + + if (action.event) { + if (!action.event->isReversable()) + continue; + bool found = false; + for (int jj = 0; jj < d->revertList.count(); ++jj) { + QDeclarative1ActionEvent *event = d->revertList.at(jj).event(); + if (event && event->typeName() == action.event->typeName()) { + if (action.event->override(event)) { + found = true; + + if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) { + action.event->copyOriginals(d->revertList.at(jj).event()); + + QDeclarative1SimpleAction r(action); + additionalReverts << r; + d->revertList.removeAt(jj); + --jj; + } else if (action.event->isRewindable()) //###why needed? + action.event->saveCurrentValues(); + + break; + } + } + } + if (!found) { + action.event->saveOriginals(); + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarative1SimpleAction r(action); + additionalReverts << r; + } + } else { + bool found = false; + action.fromBinding = QDeclarativePropertyPrivate::binding(action.property); + + for (int jj = 0; jj < d->revertList.count(); ++jj) { + if (d->revertList.at(jj).property() == action.property) { + found = true; + if (d->revertList.at(jj).binding() != action.fromBinding) { + action.deleteFromBinding(); + } + break; + } + } + + if (!found) { + if (!action.restore) { + action.deleteFromBinding();; + } else { + // Only need to revert the applyList action if the previous + // state doesn't have a higher priority revert already + QDeclarative1SimpleAction r(action); + additionalReverts << r; + } + } + } + } + + // Any reverts from a previous state that aren't carried forth + // into this state need to be translated into apply actions + for (int ii = 0; ii < d->revertList.count(); ++ii) { + bool found = false; + if (d->revertList.at(ii).event()) { + QDeclarative1ActionEvent *event = d->revertList.at(ii).event(); + if (!event->isReversable()) + continue; + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarative1Action &action = applyList.at(jj); + if (action.event && action.event->typeName() == event->typeName()) { + if (action.event->override(event)) + found = true; + } + } + } else { + for (int jj = 0; !found && jj < applyList.count(); ++jj) { + const QDeclarative1Action &action = applyList.at(jj); + if (action.property == d->revertList.at(ii).property()) + found = true; + } + } + if (!found) { + QVariant cur = d->revertList.at(ii).property().read(); + QDeclarativeAbstractBinding *delBinding = + QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0); + if (delBinding) + delBinding->destroy(); + + QDeclarative1Action a; + a.property = d->revertList.at(ii).property(); + a.fromValue = cur; + a.toValue = d->revertList.at(ii).value(); + a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding()); + a.specifiedObject = d->revertList.at(ii).specifiedObject(); + a.specifiedProperty = d->revertList.at(ii).specifiedProperty(); + a.event = d->revertList.at(ii).event(); + a.reverseEvent = d->revertList.at(ii).reverseEvent(); + if (a.event && a.event->isRewindable()) + a.event->saveCurrentValues(); + applyList << a; + // Store these special reverts in the reverting list + d->reverting << d->revertList.at(ii).property(); + } + } + // All the local reverts now become part of the ongoing revertList + d->revertList << additionalReverts; + +#ifndef QT_NO_DEBUG_STREAM + // Output for debugging + if (stateChangeDebug()) { + foreach(const QDeclarative1Action &action, applyList) { + if (action.event) + qWarning() << " QDeclarative1Action event:" << action.event->typeName(); + else + qWarning() << " QDeclarative1Action:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + + d->transitionManager.transition(applyList, trans); +} + +QDeclarative1StateOperation::ActionList QDeclarative1StateOperation::actions() +{ + return ActionList(); +} + +QDeclarative1State *QDeclarative1StateOperation::state() const +{ + Q_D(const QDeclarative1StateOperation); + return d->m_state; +} + +void QDeclarative1StateOperation::setState(QDeclarative1State *state) +{ + Q_D(QDeclarative1StateOperation); + d->m_state = state; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativestate_p.h b/src/qtquick1/util/qdeclarativestate_p.h new file mode 100644 index 0000000000..fcfa2efead --- /dev/null +++ b/src/qtquick1/util/qdeclarativestate_p.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_H +#define QDECLARATIVESTATE_H + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativeproperty.h> +#include <QtCore/qobject.h> +#include <QtCore/qsharedpointer.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QDeclarativeAbstractBinding; +class QDeclarativeBinding; +class QDeclarativeExpression; + +QT_MODULE(Declarative) + +class QDeclarative1ActionEvent; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Action +{ +public: + QDeclarative1Action(); + QDeclarative1Action(QObject *, const QString &, const QVariant &); + QDeclarative1Action(QObject *, const QString &, + QDeclarativeContext *, const QVariant &); + + bool restore:1; + bool actionDone:1; + bool reverseEvent:1; + bool deletableToBinding:1; + + QDeclarativeProperty property; + QVariant fromValue; + QVariant toValue; + + QDeclarativeAbstractBinding *fromBinding; + QWeakPointer<QDeclarativeAbstractBinding> toBinding; + QDeclarative1ActionEvent *event; + + //strictly for matching + QObject *specifiedObject; + QString specifiedProperty; + + void deleteFromBinding(); +}; + +class Q_AUTOTEST_EXPORT QDeclarative1ActionEvent +{ +public: + virtual ~QDeclarative1ActionEvent(); + virtual QString typeName() const; + + enum Reason { ActualChange, FastForward }; + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual void saveOriginals() {} + virtual bool needsCopy() { return false; } + virtual void copyOriginals(QDeclarative1ActionEvent *) {} + + virtual bool isRewindable() { return isReversable(); } + virtual void rewind() {} + virtual void saveCurrentValues() {} + virtual void saveTargetValues() {} + + virtual bool changesBindings(); + virtual void clearBindings(); + virtual bool override(QDeclarative1ActionEvent*other); +}; + +//### rename to QDeclarative1StateChange? +class QDeclarative1StateGroup; +class QDeclarative1State; +class QDeclarative1StateOperationPrivate; +class Q_DECLARATIVE_EXPORT QDeclarative1StateOperation : public QObject +{ + Q_OBJECT +public: + QDeclarative1StateOperation(QObject *parent = 0) + : QObject(parent) {} + typedef QList<QDeclarative1Action> ActionList; + + virtual ActionList actions(); + + QDeclarative1State *state() const; + void setState(QDeclarative1State *state); + +protected: + QDeclarative1StateOperation(QObjectPrivate &dd, QObject *parent = 0); + +private: + Q_DECLARE_PRIVATE(QDeclarative1StateOperation) + Q_DISABLE_COPY(QDeclarative1StateOperation) +}; + +typedef QDeclarative1StateOperation::ActionList QDeclarative1StateActions; + +class QDeclarative1Transition; +class QDeclarative1StatePrivate; +class Q_DECLARATIVE_EXPORT QDeclarative1State : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName) + Q_PROPERTY(QDeclarativeBinding *when READ when WRITE setWhen) + Q_PROPERTY(QString extend READ extends WRITE setExtends) + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1StateOperation> changes READ changes) + Q_CLASSINFO("DefaultProperty", "changes") + Q_CLASSINFO("DeferredPropertyNames", "changes") + +public: + QDeclarative1State(QObject *parent=0); + virtual ~QDeclarative1State(); + + QString name() const; + void setName(const QString &); + bool isNamed() const; + + /*'when' is a QDeclarativeBinding to limit state changes oscillation + due to the unpredictable order of evaluation of bound expressions*/ + bool isWhenKnown() const; + QDeclarativeBinding *when() const; + void setWhen(QDeclarativeBinding *); + + QString extends() const; + void setExtends(const QString &); + + QDeclarativeListProperty<QDeclarative1StateOperation> changes(); + int operationCount() const; + QDeclarative1StateOperation *operationAt(int) const; + + QDeclarative1State &operator<<(QDeclarative1StateOperation *); + + void apply(QDeclarative1StateGroup *, QDeclarative1Transition *, QDeclarative1State *revert); + void cancel(); + + QDeclarative1StateGroup *stateGroup() const; + void setStateGroup(QDeclarative1StateGroup *); + + bool containsPropertyInRevertList(QObject *target, const QString &name) const; + bool changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue); + bool changeBindingInRevertList(QObject *target, const QString &name, QDeclarativeAbstractBinding *binding); + bool removeEntryFromRevertList(QObject *target, const QString &name); + void addEntryToRevertList(const QDeclarative1Action &action); + void removeAllEntriesFromRevertList(QObject *target); + void addEntriesToRevertList(const QList<QDeclarative1Action> &actions); + QVariant valueInRevertList(QObject *target, const QString &name) const; + QDeclarativeAbstractBinding *bindingInRevertList(QObject *target, const QString &name) const; + + bool isStateActive() const; + +Q_SIGNALS: + void completed(); + +private: + Q_DECLARE_PRIVATE(QDeclarative1State) + Q_DISABLE_COPY(QDeclarative1State) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1StateOperation) +QML_DECLARE_TYPE(QDeclarative1State) + +QT_END_HEADER + +#endif // QDECLARATIVESTATE_H diff --git a/src/qtquick1/util/qdeclarativestate_p_p.h b/src/qtquick1/util/qdeclarativestate_p_p.h new file mode 100644 index 0000000000..cf3fbd2299 --- /dev/null +++ b/src/qtquick1/util/qdeclarativestate_p_p.h @@ -0,0 +1,253 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATE_P_H +#define QDECLARATIVESTATE_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 "QtQuick1/private/qdeclarativestate_p.h" + +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" +#include "QtQuick1/private/qdeclarativetransitionmanager_p_p.h" + +#include <QtDeclarative/private/qdeclarativeproperty_p.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> + +#include <QtDeclarative/private/qdeclarativebinding_p.h> + +#include <QtCore/private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QDeclarative1SimpleAction +{ +public: + enum State { StartState, EndState }; + QDeclarative1SimpleAction(const QDeclarative1Action &a, State state = StartState) + { + m_property = a.property; + m_specifiedObject = a.specifiedObject; + m_specifiedProperty = a.specifiedProperty; + m_event = a.event; + if (state == StartState) { + m_value = a.fromValue; + if (QDeclarativePropertyPrivate::binding(m_property)) { + m_binding = QDeclarativeAbstractBinding::getPointer(QDeclarativePropertyPrivate::binding(m_property)); + } + m_reverseEvent = true; + } else { + m_value = a.toValue; + m_binding = a.toBinding; + m_reverseEvent = false; + } + } + + ~QDeclarative1SimpleAction() + { + } + + QDeclarative1SimpleAction(const QDeclarative1SimpleAction &other) + : m_property(other.m_property), + m_value(other.m_value), + m_binding(QDeclarativeAbstractBinding::getPointer(other.binding())), + m_specifiedObject(other.m_specifiedObject), + m_specifiedProperty(other.m_specifiedProperty), + m_event(other.m_event), + m_reverseEvent(other.m_reverseEvent) + { + } + + QDeclarative1SimpleAction &operator =(const QDeclarative1SimpleAction &other) + { + m_property = other.m_property; + m_value = other.m_value; + m_binding = QDeclarativeAbstractBinding::getPointer(other.binding()); + m_specifiedObject = other.m_specifiedObject; + m_specifiedProperty = other.m_specifiedProperty; + m_event = other.m_event; + m_reverseEvent = other.m_reverseEvent; + + return *this; + } + + void setProperty(const QDeclarativeProperty &property) + { + m_property = property; + } + + const QDeclarativeProperty &property() const + { + return m_property; + } + + void setValue(const QVariant &value) + { + m_value = value; + } + + const QVariant &value() const + { + return m_value; + } + + void setBinding(QDeclarativeAbstractBinding *binding) + { + m_binding = QDeclarativeAbstractBinding::getPointer(binding); + } + + QDeclarativeAbstractBinding *binding() const + { + return m_binding.data(); + } + + QObject *specifiedObject() const + { + return m_specifiedObject; + } + + const QString &specifiedProperty() const + { + return m_specifiedProperty; + } + + QDeclarative1ActionEvent *event() const + { + return m_event; + } + + bool reverseEvent() const + { + return m_reverseEvent; + } + +private: + QDeclarativeProperty m_property; + QVariant m_value; + QDeclarativeAbstractBinding::Pointer m_binding; + QObject *m_specifiedObject; + QString m_specifiedProperty; + QDeclarative1ActionEvent *m_event; + bool m_reverseEvent; +}; + +class QDeclarative1StateOperationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1StateOperation) + +public: + + QDeclarative1StateOperationPrivate() + : m_state(0) {} + + QDeclarative1State *m_state; +}; + +class QDeclarative1StatePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1State) + +public: + QDeclarative1StatePrivate() + : when(0), named(false), inState(false), group(0) {} + + typedef QList<QDeclarative1SimpleAction> SimpleActionList; + + QString name; + QDeclarativeBinding *when; + bool named; + + struct OperationGuard : public QDeclarativeGuard<QDeclarative1StateOperation> + { + OperationGuard(QObject *obj, QList<OperationGuard> *l) : list(l) { (QDeclarativeGuard<QObject>&)*this = obj; } + QList<OperationGuard> *list; + void objectDestroyed(QDeclarative1StateOperation *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + QList<OperationGuard> operations; + + static void operations_append(QDeclarativeListProperty<QDeclarative1StateOperation> *prop, QDeclarative1StateOperation *op) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + op->setState(qobject_cast<QDeclarative1State*>(prop->object)); + list->append(OperationGuard(op, list)); + } + static void operations_clear(QDeclarativeListProperty<QDeclarative1StateOperation> *prop) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + QMutableListIterator<OperationGuard> listIterator(*list); + while(listIterator.hasNext()) + listIterator.next()->setState(0); + list->clear(); + } + static int operations_count(QDeclarativeListProperty<QDeclarative1StateOperation> *prop) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + return list->count(); + } + static QDeclarative1StateOperation *operations_at(QDeclarativeListProperty<QDeclarative1StateOperation> *prop, int index) { + QList<OperationGuard> *list = static_cast<QList<OperationGuard> *>(prop->data); + return list->at(index); + } + + QDeclarative1TransitionManager transitionManager; + + SimpleActionList revertList; + QList<QDeclarativeProperty> reverting; + QString extends; + mutable bool inState; + QDeclarative1StateGroup *group; + + QDeclarative1StateOperation::ActionList generateActionList(QDeclarative1StateGroup *) const; + void complete(); +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESTATE_P_H diff --git a/src/qtquick1/util/qdeclarativestategroup.cpp b/src/qtquick1/util/qdeclarativestategroup.cpp new file mode 100644 index 0000000000..f04a8d4f03 --- /dev/null +++ b/src/qtquick1/util/qdeclarativestategroup.cpp @@ -0,0 +1,473 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativestategroup_p.h" + +#include "QtQuick1/private/qdeclarativetransition_p.h" +#include "QtQuick1/private/qdeclarativestate_p_p.h" + +#include <QtDeclarative/private/qdeclarativebinding_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +#include <QtCore/qstringbuilder.h> +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +QT_BEGIN_NAMESPACE + + + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarative1StateGroupPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1StateGroup) +public: + QDeclarative1StateGroupPrivate() + : nullState(0), componentComplete(true), + ignoreTrans(false), applyingState(false), unnamedCount(0) {} + + QString currentState; + QDeclarative1State *nullState; + + static void append_state(QDeclarativeListProperty<QDeclarative1State> *list, QDeclarative1State *state); + static int count_state(QDeclarativeListProperty<QDeclarative1State> *list); + static QDeclarative1State *at_state(QDeclarativeListProperty<QDeclarative1State> *list, int index); + static void clear_states(QDeclarativeListProperty<QDeclarative1State> *list); + + QList<QDeclarative1State *> states; + QList<QDeclarative1Transition *> transitions; + + bool componentComplete; + bool ignoreTrans; + bool applyingState; + int unnamedCount; + + QDeclarative1Transition *findTransition(const QString &from, const QString &to); + void setCurrentStateInternal(const QString &state, bool = false); + bool updateAutoState(); +}; + +/*! + \qmlclass StateGroup QDeclarative1StateGroup + \ingroup qml-state-elements + \since 4.7 + \brief The StateGroup element provides state support for non-Item elements. + + Item (and all derived elements) provides built in support for states and transitions + via its \l{Item::state}{state}, \l{Item::states}{states} and \l{Item::transitions}{transitions} properties. StateGroup provides an easy way to + use this support in other (non-Item-derived) elements. + + \qml + MyCustomObject { + StateGroup { + id: myStateGroup + states: State { + name: "state1" + // ... + } + transitions: Transition { + // ... + } + } + + onSomethingHappened: myStateGroup.state = "state1"; + } + \endqml + + \sa {qmlstate}{States} {QML Animation and Transitions}{Transitions}, {QtDeclarative} +*/ + +QDeclarative1StateGroup::QDeclarative1StateGroup(QObject *parent) + : QObject(*(new QDeclarative1StateGroupPrivate), parent) +{ +} + +QDeclarative1StateGroup::~QDeclarative1StateGroup() +{ + Q_D(const QDeclarative1StateGroup); + for (int i = 0; i < d->states.count(); ++i) + d->states.at(i)->setStateGroup(0); +} + +QList<QDeclarative1State *> QDeclarative1StateGroup::states() const +{ + Q_D(const QDeclarative1StateGroup); + return d->states; +} + +/*! + \qmlproperty list<State> StateGroup::states + This property holds a list of states defined by the state group. + + \qml + StateGroup { + states: [ + State { + // State definition... + }, + State { + // ... + } + // Other states... + ] + } + \endqml + + \sa {qmlstate}{States} +*/ +QDeclarativeListProperty<QDeclarative1State> QDeclarative1StateGroup::statesProperty() +{ + Q_D(QDeclarative1StateGroup); + return QDeclarativeListProperty<QDeclarative1State>(this, &d->states, &QDeclarative1StateGroupPrivate::append_state, + &QDeclarative1StateGroupPrivate::count_state, + &QDeclarative1StateGroupPrivate::at_state, + &QDeclarative1StateGroupPrivate::clear_states); +} + +void QDeclarative1StateGroupPrivate::append_state(QDeclarativeListProperty<QDeclarative1State> *list, QDeclarative1State *state) +{ + QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object); + if (state) { + _this->d_func()->states.append(state); + state->setStateGroup(_this); + } + +} + +int QDeclarative1StateGroupPrivate::count_state(QDeclarativeListProperty<QDeclarative1State> *list) +{ + QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object); + return _this->d_func()->states.count(); +} + +QDeclarative1State *QDeclarative1StateGroupPrivate::at_state(QDeclarativeListProperty<QDeclarative1State> *list, int index) +{ + QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object); + return _this->d_func()->states.at(index); +} + +void QDeclarative1StateGroupPrivate::clear_states(QDeclarativeListProperty<QDeclarative1State> *list) +{ + QDeclarative1StateGroup *_this = static_cast<QDeclarative1StateGroup *>(list->object); + _this->d_func()->setCurrentStateInternal(QString(), true); + for (int i = 0; i < _this->d_func()->states.count(); ++i) { + _this->d_func()->states.at(i)->setStateGroup(0); + } + _this->d_func()->states.clear(); +} + +/*! + \qmlproperty list<Transition> StateGroup::transitions + This property holds a list of transitions defined by the state group. + + \qml + StateGroup { + transitions: [ + Transition { + // ... + }, + Transition { + // ... + } + // ... + ] + } + \endqml + + \sa {QML Animation and Transitions}{Transitions} +*/ +QDeclarativeListProperty<QDeclarative1Transition> QDeclarative1StateGroup::transitionsProperty() +{ + Q_D(QDeclarative1StateGroup); + return QDeclarativeListProperty<QDeclarative1Transition>(this, d->transitions); +} + +/*! + \qmlproperty string StateGroup::state + + This property holds the name of the current state of the state group. + + This property is often used in scripts to change between states. For + example: + + \js + function toggle() { + if (button.state == 'On') + button.state = 'Off'; + else + button.state = 'On'; + } + \endjs + + If the state group is in its base state (i.e. no explicit state has been + set), \c state will be a blank string. Likewise, you can return a + state group to its base state by setting its current state to \c ''. + + \sa {qmlstates}{States} +*/ +QString QDeclarative1StateGroup::state() const +{ + Q_D(const QDeclarative1StateGroup); + return d->currentState; +} + +void QDeclarative1StateGroup::setState(const QString &state) +{ + Q_D(QDeclarative1StateGroup); + if (d->currentState == state) + return; + + d->setCurrentStateInternal(state); +} + +void QDeclarative1StateGroup::classBegin() +{ + Q_D(QDeclarative1StateGroup); + d->componentComplete = false; +} + +void QDeclarative1StateGroup::componentComplete() +{ + Q_D(QDeclarative1StateGroup); + d->componentComplete = true; + + for (int ii = 0; ii < d->states.count(); ++ii) { + QDeclarative1State *state = d->states.at(ii); + if (!state->isNamed()) + state->setName(QLatin1String("anonymousState") % QString::number(++d->unnamedCount)); + } + + if (d->updateAutoState()) { + return; + } else if (!d->currentState.isEmpty()) { + QString cs = d->currentState; + d->currentState.clear(); + d->setCurrentStateInternal(cs, true); + } +} + +/*! + Returns true if the state was changed, otherwise false. +*/ +bool QDeclarative1StateGroup::updateAutoState() +{ + Q_D(QDeclarative1StateGroup); + return d->updateAutoState(); +} + +bool QDeclarative1StateGroupPrivate::updateAutoState() +{ + Q_Q(QDeclarative1StateGroup); + if (!componentComplete) + return false; + + bool revert = false; + for (int ii = 0; ii < states.count(); ++ii) { + QDeclarative1State *state = states.at(ii); + if (state->isWhenKnown()) { + if (state->isNamed()) { + if (state->when() && state->when()->evaluate().toBool()) { + if (stateChangeDebug()) + qWarning() << "Setting auto state due to:" + << state->when()->expression(); + if (currentState != state->name()) { + q->setState(state->name()); + return true; + } else { + return false; + } + } else if (state->name() == currentState) { + revert = true; + } + } + } + } + if (revert) { + bool rv = !currentState.isEmpty(); + q->setState(QString()); + return rv; + } else { + return false; + } +} + +QDeclarative1Transition *QDeclarative1StateGroupPrivate::findTransition(const QString &from, const QString &to) +{ + QDeclarative1Transition *highest = 0; + int score = 0; + bool reversed = false; + bool done = false; + + for (int ii = 0; !done && ii < transitions.count(); ++ii) { + QDeclarative1Transition *t = transitions.at(ii); + for (int ii = 0; ii < 2; ++ii) + { + if (ii && (!t->reversible() || + (t->fromState() == QLatin1String("*") && + t->toState() == QLatin1String("*")))) + break; + QStringList fromState; + QStringList toState; + + fromState = t->fromState().split(QLatin1Char(',')); + toState = t->toState().split(QLatin1Char(',')); + if (ii == 1) + qSwap(fromState, toState); + int tScore = 0; + if (fromState.contains(from)) + tScore += 2; + else if (fromState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (toState.contains(to)) + tScore += 2; + else if (toState.contains(QLatin1String("*"))) + tScore += 1; + else + continue; + + if (ii == 1) + reversed = true; + else + reversed = false; + + if (tScore == 4) { + highest = t; + done = true; + break; + } else if (tScore > score) { + score = tScore; + highest = t; + } + } + } + + if (highest) + highest->setReversed(reversed); + + return highest; +} + +void QDeclarative1StateGroupPrivate::setCurrentStateInternal(const QString &state, + bool ignoreTrans) +{ + Q_Q(QDeclarative1StateGroup); + if (!componentComplete) { + currentState = state; + return; + } + + if (applyingState) { + qmlInfo(q) << "Can't apply a state change as part of a state definition."; + return; + } + + applyingState = true; + + QDeclarative1Transition *transition = (ignoreTrans || ignoreTrans) ? 0 : findTransition(currentState, state); + if (stateChangeDebug()) { + qWarning() << this << "Changing state. From" << currentState << ". To" << state; + if (transition) + qWarning() << " using transition" << transition->fromState() + << transition->toState(); + } + + QDeclarative1State *oldState = 0; + if (!currentState.isEmpty()) { + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + oldState = states.at(ii); + break; + } + } + } + + currentState = state; + emit q->stateChanged(currentState); + + QDeclarative1State *newState = 0; + for (int ii = 0; ii < states.count(); ++ii) { + if (states.at(ii)->name() == currentState) { + newState = states.at(ii); + break; + } + } + + if (oldState == 0 || newState == 0) { + if (!nullState) { nullState = new QDeclarative1State; QDeclarative_setParent_noEvent(nullState, q); } + if (!oldState) oldState = nullState; + if (!newState) newState = nullState; + } + + newState->apply(q, transition, oldState); + applyingState = false; + if (!transition) + static_cast<QDeclarative1StatePrivate*>(QObjectPrivate::get(newState))->complete(); +} + +QDeclarative1State *QDeclarative1StateGroup::findState(const QString &name) const +{ + Q_D(const QDeclarative1StateGroup); + for (int i = 0; i < d->states.count(); ++i) { + QDeclarative1State *state = d->states.at(i); + if (state->name() == name) + return state; + } + + return 0; +} + +void QDeclarative1StateGroup::removeState(QDeclarative1State *state) +{ + Q_D(QDeclarative1StateGroup); + d->states.removeOne(state); +} + + + +QT_END_NAMESPACE + + diff --git a/src/qtquick1/util/qdeclarativestategroup_p.h b/src/qtquick1/util/qdeclarativestategroup_p.h new file mode 100644 index 0000000000..e7831d4cca --- /dev/null +++ b/src/qtquick1/util/qdeclarativestategroup_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEGROUP_H +#define QDECLARATIVESTATEGROUP_H + +#include "QtQuick1/private/qdeclarativestate_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +QT_MODULE(Declarative) + +class QDeclarative1StateGroupPrivate; +class Q_DECLARATIVE_EXPORT QDeclarative1StateGroup : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_DECLARE_PRIVATE(QDeclarative1StateGroup) + + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1State> states READ statesProperty DESIGNABLE false) + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1Transition> transitions READ transitionsProperty DESIGNABLE false) + +public: + QDeclarative1StateGroup(QObject * = 0); + virtual ~QDeclarative1StateGroup(); + + QString state() const; + void setState(const QString &); + + QDeclarativeListProperty<QDeclarative1State> statesProperty(); + QList<QDeclarative1State *> states() const; + + QDeclarativeListProperty<QDeclarative1Transition> transitionsProperty(); + + QDeclarative1State *findState(const QString &name) const; + + virtual void classBegin(); + virtual void componentComplete(); +Q_SIGNALS: + void stateChanged(const QString &); + +private: + friend class QDeclarative1State; + bool updateAutoState(); + void removeState(QDeclarative1State *state); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1StateGroup) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEGROUP_H diff --git a/src/qtquick1/util/qdeclarativestateoperations.cpp b/src/qtquick1/util/qdeclarativestateoperations.cpp new file mode 100644 index 0000000000..26a8e33f6b --- /dev/null +++ b/src/qtquick1/util/qdeclarativestateoperations.cpp @@ -0,0 +1,1591 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativestateoperations_p.h" +#include "QtQuick1/private/qdeclarativeanchors_p_p.h" +#include "QtQuick1/private/qdeclarativeitem_p.h" +#include "QtDeclarative/private/qdeclarativenullablevalue_p_p.h" + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> +#include <QtDeclarative/private/qdeclarativecontext_p.h> +#include <QtDeclarative/private/qdeclarativeproperty_p.h> +#include <QtDeclarative/private/qdeclarativebinding_p.h> +#include <QtQuick1/private/qdeclarativestate_p_p.h> + +#include <QtCore/qdebug.h> +#include <QtGui/qgraphicsitem.h> +#include <QtCore/qmath.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1ParentChangePrivate : public QDeclarative1StateOperationPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1ParentChange) +public: + QDeclarative1ParentChangePrivate() : target(0), parent(0), origParent(0), origStackBefore(0), + rewindParent(0), rewindStackBefore(0) {} + + QDeclarativeItem *target; + QDeclarativeGuard<QDeclarativeItem> parent; + QDeclarativeGuard<QDeclarativeItem> origParent; + QDeclarativeGuard<QDeclarativeItem> origStackBefore; + QDeclarativeItem *rewindParent; + QDeclarativeItem *rewindStackBefore; + + QDeclarativeNullableValue<QDeclarativeScriptString> xString; + QDeclarativeNullableValue<QDeclarativeScriptString> yString; + QDeclarativeNullableValue<QDeclarativeScriptString> widthString; + QDeclarativeNullableValue<QDeclarativeScriptString> heightString; + QDeclarativeNullableValue<QDeclarativeScriptString> scaleString; + QDeclarativeNullableValue<QDeclarativeScriptString> rotationString; + + QDeclarativeNullableValue<qreal> x; + QDeclarativeNullableValue<qreal> y; + QDeclarativeNullableValue<qreal> width; + QDeclarativeNullableValue<qreal> height; + QDeclarativeNullableValue<qreal> scale; + QDeclarativeNullableValue<qreal> rotation; + + void doChange(QDeclarativeItem *targetParent, QDeclarativeItem *stackBefore = 0); +}; + +void QDeclarative1ParentChangePrivate::doChange(QDeclarativeItem *targetParent, QDeclarativeItem *stackBefore) +{ + if (targetParent && target && target->parentItem()) { + Q_Q(QDeclarative1ParentChange); + bool ok; + const QTransform &transform = target->parentItem()->itemTransform(targetParent, &ok); + if (transform.type() >= QTransform::TxShear || !ok) { + qmlInfo(q) << QDeclarative1ParentChange::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) << QDeclarative1ParentChange::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) << QDeclarative1ParentChange::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) << QDeclarative1ParentChange::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() != QDeclarativeItem::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); +} + +/*! + \preliminary + \qmlclass ParentChange QDeclarative1ParentChange + \ingroup qml-state-elements + \brief The ParentChange element allows you to reparent an Item in a state change. + + ParentChange reparents an item while preserving its visual appearance (position, size, + rotation, and scale) on screen. You can then specify a transition to move/resize/rotate/scale + the item to its final intended appearance. + + ParentChange can only preserve visual appearance if no complex transforms are involved. + More specifically, it will not work if the transform property has been set for any + items involved in the reparenting (i.e. items in the common ancestor tree + for the original and new parent). + + The example below displays a large red rectangle and a small blue rectangle, side by side. + When the \c blueRect is clicked, it changes to the "reparented" state: its parent is changed to \c redRect and it is + positioned at (10, 10) within the red rectangle, as specified in the ParentChange. + + \snippet doc/src/snippets/declarative/parentchange.qml 0 + + \image parentchange.png + + You can specify at which point in a transition you want a ParentChange to occur by + using a ParentAnimation. +*/ + + +QDeclarative1ParentChange::QDeclarative1ParentChange(QObject *parent) + : QDeclarative1StateOperation(*(new QDeclarative1ParentChangePrivate), parent) +{ +} + +QDeclarative1ParentChange::~QDeclarative1ParentChange() +{ +} + +/*! + \qmlproperty real ParentChange::x + \qmlproperty real ParentChange::y + \qmlproperty real ParentChange::width + \qmlproperty real ParentChange::height + \qmlproperty real ParentChange::scale + \qmlproperty real ParentChange::rotation + These properties hold the new position, size, scale, and rotation + for the item in this state. +*/ +QDeclarativeScriptString QDeclarative1ParentChange::x() const +{ + Q_D(const QDeclarative1ParentChange); + return d->xString.value; +} + +void tryReal(QDeclarativeNullableValue<qreal> &value, const QString &string) +{ + bool ok = false; + qreal realValue = string.toFloat(&ok); + if (ok) + value = realValue; + else + value.invalidate(); +} + +void QDeclarative1ParentChange::setX(QDeclarativeScriptString x) +{ + Q_D(QDeclarative1ParentChange); + d->xString = x; + tryReal(d->x, x.script()); +} + +bool QDeclarative1ParentChange::xIsSet() const +{ + Q_D(const QDeclarative1ParentChange); + return d->xString.isValid(); +} + +QDeclarativeScriptString QDeclarative1ParentChange::y() const +{ + Q_D(const QDeclarative1ParentChange); + return d->yString.value; +} + +void QDeclarative1ParentChange::setY(QDeclarativeScriptString y) +{ + Q_D(QDeclarative1ParentChange); + d->yString = y; + tryReal(d->y, y.script()); +} + +bool QDeclarative1ParentChange::yIsSet() const +{ + Q_D(const QDeclarative1ParentChange); + return d->yString.isValid(); +} + +QDeclarativeScriptString QDeclarative1ParentChange::width() const +{ + Q_D(const QDeclarative1ParentChange); + return d->widthString.value; +} + +void QDeclarative1ParentChange::setWidth(QDeclarativeScriptString width) +{ + Q_D(QDeclarative1ParentChange); + d->widthString = width; + tryReal(d->width, width.script()); +} + +bool QDeclarative1ParentChange::widthIsSet() const +{ + Q_D(const QDeclarative1ParentChange); + return d->widthString.isValid(); +} + +QDeclarativeScriptString QDeclarative1ParentChange::height() const +{ + Q_D(const QDeclarative1ParentChange); + return d->heightString.value; +} + +void QDeclarative1ParentChange::setHeight(QDeclarativeScriptString height) +{ + Q_D(QDeclarative1ParentChange); + d->heightString = height; + tryReal(d->height, height.script()); +} + +bool QDeclarative1ParentChange::heightIsSet() const +{ + Q_D(const QDeclarative1ParentChange); + return d->heightString.isValid(); +} + +QDeclarativeScriptString QDeclarative1ParentChange::scale() const +{ + Q_D(const QDeclarative1ParentChange); + return d->scaleString.value; +} + +void QDeclarative1ParentChange::setScale(QDeclarativeScriptString scale) +{ + Q_D(QDeclarative1ParentChange); + d->scaleString = scale; + tryReal(d->scale, scale.script()); +} + +bool QDeclarative1ParentChange::scaleIsSet() const +{ + Q_D(const QDeclarative1ParentChange); + return d->scaleString.isValid(); +} + +QDeclarativeScriptString QDeclarative1ParentChange::rotation() const +{ + Q_D(const QDeclarative1ParentChange); + return d->rotationString.value; +} + +void QDeclarative1ParentChange::setRotation(QDeclarativeScriptString rotation) +{ + Q_D(QDeclarative1ParentChange); + d->rotationString = rotation; + tryReal(d->rotation, rotation.script()); +} + +bool QDeclarative1ParentChange::rotationIsSet() const +{ + Q_D(const QDeclarative1ParentChange); + return d->rotationString.isValid(); +} + +QDeclarativeItem *QDeclarative1ParentChange::originalParent() const +{ + Q_D(const QDeclarative1ParentChange); + return d->origParent; +} + +/*! + \qmlproperty Item ParentChange::target + This property holds the item to be reparented +*/ + +QDeclarativeItem *QDeclarative1ParentChange::object() const +{ + Q_D(const QDeclarative1ParentChange); + return d->target; +} + +void QDeclarative1ParentChange::setObject(QDeclarativeItem *target) +{ + Q_D(QDeclarative1ParentChange); + d->target = target; +} + +/*! + \qmlproperty Item ParentChange::parent + This property holds the new parent for the item in this state. +*/ + +QDeclarativeItem *QDeclarative1ParentChange::parent() const +{ + Q_D(const QDeclarative1ParentChange); + return d->parent; +} + +void QDeclarative1ParentChange::setParent(QDeclarativeItem *parent) +{ + Q_D(QDeclarative1ParentChange); + d->parent = parent; +} + +QDeclarative1StateOperation::ActionList QDeclarative1ParentChange::actions() +{ + Q_D(QDeclarative1ParentChange); + if (!d->target || !d->parent) + return ActionList(); + + ActionList actions; + + QDeclarative1Action a; + a.event = this; + actions << a; + + QDeclarativeContext *ctxt = qmlContext(this); + + if (d->xString.isValid()) { + if (d->x.isValid()) { + QDeclarative1Action xa(d->target, QLatin1String("x"), ctxt, d->x.value); + actions << xa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->xString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("x"), ctxt)); + QDeclarative1Action xa; + xa.property = newBinding->property(); + xa.toBinding = newBinding; + xa.fromValue = xa.property.read(); + xa.deletableToBinding = true; + actions << xa; + } + } + + if (d->yString.isValid()) { + if (d->y.isValid()) { + QDeclarative1Action ya(d->target, QLatin1String("y"), ctxt, d->y.value); + actions << ya; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->yString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("y"), ctxt)); + QDeclarative1Action ya; + ya.property = newBinding->property(); + ya.toBinding = newBinding; + ya.fromValue = ya.property.read(); + ya.deletableToBinding = true; + actions << ya; + } + } + + if (d->scaleString.isValid()) { + if (d->scale.isValid()) { + QDeclarative1Action sa(d->target, QLatin1String("scale"), ctxt, d->scale.value); + actions << sa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->scaleString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("scale"), ctxt)); + QDeclarative1Action sa; + sa.property = newBinding->property(); + sa.toBinding = newBinding; + sa.fromValue = sa.property.read(); + sa.deletableToBinding = true; + actions << sa; + } + } + + if (d->rotationString.isValid()) { + if (d->rotation.isValid()) { + QDeclarative1Action ra(d->target, QLatin1String("rotation"), ctxt, d->rotation.value); + actions << ra; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->rotationString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("rotation"), ctxt)); + QDeclarative1Action ra; + ra.property = newBinding->property(); + ra.toBinding = newBinding; + ra.fromValue = ra.property.read(); + ra.deletableToBinding = true; + actions << ra; + } + } + + if (d->widthString.isValid()) { + if (d->width.isValid()) { + QDeclarative1Action wa(d->target, QLatin1String("width"), ctxt, d->width.value); + actions << wa; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->widthString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("width"), ctxt)); + QDeclarative1Action wa; + wa.property = newBinding->property(); + wa.toBinding = newBinding; + wa.fromValue = wa.property.read(); + wa.deletableToBinding = true; + actions << wa; + } + } + + if (d->heightString.isValid()) { + if (d->height.isValid()) { + QDeclarative1Action ha(d->target, QLatin1String("height"), ctxt, d->height.value); + actions << ha; + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->heightString.value.script(), d->target, ctxt); + newBinding->setTarget(QDeclarativeProperty(d->target, QLatin1String("height"), ctxt)); + QDeclarative1Action ha; + ha.property = newBinding->property(); + ha.toBinding = newBinding; + ha.fromValue = ha.property.read(); + ha.deletableToBinding = true; + actions << ha; + } + } + + return actions; +} + +class AccessibleFxItem : public QDeclarativeItem +{ + Q_OBJECT + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr.data(), QDeclarativeItem) +public: + int siblingIndex() { + Q_D(QDeclarativeItem); + return d->siblingIndex; + } +}; + +void QDeclarative1ParentChange::saveOriginals() +{ + Q_D(QDeclarative1ParentChange); + saveCurrentValues(); + d->origParent = d->rewindParent; + d->origStackBefore = d->rewindStackBefore; +} + +/*void QDeclarative1ParentChange::copyOriginals(QDeclarative1ActionEvent *other) +{ + Q_D(QDeclarative1ParentChange); + QDeclarative1ParentChange *pc = static_cast<QDeclarative1ParentChange*>(other); + + d->origParent = pc->d_func()->rewindParent; + d->origStackBefore = pc->d_func()->rewindStackBefore; + + saveCurrentValues(); +}*/ + +void QDeclarative1ParentChange::execute(Reason) +{ + Q_D(QDeclarative1ParentChange); + d->doChange(d->parent); +} + +bool QDeclarative1ParentChange::isReversable() +{ + return true; +} + +void QDeclarative1ParentChange::reverse(Reason) +{ + Q_D(QDeclarative1ParentChange); + d->doChange(d->origParent, d->origStackBefore); +} + +QString QDeclarative1ParentChange::typeName() const +{ + return QLatin1String("ParentChange"); +} + +bool QDeclarative1ParentChange::override(QDeclarative1ActionEvent*other) +{ + Q_D(QDeclarative1ParentChange); + if (other->typeName() != QLatin1String("ParentChange")) + return false; + if (QDeclarative1ParentChange *otherPC = static_cast<QDeclarative1ParentChange*>(other)) + return (d->target == otherPC->object()); + return false; +} + +void QDeclarative1ParentChange::saveCurrentValues() +{ + Q_D(QDeclarative1ParentChange); + if (!d->target) { + d->rewindParent = 0; + d->rewindStackBefore = 0; + return; + } + + d->rewindParent = d->target->parentItem(); + d->rewindStackBefore = 0; + + if (!d->rewindParent) + return; + + //try to determine the item's original stack position so we can restore it + int siblingIndex = ((AccessibleFxItem*)d->target)->siblingIndex() + 1; + QList<QGraphicsItem*> children = d->rewindParent->childItems(); + for (int i = 0; i < children.count(); ++i) { + QDeclarativeItem *child = qobject_cast<QDeclarativeItem*>(children.at(i)); + if (!child) + continue; + if (((AccessibleFxItem*)child)->siblingIndex() == siblingIndex) { + d->rewindStackBefore = child; + break; + } + } +} + +void QDeclarative1ParentChange::rewind() +{ + Q_D(QDeclarative1ParentChange); + d->doChange(d->rewindParent, d->rewindStackBefore); +} + +class QDeclarative1StateChangeScriptPrivate : public QDeclarative1StateOperationPrivate +{ +public: + QDeclarative1StateChangeScriptPrivate() {} + + QDeclarativeScriptString script; + QString name; +}; + +/*! + \qmlclass StateChangeScript QDeclarative1StateChangeScript + \ingroup qml-state-elements + \brief The StateChangeScript element allows you to run a script in a state. + + A StateChangeScript is run upon entering a state. You can optionally use + ScriptAction to specify the point in the transition at which + the StateChangeScript should to be run. + + \snippet snippets/declarative/states/statechangescript.qml state and transition + + \sa ScriptAction +*/ + +QDeclarative1StateChangeScript::QDeclarative1StateChangeScript(QObject *parent) +: QDeclarative1StateOperation(*(new QDeclarative1StateChangeScriptPrivate), parent) +{ +} + +QDeclarative1StateChangeScript::~QDeclarative1StateChangeScript() +{ +} + +/*! + \qmlproperty script StateChangeScript::script + This property holds the script to run when the state is current. +*/ +QDeclarativeScriptString QDeclarative1StateChangeScript::script() const +{ + Q_D(const QDeclarative1StateChangeScript); + return d->script; +} + +void QDeclarative1StateChangeScript::setScript(const QDeclarativeScriptString &s) +{ + Q_D(QDeclarative1StateChangeScript); + d->script = s; +} + +/*! + \qmlproperty string StateChangeScript::name + This property holds the name of the script. This name can be used by a + ScriptAction to target a specific script. + + \sa ScriptAction::scriptName +*/ +QString QDeclarative1StateChangeScript::name() const +{ + Q_D(const QDeclarative1StateChangeScript); + return d->name; +} + +void QDeclarative1StateChangeScript::setName(const QString &n) +{ + Q_D(QDeclarative1StateChangeScript); + d->name = n; +} + +void QDeclarative1StateChangeScript::execute(Reason) +{ + Q_D(QDeclarative1StateChangeScript); + const QString &script = d->script.script(); + if (!script.isEmpty()) { + QDeclarativeExpression expr(d->script.context(), d->script.scopeObject(), script); + QDeclarativeData *ddata = QDeclarativeData::get(this); + if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty()) + expr.setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber); + expr.evaluate(); + if (expr.hasError()) + qmlInfo(this, expr.error()); + } +} + +QDeclarative1StateChangeScript::ActionList QDeclarative1StateChangeScript::actions() +{ + ActionList rv; + QDeclarative1Action a; + a.event = this; + rv << a; + return rv; +} + +QString QDeclarative1StateChangeScript::typeName() const +{ + return QLatin1String("StateChangeScript"); +} + +/*! + \qmlclass AnchorChanges QDeclarative1AnchorChanges + \ingroup qml-state-elements + \brief The AnchorChanges element allows you to change the anchors of an item in a state. + + The AnchorChanges element is used to modify the anchors of an item in a \l State. + + AnchorChanges cannot be used to modify the margins on an item. For this, use + PropertyChanges intead. + + In the following example we change the top and bottom anchors of an item + using AnchorChanges, and the top and bottom anchor margins using + PropertyChanges: + + \snippet doc/src/snippets/declarative/anchorchanges.qml 0 + + \image anchorchanges.png + + AnchorChanges can be animated using AnchorAnimation. + \qml + //animate our anchor changes + Transition { + AnchorAnimation {} + } + \endqml + + Margin animations can be animated using NumberAnimation. + + For more information on anchors see \l {anchor-layout}{Anchor Layouts}. +*/ + +class QDeclarative1AnchorSetPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1AnchorSet) +public: + QDeclarative1AnchorSetPrivate() + : usedAnchors(0), resetAnchors(0), fill(0), + centerIn(0)/*, leftMargin(0), rightMargin(0), topMargin(0), bottomMargin(0), + margins(0), vCenterOffset(0), hCenterOffset(0), baselineOffset(0)*/ + { + } + + QDeclarative1Anchors::Anchors usedAnchors; + QDeclarative1Anchors::Anchors resetAnchors; + + QDeclarativeItem *fill; + QDeclarativeItem *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;*/ +}; + +QDeclarative1AnchorSet::QDeclarative1AnchorSet(QObject *parent) + : QObject(*new QDeclarative1AnchorSetPrivate, parent) +{ +} + +QDeclarative1AnchorSet::~QDeclarative1AnchorSet() +{ +} + +QDeclarativeScriptString QDeclarative1AnchorSet::top() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->topScript; +} + +void QDeclarative1AnchorSet::setTop(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::TopAnchor; + d->topScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetTop(); +} + +void QDeclarative1AnchorSet::resetTop() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::TopAnchor; + d->topScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::TopAnchor; +} + +QDeclarativeScriptString QDeclarative1AnchorSet::bottom() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->bottomScript; +} + +void QDeclarative1AnchorSet::setBottom(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::BottomAnchor; + d->bottomScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBottom(); +} + +void QDeclarative1AnchorSet::resetBottom() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::BottomAnchor; + d->bottomScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::BottomAnchor; +} + +QDeclarativeScriptString QDeclarative1AnchorSet::verticalCenter() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->vCenterScript; +} + +void QDeclarative1AnchorSet::setVerticalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::VCenterAnchor; + d->vCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetVerticalCenter(); +} + +void QDeclarative1AnchorSet::resetVerticalCenter() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::VCenterAnchor; + d->vCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::VCenterAnchor; +} + +QDeclarativeScriptString QDeclarative1AnchorSet::baseline() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->baselineScript; +} + +void QDeclarative1AnchorSet::setBaseline(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::BaselineAnchor; + d->baselineScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetBaseline(); +} + +void QDeclarative1AnchorSet::resetBaseline() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::BaselineAnchor; + d->baselineScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::BaselineAnchor; +} + +QDeclarativeScriptString QDeclarative1AnchorSet::left() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->leftScript; +} + +void QDeclarative1AnchorSet::setLeft(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::LeftAnchor; + d->leftScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetLeft(); +} + +void QDeclarative1AnchorSet::resetLeft() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::LeftAnchor; + d->leftScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::LeftAnchor; +} + +QDeclarativeScriptString QDeclarative1AnchorSet::right() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->rightScript; +} + +void QDeclarative1AnchorSet::setRight(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::RightAnchor; + d->rightScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetRight(); +} + +void QDeclarative1AnchorSet::resetRight() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::RightAnchor; + d->rightScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::RightAnchor; +} + +QDeclarativeScriptString QDeclarative1AnchorSet::horizontalCenter() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->hCenterScript; +} + +void QDeclarative1AnchorSet::setHorizontalCenter(const QDeclarativeScriptString &edge) +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors |= QDeclarative1Anchors::HCenterAnchor; + d->hCenterScript = edge; + if (edge.script() == QLatin1String("undefined")) + resetHorizontalCenter(); +} + +void QDeclarative1AnchorSet::resetHorizontalCenter() +{ + Q_D(QDeclarative1AnchorSet); + d->usedAnchors &= ~QDeclarative1Anchors::HCenterAnchor; + d->hCenterScript = QDeclarativeScriptString(); + d->resetAnchors |= QDeclarative1Anchors::HCenterAnchor; +} + +QDeclarativeItem *QDeclarative1AnchorSet::fill() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->fill; +} + +void QDeclarative1AnchorSet::setFill(QDeclarativeItem *f) +{ + Q_D(QDeclarative1AnchorSet); + d->fill = f; +} + +void QDeclarative1AnchorSet::resetFill() +{ + setFill(0); +} + +QDeclarativeItem *QDeclarative1AnchorSet::centerIn() const +{ + Q_D(const QDeclarative1AnchorSet); + return d->centerIn; +} + +void QDeclarative1AnchorSet::setCenterIn(QDeclarativeItem* c) +{ + Q_D(QDeclarative1AnchorSet); + d->centerIn = c; +} + +void QDeclarative1AnchorSet::resetCenterIn() +{ + setCenterIn(0); +} + + +class QDeclarative1AnchorChangesPrivate : public QDeclarative1StateOperationPrivate +{ +public: + QDeclarative1AnchorChangesPrivate() + : target(0), anchorSet(new QDeclarative1AnchorSet), + 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) + { + + } + ~QDeclarative1AnchorChangesPrivate() { delete anchorSet; } + + QDeclarativeItem *target; + QDeclarative1AnchorSet *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; + + QDeclarative1AnchorLine rewindLeft; + QDeclarative1AnchorLine rewindRight; + QDeclarative1AnchorLine rewindHCenter; + QDeclarative1AnchorLine rewindTop; + QDeclarative1AnchorLine rewindBottom; + QDeclarative1AnchorLine rewindVCenter; + QDeclarative1AnchorLine 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; +}; + +/*! + \qmlproperty Item AnchorChanges::target + This property holds the \l Item for which the anchor changes will be applied. +*/ + +QDeclarative1AnchorChanges::QDeclarative1AnchorChanges(QObject *parent) + : QDeclarative1StateOperation(*(new QDeclarative1AnchorChangesPrivate), parent) +{ +} + +QDeclarative1AnchorChanges::~QDeclarative1AnchorChanges() +{ +} + +QDeclarative1AnchorChanges::ActionList QDeclarative1AnchorChanges::actions() +{ + Q_D(QDeclarative1AnchorChanges); + 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")); + + QDeclarativeContext *ctxt = qmlContext(this); + + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::LeftAnchor) { + d->leftBinding = new QDeclarativeBinding(d->anchorSet->d_func()->leftScript.script(), d->target, ctxt); + d->leftBinding->setTarget(d->leftProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::RightAnchor) { + d->rightBinding = new QDeclarativeBinding(d->anchorSet->d_func()->rightScript.script(), d->target, ctxt); + d->rightBinding->setTarget(d->rightProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::HCenterAnchor) { + d->hCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->hCenterScript.script(), d->target, ctxt); + d->hCenterBinding->setTarget(d->hCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::TopAnchor) { + d->topBinding = new QDeclarativeBinding(d->anchorSet->d_func()->topScript.script(), d->target, ctxt); + d->topBinding->setTarget(d->topProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::BottomAnchor) { + d->bottomBinding = new QDeclarativeBinding(d->anchorSet->d_func()->bottomScript.script(), d->target, ctxt); + d->bottomBinding->setTarget(d->bottomProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::VCenterAnchor) { + d->vCenterBinding = new QDeclarativeBinding(d->anchorSet->d_func()->vCenterScript.script(), d->target, ctxt); + d->vCenterBinding->setTarget(d->vCenterProp); + } + if (d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::BaselineAnchor) { + d->baselineBinding = new QDeclarativeBinding(d->anchorSet->d_func()->baselineScript.script(), d->target, ctxt); + d->baselineBinding->setTarget(d->baselineProp); + } + + QDeclarative1Action a; + a.event = this; + return ActionList() << a; +} + +QDeclarative1AnchorSet *QDeclarative1AnchorChanges::anchors() +{ + Q_D(QDeclarative1AnchorChanges); + return d->anchorSet; +} + +QDeclarativeItem *QDeclarative1AnchorChanges::object() const +{ + Q_D(const QDeclarative1AnchorChanges); + return d->target; +} + +void QDeclarative1AnchorChanges::setObject(QDeclarativeItem *target) +{ + Q_D(QDeclarative1AnchorChanges); + d->target = target; +} + +/*! + \qmlproperty AnchorLine AnchorChanges::anchors.left + \qmlproperty AnchorLine AnchorChanges::anchors.right + \qmlproperty AnchorLine AnchorChanges::anchors.horizontalCenter + \qmlproperty AnchorLine AnchorChanges::anchors.top + \qmlproperty AnchorLine AnchorChanges::anchors.bottom + \qmlproperty AnchorLine AnchorChanges::anchors.verticalCenter + \qmlproperty AnchorLine AnchorChanges::anchors.baseline + + These properties change the respective anchors of the item. + + To reset an anchor you can assign \c undefined: + \qml + AnchorChanges { + target: myItem + anchors.left: undefined //remove myItem's left anchor + anchors.right: otherItem.right + } + \endqml +*/ + +void QDeclarative1AnchorChanges::execute(Reason reason) +{ + Q_D(QDeclarative1AnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::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 & QDeclarative1Anchors::LeftAnchor) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarative1Anchors::RightAnchor) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarative1Anchors::HCenterAnchor) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarative1Anchors::TopAnchor) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarative1Anchors::BottomAnchor) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarative1Anchors::VCenterAnchor) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->anchorSet->d_func()->resetAnchors & QDeclarative1Anchors::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 QDeclarative1AnchorChanges::isReversable() +{ + return true; +} + +void QDeclarative1AnchorChanges::reverse(Reason reason) +{ + Q_D(QDeclarative1AnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::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 + QDeclarative1Anchors::Anchors stateVAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::Vertical_Mask; + QDeclarative1Anchors::Anchors origVAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarative1Anchors::Vertical_Mask; + QDeclarative1Anchors::Anchors stateHAnchors = d->anchorSet->d_func()->usedAnchors & QDeclarative1Anchors::Horizontal_Mask; + QDeclarative1Anchors::Anchors origHAnchors = targetPrivate->anchors()->usedAnchors() & QDeclarative1Anchors::Horizontal_Mask; + + bool stateSetWidth = (stateHAnchors && + stateHAnchors != QDeclarative1Anchors::LeftAnchor && + stateHAnchors != QDeclarative1Anchors::RightAnchor && + stateHAnchors != QDeclarative1Anchors::HCenterAnchor); + bool origSetWidth = (origHAnchors && + origHAnchors != QDeclarative1Anchors::LeftAnchor && + origHAnchors != QDeclarative1Anchors::RightAnchor && + origHAnchors != QDeclarative1Anchors::HCenterAnchor); + if (d->origWidth.isValid() && stateSetWidth && !origSetWidth) + d->target->setWidth(d->origWidth.value); + + bool stateSetHeight = (stateVAnchors && + stateVAnchors != QDeclarative1Anchors::TopAnchor && + stateVAnchors != QDeclarative1Anchors::BottomAnchor && + stateVAnchors != QDeclarative1Anchors::VCenterAnchor && + stateVAnchors != QDeclarative1Anchors::BaselineAnchor); + bool origSetHeight = (origVAnchors && + origVAnchors != QDeclarative1Anchors::TopAnchor && + origVAnchors != QDeclarative1Anchors::BottomAnchor && + origVAnchors != QDeclarative1Anchors::VCenterAnchor && + origVAnchors != QDeclarative1Anchors::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 QDeclarative1AnchorChanges::typeName() const +{ + return QLatin1String("AnchorChanges"); +} + +QList<QDeclarative1Action> QDeclarative1AnchorChanges::additionalActions() +{ + Q_D(QDeclarative1AnchorChanges); + QList<QDeclarative1Action> extra; + + QDeclarative1Anchors::Anchors combined = d->anchorSet->d_func()->usedAnchors | d->anchorSet->d_func()->resetAnchors; + bool hChange = combined & QDeclarative1Anchors::Horizontal_Mask; + bool vChange = combined & QDeclarative1Anchors::Vertical_Mask; + + if (d->target) { + QDeclarativeContext *ctxt = qmlContext(this); + QDeclarative1Action a; + if (hChange && d->fromX != d->toX) { + a.property = QDeclarativeProperty(d->target, QLatin1String("x"), ctxt); + a.toValue = d->toX; + extra << a; + } + if (vChange && d->fromY != d->toY) { + a.property = QDeclarativeProperty(d->target, QLatin1String("y"), ctxt); + a.toValue = d->toY; + extra << a; + } + if (hChange && d->fromWidth != d->toWidth) { + a.property = QDeclarativeProperty(d->target, QLatin1String("width"), ctxt); + a.toValue = d->toWidth; + extra << a; + } + if (vChange && d->fromHeight != d->toHeight) { + a.property = QDeclarativeProperty(d->target, QLatin1String("height"), ctxt); + a.toValue = d->toHeight; + extra << a; + } + } + + return extra; +} + +bool QDeclarative1AnchorChanges::changesBindings() +{ + return true; +} + +void QDeclarative1AnchorChanges::saveOriginals() +{ + Q_D(QDeclarative1AnchorChanges); + 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); + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::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 QDeclarative1AnchorChanges::copyOriginals(QDeclarative1ActionEvent *other) +{ + Q_D(QDeclarative1AnchorChanges); + QDeclarative1AnchorChanges *ac = static_cast<QDeclarative1AnchorChanges*>(other); + QDeclarative1AnchorChangesPrivate *acp = ac->d_func(); + + QDeclarative1Anchors::Anchors combined = acp->anchorSet->d_func()->usedAnchors | + acp->anchorSet->d_func()->resetAnchors; + + //probably also need to revert some things + d->applyOrigLeft = (combined & QDeclarative1Anchors::LeftAnchor); + d->applyOrigRight = (combined & QDeclarative1Anchors::RightAnchor); + d->applyOrigHCenter = (combined & QDeclarative1Anchors::HCenterAnchor); + d->applyOrigTop = (combined & QDeclarative1Anchors::TopAnchor); + d->applyOrigBottom = (combined & QDeclarative1Anchors::BottomAnchor); + d->applyOrigVCenter = (combined & QDeclarative1Anchors::VCenterAnchor); + d->applyOrigBaseline = (combined & QDeclarative1Anchors::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 QDeclarative1AnchorChanges::clearBindings() +{ + Q_D(QDeclarative1AnchorChanges); + 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(); + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::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 + QDeclarative1Anchors::Anchors combined = d->anchorSet->d_func()->resetAnchors | + d->anchorSet->d_func()->usedAnchors; + if (d->applyOrigLeft || (combined & QDeclarative1Anchors::LeftAnchor)) { + targetPrivate->anchors()->resetLeft(); + QDeclarativePropertyPrivate::setBinding(d->leftProp, 0); + } + if (d->applyOrigRight || (combined & QDeclarative1Anchors::RightAnchor)) { + targetPrivate->anchors()->resetRight(); + QDeclarativePropertyPrivate::setBinding(d->rightProp, 0); + } + if (d->applyOrigHCenter || (combined & QDeclarative1Anchors::HCenterAnchor)) { + targetPrivate->anchors()->resetHorizontalCenter(); + QDeclarativePropertyPrivate::setBinding(d->hCenterProp, 0); + } + if (d->applyOrigTop || (combined & QDeclarative1Anchors::TopAnchor)) { + targetPrivate->anchors()->resetTop(); + QDeclarativePropertyPrivate::setBinding(d->topProp, 0); + } + if (d->applyOrigBottom || (combined & QDeclarative1Anchors::BottomAnchor)) { + targetPrivate->anchors()->resetBottom(); + QDeclarativePropertyPrivate::setBinding(d->bottomProp, 0); + } + if (d->applyOrigVCenter || (combined & QDeclarative1Anchors::VCenterAnchor)) { + targetPrivate->anchors()->resetVerticalCenter(); + QDeclarativePropertyPrivate::setBinding(d->vCenterProp, 0); + } + if (d->applyOrigBaseline || (combined & QDeclarative1Anchors::BaselineAnchor)) { + targetPrivate->anchors()->resetBaseline(); + QDeclarativePropertyPrivate::setBinding(d->baselineProp, 0); + } +} + +bool QDeclarative1AnchorChanges::override(QDeclarative1ActionEvent*other) +{ + if (other->typeName() != QLatin1String("AnchorChanges")) + return false; + if (static_cast<QDeclarative1ActionEvent*>(this) == other) + return true; + if (static_cast<QDeclarative1AnchorChanges*>(other)->object() == object()) + return true; + return false; +} + +void QDeclarative1AnchorChanges::rewind() +{ + Q_D(QDeclarative1AnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::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 QDeclarative1AnchorChanges::saveCurrentValues() +{ + Q_D(QDeclarative1AnchorChanges); + if (!d->target) + return; + + QDeclarativeItemPrivate *targetPrivate = QDeclarativeItemPrivate::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 QDeclarative1AnchorChanges::saveTargetValues() +{ + Q_D(QDeclarative1AnchorChanges); + 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 <qdeclarativestateoperations.moc> +#include <moc_qdeclarativestateoperations_p.cpp> + + + +QT_END_NAMESPACE + diff --git a/src/qtquick1/util/qdeclarativestateoperations_p.h b/src/qtquick1/util/qdeclarativestateoperations_p.h new file mode 100644 index 0000000000..7d0b618c3b --- /dev/null +++ b/src/qtquick1/util/qdeclarativestateoperations_p.h @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTATEOPERATIONS_H +#define QDECLARATIVESTATEOPERATIONS_H + +#include "QtQuick1/private/qdeclarativestate_p.h" + +#include <QtQuick1/qdeclarativeitem.h> +#include <QtQuick1/private/qdeclarativeanchors_p.h> +#include <QtDeclarative/qdeclarativescriptstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1ParentChangePrivate; +class Q_AUTOTEST_EXPORT QDeclarative1ParentChange : public QDeclarative1StateOperation, public QDeclarative1ActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1ParentChange) + + Q_PROPERTY(QDeclarativeItem *target READ object WRITE setObject) + Q_PROPERTY(QDeclarativeItem *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: + QDeclarative1ParentChange(QObject *parent=0); + ~QDeclarative1ParentChange(); + + QDeclarativeItem *object() const; + void setObject(QDeclarativeItem *); + + QDeclarativeItem *parent() const; + void setParent(QDeclarativeItem *); + + QDeclarativeItem *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(QDeclarative1ActionEvent*); + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarative1ActionEvent*other); + virtual void rewind(); + virtual void saveCurrentValues(); +}; + +class QDeclarative1StateChangeScriptPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1StateChangeScript : public QDeclarative1StateOperation, public QDeclarative1ActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1StateChangeScript) + + Q_PROPERTY(QDeclarativeScriptString script READ script WRITE setScript) + Q_PROPERTY(QString name READ name WRITE setName) + +public: + QDeclarative1StateChangeScript(QObject *parent=0); + ~QDeclarative1StateChangeScript(); + + virtual ActionList actions(); + + virtual QString typeName() const; + + QDeclarativeScriptString script() const; + void setScript(const QDeclarativeScriptString &); + + QString name() const; + void setName(const QString &); + + virtual void execute(Reason reason = ActualChange); +}; + +class QDeclarative1AnchorChanges; +class QDeclarative1AnchorSetPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1AnchorSet : 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(QDeclarativeItem *fill READ fill WRITE setFill RESET resetFill) + //Q_PROPERTY(QDeclarativeItem *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: + QDeclarative1AnchorSet(QObject *parent=0); + virtual ~QDeclarative1AnchorSet(); + + 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(); + + QDeclarativeItem *fill() const; + void setFill(QDeclarativeItem *); + void resetFill(); + + QDeclarativeItem *centerIn() const; + void setCenterIn(QDeclarativeItem *); + 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);*/ + + QDeclarative1Anchors::Anchors usedAnchors() const; + +/*Q_SIGNALS: + void leftMarginChanged(); + void rightMarginChanged(); + void topMarginChanged(); + void bottomMarginChanged(); + void marginsChanged(); + void verticalCenterOffsetChanged(); + void horizontalCenterOffsetChanged(); + void baselineOffsetChanged();*/ + +private: + friend class QDeclarative1AnchorChanges; + Q_DISABLE_COPY(QDeclarative1AnchorSet) + Q_DECLARE_PRIVATE(QDeclarative1AnchorSet) +}; + +class QDeclarative1AnchorChangesPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1AnchorChanges : public QDeclarative1StateOperation, public QDeclarative1ActionEvent +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1AnchorChanges) + + Q_PROPERTY(QDeclarativeItem *target READ object WRITE setObject) + Q_PROPERTY(QDeclarative1AnchorSet *anchors READ anchors CONSTANT) + +public: + QDeclarative1AnchorChanges(QObject *parent=0); + ~QDeclarative1AnchorChanges(); + + virtual ActionList actions(); + + QDeclarative1AnchorSet *anchors(); + + QDeclarativeItem *object() const; + void setObject(QDeclarativeItem *); + + virtual void execute(Reason reason = ActualChange); + virtual bool isReversable(); + virtual void reverse(Reason reason = ActualChange); + virtual QString typeName() const; + virtual bool override(QDeclarative1ActionEvent*other); + virtual bool changesBindings(); + virtual void saveOriginals(); + virtual bool needsCopy() { return true; } + virtual void copyOriginals(QDeclarative1ActionEvent*); + virtual void clearBindings(); + virtual void rewind(); + virtual void saveCurrentValues(); + + QList<QDeclarative1Action> additionalActions(); + virtual void saveTargetValues(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1ParentChange) +QML_DECLARE_TYPE(QDeclarative1StateChangeScript) +QML_DECLARE_TYPE(QDeclarative1AnchorSet) +QML_DECLARE_TYPE(QDeclarative1AnchorChanges) + +QT_END_HEADER + +#endif // QDECLARATIVESTATEOPERATIONS_H diff --git a/src/qtquick1/util/qdeclarativestyledtext.cpp b/src/qtquick1/util/qdeclarativestyledtext.cpp new file mode 100644 index 0000000000..46d2224f5e --- /dev/null +++ b/src/qtquick1/util/qdeclarativestyledtext.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QStack> +#include <QVector> +#include <QPainter> +#include <QTextLayout> +#include <QDebug> +#include <qmath.h> +#include "QtQuick1/private/qdeclarativestyledtext_p.h" + +/* + QDeclarative1StyledText supports few tags: + + <b></b> - bold + <i></i> - italic + <br> - new line + <font color="color_name" size="1-7"></font> + + The opening and closing tags must be correctly nested. +*/ + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1StyledTextPrivate +{ +public: + QDeclarative1StyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l), baseFont(layout.font()) {} + + void parse(); + bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format); + bool parseCloseTag(const QChar *&ch, const QString &textIn); + void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut); + bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format); + QPair<QStringRef,QStringRef> parseAttribute(const QChar *&ch, const QString &textIn); + QStringRef parseValue(const QChar *&ch, const QString &textIn); + + inline void skipSpace(const QChar *&ch) { + while (ch->isSpace() && !ch->isNull()) + ++ch; + } + + QString text; + QTextLayout &layout; + QFont baseFont; + + static const QChar lessThan; + static const QChar greaterThan; + static const QChar equals; + static const QChar singleQuote; + static const QChar doubleQuote; + static const QChar slash; + static const QChar ampersand; +}; + +const QChar QDeclarative1StyledTextPrivate::lessThan(QLatin1Char('<')); +const QChar QDeclarative1StyledTextPrivate::greaterThan(QLatin1Char('>')); +const QChar QDeclarative1StyledTextPrivate::equals(QLatin1Char('=')); +const QChar QDeclarative1StyledTextPrivate::singleQuote(QLatin1Char('\'')); +const QChar QDeclarative1StyledTextPrivate::doubleQuote(QLatin1Char('\"')); +const QChar QDeclarative1StyledTextPrivate::slash(QLatin1Char('/')); +const QChar QDeclarative1StyledTextPrivate::ampersand(QLatin1Char('&')); + +QDeclarative1StyledText::QDeclarative1StyledText(const QString &string, QTextLayout &layout) +: d(new QDeclarative1StyledTextPrivate(string, layout)) +{ +} + +QDeclarative1StyledText::~QDeclarative1StyledText() +{ + delete d; +} + +void QDeclarative1StyledText::parse(const QString &string, QTextLayout &layout) +{ + if (string.isEmpty()) + return; + QDeclarative1StyledText styledText(string, layout); + styledText.d->parse(); +} + +void QDeclarative1StyledTextPrivate::parse() +{ + QList<QTextLayout::FormatRange> ranges; + QStack<QTextCharFormat> formatStack; + + QString drawText; + drawText.reserve(text.count()); + + int textStart = 0; + int textLength = 0; + int rangeStart = 0; + const QChar *ch = text.constData(); + while (!ch->isNull()) { + if (*ch == lessThan) { + if (textLength) + drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); + } + rangeStart = drawText.length(); + ++ch; + if (*ch == slash) { + ++ch; + if (parseCloseTag(ch, text)) { + if (formatStack.count()) + formatStack.pop(); + } + } else { + QTextCharFormat format; + if (formatStack.count()) + format = formatStack.top(); + if (parseTag(ch, text, drawText, format)) + formatStack.push(format); + } + textStart = ch - text.constData() + 1; + textLength = 0; + } else if (*ch == ampersand) { + ++ch; + drawText.append(QStringRef(&text, textStart, textLength)); + parseEntity(ch, text, drawText); + textStart = ch - text.constData() + 1; + textLength = 0; + } else { + ++textLength; + } + if (!ch->isNull()) + ++ch; + } + if (textLength) + drawText.append(QStringRef(&text, textStart, textLength)); + if (rangeStart != drawText.length() && formatStack.count()) { + QTextLayout::FormatRange formatRange; + formatRange.format = formatStack.top(); + formatRange.start = rangeStart; + formatRange.length = drawText.length() - rangeStart; + ranges.append(formatRange); + } + + layout.setText(drawText); + layout.setAdditionalFormats(ranges); +} + +bool QDeclarative1StyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) + format.setFontWeight(QFont::Bold); + else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) { + textOut.append(QChar(QChar::LineSeparator)); + return false; + } + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + format.setFontItalic(true); + } + return true; + } else if (ch->isSpace()) { + // may have params. + QStringRef tag(&textIn, tagStart, tagLength); + if (tag == QLatin1String("font")) + return parseFontAttributes(ch, textIn, format); + if (*ch == greaterThan || ch->isNull()) + continue; + } else if (*ch != slash){ + tagLength++; + } + ++ch; + } + + return false; +} + +bool QDeclarative1StyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int tagStart = ch - textIn.constData(); + int tagLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + QStringRef tag(&textIn, tagStart, tagLength); + const QChar char0 = tag.at(0); + if (char0 == QLatin1Char('b')) { + if (tagLength == 1) + return true; + else if (tag.at(1) == QLatin1Char('r') && tagLength == 2) + return true; + } else if (char0 == QLatin1Char('i')) { + if (tagLength == 1) + return true; + } else if (tag == QLatin1String("font")) { + return true; + } + return false; + } else if (!ch->isSpace()){ + tagLength++; + } + ++ch; + } + + return false; +} + +void QDeclarative1StyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut) +{ + int entityStart = ch - textIn.constData(); + int entityLength = 0; + while (!ch->isNull()) { + if (*ch == QLatin1Char(';')) { + QStringRef entity(&textIn, entityStart, entityLength); + if (entity == QLatin1String("gt")) + textOut += QChar(62); + else if (entity == QLatin1String("lt")) + textOut += QChar(60); + else if (entity == QLatin1String("amp")) + textOut += QChar(38); + return; + } + ++entityLength; + ++ch; + } +} + +bool QDeclarative1StyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format) +{ + bool valid = false; + QPair<QStringRef,QStringRef> attr; + do { + attr = parseAttribute(ch, textIn); + if (attr.first == QLatin1String("color")) { + valid = true; + format.setForeground(QColor(attr.second.toString())); + } else if (attr.first == QLatin1String("size")) { + valid = true; + int size = attr.second.toString().toInt(); + if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+')) + size += 3; + if (size >= 1 && size <= 7) { + static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 }; + format.setFontPointSize(baseFont.pointSize() * scaling[size-1]); + } + } + } while (!ch->isNull() && !attr.first.isEmpty()); + + return valid; +} + +QPair<QStringRef,QStringRef> QDeclarative1StyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn) +{ + skipSpace(ch); + + int attrStart = ch - textIn.constData(); + int attrLength = 0; + while (!ch->isNull()) { + if (*ch == greaterThan) { + break; + } else if (*ch == equals) { + ++ch; + if (*ch != singleQuote && *ch != doubleQuote) { + while (*ch != greaterThan && !ch->isNull()) + ++ch; + break; + } + ++ch; + if (!attrLength) + break; + QStringRef attr(&textIn, attrStart, attrLength); + QStringRef val = parseValue(ch, textIn); + if (!val.isEmpty()) + return QPair<QStringRef,QStringRef>(attr,val); + break; + } else { + ++attrLength; + } + ++ch; + } + + return QPair<QStringRef,QStringRef>(); +} + +QStringRef QDeclarative1StyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn) +{ + int valStart = ch - textIn.constData(); + int valLength = 0; + while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) { + ++valLength; + ++ch; + } + if (ch->isNull()) + return QStringRef(); + ++ch; // skip quote + + return QStringRef(&textIn, valStart, valLength); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativestyledtext_p.h b/src/qtquick1/util/qdeclarativestyledtext_p.h new file mode 100644 index 0000000000..43a391a9d5 --- /dev/null +++ b/src/qtquick1/util/qdeclarativestyledtext_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESTYLEDTEXT_H +#define QDECLARATIVESTYLEDTEXT_H + +#include <QSizeF> + +QT_BEGIN_NAMESPACE + +class QPainter; +class QPointF; +class QString; +class QTextLayout; + +class QDeclarative1StyledTextPrivate; + +class Q_AUTOTEST_EXPORT QDeclarative1StyledText +{ +public: + static void parse(const QString &string, QTextLayout &layout); + +private: + QDeclarative1StyledText(const QString &string, QTextLayout &layout); + ~QDeclarative1StyledText(); + + QDeclarative1StyledTextPrivate *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtquick1/util/qdeclarativesystempalette.cpp b/src/qtquick1/util/qdeclarativesystempalette.cpp new file mode 100644 index 0000000000..311a2bf16e --- /dev/null +++ b/src/qtquick1/util/qdeclarativesystempalette.cpp @@ -0,0 +1,316 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativesystempalette_p.h" + +#include <QApplication> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + +class QDeclarative1SystemPalettePrivate : public QObjectPrivate +{ +public: + QPalette palette; + QPalette::ColorGroup group; +}; + + + +/*! + \qmlclass SystemPalette QDeclarative1SystemPalette + \ingroup qml-utility-elements + \since 4.7 + \brief The SystemPalette element provides access to the Qt palettes. + + The SystemPalette element provides access to the Qt application + palettes. This provides information about the standard colors used + for application windows, buttons and other features. These colors + are grouped into three \e {color groups}: \c Active, \c Inactive, + and \c Disabled. See the QPalette documentation for details about + color groups and the properties provided by SystemPalette. + + This can be used to color items in a way that provides a more + native look and feel. + + The following example creates a palette from the \c Active color + group and uses this to color the window and text items + appropriately: + + \snippet doc/src/snippets/declarative/systempalette.qml 0 + + \sa QPalette +*/ +QDeclarative1SystemPalette::QDeclarative1SystemPalette(QObject *parent) + : QObject(*(new QDeclarative1SystemPalettePrivate), parent) +{ + Q_D(QDeclarative1SystemPalette); + d->palette = QApplication::palette(); + d->group = QPalette::Active; + qApp->installEventFilter(this); +} + +QDeclarative1SystemPalette::~QDeclarative1SystemPalette() +{ +} + +/*! + \qmlproperty color SystemPalette::window + The window (general background) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::window() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Window); +} + +/*! + \qmlproperty color SystemPalette::windowText + The window text (general foreground) color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::windowText() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::WindowText); +} + +/*! + \qmlproperty color SystemPalette::base + The base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::base() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Base); +} + +/*! + \qmlproperty color SystemPalette::text + The text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::text() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Text); +} + +/*! + \qmlproperty color SystemPalette::alternateBase + The alternate base color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::alternateBase() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::AlternateBase); +} + +/*! + \qmlproperty color SystemPalette::button + The button color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::button() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Button); +} + +/*! + \qmlproperty color SystemPalette::buttonText + The button text foreground color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::buttonText() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::ButtonText); +} + +/*! + \qmlproperty color SystemPalette::light + The light color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::light() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Light); +} + +/*! + \qmlproperty color SystemPalette::midlight + The midlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::midlight() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Midlight); +} + +/*! + \qmlproperty color SystemPalette::dark + The dark color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::dark() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Dark); +} + +/*! + \qmlproperty color SystemPalette::mid + The mid color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::mid() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Mid); +} + +/*! + \qmlproperty color SystemPalette::shadow + The shadow color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::shadow() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Shadow); +} + +/*! + \qmlproperty color SystemPalette::highlight + The highlight color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::highlight() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::Highlight); +} + +/*! + \qmlproperty color SystemPalette::highlightedText + The highlighted text color of the current color group. + + \sa QPalette::ColorRole +*/ +QColor QDeclarative1SystemPalette::highlightedText() const +{ + Q_D(const QDeclarative1SystemPalette); + return d->palette.color(d->group, QPalette::HighlightedText); +} + +/*! + \qmlproperty enumeration SystemPalette::colorGroup + + The color group of the palette. This can be one of: + + \list + \o SystemPalette.Active (default) + \o SystemPalette.Inactive + \o SystemPalette.Disabled + \endlist + + \sa QPalette::ColorGroup +*/ +QDeclarative1SystemPalette::ColorGroup QDeclarative1SystemPalette::colorGroup() const +{ + Q_D(const QDeclarative1SystemPalette); + return (QDeclarative1SystemPalette::ColorGroup)d->group; +} + +void QDeclarative1SystemPalette::setColorGroup(QDeclarative1SystemPalette::ColorGroup colorGroup) +{ + Q_D(QDeclarative1SystemPalette); + d->group = (QPalette::ColorGroup)colorGroup; + emit paletteChanged(); +} + +bool QDeclarative1SystemPalette::eventFilter(QObject *watched, QEvent *event) +{ + if (watched == qApp) { + if (event->type() == QEvent::ApplicationPaletteChange) { + QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + return false; + } + } + return QObject::eventFilter(watched, event); +} + +bool QDeclarative1SystemPalette::event(QEvent *event) +{ + Q_D(QDeclarative1SystemPalette); + if (event->type() == QEvent::ApplicationPaletteChange) { + d->palette = QApplication::palette(); + emit paletteChanged(); + return true; + } + return QObject::event(event); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativesystempalette_p.h b/src/qtquick1/util/qdeclarativesystempalette_p.h new file mode 100644 index 0000000000..3dd1933748 --- /dev/null +++ b/src/qtquick1/util/qdeclarativesystempalette_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESYSTEMPALETTE_H +#define QDECLARATIVESYSTEMPALETTE_H + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QPalette> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1SystemPalettePrivate; +class Q_AUTOTEST_EXPORT QDeclarative1SystemPalette : public QObject +{ + Q_OBJECT + Q_ENUMS(ColorGroup) + Q_DECLARE_PRIVATE(QDeclarative1SystemPalette) + + Q_PROPERTY(QDeclarative1SystemPalette::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY paletteChanged) + Q_PROPERTY(QColor window READ window NOTIFY paletteChanged) + Q_PROPERTY(QColor windowText READ windowText NOTIFY paletteChanged) + Q_PROPERTY(QColor base READ base NOTIFY paletteChanged) + Q_PROPERTY(QColor text READ text NOTIFY paletteChanged) + Q_PROPERTY(QColor alternateBase READ alternateBase NOTIFY paletteChanged) + Q_PROPERTY(QColor button READ button NOTIFY paletteChanged) + Q_PROPERTY(QColor buttonText READ buttonText NOTIFY paletteChanged) + Q_PROPERTY(QColor light READ light NOTIFY paletteChanged) + Q_PROPERTY(QColor midlight READ midlight NOTIFY paletteChanged) + Q_PROPERTY(QColor dark READ dark NOTIFY paletteChanged) + Q_PROPERTY(QColor mid READ mid NOTIFY paletteChanged) + Q_PROPERTY(QColor shadow READ shadow NOTIFY paletteChanged) + Q_PROPERTY(QColor highlight READ highlight NOTIFY paletteChanged) + Q_PROPERTY(QColor highlightedText READ highlightedText NOTIFY paletteChanged) + +public: + QDeclarative1SystemPalette(QObject *parent=0); + ~QDeclarative1SystemPalette(); + + enum ColorGroup { Active = QPalette::Active, Inactive = QPalette::Inactive, Disabled = QPalette::Disabled }; + + QColor window() const; + QColor windowText() const; + + QColor base() const; + QColor text() const; + QColor alternateBase() const; + + QColor button() const; + QColor buttonText() const; + + QColor light() const; + QColor midlight() const; + QColor dark() const; + QColor mid() const; + QColor shadow() const; + + QColor highlight() const; + QColor highlightedText() const; + + QDeclarative1SystemPalette::ColorGroup colorGroup() const; + void setColorGroup(QDeclarative1SystemPalette::ColorGroup); + +Q_SIGNALS: + void paletteChanged(); + +private: + bool eventFilter(QObject *watched, QEvent *event); + bool event(QEvent *event); + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1SystemPalette) + +QT_END_HEADER + +#endif // QDECLARATIVESYSTEMPALETTE_H diff --git a/src/qtquick1/util/qdeclarativetimeline.cpp b/src/qtquick1/util/qdeclarativetimeline.cpp new file mode 100644 index 0000000000..0b5cbc85b3 --- /dev/null +++ b/src/qtquick1/util/qdeclarativetimeline.cpp @@ -0,0 +1,951 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativetimeline_p_p.h" + +#include <QDebug> +#include <QMutex> +#include <QThread> +#include <QWaitCondition> +#include <QEvent> +#include <QCoreApplication> +#include <QEasingCurve> +#include <QTime> + +QT_BEGIN_NAMESPACE + + + +struct Update { + Update(QDeclarative1TimeLineValue *_g, qreal _v) + : g(_g), v(_v) {} + Update(const QDeclarative1TimeLineCallback &_e) + : g(0), v(0), e(_e) {} + + QDeclarative1TimeLineValue *g; + qreal v; + QDeclarative1TimeLineCallback e; +}; + +struct QDeclarative1TimeLinePrivate +{ + QDeclarative1TimeLinePrivate(QDeclarative1TimeLine *); + + struct Op { + enum Type { + Pause, + Set, + Move, + MoveBy, + Accel, + AccelDistance, + Execute + }; + Op() {} + Op(Type t, int l, qreal v, qreal v2, int o, + const QDeclarative1TimeLineCallback &ev = QDeclarative1TimeLineCallback(), const QEasingCurve &es = QEasingCurve()) + : type(t), length(l), value(v), value2(v2), order(o), event(ev), + easing(es) {} + Op(const Op &o) + : type(o.type), length(o.length), value(o.value), value2(o.value2), + order(o.order), event(o.event), easing(o.easing) {} + Op &operator=(const Op &o) { + type = o.type; length = o.length; value = o.value; + value2 = o.value2; order = o.order; event = o.event; + easing = o.easing; + return *this; + } + + Type type; + int length; + qreal value; + qreal value2; + + int order; + QDeclarative1TimeLineCallback event; + QEasingCurve easing; + }; + struct TimeLine + { + TimeLine() : length(0), consumedOpLength(0), base(0.) {} + QList<Op> ops; + int length; + int consumedOpLength; + qreal base; + }; + + int length; + int syncPoint; + typedef QHash<QDeclarative1TimeLineObject *, TimeLine> Ops; + Ops ops; + QDeclarative1TimeLine *q; + + void add(QDeclarative1TimeLineObject &, const Op &); + qreal value(const Op &op, int time, qreal base, bool *) const; + + int advance(int); + + bool clockRunning; + int prevTime; + + int order; + + QDeclarative1TimeLine::SyncMode syncMode; + int syncAdj; + QList<QPair<int, Update> > *updateQueue; +}; + +QDeclarative1TimeLinePrivate::QDeclarative1TimeLinePrivate(QDeclarative1TimeLine *parent) +: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QDeclarative1TimeLine::LocalSync), syncAdj(0), updateQueue(0) +{ +} + +void QDeclarative1TimeLinePrivate::add(QDeclarative1TimeLineObject &g, const Op &o) +{ + if (g._t && g._t != q) { + qWarning() << "QDeclarative1TimeLine: Cannot modify a QDeclarative1TimeLineValue owned by" + << "another timeline."; + return; + } + g._t = q; + + Ops::Iterator iter = ops.find(&g); + if (iter == ops.end()) { + iter = ops.insert(&g, TimeLine()); + if (syncPoint > 0) + q->pause(g, syncPoint); + } + if (!iter->ops.isEmpty() && + o.type == Op::Pause && + iter->ops.last().type == Op::Pause) { + iter->ops.last().length += o.length; + iter->length += o.length; + } else { + iter->ops.append(o); + iter->length += o.length; + } + + if (iter->length > length) + length = iter->length; + + if (!clockRunning) { + q->stop(); + prevTime = 0; + clockRunning = true; + + if (syncMode == QDeclarative1TimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + q->start(); +/* q->tick(0); + if (syncMode == QDeclarative1TimeLine::LocalSync) { + syncAdj = -1; + } else { + syncAdj = 0; + } + */ + } +} + +qreal QDeclarative1TimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const +{ + Q_ASSERT(time >= 0); + Q_ASSERT(time <= op.length); + *changed = true; + + switch(op.type) { + case Op::Pause: + *changed = false; + return base; + case Op::Set: + return op.value; + case Op::Move: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return op.value; + } else { + qreal delta = op.value - base; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::MoveBy: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value; + } else { + qreal delta = op.value; + qreal pTime = (qreal)(time) / (qreal)op.length; + if (op.easing.type() == QEasingCurve::Linear) + return base + delta * pTime; + else + return base + delta * op.easing.valueForProgress(pTime); + } + case Op::Accel: + if (time == 0) { + return base; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal delta = op.value * t + 0.5f * op.value2 * t * t; + return base + delta; + } + case Op::AccelDistance: + if (time == 0) { + return base; + } else if (time == (op.length)) { + return base + op.value2; + } else { + qreal t = (qreal)(time) / 1000.0f; + qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length; + qreal delta = op.value * t + 0.5f * accel * t * t; + return base + delta; + + } + case Op::Execute: + op.event.d0(op.event.d1); + *changed = false; + return -1; + } + + return base; +} + +/*! + \internal + \class QDeclarative1TimeLine + \brief The QDeclarative1TimeLine class provides a timeline for controlling animations. + + QDeclarative1TimeLine is similar to QTimeLine except: + \list + \i It updates QDeclarative1TimeLineValue instances directly, rather than maintaining a single + current value. + + For example, the following animates a simple value over 200 milliseconds: + \code + QDeclarative1TimeLineValue v(<starting value>); + QDeclarative1TimeLine tl; + tl.move(v, 100., 200); + tl.start() + \endcode + + If your program needs to know when values are changed, it can either + connect to the QDeclarative1TimeLine's updated() signal, or inherit from QDeclarative1TimeLineValue + and reimplement the QDeclarative1TimeLineValue::setValue() method. + + \i Supports multiple QDeclarative1TimeLineValue, arbitrary start and end values and allows + animations to be strung together for more complex effects. + + For example, the following animation moves the x and y coordinates of + an object from wherever they are to the position (100, 100) in 50 + milliseconds and then further animates them to (100, 200) in 50 + milliseconds: + + \code + QDeclarative1TimeLineValue x(<starting value>); + QDeclarative1TimeLineValue y(<starting value>); + + QDeclarative1TimeLine tl; + tl.start(); + + tl.move(x, 100., 50); + tl.move(y, 100., 50); + tl.move(y, 200., 50); + \endcode + + \i All QDeclarative1TimeLine instances share a single, synchronized clock. + + Actions scheduled within the same event loop tick are scheduled + synchronously against each other, regardless of the wall time between the + scheduling. Synchronized scheduling applies both to within the same + QDeclarative1TimeLine and across separate QDeclarative1TimeLine's within the same process. + + \endlist + + Currently easing functions are not supported. +*/ + + +/*! + Construct a new QDeclarative1TimeLine with the specified \a parent. +*/ +QDeclarative1TimeLine::QDeclarative1TimeLine(QObject *parent) +: QAbstractAnimation(parent) +{ + d = new QDeclarative1TimeLinePrivate(this); +} + +/*! + Destroys the time line. Any inprogress animations are canceled, but not + completed. +*/ +QDeclarative1TimeLine::~QDeclarative1TimeLine() +{ + for (QDeclarative1TimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + iter.key()->_t = 0; + + delete d; d = 0; +} + +/*! + \enum QDeclarative1TimeLine::SyncMode + */ + +/*! + Return the timeline's synchronization mode. + */ +QDeclarative1TimeLine::SyncMode QDeclarative1TimeLine::syncMode() const +{ + return d->syncMode; +} + +/*! + Set the timeline's synchronization mode to \a syncMode. + */ +void QDeclarative1TimeLine::setSyncMode(SyncMode syncMode) +{ + d->syncMode = syncMode; +} + +/*! + Pause \a obj for \a time milliseconds. +*/ +void QDeclarative1TimeLine::pause(QDeclarative1TimeLineObject &obj, int time) +{ + if (time <= 0) return; + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Pause, time, 0., 0., d->order++); + d->add(obj, op); +} + +/*! + Execute the \a event. + */ +void QDeclarative1TimeLine::callback(const QDeclarative1TimeLineCallback &callback) +{ + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback); + d->add(*callback.callbackObject(), op); +} + +/*! + Set the \a value of \a timeLineValue. +*/ +void QDeclarative1TimeLine::set(QDeclarative1TimeLineValue &timeLineValue, qreal value) +{ + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Set, 0, value, 0., d->order++); + d->add(timeLineValue, op); +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate. Although the \a acceleration is technically + a deceleration, it should always be positive. The QDeclarative1TimeLine will ensure + that the deceleration is in the opposite direction to the initial velocity. +*/ +int QDeclarative1TimeLine::accel(QDeclarative1TimeLineValue &timeLineValue, qreal velocity, qreal acceleration) +{ + if (acceleration == 0.0f) + return -1; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + \overload + + Decelerate \a timeLineValue from the starting \a velocity to zero at the + given \a acceleration rate over a maximum distance of maxDistance. + + If necessary, QDeclarative1TimeLine will reduce the acceleration to ensure that the + entire operation does not require a move of more than \a maxDistance. + \a maxDistance should always be positive. +*/ +int QDeclarative1TimeLine::accel(QDeclarative1TimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance) +{ + if (maxDistance == 0.0f || acceleration == 0.0f) + return -1; + + Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f); + + qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance); + if (maxAccel > acceleration) + acceleration = maxAccel; + + if ((velocity > 0.0f) == (acceleration > 0.0f)) + acceleration = acceleration * -1.0f; + + int time = static_cast<int>(-1000 * velocity / acceleration); + + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Decelerate \a timeLineValue from the starting \a velocity to zero over the given + \a distance. This is like accel(), but the QDeclarative1TimeLine calculates the exact + deceleration to use. + + \a distance should be positive. +*/ +int QDeclarative1TimeLine::accelDistance(QDeclarative1TimeLineValue &timeLineValue, qreal velocity, qreal distance) +{ + if (distance == 0.0f || velocity == 0.0f) + return -1; + + Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f)); + + int time = static_cast<int>(1000 * (2.0f * distance) / velocity); + + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++); + d->add(timeLineValue, op); + + return time; +} + +/*! + Linearly change the \a timeLineValue from its current value to the given + \a destination value over \a time milliseconds. +*/ +void QDeclarative1TimeLine::move(QDeclarative1TimeLineValue &timeLineValue, qreal destination, int time) +{ + if (time <= 0) return; + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value to the given \a destination + value over \a time milliseconds using the \a easing curve. + */ +void QDeclarative1TimeLine::move(QDeclarative1TimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QDeclarative1TimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Linearly change the \a timeLineValue from its current value by the \a change amount + over \a time milliseconds. +*/ +void QDeclarative1TimeLine::moveBy(QDeclarative1TimeLineValue &timeLineValue, qreal change, int time) +{ + if (time <= 0) return; + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++); + d->add(timeLineValue, op); +} + +/*! + Change the \a timeLineValue from its current value by the \a change amount over + \a time milliseconds using the \a easing curve. + */ +void QDeclarative1TimeLine::moveBy(QDeclarative1TimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time) +{ + if (time <= 0) return; + QDeclarative1TimeLinePrivate::Op op(QDeclarative1TimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QDeclarative1TimeLineCallback(), easing); + d->add(timeLineValue, op); +} + +/*! + Cancel (but don't complete) all scheduled actions for \a timeLineValue. +*/ +void QDeclarative1TimeLine::reset(QDeclarative1TimeLineValue &timeLineValue) +{ + if (!timeLineValue._t) + return; + if (timeLineValue._t != this) { + qWarning() << "QDeclarative1TimeLine: Cannot reset a QDeclarative1TimeLineValue owned by another timeline."; + return; + } + remove(&timeLineValue); + timeLineValue._t = 0; +} + +int QDeclarative1TimeLine::duration() const +{ + return -1; +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo + within this timeline. + + Following operations on \a timeLineValue in this timeline will be scheduled after + all the currently scheduled actions on \a syncTo are complete. In + pseudo-code this is equivalent to: + \code + QDeclarative1TimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue))) + \endcode +*/ +void QDeclarative1TimeLine::sync(QDeclarative1TimeLineValue &timeLineValue, QDeclarative1TimeLineValue &syncTo) +{ + QDeclarative1TimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo); + if (iter == d->ops.end()) + return; + int length = iter->length; + + iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, length); + } else { + int glength = iter->length; + pause(timeLineValue, length - glength); + } +} + +/*! + Synchronize the end point of \a timeLineValue to the endpoint of the longest + action cursrently scheduled in the timeline. + + In pseudo-code, this is equivalent to: + \code + QDeclarative1TimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue)) + \endcode +*/ +void QDeclarative1TimeLine::sync(QDeclarative1TimeLineValue &timeLineValue) +{ + QDeclarative1TimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue); + if (iter == d->ops.end()) { + pause(timeLineValue, d->length); + } else { + pause(timeLineValue, d->length - iter->length); + } +} + +/* + Synchronize all currently and future scheduled values in this timeline to + the longest action currently scheduled. + + For example: + \code + value1->setValue(0.); + value2->setValue(0.); + value3->setValue(0.); + QDeclarative1TimeLine tl; + ... + tl.move(value1, 10, 200); + tl.move(value2, 10, 100); + tl.sync(); + tl.move(value2, 20, 100); + tl.move(value3, 20, 100); + \endcode + + will result in: + + \table + \header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms + \row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10 + \row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0 + \row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0 + \endtable +*/ + +/*void QDeclarative1TimeLine::sync() +{ + for (QDeclarative1TimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) + pause(*iter.key(), d->length - iter->length); + d->syncPoint = d->length; +}*/ + +/*! + \internal + + Temporary hack. + */ +void QDeclarative1TimeLine::setSyncPoint(int sp) +{ + d->syncPoint = sp; +} + +/*! + \internal + + Temporary hack. + */ +int QDeclarative1TimeLine::syncPoint() const +{ + return d->syncPoint; +} + +/*! + Returns true if the timeline is active. An active timeline is one where + QDeclarative1TimeLineValue actions are still pending. +*/ +bool QDeclarative1TimeLine::isActive() const +{ + return !d->ops.isEmpty(); +} + +/*! + Completes the timeline. All queued actions are played to completion, and then discarded. For example, + \code + QDeclarative1TimeLineValue v(0.); + QDeclarative1TimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.complete(); + // v.value() == 100. + \endcode +*/ +void QDeclarative1TimeLine::complete() +{ + d->advance(d->length); +} + +/*! + Resets the timeline. All queued actions are discarded and QDeclarative1TimeLineValue's retain their current value. For example, + \code + QDeclarative1TimeLineValue v(0.); + QDeclarative1TimeLine tl; + tl.move(v, 100., 1000.); + // 500 ms passes + // v.value() == 50. + tl.clear(); + // v.value() == 50. + \endcode +*/ +void QDeclarative1TimeLine::clear() +{ + for (QDeclarative1TimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter) + iter.key()->_t = 0; + d->ops.clear(); + d->length = 0; + d->syncPoint = 0; + //XXX need stop here? +} + +int QDeclarative1TimeLine::time() const +{ + return d->prevTime; +} + +/*! + \fn void QDeclarative1TimeLine::updated() + + Emitted each time the timeline modifies QDeclarative1TimeLineValues. Even if multiple + QDeclarative1TimeLineValues are changed, this signal is only emitted once for each clock tick. +*/ + +void QDeclarative1TimeLine::updateCurrentTime(int v) +{ + if (d->syncAdj == -1) + d->syncAdj = v; + v -= d->syncAdj; + + int timeChanged = v - d->prevTime; +#if 0 + if (!timeChanged) + return; +#endif + d->prevTime = v; + d->advance(timeChanged); + emit updated(); + + // Do we need to stop the clock? + if (d->ops.isEmpty()) { + stop(); + d->prevTime = 0; + d->clockRunning = false; + emit completed(); + } /*else if (pauseTime > 0) { + GfxClock::cancelClock(); + d->prevTime = 0; + GfxClock::pauseFor(pauseTime); + d->syncAdj = 0; + d->clockRunning = false; + }*/ else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + d->syncAdj = 0; + start(); + } +} + +bool operator<(const QPair<int, Update> &lhs, + const QPair<int, Update> &rhs) +{ + return lhs.first < rhs.first; +} + +int QDeclarative1TimeLinePrivate::advance(int t) +{ + int pauseTime = -1; + + // XXX - surely there is a more efficient way? + do { + pauseTime = -1; + // Minimal advance time + int advanceTime = t; + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) { + TimeLine &tl = *iter; + Op &op = tl.ops.first(); + int length = op.length - tl.consumedOpLength; + + if (length < advanceTime) { + advanceTime = length; + if (advanceTime == 0) + break; + } + } + t -= advanceTime; + + // Process until then. A zero length advance time will only process + // sets. + QList<QPair<int, Update> > updates; + + for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) { + QDeclarative1TimeLineValue *v = static_cast<QDeclarative1TimeLineValue *>(iter.key()); + TimeLine &tl = *iter; + Q_ASSERT(!tl.ops.isEmpty()); + + do { + Op &op = tl.ops.first(); + if (advanceTime == 0 && op.length != 0) + continue; + + if (tl.consumedOpLength == 0 && + op.type != Op::Pause && + op.type != Op::Execute) + tl.base = v->value(); + + if ((tl.consumedOpLength + advanceTime) == op.length) { + if (op.type == Op::Execute) { + updates << qMakePair(op.order, Update(op.event)); + } else { + bool changed = false; + qreal val = value(op, op.length, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + } + tl.length -= qMin(advanceTime, tl.length); + tl.consumedOpLength = 0; + tl.ops.removeFirst(); + } else { + tl.consumedOpLength += advanceTime; + bool changed = false; + qreal val = value(op, tl.consumedOpLength, tl.base, &changed); + if (changed) + updates << qMakePair(op.order, Update(v, val)); + tl.length -= qMin(advanceTime, tl.length); + break; + } + + } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0); + + + if (tl.ops.isEmpty()) { + iter = ops.erase(iter); + v->_t = 0; + } else { + if (tl.ops.first().type == Op::Pause && pauseTime != 0) { + int opPauseTime = tl.ops.first().length - tl.consumedOpLength; + if (pauseTime == -1 || opPauseTime < pauseTime) + pauseTime = opPauseTime; + } else { + pauseTime = 0; + } + ++iter; + } + } + + length -= qMin(length, advanceTime); + syncPoint -= advanceTime; + + qSort(updates.begin(), updates.end()); + updateQueue = &updates; + for (int ii = 0; ii < updates.count(); ++ii) { + const Update &v = updates.at(ii).second; + if (v.g) { + v.g->setValue(v.v); + } else { + v.e.d0(v.e.d1); + } + } + updateQueue = 0; + } while(t); + + return pauseTime; +} + +void QDeclarative1TimeLine::remove(QDeclarative1TimeLineObject *v) +{ + QDeclarative1TimeLinePrivate::Ops::Iterator iter = d->ops.find(v); + Q_ASSERT(iter != d->ops.end()); + + int len = iter->length; + d->ops.erase(iter); + if (len == d->length) { + // We need to recalculate the length + d->length = 0; + for (QDeclarative1TimeLinePrivate::Ops::Iterator iter = d->ops.begin(); + iter != d->ops.end(); + ++iter) { + + if (iter->length > d->length) + d->length = iter->length; + + } + } + if (d->ops.isEmpty()) { + stop(); + d->clockRunning = false; + } else if (/*!GfxClock::isActive()*/ state() != Running) { + stop(); + d->prevTime = 0; + d->clockRunning = true; + + if (d->syncMode == QDeclarative1TimeLine::LocalSync) { + d->syncAdj = -1; + } else { + d->syncAdj = 0; + } + start(); + } + + if (d->updateQueue) { + for (int ii = 0; ii < d->updateQueue->count(); ++ii) { + if (d->updateQueue->at(ii).second.g == v || + d->updateQueue->at(ii).second.e.callbackObject() == v) { + d->updateQueue->removeAt(ii); + --ii; + } + } + } + + +} + +/*! + \internal + \class QDeclarative1TimeLineValue + \brief The QDeclarative1TimeLineValue class provides a value that can be modified by QDeclarative1TimeLine. +*/ + +/*! + \fn QDeclarative1TimeLineValue::QDeclarative1TimeLineValue(qreal value = 0) + + Construct a new QDeclarative1TimeLineValue with an initial \a value. +*/ + +/*! + \fn qreal QDeclarative1TimeLineValue::value() const + + Return the current value. +*/ + +/*! + \fn void QDeclarative1TimeLineValue::setValue(qreal value) + + Set the current \a value. +*/ + +/*! + \fn QDeclarative1TimeLine *QDeclarative1TimeLineValue::timeLine() const + + If a QDeclarative1TimeLine is operating on this value, return a pointer to it, + otherwise return null. +*/ + + +QDeclarative1TimeLineObject::QDeclarative1TimeLineObject() +: _t(0) +{ +} + +QDeclarative1TimeLineObject::~QDeclarative1TimeLineObject() +{ + if (_t) { + _t->remove(this); + _t = 0; + } +} + +QDeclarative1TimeLineCallback::QDeclarative1TimeLineCallback() +: d0(0), d1(0), d2(0) +{ +} + +QDeclarative1TimeLineCallback::QDeclarative1TimeLineCallback(QDeclarative1TimeLineObject *b, Callback f, void *d) +: d0(f), d1(d), d2(b) +{ +} + +QDeclarative1TimeLineCallback::QDeclarative1TimeLineCallback(const QDeclarative1TimeLineCallback &o) +: d0(o.d0), d1(o.d1), d2(o.d2) +{ +} + +QDeclarative1TimeLineCallback &QDeclarative1TimeLineCallback::operator=(const QDeclarative1TimeLineCallback &o) +{ + d0 = o.d0; + d1 = o.d1; + d2 = o.d2; + return *this; +} + +QDeclarative1TimeLineObject *QDeclarative1TimeLineCallback::callbackObject() const +{ + return d2; +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativetimeline_p_p.h b/src/qtquick1/util/qdeclarativetimeline_p_p.h new file mode 100644 index 0000000000..73f1ebfabe --- /dev/null +++ b/src/qtquick1/util/qdeclarativetimeline_p_p.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMELINE_H +#define QDECLARATIVETIMELINE_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/QObject> +#include <QtCore/QAbstractAnimation> + +QT_BEGIN_NAMESPACE + +class QEasingCurve; +class QDeclarative1TimeLineValue; +class QDeclarative1TimeLineCallback; +struct QDeclarative1TimeLinePrivate; +class QDeclarative1TimeLineObject; +class Q_AUTOTEST_EXPORT QDeclarative1TimeLine : public QAbstractAnimation +{ +Q_OBJECT +public: + QDeclarative1TimeLine(QObject *parent = 0); + ~QDeclarative1TimeLine(); + + enum SyncMode { LocalSync, GlobalSync }; + SyncMode syncMode() const; + void setSyncMode(SyncMode); + + void pause(QDeclarative1TimeLineObject &, int); + void callback(const QDeclarative1TimeLineCallback &); + void set(QDeclarative1TimeLineValue &, qreal); + + int accel(QDeclarative1TimeLineValue &, qreal velocity, qreal accel); + int accel(QDeclarative1TimeLineValue &, qreal velocity, qreal accel, qreal maxDistance); + int accelDistance(QDeclarative1TimeLineValue &, qreal velocity, qreal distance); + + void move(QDeclarative1TimeLineValue &, qreal destination, int time = 500); + void move(QDeclarative1TimeLineValue &, qreal destination, const QEasingCurve &, int time = 500); + void moveBy(QDeclarative1TimeLineValue &, qreal change, int time = 500); + void moveBy(QDeclarative1TimeLineValue &, qreal change, const QEasingCurve &, int time = 500); + + void sync(); + void setSyncPoint(int); + int syncPoint() const; + + void sync(QDeclarative1TimeLineValue &); + void sync(QDeclarative1TimeLineValue &, QDeclarative1TimeLineValue &); + + void reset(QDeclarative1TimeLineValue &); + + void complete(); + void clear(); + bool isActive() const; + + int time() const; + + virtual int duration() const; +Q_SIGNALS: + void updated(); + void completed(); + +protected: + virtual void updateCurrentTime(int); + +private: + void remove(QDeclarative1TimeLineObject *); + friend class QDeclarative1TimeLineObject; + friend struct QDeclarative1TimeLinePrivate; + QDeclarative1TimeLinePrivate *d; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1TimeLineObject +{ +public: + QDeclarative1TimeLineObject(); + virtual ~QDeclarative1TimeLineObject(); + +protected: + friend class QDeclarative1TimeLine; + friend struct QDeclarative1TimeLinePrivate; + QDeclarative1TimeLine *_t; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1TimeLineValue : public QDeclarative1TimeLineObject +{ +public: + QDeclarative1TimeLineValue(qreal v = 0.) : _v(v) {} + + virtual qreal value() const { return _v; } + virtual void setValue(qreal v) { _v = v; } + + QDeclarative1TimeLine *timeLine() const { return _t; } + + operator qreal() const { return _v; } + QDeclarative1TimeLineValue &operator=(qreal v) { setValue(v); return *this; } +private: + friend class QDeclarative1TimeLine; + friend struct QDeclarative1TimeLinePrivate; + qreal _v; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1TimeLineCallback +{ +public: + typedef void (*Callback)(void *); + + QDeclarative1TimeLineCallback(); + QDeclarative1TimeLineCallback(QDeclarative1TimeLineObject *b, Callback, void * = 0); + QDeclarative1TimeLineCallback(const QDeclarative1TimeLineCallback &o); + + QDeclarative1TimeLineCallback &operator=(const QDeclarative1TimeLineCallback &o); + QDeclarative1TimeLineObject *callbackObject() const; + +private: + friend struct QDeclarative1TimeLinePrivate; + Callback d0; + void *d1; + QDeclarative1TimeLineObject *d2; +}; + +template<class T> +class QDeclarative1TimeLineValueProxy : public QDeclarative1TimeLineValue +{ +public: + QDeclarative1TimeLineValueProxy(T *cls, void (T::*func)(qreal), qreal v = 0.) + : QDeclarative1TimeLineValue(v), _class(cls), _setFunctionReal(func), _setFunctionInt(0) + { + Q_ASSERT(_class); + } + + QDeclarative1TimeLineValueProxy(T *cls, void (T::*func)(int), qreal v = 0.) + : QDeclarative1TimeLineValue(v), _class(cls), _setFunctionReal(0), _setFunctionInt(func) + { + Q_ASSERT(_class); + } + + virtual void setValue(qreal v) + { + QDeclarative1TimeLineValue::setValue(v); + if (_setFunctionReal) (_class->*_setFunctionReal)(v); + else if (_setFunctionInt) (_class->*_setFunctionInt)((int)v); + } + +private: + T *_class; + void (T::*_setFunctionReal)(qreal); + void (T::*_setFunctionInt)(int); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qtquick1/util/qdeclarativetimer.cpp b/src/qtquick1/util/qdeclarativetimer.cpp new file mode 100644 index 0000000000..2456dc06cf --- /dev/null +++ b/src/qtquick1/util/qdeclarativetimer.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativetimer_p.h" + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qpauseanimation.h> +#include <qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + + + + + +class QDeclarative1TimerPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Timer) +public: + QDeclarative1TimerPrivate() + : interval(1000), running(false), repeating(false), triggeredOnStart(false) + , classBegun(false), componentComplete(false), firstTick(true) {} + int interval; + QPauseAnimation pause; + bool running : 1; + bool repeating : 1; + bool triggeredOnStart : 1; + bool classBegun : 1; + bool componentComplete : 1; + bool firstTick : 1; +}; + +/*! + \qmlclass Timer QDeclarative1Timer + \ingroup qml-utility-elements + \since 4.7 + \brief The Timer item triggers a handler at a specified interval. + + A Timer can be used to trigger an action either once, or repeatedly + at a given interval. + + Here is a Timer that shows the current date and time, and updates + the text every 500 milliseconds. It uses the JavaScript \c Date + object to access the current time. + + \qml + import QtQuick 1.0 + + Item { + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + + Text { id: time } + } + \endqml + + The Timer element is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of Timer will be + at best 16ms. + + If the Timer is running and one of its properties is changed, the + elapsed time will be reset. For example, if a Timer with interval of + 1000ms has its \e repeat property changed 500ms after starting, the + elapsed time will be reset to 0, and the Timer will be triggered + 1000ms later. + + \sa {declarative/toys/clocks}{Clocks example} +*/ + +QDeclarative1Timer::QDeclarative1Timer(QObject *parent) + : QObject(*(new QDeclarative1TimerPrivate), parent) +{ + Q_D(QDeclarative1Timer); + connect(&d->pause, SIGNAL(currentLoopChanged(int)), this, SLOT(ticked())); + connect(&d->pause, SIGNAL(finished()), this, SLOT(finished())); + d->pause.setLoopCount(1); + d->pause.setDuration(d->interval); +} + +/*! + \qmlproperty int Timer::interval + + Sets the \a interval between triggers, in milliseconds. + + The default interval is 1000 milliseconds. +*/ +void QDeclarative1Timer::setInterval(int interval) +{ + Q_D(QDeclarative1Timer); + if (interval != d->interval) { + d->interval = interval; + update(); + emit intervalChanged(); + } +} + +int QDeclarative1Timer::interval() const +{ + Q_D(const QDeclarative1Timer); + return d->interval; +} + +/*! + \qmlproperty bool Timer::running + + If set to true, starts the timer; otherwise stops the timer. + For a non-repeating timer, \a running is set to false after the + timer has been triggered. + + \a running defaults to false. + + \sa repeat +*/ +bool QDeclarative1Timer::isRunning() const +{ + Q_D(const QDeclarative1Timer); + return d->running; +} + +void QDeclarative1Timer::setRunning(bool running) +{ + Q_D(QDeclarative1Timer); + if (d->running != running) { + d->running = running; + d->firstTick = true; + emit runningChanged(); + update(); + } +} + +/*! + \qmlproperty bool Timer::repeat + + If \a repeat is true the timer is triggered repeatedly at the + specified interval; otherwise, the timer will trigger once at the + specified interval and then stop (i.e. running will be set to false). + + \a repeat defaults to false. + + \sa running +*/ +bool QDeclarative1Timer::isRepeating() const +{ + Q_D(const QDeclarative1Timer); + return d->repeating; +} + +void QDeclarative1Timer::setRepeating(bool repeating) +{ + Q_D(QDeclarative1Timer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + emit repeatChanged(); + } +} + +/*! + \qmlproperty bool Timer::triggeredOnStart + + When a timer is started, the first trigger is usually after the specified + interval has elapsed. It is sometimes desirable to trigger immediately + when the timer is started; for example, to establish an initial + state. + + If \a triggeredOnStart is true, the timer is triggered immediately + when started, and subsequently at the specified interval. Note that if + \e repeat is set to false, the timer is triggered twice; once on start, + and again at the interval. + + \a triggeredOnStart defaults to false. + + \sa running +*/ +bool QDeclarative1Timer::triggeredOnStart() const +{ + Q_D(const QDeclarative1Timer); + return d->triggeredOnStart; +} + +void QDeclarative1Timer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QDeclarative1Timer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + emit triggeredOnStartChanged(); + } +} + +/*! + \qmlmethod Timer::start() + \brief Starts the timer. + + If the timer is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QDeclarative1Timer::start() +{ + setRunning(true); +} + +/*! + \qmlmethod Timer::stop() + \brief Stops the timer. + + If the timer is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). +*/ +void QDeclarative1Timer::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod Timer::restart() + \brief Restarts the timer. + + If the Timer is not running it will be started, otherwise it will be + stopped, reset to initial state and started. The \c running property + will be true following a call to \c restart(). +*/ +void QDeclarative1Timer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QDeclarative1Timer::update() +{ + Q_D(QDeclarative1Timer); + if (d->classBegun && !d->componentComplete) + return; + d->pause.stop(); + if (d->running) { + d->pause.setCurrentTime(0); + d->pause.setLoopCount(d->repeating ? -1 : 1); + d->pause.setDuration(d->interval); + d->pause.start(); + if (d->triggeredOnStart && d->firstTick) { + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); + } + } +} + +void QDeclarative1Timer::classBegin() +{ + Q_D(QDeclarative1Timer); + d->classBegun = true; +} + +void QDeclarative1Timer::componentComplete() +{ + Q_D(QDeclarative1Timer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QDeclarative1Timer::ticked() +{ + Q_D(QDeclarative1Timer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QDeclarative1Timer::finished() +{ + Q_D(QDeclarative1Timer); + if (d->repeating || !d->running) + return; + emit triggered(); + d->running = false; + d->firstTick = false; + emit runningChanged(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativetimer_p.h b/src/qtquick1/util/qdeclarativetimer_p.h new file mode 100644 index 0000000000..5465773280 --- /dev/null +++ b/src/qtquick1/util/qdeclarativetimer_p.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETIMER_H +#define QDECLARATIVETIMER_H + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/qobject.h> +#include <QtCore/qabstractanimation.h> + +#include <QtDeclarative/private/qdeclarativeglobal_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1TimerPrivate; +class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarative1Timer : public QObject, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1Timer) + Q_INTERFACES(QDeclarativeParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) + Q_PROPERTY(QObject *parent READ parent CONSTANT) + +public: + QDeclarative1Timer(QObject *parent=0); + + void setInterval(int interval); + int interval() const; + + bool isRunning() const; + void setRunning(bool running); + + bool isRepeating() const; + void setRepeating(bool repeating); + + bool triggeredOnStart() const; + void setTriggeredOnStart(bool triggeredOnStart); + +protected: + void classBegin(); + void componentComplete(); + +public Q_SLOTS: + void start(); + void stop(); + void restart(); + +Q_SIGNALS: + void triggered(); + void runningChanged(); + void intervalChanged(); + void repeatChanged(); + void triggeredOnStartChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); + void finished(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Timer) + +QT_END_HEADER + +#endif diff --git a/src/qtquick1/util/qdeclarativetransition.cpp b/src/qtquick1/util/qdeclarativetransition.cpp new file mode 100644 index 0000000000..7a2d4af94e --- /dev/null +++ b/src/qtquick1/util/qdeclarativetransition.cpp @@ -0,0 +1,349 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativestate_p.h" +#include "QtQuick1/private/qdeclarativestategroup_p.h" +#include "QtQuick1/private/qdeclarativestate_p_p.h" +#include "QtQuick1/private/qdeclarativestateoperations_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" +#include "QtQuick1/private/qdeclarativetransitionmanager_p_p.h" + +#include <QParallelAnimationGroup> + +QT_BEGIN_NAMESPACE + + + +/*! + \qmlclass Transition QDeclarative1Transition + \ingroup qml-animation-transition + \since 4.7 + \brief The Transition element defines animated transitions that occur on state changes. + + A Transition defines the animations to be applied when a \l State change occurs. + + For example, the following \l Rectangle has two states: the default state, and + an added "moved" state. In the "moved state, the rectangle's position changes + to (50, 50). The added Transition specifies that when the rectangle + changes between the default and the "moved" state, any changes + to the \c x and \c y properties should be animated, using an \c Easing.InOutQuad. + + \snippet doc/src/snippets/declarative/transition.qml 0 + + Notice the example does not require \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values for the NumberAnimation. As a convenience, + these properties are automatically set to the values of \c x and \c y before + and after the state change; the \c from values are provided by + the current values of \c x and \c y, and the \c to values are provided by + the PropertyChanges object. If you wish, you can provide \l{PropertyAnimation::}{to} and + \l{PropertyAnimation::}{from} values anyway to override the default values. + + By default, a Transition's animations are applied for any state change in the + parent item. The Transition \l {Transition::}{from} and \l {Transition::}{to} + values can be set to restrict the animations to only be applied when changing + from one particular state to another. + + To define multiple transitions, specify \l Item::transitions as a list: + + \snippet doc/src/snippets/declarative/transitions-list.qml list of transitions + + If multiple Transitions are specified, only a single (best-matching) Transition will be applied for any particular + state change. In the example above, when changing to \c state1, the first transition will be used, rather + than the more generic second transition. + + If a state change has a Transition that matches the same property as a + \l Behavior, the Transition animation overrides the \l Behavior for that + state change. + + \sa {QML Animation and Transitions}, {declarative/animation/states}{states example}, {qmlstates}{States}, {QtDeclarative} +*/ + +//ParallelAnimationWrapper_1 allows us to do a "callback" when the animation finishes, rather than connecting +//and disconnecting signals and slots frequently +class ParallelAnimationWrapper_1 : public QParallelAnimationGroup +{ + Q_OBJECT +public: + ParallelAnimationWrapper_1(QObject *parent = 0) : QParallelAnimationGroup(parent) {} + QDeclarative1TransitionPrivate *trans; +protected: + virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState); +}; + +class QDeclarative1TransitionPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1Transition) +public: + QDeclarative1TransitionPrivate() + : fromState(QLatin1String("*")), toState(QLatin1String("*")), + reversed(false), reversible(false), endState(0) + { + group.trans = this; + } + + QString fromState; + QString toState; + bool reversed; + bool reversible; + ParallelAnimationWrapper_1 group; + QDeclarative1TransitionManager *endState; + + void complete() + { + endState->complete(); + } + static void append_animation(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list, QDeclarative1AbstractAnimation *a); + static int animation_count(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list); + static QDeclarative1AbstractAnimation* animation_at(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list, int pos); + static void clear_animations(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list); + QList<QDeclarative1AbstractAnimation *> animations; +}; + +void QDeclarative1TransitionPrivate::append_animation(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list, QDeclarative1AbstractAnimation *a) +{ + QDeclarative1Transition *q = static_cast<QDeclarative1Transition *>(list->object); + q->d_func()->animations.append(a); + q->d_func()->group.addAnimation(a->qtAnimation()); + a->setDisableUserControl(); +} + +int QDeclarative1TransitionPrivate::animation_count(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list) +{ + QDeclarative1Transition *q = static_cast<QDeclarative1Transition *>(list->object); + return q->d_func()->animations.count(); +} + +QDeclarative1AbstractAnimation* QDeclarative1TransitionPrivate::animation_at(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list, int pos) +{ + QDeclarative1Transition *q = static_cast<QDeclarative1Transition *>(list->object); + return q->d_func()->animations.at(pos); +} + +void QDeclarative1TransitionPrivate::clear_animations(QDeclarativeListProperty<QDeclarative1AbstractAnimation> *list) +{ + QDeclarative1Transition *q = static_cast<QDeclarative1Transition *>(list->object); + while (q->d_func()->animations.count()) { + QDeclarative1AbstractAnimation *firstAnim = q->d_func()->animations.at(0); + q->d_func()->group.removeAnimation(firstAnim->qtAnimation()); + q->d_func()->animations.removeAll(firstAnim); + } +} + +void ParallelAnimationWrapper_1::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState) +{ + QParallelAnimationGroup::updateState(newState, oldState); + if (newState == Stopped && (duration() == -1 + || (direction() == QAbstractAnimation::Forward && currentLoopTime() == duration()) + || (direction() == QAbstractAnimation::Backward && currentLoopTime() == 0))) + { + trans->complete(); + } +} + + + +QDeclarative1Transition::QDeclarative1Transition(QObject *parent) + : QObject(*(new QDeclarative1TransitionPrivate), parent) +{ +} + +QDeclarative1Transition::~QDeclarative1Transition() +{ +} + +void QDeclarative1Transition::stop() +{ + Q_D(QDeclarative1Transition); + d->group.stop(); +} + +void QDeclarative1Transition::setReversed(bool r) +{ + Q_D(QDeclarative1Transition); + d->reversed = r; +} + +void QDeclarative1Transition::prepare(QDeclarative1StateOperation::ActionList &actions, + QList<QDeclarativeProperty> &after, + QDeclarative1TransitionManager *endState) +{ + Q_D(QDeclarative1Transition); + + qmlExecuteDeferred(this); + + if (d->reversed) { + for (int ii = d->animations.count() - 1; ii >= 0; --ii) { + d->animations.at(ii)->transition(actions, after, QDeclarative1AbstractAnimation::Backward); + } + } else { + for (int ii = 0; ii < d->animations.count(); ++ii) { + d->animations.at(ii)->transition(actions, after, QDeclarative1AbstractAnimation::Forward); + } + } + + d->endState = endState; + d->group.setDirection(d->reversed ? QAbstractAnimation::Backward : QAbstractAnimation::Forward); + d->group.start(); +} + +/*! + \qmlproperty string Transition::from + \qmlproperty string Transition::to + + These properties indicate the state changes that trigger the transition. + + The default values for these properties is "*" (that is, any state). + + For example, the following transition has not set the \c to and \c from + properties, so the animation is always applied when changing between + the two states (i.e. when the mouse is pressed and released). + + \snippet doc/src/snippets/declarative/transition-from-to.qml 0 + + If the transition was changed to this: + + \snippet doc/src/snippets/declarative/transition-from-to-modified.qml modified transition + + The animation would only be applied when changing from the default state to + the "brighter" state (i.e. when the mouse is pressed, but not on release). + + \sa reversible +*/ +QString QDeclarative1Transition::fromState() const +{ + Q_D(const QDeclarative1Transition); + return d->fromState; +} + +void QDeclarative1Transition::setFromState(const QString &f) +{ + Q_D(QDeclarative1Transition); + if (f == d->fromState) + return; + + d->fromState = f; + emit fromChanged(); +} + +/*! + \qmlproperty bool Transition::reversible + This property holds whether the transition should be automatically reversed when the conditions that triggered this transition are reversed. + + The default value is false. + + By default, transitions run in parallel and are applied to all state + changes if the \l from and \l to states have not been set. In this + situation, the transition is automatically applied when a state change + is reversed, and it is not necessary to set this property to reverse + the transition. + + However, if a SequentialAnimation is used, or if the \l from or \l to + properties have been set, this property will need to be set to reverse + a transition when a state change is reverted. For example, the following + transition applies a sequential animation when the mouse is pressed, + and reverses the sequence of the animation when the mouse is released: + + \snippet doc/src/snippets/declarative/transition-reversible.qml 0 + + If the transition did not set the \c to and \c reversible values, then + on the mouse release, the transition would play the PropertyAnimation + before the ColorAnimation instead of reversing the sequence. +*/ +bool QDeclarative1Transition::reversible() const +{ + Q_D(const QDeclarative1Transition); + return d->reversible; +} + +void QDeclarative1Transition::setReversible(bool r) +{ + Q_D(QDeclarative1Transition); + if (r == d->reversible) + return; + + d->reversible = r; + emit reversibleChanged(); +} + +QString QDeclarative1Transition::toState() const +{ + Q_D(const QDeclarative1Transition); + return d->toState; +} + +void QDeclarative1Transition::setToState(const QString &t) +{ + Q_D(QDeclarative1Transition); + if (t == d->toState) + return; + + d->toState = t; + emit toChanged(); +} + +/*! + \qmlproperty list<Animation> Transition::animations + \default + + This property holds a list of the animations to be run for this transition. + + \snippet examples/declarative/toys/dynamicscene/dynamicscene.qml top-level transitions + + The top-level animations are run in parallel. To run them sequentially, + define them within a SequentialAnimation: + + \snippet doc/src/snippets/declarative/transition-reversible.qml sequential animations +*/ +QDeclarativeListProperty<QDeclarative1AbstractAnimation> QDeclarative1Transition::animations() +{ + Q_D(QDeclarative1Transition); + return QDeclarativeListProperty<QDeclarative1AbstractAnimation>(this, &d->animations, QDeclarative1TransitionPrivate::append_animation, + QDeclarative1TransitionPrivate::animation_count, + QDeclarative1TransitionPrivate::animation_at, + QDeclarative1TransitionPrivate::clear_animations); +} + + + +QT_END_NAMESPACE + +#include <qdeclarativetransition.moc> diff --git a/src/qtquick1/util/qdeclarativetransition_p.h b/src/qtquick1/util/qdeclarativetransition_p.h new file mode 100644 index 0000000000..f3f7afae6d --- /dev/null +++ b/src/qtquick1/util/qdeclarativetransition_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITION_H +#define QDECLARATIVETRANSITION_H + +#include "QtQuick1/private/qdeclarativestate_p.h" + +#include <QtDeclarative/qdeclarative.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1AbstractAnimation; +class QDeclarative1TransitionPrivate; +class QDeclarative1TransitionManager; +class Q_DECLARATIVE_EXPORT QDeclarative1Transition : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QDeclarative1Transition) + + Q_PROPERTY(QString from READ fromState WRITE setFromState NOTIFY fromChanged) + Q_PROPERTY(QString to READ toState WRITE setToState NOTIFY toChanged) + Q_PROPERTY(bool reversible READ reversible WRITE setReversible NOTIFY reversibleChanged) + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1AbstractAnimation> animations READ animations) + Q_CLASSINFO("DefaultProperty", "animations") + Q_CLASSINFO("DeferredPropertyNames", "animations") + +public: + QDeclarative1Transition(QObject *parent=0); + ~QDeclarative1Transition(); + + QString fromState() const; + void setFromState(const QString &); + + QString toState() const; + void setToState(const QString &); + + bool reversible() const; + void setReversible(bool); + + QDeclarativeListProperty<QDeclarative1AbstractAnimation> animations(); + + void prepare(QDeclarative1StateOperation::ActionList &actions, + QList<QDeclarativeProperty> &after, + QDeclarative1TransitionManager *end); + + void setReversed(bool r); + void stop(); + +Q_SIGNALS: + void fromChanged(); + void toChanged(); + void reversibleChanged(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1Transition) + +QT_END_HEADER + +#endif // QDECLARATIVETRANSITION_H diff --git a/src/qtquick1/util/qdeclarativetransitionmanager.cpp b/src/qtquick1/util/qdeclarativetransitionmanager.cpp new file mode 100644 index 0000000000..914168119a --- /dev/null +++ b/src/qtquick1/util/qdeclarativetransitionmanager.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativetransitionmanager_p_p.h" + +#include "QtQuick1/private/qdeclarativestate_p_p.h" +#include "QtQuick1/private/qdeclarativestate_p.h" + +#include <QtDeclarative/private/qdeclarativebinding_p.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> +#include <QtDeclarative/private/qdeclarativeproperty_p.h> + +QT_BEGIN_NAMESPACE + + + +DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG); + +class QDeclarative1TransitionManagerPrivate +{ +public: + QDeclarative1TransitionManagerPrivate() + : state(0) {} + + void applyBindings(); + typedef QList<QDeclarative1SimpleAction> SimpleActionList; + QDeclarative1State *state; + QDeclarativeGuard<QDeclarative1Transition> transition; + QDeclarative1StateOperation::ActionList bindingsList; + SimpleActionList completeList; +}; + +QDeclarative1TransitionManager::QDeclarative1TransitionManager() +: d(new QDeclarative1TransitionManagerPrivate) +{ +} + +void QDeclarative1TransitionManager::setState(QDeclarative1State *s) +{ + d->state = s; +} + +QDeclarative1TransitionManager::~QDeclarative1TransitionManager() +{ + delete d; d = 0; +} + +void QDeclarative1TransitionManager::complete() +{ + d->applyBindings(); + + for (int ii = 0; ii < d->completeList.count(); ++ii) { + const QDeclarativeProperty &prop = d->completeList.at(ii).property(); + prop.write(d->completeList.at(ii).value()); + } + + d->completeList.clear(); + + if (d->state) + static_cast<QDeclarative1StatePrivate*>(QObjectPrivate::get(d->state))->complete(); +} + +void QDeclarative1TransitionManagerPrivate::applyBindings() +{ + foreach(const QDeclarative1Action &action, bindingsList) { + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data()); + } else if (action.event) { + if (action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } + + } + + bindingsList.clear(); +} + +void QDeclarative1TransitionManager::transition(const QList<QDeclarative1Action> &list, + QDeclarative1Transition *transition) +{ + cancel(); + + QDeclarative1StateOperation::ActionList applyList = list; + // Determine which actions are binding changes. + foreach(const QDeclarative1Action &action, applyList) { + if (action.toBinding) + d->bindingsList << action; + if (action.fromBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Disable current binding + if (action.event && action.event->changesBindings()) { //### assume isReversable()? + d->bindingsList << action; + action.event->clearBindings(); + } + } + + // Animated transitions need both the start and the end value for + // each property change. In the presence of bindings, the end values + // are non-trivial to calculate. As a "best effort" attempt, we first + // apply all the property and binding changes, then read all the actual + // final values, then roll back the changes and proceed as normal. + // + // This doesn't catch everything, and it might be a little fragile in + // some cases - but whatcha going to do? + + if (!d->bindingsList.isEmpty()) { + + // Apply all the property and binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + const QDeclarative1Action &action = applyList.at(ii); + if (!action.toBinding.isNull()) { + QDeclarativePropertyPrivate::setBinding(action.property, action.toBinding.data(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (!action.event) { + QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } else if (action.event->isReversable()) { + if (action.reverseEvent) + action.event->reverse(QDeclarative1ActionEvent::FastForward); + else + action.event->execute(QDeclarative1ActionEvent::FastForward); + } + } + + // Read all the end values for binding changes + for (int ii = 0; ii < applyList.size(); ++ii) { + QDeclarative1Action *action = &applyList[ii]; + if (action->event) { + action->event->saveTargetValues(); + continue; + } + const QDeclarativeProperty &prop = action->property; + if (!action->toBinding.isNull() || !action->toValue.isValid()) { + action->toValue = prop.read(); + } + } + + // Revert back to the original values + foreach(const QDeclarative1Action &action, applyList) { + if (action.event) { + if (action.event->isReversable()) { + action.event->clearBindings(); + action.event->rewind(); + action.event->clearBindings(); //### shouldn't be needed + } + continue; + } + + if (action.toBinding) + QDeclarativePropertyPrivate::setBinding(action.property, 0); // Make sure this is disabled during the transition + + QDeclarativePropertyPrivate::write(action.property, action.fromValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding); + } + } + + if (transition) { + QList<QDeclarativeProperty> touched; + d->transition = transition; + d->transition->prepare(applyList, touched, this); + + // Modify the action list to remove actions handled in the transition + for (int ii = 0; ii < applyList.count(); ++ii) { + const QDeclarative1Action &action = applyList.at(ii); + + if (action.event) { + + if (action.actionDone) { + applyList.removeAt(ii); + --ii; + } + + } else { + + if (touched.contains(action.property)) { + if (action.toValue != action.fromValue) + d->completeList << + QDeclarative1SimpleAction(action, QDeclarative1SimpleAction::EndState); + + applyList.removeAt(ii); + --ii; + } + + } + } + } + + // Any actions remaining have not been handled by the transition and should + // be applied immediately. We skip applying bindings, as they are all + // applied at the end in applyBindings() to avoid any nastiness mid + // transition + foreach(const QDeclarative1Action &action, applyList) { + if (action.event && !action.event->changesBindings()) { + if (action.event->isReversable() && action.reverseEvent) + action.event->reverse(); + else + action.event->execute(); + } else if (!action.event && !action.toBinding) { + action.property.write(action.toValue); + } + } +#ifndef QT_NO_DEBUG_STREAM + if (stateChangeDebug()) { + foreach(const QDeclarative1Action &action, applyList) { + if (action.event) + qWarning() << " No transition for event:" << action.event->typeName(); + else + qWarning() << " No transition for:" << action.property.object() + << action.property.name() << "From:" << action.fromValue + << "To:" << action.toValue; + } + } +#endif + if (!transition) + d->applyBindings(); +} + +void QDeclarative1TransitionManager::cancel() +{ + if (d->transition) { + // ### this could potentially trigger a complete in rare circumstances + d->transition->stop(); + d->transition = 0; + } + + for(int i = 0; i < d->bindingsList.count(); ++i) { + QDeclarative1Action action = d->bindingsList[i]; + if (!action.toBinding.isNull() && action.deletableToBinding) { + QDeclarativePropertyPrivate::setBinding(action.property, 0); + action.toBinding.data()->destroy(); + action.toBinding.clear(); + action.deletableToBinding = false; + } else if (action.event) { + //### what do we do here? + } + + } + d->bindingsList.clear(); + d->completeList.clear(); +} + + + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativetransitionmanager_p_p.h b/src/qtquick1/util/qdeclarativetransitionmanager_p_p.h new file mode 100644 index 0000000000..3f4100786f --- /dev/null +++ b/src/qtquick1/util/qdeclarativetransitionmanager_p_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVETRANSITIONMANAGER_P_H +#define QDECLARATIVETRANSITIONMANAGER_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 "QtQuick1/private/qdeclarativestateoperations_p.h" + +QT_BEGIN_NAMESPACE + +class QDeclarative1StatePrivate; +class QDeclarative1TransitionManagerPrivate; +class Q_AUTOTEST_EXPORT QDeclarative1TransitionManager +{ +public: + QDeclarative1TransitionManager(); + ~QDeclarative1TransitionManager(); + + void transition(const QList<QDeclarative1Action> &, QDeclarative1Transition *transition); + + void cancel(); + +private: + Q_DISABLE_COPY(QDeclarative1TransitionManager) + QDeclarative1TransitionManagerPrivate *d; + + void complete(); + void setState(QDeclarative1State *); + + friend class QDeclarative1State; + friend class QDeclarative1TransitionPrivate; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVETRANSITIONMANAGER_P_H diff --git a/src/qtquick1/util/qdeclarativeutilmodule.cpp b/src/qtquick1/util/qdeclarativeutilmodule.cpp new file mode 100644 index 0000000000..65afa3655f --- /dev/null +++ b/src/qtquick1/util/qdeclarativeutilmodule.cpp @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativeutilmodule_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p.h" +#include "QtQuick1/private/qdeclarativeanimation_p_p.h" +#include "QtQuick1/private/qdeclarativebehavior_p.h" +#include "QtQuick1/private/qdeclarativebind_p.h" +#include "QtQuick1/private/qdeclarativeconnections_p.h" +#include "QtQuick1/private/qdeclarativesmoothedanimation_p.h" +#include "QtQuick1/private/qdeclarativefontloader_p.h" +#include "QtQuick1/private/qdeclarativelistaccessor_p.h" +//#include "QtQuick1/private/qdeclarativelistmodel_p.h" +#include "QtQuick1/private/qdeclarativeopenmetaobject_p.h" +#include "QtQuick1/private/qdeclarativepackage_p.h" +#include "QtQuick1/private/qdeclarativepixmapcache_p.h" +#include "QtQuick1/private/qdeclarativepropertychanges_p.h" +#include "QtQuick1/private/qdeclarativespringanimation_p.h" +#include "QtQuick1/private/qdeclarativestategroup_p.h" +#include "QtQuick1/private/qdeclarativestateoperations_p.h" +#include "QtQuick1/private/qdeclarativestate_p.h" +#include "QtQuick1/private/qdeclarativestate_p_p.h" +#include "QtQuick1/private/qdeclarativestyledtext_p.h" +#include "QtQuick1/private/qdeclarativesystempalette_p.h" +#include "QtQuick1/private/qdeclarativetimeline_p_p.h" +#include "QtQuick1/private/qdeclarativetimer_p.h" +#include "QtQuick1/private/qdeclarativetransitionmanager_p_p.h" +#include "QtQuick1/private/qdeclarativetransition_p.h" +#include "QtQuick1/private/qdeclarativeapplication_p.h" +#include "QtQuick1/qdeclarativeview.h" +#include <QtDeclarative/qdeclarativeinfo.h> +#include <QtDeclarative/private/qdeclarativetypenotavailable_p.h> +#ifndef QT_NO_XMLPATTERNS +#include "QtQuick1/private/qdeclarativexmllistmodel_p.h" +#endif + + +void QDeclarative1UtilModule::defineModule(QDeclarativeQtQuick1Module::Module module) +{ + qmlRegisterType<QDeclarative1Anchors>(); + qmlRegisterType<QDeclarative1StateOperation>(); + qmlRegisterType<QDeclarative1AnchorSet>(); + + if (module == QDeclarativeQtQuick1Module::QtQuick1) { + qmlRegisterUncreatableType<QDeclarative1Application>("QtQuick",1,1,"Application", QDeclarative1Application::tr("Application is an abstract class")); + + qmlRegisterType<QDeclarative1AnchorAnimation>("QtQuick",1,0,"AnchorAnimation"); + qmlRegisterType<QDeclarative1AnchorChanges>("QtQuick",1,0,"AnchorChanges"); + qmlRegisterType<QDeclarative1Behavior>("QtQuick",1,0,"Behavior"); + qmlRegisterType<QDeclarative1Bind>("QtQuick",1,0,"Binding"); + qmlRegisterType<QDeclarative1ColorAnimation>("QtQuick",1,0,"ColorAnimation"); + qmlRegisterType<QDeclarative1Connections>("QtQuick",1,0,"Connections"); + qmlRegisterType<QDeclarative1SmoothedAnimation>("QtQuick",1,0,"SmoothedAnimation"); + qmlRegisterType<QDeclarative1FontLoader>("QtQuick",1,0,"FontLoader"); + // qmlRegisterType<QDeclarative1ListElement>("QtQuick",1,0,"ListElement"); + qmlRegisterType<QDeclarative1NumberAnimation>("QtQuick",1,0,"NumberAnimation"); + qmlRegisterType<QDeclarative1Package>("QtQuick",1,0,"Package"); + qmlRegisterType<QDeclarative1ParallelAnimation>("QtQuick",1,0,"ParallelAnimation"); + qmlRegisterType<QDeclarative1ParentAnimation>("QtQuick",1,0,"ParentAnimation"); + qmlRegisterType<QDeclarative1ParentChange>("QtQuick",1,0,"ParentChange"); + qmlRegisterType<QDeclarative1PauseAnimation>("QtQuick",1,0,"PauseAnimation"); + qmlRegisterType<QDeclarative1PropertyAction>("QtQuick",1,0,"PropertyAction"); + qmlRegisterType<QDeclarative1PropertyAnimation>("QtQuick",1,0,"PropertyAnimation"); + qmlRegisterType<QDeclarative1RotationAnimation>("QtQuick",1,0,"RotationAnimation"); + qmlRegisterType<QDeclarative1ScriptAction>("QtQuick",1,0,"ScriptAction"); + qmlRegisterType<QDeclarative1SequentialAnimation>("QtQuick",1,0,"SequentialAnimation"); + qmlRegisterType<QDeclarative1SpringAnimation>("QtQuick",1,0,"SpringAnimation"); + qmlRegisterType<QDeclarative1StateChangeScript>("QtQuick",1,0,"StateChangeScript"); + qmlRegisterType<QDeclarative1StateGroup>("QtQuick",1,0,"StateGroup"); + qmlRegisterType<QDeclarative1State>("QtQuick",1,0,"State"); + qmlRegisterType<QDeclarative1SystemPalette>("QtQuick",1,0,"SystemPalette"); + qmlRegisterType<QDeclarative1Timer>("QtQuick",1,0,"Timer"); + qmlRegisterType<QDeclarative1Transition>("QtQuick",1,0,"Transition"); + qmlRegisterType<QDeclarative1Vector3dAnimation>("QtQuick",1,0,"Vector3dAnimation"); +#ifdef QT_NO_XMLPATTERNS + qmlRegisterTypeNotAvailable("QtQuick",1,0,"XmlListModel", + qApp->translate("QDeclarative1XmlListModel","Qt was built without support for xmlpatterns")); + qmlRegisterTypeNotAvailable("QtQuick",1,0,"XmlRole", + qApp->translate("QDeclarative1XmlListModel","Qt was built without support for xmlpatterns")); +#else + qmlRegisterType<QDeclarative1XmlListModel>("QtQuick",1,0,"XmlListModel"); + qmlRegisterType<QDeclarative1XmlListModelRole>("QtQuick",1,0,"XmlRole"); +#endif + + + qmlRegisterUncreatableType<QDeclarative1AbstractAnimation>("QtQuick",1,0,"Animation",QDeclarative1AbstractAnimation::tr("Animation is an abstract class")); + + // qmlRegisterCustomType<QDeclarative1ListModel>("QtQuick",1,0,"ListModel", new QDeclarative1ListModelParser); + qmlRegisterCustomType<QDeclarative1PropertyChanges>("QtQuick",1,0,"PropertyChanges", new QDeclarative1PropertyChangesParser); + qmlRegisterCustomType<QDeclarative1Connections>("QtQuick",1,0,"Connections", new QDeclarative1ConnectionsParser); + } else if (module == QDeclarativeQtQuick1Module::Qt47) { + qmlRegisterType<QDeclarative1AnchorAnimation>("Qt",4,7,"AnchorAnimation"); + qmlRegisterType<QDeclarative1AnchorChanges>("Qt",4,7,"AnchorChanges"); + qmlRegisterType<QDeclarative1Behavior>("Qt",4,7,"Behavior"); + qmlRegisterType<QDeclarative1Bind>("Qt",4,7,"Binding"); + qmlRegisterType<QDeclarative1ColorAnimation>("Qt",4,7,"ColorAnimation"); + qmlRegisterType<QDeclarative1Connections>("Qt",4,7,"Connections"); + qmlRegisterType<QDeclarative1SmoothedAnimation>("Qt",4,7,"SmoothedAnimation"); + qmlRegisterType<QDeclarative1FontLoader>("Qt",4,7,"FontLoader"); + // qmlRegisterType<QDeclarative1ListElement>("Qt",4,7,"ListElement"); + qmlRegisterType<QDeclarative1NumberAnimation>("Qt",4,7,"NumberAnimation"); + qmlRegisterType<QDeclarative1Package>("Qt",4,7,"Package"); + qmlRegisterType<QDeclarative1ParallelAnimation>("Qt",4,7,"ParallelAnimation"); + qmlRegisterType<QDeclarative1ParentAnimation>("Qt",4,7,"ParentAnimation"); + qmlRegisterType<QDeclarative1ParentChange>("Qt",4,7,"ParentChange"); + qmlRegisterType<QDeclarative1PauseAnimation>("Qt",4,7,"PauseAnimation"); + qmlRegisterType<QDeclarative1PropertyAction>("Qt",4,7,"PropertyAction"); + qmlRegisterType<QDeclarative1PropertyAnimation>("Qt",4,7,"PropertyAnimation"); + qmlRegisterType<QDeclarative1RotationAnimation>("Qt",4,7,"RotationAnimation"); + qmlRegisterType<QDeclarative1ScriptAction>("Qt",4,7,"ScriptAction"); + qmlRegisterType<QDeclarative1SequentialAnimation>("Qt",4,7,"SequentialAnimation"); + qmlRegisterType<QDeclarative1SpringAnimation>("Qt",4,7,"SpringAnimation"); + qmlRegisterType<QDeclarative1StateChangeScript>("Qt",4,7,"StateChangeScript"); + qmlRegisterType<QDeclarative1StateGroup>("Qt",4,7,"StateGroup"); + qmlRegisterType<QDeclarative1State>("Qt",4,7,"State"); + qmlRegisterType<QDeclarative1SystemPalette>("Qt",4,7,"SystemPalette"); + qmlRegisterType<QDeclarative1Timer>("Qt",4,7,"Timer"); + qmlRegisterType<QDeclarative1Transition>("Qt",4,7,"Transition"); + qmlRegisterType<QDeclarative1Vector3dAnimation>("Qt",4,7,"Vector3dAnimation"); +#ifdef QT_NO_XMLPATTERNS + qmlRegisterTypeNotAvailable("Qt",4,7,"XmlListModel", + qApp->translate("QDeclarative1XmlListModel","Qt was built without support for xmlpatterns")); + qmlRegisterTypeNotAvailable("Qt",4,7,"XmlRole", + qApp->translate("QDeclarative1XmlListModel","Qt was built without support for xmlpatterns")); +#else + qmlRegisterType<QDeclarative1XmlListModel>("Qt",4,7,"XmlListModel"); + qmlRegisterType<QDeclarative1XmlListModelRole>("Qt",4,7,"XmlRole"); +#endif + + qmlRegisterUncreatableType<QDeclarative1AbstractAnimation>("Qt",4,7,"Animation",QDeclarative1AbstractAnimation::tr("Animation is an abstract class")); + + // qmlRegisterCustomType<QDeclarative1ListModel>("Qt", 4,7, "ListModel", new QDeclarative1ListModelParser); + qmlRegisterCustomType<QDeclarative1PropertyChanges>("Qt", 4, 7, "PropertyChanges", new QDeclarative1PropertyChangesParser); + qmlRegisterCustomType<QDeclarative1Connections>("Qt", 4, 7, "Connections", new QDeclarative1ConnectionsParser); + } +} + + + diff --git a/src/qtquick1/util/qdeclarativeutilmodule_p.h b/src/qtquick1/util/qdeclarativeutilmodule_p.h new file mode 100644 index 0000000000..b6561ccd3f --- /dev/null +++ b/src/qtquick1/util/qdeclarativeutilmodule_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEUTILMODULE_H +#define QDECLARATIVEUTILMODULE_H + +#include <QtDeclarative/qdeclarative.h> +#include "../qtquick1_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarative1UtilModule +{ +public: + static void defineModule(QDeclarativeQtQuick1Module::Module module); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEUTILMODULE_H diff --git a/src/qtquick1/util/qdeclarativeview.cpp b/src/qtquick1/util/qdeclarativeview.cpp new file mode 100644 index 0000000000..b6ba7a4fcb --- /dev/null +++ b/src/qtquick1/util/qdeclarativeview.cpp @@ -0,0 +1,737 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/qdeclarativeview.h" + +#include <QtDeclarative/qdeclarative.h> +#include <QtQuick1/qdeclarativeitem.h> +#include <QtDeclarative/qdeclarativeengine.h> +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/private/qdeclarativeglobal_p.h> +#include <QtDeclarative/private/qdeclarativeguard_p.h> + +#include <QtDeclarative/private/qdeclarativedebugtrace_p.h> +#include <QtDeclarative/private/qdeclarativeinspectorservice_p.h> + +#include <qscriptvalueiterator.h> +#include <qdebug.h> +#include <qtimer.h> +#include <qevent.h> +#include <qdir.h> +#include <qcoreapplication.h> +#include <qfontdatabase.h> +#include <qicon.h> +#include <qurl.h> +#include <qlayout.h> +#include <qwidget.h> +#include <qgraphicswidget.h> +#include <qbasictimer.h> +#include <QtCore/qabstractanimation.h> +#include <private/qgraphicsview_p.h> +#include <QtQuick1/private/qdeclarativeitem_p.h> +#include <private/qabstractanimation_p.h> +#include <QtQuick1/private/qdeclarativeitemchangelistener_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(frameRateDebug, QML_SHOW_FRAMERATE) + +class QDeclarative1Scene : public QGraphicsScene +{ +public: + QDeclarative1Scene(QObject *parent = 0); + +protected: + virtual void keyPressEvent(QKeyEvent *); + virtual void keyReleaseEvent(QKeyEvent *); + + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *); +}; + +QDeclarative1Scene::QDeclarative1Scene(QObject *parent) : QGraphicsScene(parent) +{ +} + +void QDeclarative1Scene::keyPressEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QGraphicsScene::keyPressEvent(e); +} + +void QDeclarative1Scene::keyReleaseEvent(QKeyEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Key); + + QGraphicsScene::keyReleaseEvent(e); +} + +void QDeclarative1Scene::mouseMoveEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mouseMoveEvent(e); +} + +void QDeclarative1Scene::mousePressEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mousePressEvent(e); +} + +void QDeclarative1Scene::mouseReleaseEvent(QGraphicsSceneMouseEvent *e) +{ + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::Mouse); + + QGraphicsScene::mouseReleaseEvent(e); +} + +class QDeclarativeViewPrivate : public QGraphicsViewPrivate, public QDeclarativeItemChangeListener +{ + Q_DECLARE_PUBLIC(QDeclarativeView) +public: + QDeclarativeViewPrivate() + : root(0), declarativeItemRoot(0), graphicsWidgetRoot(0), component(0), + resizeMode(QDeclarativeView::SizeViewToRootObject), initialSize(0,0) {} + ~QDeclarativeViewPrivate() { delete root; delete engine; } + void execute(); + void itemGeometryChanged(QDeclarativeItem *item, const QRectF &newGeometry, const QRectF &oldGeometry); + void initResize(); + void updateSize(); + inline QSize rootObjectSize() const; + + QDeclarativeGuard<QGraphicsObject> root; + QDeclarativeGuard<QDeclarativeItem> declarativeItemRoot; + QDeclarativeGuard<QGraphicsWidget> graphicsWidgetRoot; + + QUrl source; + + QDeclarativeEngine* engine; + QDeclarativeComponent *component; + QBasicTimer resizetimer; + + QDeclarativeView::ResizeMode resizeMode; + QSize initialSize; + QElapsedTimer frameTimer; + + void init(); +}; + +void QDeclarativeViewPrivate::execute() +{ + Q_Q(QDeclarativeView); + 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 QDeclarativeViewPrivate::itemGeometryChanged(QDeclarativeItem *resizeItem, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QDeclarativeView); + if (resizeItem == root && resizeMode == QDeclarativeView::SizeViewToRootObject) { + // wait for both width and height to be changed + resizetimer.start(0,q); + } + QDeclarativeItemChangeListener::itemGeometryChanged(resizeItem, newGeometry, oldGeometry); +} + +/*! + \class QDeclarativeView + \since 4.7 + \brief The QDeclarativeView class provides a widget for displaying a Qt Declarative user interface. + + QDeclarativeItem objects can be placed on a standard QGraphicsScene and + displayed with QGraphicsView. QDeclarativeView is a QGraphicsView subclass + provided as a convenience for displaying QML files, and connecting between + QML and C++ Qt objects. + + QDeclarativeView provides: + + \list + \o Management of QDeclarativeComponent loading and object creation + \o Initialization of QGraphicsView for optimal performance with QML using these settings: + \list + \o QGraphicsView::setOptimizationFlags(QGraphicsView::DontSavePainterState) + \o QGraphicsView::setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate) + \o QGraphicsScene::setItemIndexMethod(QGraphicsScene::NoIndex) + \endlist + \o Initialization of QGraphicsView for QML key handling using these settings: + \list + \o QGraphicsView::viewport()->setFocusPolicy(Qt::NoFocus) + \o QGraphicsView::setFocusPolicy(Qt::StrongFocus) + \o QGraphicsScene::setStickyFocus(true) + \endlist + \endlist + + Typical usage: + + \code + QDeclarativeView *view = new QDeclarativeView; + view->setSource(QUrl::fromLocalFile("myqmlfile.qml")); + view->show(); + \endcode + + Since QDeclarativeView is a QWidget-based class, it can be used to + display QML interfaces within QWidget-based GUI applications that do not + use the Graphics View framework. + + To receive errors related to loading and executing QML with QDeclarativeView, + you can connect to the statusChanged() signal and monitor for QDeclarativeView::Error. + The errors are available via QDeclarativeView::errors(). + + If you're using your own QGraphicsScene-based scene with QDeclarativeView, remember to + enable scene's sticky focus mode and to set itemIndexMethod to QGraphicsScene::NoIndex. + + \sa {Integrating QML Code with Existing Qt UI Code}, {Using QML Bindings in C++ Applications} +*/ + + +/*! \fn void QDeclarativeView::sceneResized(QSize size) + This signal is emitted when the view is resized to \a size. +*/ + +/*! \fn void QDeclarativeView::statusChanged(QDeclarativeView::Status status) + This signal is emitted when the component's current \a status changes. +*/ + +/*! + \fn QDeclarativeView::QDeclarativeView(QWidget *parent) + + Constructs a QDeclarativeView with the given \a parent. +*/ +QDeclarativeView::QDeclarativeView(QWidget *parent) + : QGraphicsView(*(new QDeclarativeViewPrivate), parent) +{ + Q_D(QDeclarativeView); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); +} + +/*! + \fn QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent) + + Constructs a QDeclarativeView with the given QML \a source and \a parent. +*/ +QDeclarativeView::QDeclarativeView(const QUrl &source, QWidget *parent) + : QGraphicsView(*(new QDeclarativeViewPrivate), parent) +{ + Q_D(QDeclarativeView); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + d->init(); + setSource(source); +} + +void QDeclarativeViewPrivate::init() +{ + Q_Q(QDeclarativeView); + engine = new QDeclarativeEngine(); + q->setScene(new QDeclarative1Scene(q)); + + q->setOptimizationFlags(QGraphicsView::DontSavePainterState); + q->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + q->setFrameStyle(0); + + // These seem to give the best performance + q->setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); + q->scene()->setItemIndexMethod(QGraphicsScene::NoIndex); + q->viewport()->setFocusPolicy(Qt::NoFocus); + q->setFocusPolicy(Qt::StrongFocus); + + q->scene()->setStickyFocus(true); //### needed for correct focus handling + +#ifdef QDECLARATIVEVIEW_NOBACKGROUND + q->setAttribute(Qt::WA_OpaquePaintEvent); + q->setAttribute(Qt::WA_NoSystemBackground); + q->viewport()->setAttribute(Qt::WA_OpaquePaintEvent); + q->viewport()->setAttribute(Qt::WA_NoSystemBackground); +#endif + + QDeclarativeInspectorService::instance()->addView(q); +} + +/*! + Destroys the view. + */ +QDeclarativeView::~QDeclarativeView() +{ + QDeclarativeInspectorService::instance()->removeView(this); +} + +/*! \property QDeclarativeView::source + \brief The URL of the source of the QML component. + + Changing this property causes the QML component to be reloaded. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + */ + +/*! + Sets the source to the \a url, loads the QML component and instantiates it. + + Ensure that the URL provided is full and correct, in particular, use + \l QUrl::fromLocalFile() when loading a file from the local filesystem. + + Calling this methods multiple times with the same url will result + in the QML being reloaded. + */ +void QDeclarativeView::setSource(const QUrl& url) +{ + Q_D(QDeclarativeView); + d->source = url; + d->execute(); +} + +/*! + Returns the source URL, if set. + + \sa setSource() + */ +QUrl QDeclarativeView::source() const +{ + Q_D(const QDeclarativeView); + return d->source; +} + +/*! + Returns a pointer to the QDeclarativeEngine used for instantiating + QML Components. + */ +QDeclarativeEngine* QDeclarativeView::engine() const +{ + Q_D(const QDeclarativeView); + return d->engine; +} + +/*! + This function returns the root of the context hierarchy. Each QML + component is instantiated in a QDeclarativeContext. QDeclarativeContext's are + essential for passing data to QML components. In QML, contexts are + arranged hierarchically and this hierarchy is managed by the + QDeclarativeEngine. + */ +QDeclarativeContext* QDeclarativeView::rootContext() const +{ + Q_D(const QDeclarativeView); + return d->engine->rootContext(); +} + +/*! + \enum QDeclarativeView::Status + Specifies the loading status of the QDeclarativeView. + + \value Null This QDeclarativeView has no source set. + \value Ready This QDeclarativeView has loaded and created the QML component. + \value Loading This QDeclarativeView is loading network data. + \value Error One or more errors has occurred. Call errors() to retrieve a list + of errors. +*/ + +/*! \enum QDeclarativeView::ResizeMode + + This enum specifies how to resize the view. + + \value SizeViewToRootObject The view resizes with the root item in the QML. + \value SizeRootObjectToView The view will automatically resize the root item to the size of the view. +*/ + +/*! + \property QDeclarativeView::status + The component's current \l{QDeclarativeView::Status} {status}. +*/ + +QDeclarativeView::Status QDeclarativeView::status() const +{ + Q_D(const QDeclarativeView); + if (!d->component) + return QDeclarativeView::Null; + + return QDeclarativeView::Status(d->component->status()); +} + +/*! + Return the list of errors that occurred during the last compile or create + operation. When the status is not Error, an empty list is returned. +*/ +QList<QDeclarativeError> QDeclarativeView::errors() const +{ + Q_D(const QDeclarativeView); + if (d->component) + return d->component->errors(); + return QList<QDeclarativeError>(); +} + +/*! + \property QDeclarativeView::resizeMode + \brief whether the view should resize the canvas contents + + If this property is set to SizeViewToRootObject (the default), the view + resizes with the root item in the QML. + + If this property is set to SizeRootObjectToView, the view will + automatically resize the root item. + + Regardless of this property, the sizeHint of the view + is the initial size of the root item. Note though that + since QML may load dynamically, that size may change. +*/ + +void QDeclarativeView::setResizeMode(ResizeMode mode) +{ + Q_D(QDeclarativeView); + if (d->resizeMode == mode) + return; + + if (d->declarativeItemRoot) { + if (d->resizeMode == SizeViewToRootObject) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(d->declarativeItemRoot)); + p->removeItemChangeListener(d, QDeclarativeItemPrivate::Geometry); + } + } else if (d->graphicsWidgetRoot) { + if (d->resizeMode == QDeclarativeView::SizeViewToRootObject) { + d->graphicsWidgetRoot->removeEventFilter(this); + } + } + + d->resizeMode = mode; + if (d->root) { + d->initResize(); + } +} + +void QDeclarativeViewPrivate::initResize() +{ + Q_Q(QDeclarativeView); + if (declarativeItemRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QDeclarativeItemPrivate *p = + static_cast<QDeclarativeItemPrivate *>(QGraphicsItemPrivate::get(declarativeItemRoot)); + p->addItemChangeListener(this, QDeclarativeItemPrivate::Geometry); + } + } else if (graphicsWidgetRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + graphicsWidgetRoot->installEventFilter(q); + } + } + updateSize(); +} + +void QDeclarativeViewPrivate::updateSize() +{ + Q_Q(QDeclarativeView); + if (!root) + return; + if (declarativeItemRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QSize newSize = QSize(declarativeItemRoot->width(), declarativeItemRoot->height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) { + if (!qFuzzyCompare(q->width(), declarativeItemRoot->width())) + declarativeItemRoot->setWidth(q->width()); + if (!qFuzzyCompare(q->height(), declarativeItemRoot->height())) + declarativeItemRoot->setHeight(q->height()); + } + } else if (graphicsWidgetRoot) { + if (resizeMode == QDeclarativeView::SizeViewToRootObject) { + QSize newSize = QSize(graphicsWidgetRoot->size().width(), graphicsWidgetRoot->size().height()); + if (newSize.isValid() && newSize != q->size()) { + q->resize(newSize); + } + } else if (resizeMode == QDeclarativeView::SizeRootObjectToView) { + QSizeF newSize = QSize(q->size().width(), q->size().height()); + if (newSize.isValid() && newSize != graphicsWidgetRoot->size()) { + graphicsWidgetRoot->resize(newSize); + } + } + } + q->updateGeometry(); +} + +QSize QDeclarativeViewPrivate::rootObjectSize() const +{ + QSize rootObjectSize(0,0); + int widthCandidate = -1; + int heightCandidate = -1; + if (root) { + QSizeF size = root->boundingRect().size(); + widthCandidate = size.width(); + heightCandidate = size.height(); + } + if (widthCandidate > 0) { + rootObjectSize.setWidth(widthCandidate); + } + if (heightCandidate > 0) { + rootObjectSize.setHeight(heightCandidate); + } + return rootObjectSize; +} + +QDeclarativeView::ResizeMode QDeclarativeView::resizeMode() const +{ + Q_D(const QDeclarativeView); + return d->resizeMode; +} + +/*! + \internal + */ +void QDeclarativeView::continueExecute() +{ + Q_D(QDeclarativeView); + 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; + } + + setRootObject(obj); + emit statusChanged(status()); +} + +/*! + \internal +*/ +void QDeclarativeView::setRootObject(QObject *obj) +{ + Q_D(QDeclarativeView); + if (d->root == obj || !scene()) + return; + if (QDeclarativeItem *declarativeItem = qobject_cast<QDeclarativeItem *>(obj)) { + scene()->addItem(declarativeItem); + d->root = declarativeItem; + d->declarativeItemRoot = declarativeItem; + } else if (QGraphicsObject *graphicsObject = qobject_cast<QGraphicsObject *>(obj)) { + scene()->addItem(graphicsObject); + d->root = graphicsObject; + if (graphicsObject->isWidget()) { + d->graphicsWidgetRoot = static_cast<QGraphicsWidget*>(graphicsObject); + } else { + qWarning() << "QDeclarativeView::resizeMode is not honored for components of type QGraphicsObject"; + } + } else if (obj) { + qWarning() << "QDeclarativeView only supports loading of root objects that derive from QGraphicsObject"; + if (QWidget* widget = qobject_cast<QWidget *>(obj)) { + window()->setAttribute(Qt::WA_OpaquePaintEvent, false); + window()->setAttribute(Qt::WA_NoSystemBackground, false); + if (layout() && layout()->count()) { + // Hide the QGraphicsView in GV mode. + QLayoutItem *item = layout()->itemAt(0); + if (item->widget()) + item->widget()->hide(); + } + widget->setParent(this); + if (isVisible()) { + widget->setVisible(true); + } + resize(widget->size()); + } + } + + if (d->root) { + d->initialSize = d->rootObjectSize(); + if (d->initialSize != size()) { + if (!(parentWidget() && parentWidget()->layout())) { + resize(d->initialSize); + } + } + d->initResize(); + } +} + +/*! + \internal + If the \l {QTimerEvent} {timer event} \a e is this + view's resize timer, sceneResized() is emitted. + */ +void QDeclarativeView::timerEvent(QTimerEvent* e) +{ + Q_D(QDeclarativeView); + if (!e || e->timerId() == d->resizetimer.timerId()) { + d->updateSize(); + d->resizetimer.stop(); + } +} + +/*! \internal */ +bool QDeclarativeView::eventFilter(QObject *watched, QEvent *e) +{ + Q_D(QDeclarativeView); + if (watched == d->root && d->resizeMode == SizeViewToRootObject) { + if (d->graphicsWidgetRoot) { + if (e->type() == QEvent::GraphicsSceneResize) { + d->updateSize(); + } + } + } + return QGraphicsView::eventFilter(watched, e); +} + +/*! + \internal + Preferred size follows the root object geometry. +*/ +QSize QDeclarativeView::sizeHint() const +{ + Q_D(const QDeclarativeView); + QSize rootObjectSize = d->rootObjectSize(); + if (rootObjectSize.isEmpty()) { + return size(); + } else { + return rootObjectSize; + } +} + +/*! + Returns the initial size of the root object +*/ +QSize QDeclarativeView::initialSize() const +{ + Q_D(const QDeclarativeView); + return d->initialSize; +} + +/*! + Returns the view's root \l {QGraphicsObject} {item}. + */ +QGraphicsObject *QDeclarativeView::rootObject() const +{ + Q_D(const QDeclarativeView); + return d->root; +} + +/*! + \internal + This function handles the \l {QResizeEvent} {resize event} + \a e. + */ +void QDeclarativeView::resizeEvent(QResizeEvent *e) +{ + Q_D(QDeclarativeView); + if (d->resizeMode == SizeRootObjectToView) { + d->updateSize(); + } + if (d->declarativeItemRoot) { + setSceneRect(QRectF(0, 0, d->declarativeItemRoot->width(), d->declarativeItemRoot->height())); + } else if (d->root) { + setSceneRect(d->root->boundingRect()); + } else { + setSceneRect(rect()); + } + emit sceneResized(e->size()); + QGraphicsView::resizeEvent(e); +} + +/*! + \internal +*/ +void QDeclarativeView::paintEvent(QPaintEvent *event) +{ + Q_D(QDeclarativeView); + + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint); + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting); + + int time = 0; + if (frameRateDebug()) + time = d->frameTimer.restart(); + + QGraphicsView::paintEvent(event); + + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting); + + if (frameRateDebug()) + qDebug() << "paintEvent:" << d->frameTimer.elapsed() << "time since last frame:" << time; + +#if QT_SHOW_DECLARATIVEVIEW_FPS + static QTime timer; + static int frames; + + if (frames == 0) { + timer.start(); + } else if (timer.elapsed() > 5000) { + qreal avgtime = timer.elapsed() / (qreal) frames; + qDebug("Average time per frame: %f ms (%i fps)", avgtime, int(1000 / avgtime)); + timer.start(); + frames = 0; + } + ++frames; + scene()->update(); +#endif + +} + +QT_END_NAMESPACE diff --git a/src/qtquick1/util/qdeclarativeview.h b/src/qtquick1/util/qdeclarativeview.h new file mode 100644 index 0000000000..cfe1d458dc --- /dev/null +++ b/src/qtquick1/util/qdeclarativeview.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEVIEW_H +#define QDECLARATIVEVIEW_H + +#include <QtCore/qdatetime.h> +#include <QtCore/qurl.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtGui/qgraphicsview.h> +#include <QtGui/qwidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGraphicsObject; +class QDeclarativeEngine; +class QDeclarativeContext; +class QDeclarativeError; + +QT_MODULE(Declarative) + +class QDeclarativeViewPrivate; +class Q_DECLARATIVE_EXPORT QDeclarativeView : public QGraphicsView +{ + 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 QDeclarativeView(QWidget *parent = 0); + QDeclarativeView(const QUrl &source, QWidget *parent = 0); + virtual ~QDeclarativeView(); + + QUrl source() const; + + QDeclarativeEngine* engine() const; + QDeclarativeContext* rootContext() const; + + QGraphicsObject *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 sceneResized(QSize size); // ??? + void statusChanged(QDeclarativeView::Status); + +private Q_SLOTS: + void continueExecute(); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void paintEvent(QPaintEvent *event); + virtual void timerEvent(QTimerEvent*); + virtual void setRootObject(QObject *obj); + virtual bool eventFilter(QObject *watched, QEvent *e); + +private: + Q_DISABLE_COPY(QDeclarativeView) + Q_DECLARE_PRIVATE(QDeclarativeView) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDECLARATIVEVIEW_H diff --git a/src/qtquick1/util/qdeclarativexmllistmodel.cpp b/src/qtquick1/util/qdeclarativexmllistmodel.cpp new file mode 100644 index 0000000000..1f89f95a34 --- /dev/null +++ b/src/qtquick1/util/qdeclarativexmllistmodel.cpp @@ -0,0 +1,1058 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "QtQuick1/private/qdeclarativexmllistmodel_p.h" + +#include <QtDeclarative/qdeclarativecontext.h> +#include <QtDeclarative/private/qdeclarativeengine_p.h> + +#include <QDebug> +#include <QStringList> +#include <QMap> +#include <QApplication> +#include <QThread> +#include <QXmlQuery> +#include <QXmlResultItems> +#include <QXmlNodeModelIndex> +#include <QBuffer> +#include <QNetworkRequest> +#include <QNetworkReply> +#include <QTimer> +#include <QMutex> + +#include <private/qobject_p.h> + +Q_DECLARE_METATYPE(QDeclarative1XmlQueryResult) + +QT_BEGIN_NAMESPACE + + + + +typedef QPair<int, int> QDeclarative1XmlListRange; + +#define XMLLISTMODEL_CLEAR_ID 0 + +/*! + \qmlclass XmlRole QDeclarative1XmlListModelRole + \ingroup qml-working-with-data + \since 4.7 + \brief The XmlRole element allows you to specify a role for an XmlListModel. + + \sa {QtDeclarative} +*/ + +/*! + \qmlproperty string XmlRole::name + + The name for the role. This name is used to access the model data for this role. + + For example, the following model has a role named "title", which can be accessed + from the view's delegate: + + \qml + XmlListModel { + id: xmlModel + // ... + XmlRole { + name: "title" + query: "title/string()" + } + } + \endqml + + \qml + ListView { + model: xmlModel + delegate: Text { text: title } + } + \endqml +*/ + +/*! + \qmlproperty string XmlRole::query + The relative XPath expression query for this role. The query must be relative; it cannot start + with a '/'. + + For example, if there is an XML document like this: + + \quotefile doc/src/snippets/declarative/xmlrole.xml + + Here are some valid XPath expressions for XmlRole queries on this document: + + \snippet doc/src/snippets/declarative/xmlrole.qml 0 + \dots 4 + \snippet doc/src/snippets/declarative/xmlrole.qml 1 + + See the \l{http://www.w3.org/TR/xpath20/}{W3C XPath 2.0 specification} for more information. +*/ + +/*! + \qmlproperty bool XmlRole::isKey + Defines whether this is a key role. + + Key roles are used to to determine whether a set of values should + be updated or added to the XML list model when XmlListModel::reload() + is called. + + \sa XmlListModel +*/ + +struct XmlQueryJob +{ + int queryId; + QByteArray data; + QString query; + QString namespaces; + QStringList roleQueries; + QList<void*> roleQueryErrorId; // the ptr to send back if there is an error + QStringList keyRoleQueries; + QStringList keyRoleResultsCache; + QString prefix; +}; + +class QDeclarative1XmlQuery : public QObject +{ + Q_OBJECT +public: + QDeclarative1XmlQuery(QObject *parent=0) + : QObject(parent), m_queryIds(XMLLISTMODEL_CLEAR_ID + 1) { + qRegisterMetaType<QDeclarative1XmlQueryResult>("QDeclarative1XmlQueryResult"); + moveToThread(&m_thread); + m_thread.start(QThread::IdlePriority); + } + + ~QDeclarative1XmlQuery() { + if(m_thread.isRunning()) { + m_thread.quit(); + m_thread.wait(); + } + } + + void abort(int id) { + QMutexLocker ml(&m_mutex); + if (id != -1) { + m_jobs.remove(id); + } + } + + int doQuery(QString query, QString namespaces, QByteArray data, QList<QDeclarative1XmlListModelRole *>* roleObjects, QStringList keyRoleResultsCache) { + { + QMutexLocker m1(&m_mutex); + m_queryIds.ref(); + if (m_queryIds <= 0) + m_queryIds = 1; + } + + XmlQueryJob job; + job.queryId = m_queryIds; + job.data = data; + job.query = QLatin1String("doc($src)") + query; + job.namespaces = namespaces; + job.keyRoleResultsCache = keyRoleResultsCache; + + for (int i=0; i<roleObjects->count(); i++) { + if (!roleObjects->at(i)->isValid()) { + job.roleQueries << QString(); + continue; + } + job.roleQueries << roleObjects->at(i)->query(); + job.roleQueryErrorId << static_cast<void*>(roleObjects->at(i)); + if (roleObjects->at(i)->isKey()) + job.keyRoleQueries << job.roleQueries.last(); + } + + { + QMutexLocker ml(&m_mutex); + m_jobs.insert(m_queryIds, job); + } + + QMetaObject::invokeMethod(this, "processQuery", Qt::QueuedConnection, Q_ARG(int, job.queryId)); + return job.queryId; + } + +private slots: + void processQuery(int queryId) { + XmlQueryJob job; + + { + QMutexLocker ml(&m_mutex); + if (!m_jobs.contains(queryId)) + return; + job = m_jobs.value(queryId); + } + + QDeclarative1XmlQueryResult result; + result.queryId = job.queryId; + doQueryJob(&job, &result); + doSubQueryJob(&job, &result); + + { + QMutexLocker ml(&m_mutex); + if (m_jobs.contains(queryId)) { + emit queryCompleted(result); + m_jobs.remove(queryId); + } + } + } + +Q_SIGNALS: + void queryCompleted(const QDeclarative1XmlQueryResult &); + void error(void*, const QString&); + +protected: + + +private: + void doQueryJob(XmlQueryJob *job, QDeclarative1XmlQueryResult *currentResult); + void doSubQueryJob(XmlQueryJob *job, QDeclarative1XmlQueryResult *currentResult); + void getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const; + void addIndexToRangeList(QList<QDeclarative1XmlListRange> *ranges, int index) const; + +private: + QMutex m_mutex; + QThread m_thread; + QMap<int, XmlQueryJob> m_jobs; + QAtomicInt m_queryIds; +}; + +Q_GLOBAL_STATIC(QDeclarative1XmlQuery, globalXmlQuery) + +void QDeclarative1XmlQuery::doQueryJob(XmlQueryJob *currentJob, QDeclarative1XmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QString r; + QXmlQuery query; + QBuffer buffer(¤tJob->data); + buffer.open(QIODevice::ReadOnly); + query.bindVariable(QLatin1String("src"), &buffer); + query.setQuery(currentJob->namespaces + currentJob->query); + query.evaluateTo(&r); + + //always need a single root element + QByteArray xml = "<dummy:items xmlns:dummy=\"http://qtsotware.com/dummy\">\n" + r.toUtf8() + "</dummy:items>"; + QBuffer b(&xml); + b.open(QIODevice::ReadOnly); + + QString namespaces = QLatin1String("declare namespace dummy=\"http://qtsotware.com/dummy\";\n") + currentJob->namespaces; + QString prefix = QLatin1String("doc($inputDocument)/dummy:items") + + currentJob->query.mid(currentJob->query.lastIndexOf(QLatin1Char('/'))); + + //figure out how many items we are dealing with + int count = -1; + { + QXmlResultItems result; + QXmlQuery countquery; + countquery.bindVariable(QLatin1String("inputDocument"), &b); + countquery.setQuery(namespaces + QLatin1String("count(") + prefix + QLatin1Char(')')); + countquery.evaluateTo(&result); + QXmlItem item(result.next()); + if (item.isAtomicValue()) + count = item.toAtomicValue().toInt(); + } + + currentJob->data = xml; + currentJob->prefix = namespaces + prefix + QLatin1Char('/'); + currentResult->size = (count > 0 ? count : 0); +} + +void QDeclarative1XmlQuery::getValuesOfKeyRoles(const XmlQueryJob& currentJob, QStringList *values, QXmlQuery *query) const +{ + const QStringList &keysQueries = currentJob.keyRoleQueries; + QString keysQuery; + if (keysQueries.count() == 1) + keysQuery = currentJob.prefix + keysQueries[0]; + else if (keysQueries.count() > 1) + keysQuery = currentJob.prefix + QLatin1String("concat(") + keysQueries.join(QLatin1String(",")) + QLatin1String(")"); + + if (!keysQuery.isEmpty()) { + query->setQuery(keysQuery); + QXmlResultItems resultItems; + query->evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + values->append(item.toAtomicValue().toString()); + item = resultItems.next(); + } + } +} + +void QDeclarative1XmlQuery::addIndexToRangeList(QList<QDeclarative1XmlListRange> *ranges, int index) const { + if (ranges->isEmpty()) + ranges->append(qMakePair(index, 1)); + else if (ranges->last().first + ranges->last().second == index) + ranges->last().second += 1; + else + ranges->append(qMakePair(index, 1)); +} + +void QDeclarative1XmlQuery::doSubQueryJob(XmlQueryJob *currentJob, QDeclarative1XmlQueryResult *currentResult) +{ + Q_ASSERT(currentJob->queryId != -1); + + QBuffer b(¤tJob->data); + b.open(QIODevice::ReadOnly); + + QXmlQuery subquery; + subquery.bindVariable(QLatin1String("inputDocument"), &b); + + QStringList keyRoleResults; + getValuesOfKeyRoles(*currentJob, &keyRoleResults, &subquery); + + // See if any values of key roles have been inserted or removed. + + if (currentJob->keyRoleResultsCache.isEmpty()) { + currentResult->inserted << qMakePair(0, currentResult->size); + } else { + if (keyRoleResults != currentJob->keyRoleResultsCache) { + QStringList temp; + for (int i=0; i<currentJob->keyRoleResultsCache.count(); i++) { + if (!keyRoleResults.contains(currentJob->keyRoleResultsCache[i])) + addIndexToRangeList(¤tResult->removed, i); + else + temp << currentJob->keyRoleResultsCache[i]; + } + + for (int i=0; i<keyRoleResults.count(); i++) { + if (temp.count() == i || keyRoleResults[i] != temp[i]) { + temp.insert(i, keyRoleResults[i]); + addIndexToRangeList(¤tResult->inserted, i); + } + } + } + } + currentResult->keyRoleResultsCache = keyRoleResults; + + // Get the new values for each role. + //### we might be able to condense even further (query for everything in one go) + const QStringList &queries = currentJob->roleQueries; + for (int i = 0; i < queries.size(); ++i) { + QList<QVariant> resultList; + if (!queries[i].isEmpty()) { + subquery.setQuery(currentJob->prefix + QLatin1String("(let $v := string(") + queries[i] + QLatin1String(") return if ($v) then ") + queries[i] + QLatin1String(" else \"\")")); + if (subquery.isValid()) { + QXmlResultItems resultItems; + subquery.evaluateTo(&resultItems); + QXmlItem item(resultItems.next()); + while (!item.isNull()) { + resultList << item.toAtomicValue(); //### we used to trim strings + item = resultItems.next(); + } + } else { + emit error(currentJob->roleQueryErrorId.at(i), queries[i]); + } + } + //### should warn here if things have gone wrong. + while (resultList.count() < currentResult->size) + resultList << QVariant(); + currentResult->data << resultList; + b.seek(0); + } + + //this method is much slower, but works better for incremental loading + /*for (int j = 0; j < m_size; ++j) { + QList<QVariant> resultList; + for (int i = 0; i < m_roleObjects->size(); ++i) { + QDeclarative1XmlListModelRole *role = m_roleObjects->at(i); + subquery.setQuery(m_prefix.arg(j+1) + role->query()); + if (role->isStringList()) { + QStringList data; + subquery.evaluateTo(&data); + resultList << QVariant(data); + //qDebug() << data; + } else { + QString s; + subquery.evaluateTo(&s); + if (role->isCData()) { + //un-escape + s.replace(QLatin1String("<"), QLatin1String("<")); + s.replace(QLatin1String(">"), QLatin1String(">")); + s.replace(QLatin1String("&"), QLatin1String("&")); + } + resultList << s.trimmed(); + //qDebug() << s; + } + b.seek(0); + } + m_modelData << resultList; + }*/ +} + +class QDeclarative1XmlListModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarative1XmlListModel) +public: + QDeclarative1XmlListModelPrivate() + : isComponentComplete(true), size(-1), highestRole(Qt::UserRole) + , reply(0), status(QDeclarative1XmlListModel::Null), progress(0.0) + , queryId(-1), roleObjects(), redirectCount(0) {} + + + void notifyQueryStarted(bool remoteSource) { + Q_Q(QDeclarative1XmlListModel); + progress = remoteSource ? 0.0 : 1.0; + status = QDeclarative1XmlListModel::Loading; + errorString.clear(); + emit q->progressChanged(progress); + emit q->statusChanged(status); + } + + bool isComponentComplete; + QUrl src; + QString xml; + QString query; + QString namespaces; + int size; + QList<int> roles; + QStringList roleNames; + int highestRole; + QNetworkReply *reply; + QDeclarative1XmlListModel::Status status; + QString errorString; + qreal progress; + int queryId; + QStringList keyRoleResultsCache; + QList<QDeclarative1XmlListModelRole *> roleObjects; + static void append_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list, QDeclarative1XmlListModelRole *role); + static void clear_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list); + QList<QList<QVariant> > data; + int redirectCount; +}; + + +void QDeclarative1XmlListModelPrivate::append_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list, QDeclarative1XmlListModelRole *role) +{ + QDeclarative1XmlListModel *_this = qobject_cast<QDeclarative1XmlListModel *>(list->object); + if (_this && role) { + int i = _this->d_func()->roleObjects.count(); + _this->d_func()->roleObjects.append(role); + if (_this->d_func()->roleNames.contains(role->name())) { + qmlInfo(role) << QObject::tr("\"%1\" duplicates a previous role name and will be disabled.").arg(role->name()); + return; + } + _this->d_func()->roles.insert(i, _this->d_func()->highestRole); + _this->d_func()->roleNames.insert(i, role->name()); + ++_this->d_func()->highestRole; + } +} + +//### clear needs to invalidate any cached data (in data table) as well +// (and the model should emit the appropriate signals) +void QDeclarative1XmlListModelPrivate::clear_role(QDeclarativeListProperty<QDeclarative1XmlListModelRole> *list) +{ + QDeclarative1XmlListModel *_this = static_cast<QDeclarative1XmlListModel *>(list->object); + _this->d_func()->roles.clear(); + _this->d_func()->roleNames.clear(); + _this->d_func()->roleObjects.clear(); +} + +/*! + \qmlclass XmlListModel QDeclarative1XmlListModel + \ingroup qml-working-with-data + \since 4.7 + \brief The XmlListModel element is used to specify a read-only model using XPath expressions. + + XmlListModel is used to create a read-only model from XML data. It can be used as a data source + for view elements (such as ListView, PathView, GridView) and other elements that interact with model + data (such as \l Repeater). + + For example, if there is a XML document at http://www.mysite.com/feed.xml like this: + + \code + <?xml version="1.0" encoding="utf-8"?> + <rss version="2.0"> + ... + <channel> + <item> + <title>A blog post</title> + <pubDate>Sat, 07 Sep 2010 10:00:01 GMT</pubDate> + </item> + <item> + <title>Another blog post</title> + <pubDate>Sat, 07 Sep 2010 15:35:01 GMT</pubDate> + </item> + </channel> + </rss> + \endcode + + A XmlListModel could create a model from this data, like this: + + \qml + import QtQuick 1.0 + + XmlListModel { + id: xmlModel + source: "http://www.mysite.com/feed.xml" + query: "/rss/channel/item" + + XmlRole { name: "title"; query: "title/string()" } + XmlRole { name: "pubDate"; query: "pubDate/string()" } + } + \endqml + + The \l {XmlListModel::query}{query} value of "/rss/channel/item" specifies that the XmlListModel should generate + a model item for each \c <item> in the XML document. + + The XmlRole objects define the + model item attributes. Here, each model item will have \c title and \c pubDate + attributes that match the \c title and \c pubDate values of its corresponding \c <item>. + (See \l XmlRole::query for more examples of valid XPath expressions for XmlRole.) + + The model could be used in a ListView, like this: + + \qml + ListView { + width: 180; height: 300 + model: xmlModel + delegate: Text { text: title + ": " + pubDate } + } + \endqml + + \image qml-xmllistmodel-example.png + + The XmlListModel data is loaded asynchronously, and \l status + is set to \c XmlListModel.Ready when loading is complete. + Note this means when XmlListModel is used for a view, the view is not + populated until the model is loaded. + + + \section2 Using key XML roles + + You can define certain roles as "keys" so that when reload() is called, + the model will only add and refresh data that contains new values for + these keys. + + For example, if above role for "pubDate" was defined like this instead: + + \qml + XmlRole { name: "pubDate"; query: "pubDate/string()"; isKey: true } + \endqml + + Then when reload() is called, the model will only add and reload + items with a "pubDate" value that is not already + present in the model. + + This is useful when displaying the contents of XML documents that + are incrementally updated (such as RSS feeds) to avoid repainting the + entire contents of a model in a view. + + If multiple key roles are specified, the model only adds and reload items + with a combined value of all key roles that is not already present in + the model. + + \sa {RSS News} +*/ + +QDeclarative1XmlListModel::QDeclarative1XmlListModel(QObject *parent) + : QListModelInterface(*(new QDeclarative1XmlListModelPrivate), parent) +{ + connect(globalXmlQuery(), SIGNAL(queryCompleted(QDeclarative1XmlQueryResult)), + this, SLOT(queryCompleted(QDeclarative1XmlQueryResult))); + connect(globalXmlQuery(), SIGNAL(error(void*,QString)), + this, SLOT(queryError(void*,QString))); +} + +QDeclarative1XmlListModel::~QDeclarative1XmlListModel() +{ +} + +/*! + \qmlproperty list<XmlRole> XmlListModel::roles + + The roles to make available for this model. +*/ +QDeclarativeListProperty<QDeclarative1XmlListModelRole> QDeclarative1XmlListModel::roleObjects() +{ + Q_D(QDeclarative1XmlListModel); + QDeclarativeListProperty<QDeclarative1XmlListModelRole> list(this, d->roleObjects); + list.append = &QDeclarative1XmlListModelPrivate::append_role; + list.clear = &QDeclarative1XmlListModelPrivate::clear_role; + return list; +} + +QHash<int,QVariant> QDeclarative1XmlListModel::data(int index, const QList<int> &roles) const +{ + Q_D(const QDeclarative1XmlListModel); + QHash<int, QVariant> rv; + for (int i = 0; i < roles.size(); ++i) { + int role = roles.at(i); + int roleIndex = d->roles.indexOf(role); + rv.insert(role, roleIndex == -1 ? QVariant() : d->data.value(roleIndex).value(index)); + } + return rv; +} + +QVariant QDeclarative1XmlListModel::data(int index, int role) const +{ + Q_D(const QDeclarative1XmlListModel); + int roleIndex = d->roles.indexOf(role); + return (roleIndex == -1) ? QVariant() : d->data.value(roleIndex).value(index); +} + +/*! + \qmlproperty int XmlListModel::count + The number of data entries in the model. +*/ +int QDeclarative1XmlListModel::count() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->size; +} + +QList<int> QDeclarative1XmlListModel::roles() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->roles; +} + +QString QDeclarative1XmlListModel::toString(int role) const +{ + Q_D(const QDeclarative1XmlListModel); + int index = d->roles.indexOf(role); + if (index == -1) + return QString(); + return d->roleNames.at(index); +} + +/*! + \qmlproperty url XmlListModel::source + The location of the XML data source. + + If both \c source and \l xml are set, \l xml is used. +*/ +QUrl QDeclarative1XmlListModel::source() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->src; +} + +void QDeclarative1XmlListModel::setSource(const QUrl &src) +{ + Q_D(QDeclarative1XmlListModel); + if (d->src != src) { + d->src = src; + if (d->xml.isEmpty()) // src is only used if d->xml is not set + reload(); + emit sourceChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::xml + This property holds the XML data for this model, if set. + + The text is assumed to be UTF-8 encoded. + + If both \l source and \c xml are set, \c xml is used. +*/ +QString QDeclarative1XmlListModel::xml() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->xml; +} + +void QDeclarative1XmlListModel::setXml(const QString &xml) +{ + Q_D(QDeclarative1XmlListModel); + if (d->xml != xml) { + d->xml = xml; + reload(); + emit xmlChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::query + An absolute XPath query representing the base query for creating model items + from this model's XmlRole objects. The query should start with '/' or '//'. +*/ +QString QDeclarative1XmlListModel::query() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->query; +} + +void QDeclarative1XmlListModel::setQuery(const QString &query) +{ + Q_D(QDeclarative1XmlListModel); + if (!query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << QCoreApplication::translate("QDeclarative1XmlRoleList", "An XmlListModel query must start with '/' or \"//\""); + return; + } + + if (d->query != query) { + d->query = query; + reload(); + emit queryChanged(); + } +} + +/*! + \qmlproperty string XmlListModel::namespaceDeclarations + The namespace declarations to be used in the XPath queries. + + The namespaces should be declared as in XQuery. For example, if a requested document + at http://mysite.com/feed.xml uses the namespace "http://www.w3.org/2005/Atom", + this can be declared as the default namespace: + + \qml + XmlListModel { + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + namespaceDeclarations: "declare default element namespace 'http://www.w3.org/2005/Atom';" + + XmlRole { name: "title"; query: "title/string()" } + } + \endqml +*/ +QString QDeclarative1XmlListModel::namespaceDeclarations() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->namespaces; +} + +void QDeclarative1XmlListModel::setNamespaceDeclarations(const QString &declarations) +{ + Q_D(QDeclarative1XmlListModel); + if (d->namespaces != declarations) { + d->namespaces = declarations; + reload(); + emit namespaceDeclarationsChanged(); + } +} + +/*! + \qmlmethod object XmlListModel::get(int index) + + Returns the item at \a index in the model. + + For example, for a model like this: + + \qml + XmlListModel { + id: model + source: "http://mysite.com/feed.xml" + query: "/feed/entry" + XmlRole { name: "title"; query: "title/string()" } + } + \endqml + + This will access the \c title value for the first item in the model: + + \js + var title = model.get(0).title; + \endjs +*/ +QDeclarativeV8Handle QDeclarative1XmlListModel::get(int index) const +{ + // Must be called with a context and handle scope + Q_D(const QDeclarative1XmlListModel); + + if (index < 0 || index >= count()) + return QDeclarativeV8Handle::fromHandle(v8::Undefined()); + + QDeclarativeEngine *engine = qmlContext(this)->engine(); + QV8Engine *v8engine = QDeclarativeEnginePrivate::getV8Engine(engine); + v8::Local<v8::Object> rv = v8::Object::New(); + for (int ii = 0; ii < d->roleObjects.count(); ++ii) + rv->Set(v8engine->toString(d->roleObjects[ii]->name()), + v8engine->fromVariant(d->data.value(ii).value(index))); + + return QDeclarativeV8Handle::fromHandle(rv); +} + +/*! + \qmlproperty enumeration XmlListModel::status + Specifies the model loading status, which can be one of the following: + + \list + \o XmlListModel.Null - No XML data has been set for this model. + \o XmlListModel.Ready - The XML data has been loaded into the model. + \o XmlListModel.Loading - The model is in the process of reading and loading XML data. + \o XmlListModel.Error - An error occurred while the model was loading. See errorString() for details + about the error. + \endlist + + \sa progress + +*/ +QDeclarative1XmlListModel::Status QDeclarative1XmlListModel::status() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->status; +} + +/*! + \qmlproperty real XmlListModel::progress + + This indicates the current progress of the downloading of the XML data + source. This value ranges from 0.0 (no data downloaded) to + 1.0 (all data downloaded). If the XML data is not from a remote source, + the progress becomes 1.0 as soon as the data is read. + + Note that when the progress is 1.0, the XML data has been downloaded, but + it is yet to be loaded into the model at this point. Use the status + property to find out when the XML data has been read and loaded into + the model. + + \sa status, source +*/ +qreal QDeclarative1XmlListModel::progress() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->progress; +} + +/*! + \qmlmethod void XmlListModel::errorString() + + Returns a string description of the last error that occurred + if \l status is XmlListModel::Error. +*/ +QString QDeclarative1XmlListModel::errorString() const +{ + Q_D(const QDeclarative1XmlListModel); + return d->errorString; +} + +void QDeclarative1XmlListModel::classBegin() +{ + Q_D(QDeclarative1XmlListModel); + d->isComponentComplete = false; +} + +void QDeclarative1XmlListModel::componentComplete() +{ + Q_D(QDeclarative1XmlListModel); + d->isComponentComplete = true; + reload(); +} + +/*! + \qmlmethod XmlListModel::reload() + + Reloads the model. + + If no key roles have been specified, all existing model + data is removed, and the model is rebuilt from scratch. + + Otherwise, items are only added if the model does not already + contain items with matching key role values. + + \sa {Using key XML roles}, XmlRole::isKey +*/ +void QDeclarative1XmlListModel::reload() +{ + Q_D(QDeclarative1XmlListModel); + + if (!d->isComponentComplete) + return; + + globalXmlQuery()->abort(d->queryId); + d->queryId = -1; + + if (d->size < 0) + d->size = 0; + + if (d->reply) { + d->reply->abort(); + if (d->reply) { + // abort will generally have already done this (and more) + d->reply->deleteLater(); + d->reply = 0; + } + } + + if (!d->xml.isEmpty()) { + d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache); + d->notifyQueryStarted(false); + + } else if (d->src.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + d->notifyQueryStarted(false); + QTimer::singleShot(0, this, SLOT(dataCleared())); + + } else { + d->notifyQueryStarted(true); + QNetworkRequest req(d->src); + req.setRawHeader("Accept", "application/xml,*/*"); + d->reply = qmlContext(this)->engine()->networkAccessManager()->get(req); + QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished())); + QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(requestProgress(qint64,qint64))); + } +} + +#define XMLLISTMODEL_MAX_REDIRECT 16 + +void QDeclarative1XmlListModel::requestFinished() +{ + Q_D(QDeclarative1XmlListModel); + + d->redirectCount++; + if (d->redirectCount < XMLLISTMODEL_MAX_REDIRECT) { + 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; + + if (d->reply->error() != QNetworkReply::NoError) { + d->errorString = d->reply->errorString(); + disconnect(d->reply, 0, this, 0); + d->reply->deleteLater(); + d->reply = 0; + + int count = this->count(); + d->data.clear(); + d->size = 0; + if (count > 0) { + emit itemsRemoved(0, count); + emit countChanged(); + } + + d->status = Error; + d->queryId = -1; + emit statusChanged(d->status); + } else { + QByteArray data = d->reply->readAll(); + if (data.isEmpty()) { + d->queryId = XMLLISTMODEL_CLEAR_ID; + QTimer::singleShot(0, this, SLOT(dataCleared())); + } else { + d->queryId = globalXmlQuery()->doQuery(d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache); + } + disconnect(d->reply, 0, this, 0); + d->reply->deleteLater(); + d->reply = 0; + + d->progress = 1.0; + emit progressChanged(d->progress); + } +} + +void QDeclarative1XmlListModel::requestProgress(qint64 received, qint64 total) +{ + Q_D(QDeclarative1XmlListModel); + if (d->status == Loading && total > 0) { + d->progress = qreal(received)/total; + emit progressChanged(d->progress); + } +} + +void QDeclarative1XmlListModel::dataCleared() +{ + Q_D(QDeclarative1XmlListModel); + QDeclarative1XmlQueryResult r; + r.queryId = XMLLISTMODEL_CLEAR_ID; + r.size = 0; + r.removed << qMakePair(0, count()); + r.keyRoleResultsCache = d->keyRoleResultsCache; + queryCompleted(r); +} + +void QDeclarative1XmlListModel::queryError(void* object, const QString& error) +{ + // Be extra careful, object may no longer exist, it's just an ID. + Q_D(QDeclarative1XmlListModel); + for (int i=0; i<d->roleObjects.count(); i++) { + if (d->roleObjects.at(i) == static_cast<QDeclarative1XmlListModelRole*>(object)) { + qmlInfo(d->roleObjects.at(i)) << QObject::tr("invalid query: \"%1\"").arg(error); + return; + } + } + qmlInfo(this) << QObject::tr("invalid query: \"%1\"").arg(error); +} + +void QDeclarative1XmlListModel::queryCompleted(const QDeclarative1XmlQueryResult &result) +{ + Q_D(QDeclarative1XmlListModel); + if (result.queryId != d->queryId) + return; + + int origCount = d->size; + bool sizeChanged = result.size != d->size; + + d->size = result.size; + d->data = result.data; + d->keyRoleResultsCache = result.keyRoleResultsCache; + d->status = Ready; + d->errorString.clear(); + d->queryId = -1; + + bool hasKeys = false; + for (int i=0; i<d->roleObjects.count(); i++) { + if (d->roleObjects[i]->isKey()) { + hasKeys = true; + break; + } + } + if (!hasKeys) { + if (!(origCount == 0 && d->size == 0)) { + emit itemsRemoved(0, origCount); + emit itemsInserted(0, d->size); + emit countChanged(); + } + + } else { + for (int i=0; i<result.removed.count(); i++) + emit itemsRemoved(result.removed[i].first, result.removed[i].second); + for (int i=0; i<result.inserted.count(); i++) + emit itemsInserted(result.inserted[i].first, result.inserted[i].second); + + if (sizeChanged) + emit countChanged(); + } + + emit statusChanged(d->status); +} + + + +QT_END_NAMESPACE + +#include <qdeclarativexmllistmodel.moc> diff --git a/src/qtquick1/util/qdeclarativexmllistmodel_p.h b/src/qtquick1/util/qdeclarativexmllistmodel_p.h new file mode 100644 index 0000000000..7b2ddbf268 --- /dev/null +++ b/src/qtquick1/util/qdeclarativexmllistmodel_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** 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$ +** GNU Lesser General Public License Usage +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEXMLLISTMODEL_H +#define QDECLARATIVEXMLLISTMODEL_H + +#include <QtDeclarative/qdeclarative.h> +#include <QtDeclarative/qdeclarativeinfo.h> + +#include <QtCore/qurl.h> +#include <QtCore/qstringlist.h> + +#include <QtDeclarative/private/qlistmodelinterface_p.h> +#include <private/qv8engine_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Declarative) + +class QDeclarativeContext; +class QDeclarative1XmlListModelRole; +class QDeclarative1XmlListModelPrivate; + +struct QDeclarative1XmlQueryResult { + int queryId; + int size; + QList<QList<QVariant> > data; + QList<QPair<int, int> > inserted; + QList<QPair<int, int> > removed; + QStringList keyRoleResultsCache; +}; + +class Q_AUTOTEST_EXPORT QDeclarative1XmlListModel : public QListModelInterface, public QDeclarativeParserStatus +{ + Q_OBJECT + Q_INTERFACES(QDeclarativeParserStatus) + Q_ENUMS(Status) + + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(qreal progress READ progress NOTIFY progressChanged) + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(QString xml READ xml WRITE setXml NOTIFY xmlChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(QString namespaceDeclarations READ namespaceDeclarations WRITE setNamespaceDeclarations NOTIFY namespaceDeclarationsChanged) + Q_PROPERTY(QDeclarativeListProperty<QDeclarative1XmlListModelRole> roles READ roleObjects) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_CLASSINFO("DefaultProperty", "roles") + +public: + QDeclarative1XmlListModel(QObject *parent = 0); + ~QDeclarative1XmlListModel(); + + virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const; + virtual QVariant data(int index, int role) const; + virtual int count() const; + virtual QList<int> roles() const; + virtual QString toString(int role) const; + + QDeclarativeListProperty<QDeclarative1XmlListModelRole> roleObjects(); + + QUrl source() const; + void setSource(const QUrl&); + + QString xml() const; + void setXml(const QString&); + + QString query() const; + void setQuery(const QString&); + + QString namespaceDeclarations() const; + void setNamespaceDeclarations(const QString&); + + Q_INVOKABLE QDeclarativeV8Handle get(int index) const; + + enum Status { Null, Ready, Loading, Error }; + Status status() const; + qreal progress() const; + + Q_INVOKABLE QString errorString() const; + + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void statusChanged(QDeclarative1XmlListModel::Status); + void progressChanged(qreal progress); + void countChanged(); + void sourceChanged(); + void xmlChanged(); + void queryChanged(); + void namespaceDeclarationsChanged(); + +public Q_SLOTS: + // ### need to use/expose Expiry to guess when to call this? + // ### property to auto-call this on reasonable Expiry? + // ### LastModified/Age also useful to guess. + // ### Probably also applies to other network-requesting types. + void reload(); + +private Q_SLOTS: + void requestFinished(); + void requestProgress(qint64,qint64); + void dataCleared(); + void queryCompleted(const QDeclarative1XmlQueryResult &); + void queryError(void* object, const QString& error); + +private: + Q_DECLARE_PRIVATE(QDeclarative1XmlListModel) + Q_DISABLE_COPY(QDeclarative1XmlListModel) +}; + +class Q_AUTOTEST_EXPORT QDeclarative1XmlListModelRole : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(bool isKey READ isKey WRITE setIsKey NOTIFY isKeyChanged) +public: + QDeclarative1XmlListModelRole() : m_isKey(false) {} + ~QDeclarative1XmlListModelRole() {} + + QString name() const { return m_name; } + void setName(const QString &name) { + if (name == m_name) + return; + m_name = name; + emit nameChanged(); + } + + QString query() const { return m_query; } + void setQuery(const QString &query) + { + if (query.startsWith(QLatin1Char('/'))) { + qmlInfo(this) << tr("An XmlRole query must not start with '/'"); + return; + } + if (m_query == query) + return; + m_query = query; + emit queryChanged(); + } + + bool isKey() const { return m_isKey; } + void setIsKey(bool b) { + if (m_isKey == b) + return; + m_isKey = b; + emit isKeyChanged(); + } + + bool isValid() { + return !m_name.isEmpty() && !m_query.isEmpty(); + } + +Q_SIGNALS: + void nameChanged(); + void queryChanged(); + void isKeyChanged(); + +private: + QString m_name; + QString m_query; + bool m_isKey; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarative1XmlListModel) +QML_DECLARE_TYPE(QDeclarative1XmlListModelRole) + +QT_END_HEADER + +#endif // QDECLARATIVEXMLLISTMODEL_H diff --git a/src/qtquick1/util/util.pri b/src/qtquick1/util/util.pri new file mode 100644 index 0000000000..c5e68b78e3 --- /dev/null +++ b/src/qtquick1/util/util.pri @@ -0,0 +1,69 @@ +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qdeclarativeapplication.cpp \ + $$PWD/qdeclarativeview.cpp \ + $$PWD/qdeclarativeconnections.cpp \ + $$PWD/qdeclarativepackage.cpp \ + $$PWD/qdeclarativeanimation.cpp \ + $$PWD/qdeclarativesystempalette.cpp \ + $$PWD/qdeclarativespringanimation.cpp \ + $$PWD/qdeclarativesmoothedanimation.cpp \ + $$PWD/qdeclarativestate.cpp\ + $$PWD/qdeclarativetransitionmanager.cpp \ + $$PWD/qdeclarativestateoperations.cpp \ + $$PWD/qdeclarativepropertychanges.cpp \ + $$PWD/qdeclarativestategroup.cpp \ + $$PWD/qdeclarativetransition.cpp \ +# $$PWD/qdeclarativelistmodel.cpp\ +# $$PWD/qdeclarativelistmodelworkeragent.cpp \ + $$PWD/qdeclarativelistaccessor.cpp \ + $$PWD/qdeclarativeopenmetaobject.cpp \ + $$PWD/qdeclarativetimeline.cpp \ + $$PWD/qdeclarativetimer.cpp \ + $$PWD/qdeclarativebind.cpp \ + $$PWD/qdeclarativepixmapcache.cpp \ + $$PWD/qdeclarativebehavior.cpp \ + $$PWD/qdeclarativefontloader.cpp \ + $$PWD/qdeclarativestyledtext.cpp \ + $$PWD/qdeclarativeutilmodule.cpp\ + +HEADERS += \ + $$PWD/qdeclarativeapplication_p.h \ + $$PWD/qdeclarativeutilmodule_p.h\ + $$PWD/qdeclarativeview.h \ + $$PWD/qdeclarativeconnections_p.h \ + $$PWD/qdeclarativepackage_p.h \ + $$PWD/qdeclarativeanimation_p.h \ + $$PWD/qdeclarativeanimation_p_p.h \ + $$PWD/qdeclarativesystempalette_p.h \ + $$PWD/qdeclarativespringanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p.h \ + $$PWD/qdeclarativesmoothedanimation_p_p.h \ + $$PWD/qdeclarativestate_p.h\ + $$PWD/qdeclarativestateoperations_p.h \ + $$PWD/qdeclarativepropertychanges_p.h \ + $$PWD/qdeclarativestate_p_p.h\ + $$PWD/qdeclarativetransitionmanager_p_p.h \ + $$PWD/qdeclarativestategroup_p.h \ + $$PWD/qdeclarativetransition_p.h \ +# $$PWD/qdeclarativelistmodel_p.h\ +# $$PWD/qdeclarativelistmodel_p_p.h\ +# $$PWD/qdeclarativelistmodelworkeragent_p.h \ + $$PWD/qdeclarativelistaccessor_p.h \ + $$PWD/qdeclarativeopenmetaobject_p.h \ + $$PWD/qdeclarativetimeline_p_p.h \ + $$PWD/qdeclarativetimer_p.h \ + $$PWD/qdeclarativebind_p.h \ + $$PWD/qdeclarativepixmapcache_p.h \ + $$PWD/qdeclarativebehavior_p.h \ + $$PWD/qdeclarativefontloader_p.h \ + $$PWD/qdeclarativestyledtext_p.h \ + +contains(QT_CONFIG, xmlpatterns) { + QT+=xmlpatterns + SOURCES += $$PWD/qdeclarativexmllistmodel.cpp + HEADERS += $$PWD/qdeclarativexmllistmodel_p.h +} else { + DEFINES += QT_NO_XMLPATTERNS +} |