diff options
Diffstat (limited to 'src/qtquick1/graphicsitems')
88 files changed, 41140 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 |