diff options
Diffstat (limited to 'src/declarative/graphicsitems')
33 files changed, 2099 insertions, 606 deletions
diff --git a/src/declarative/graphicsitems/qdeclarativeanchors.cpp b/src/declarative/graphicsitems/qdeclarativeanchors.cpp index 444bbd4..5ff6d2c 100644 --- a/src/declarative/graphicsitems/qdeclarativeanchors.cpp +++ b/src/declarative/graphicsitems/qdeclarativeanchors.cpp @@ -175,16 +175,19 @@ QDeclarativeAnchors::~QDeclarativeAnchors() void QDeclarativeAnchorsPrivate::fillChanged() { + Q_Q(QDeclarativeAnchors); if (!fill || !isItemComplete()) return; if (updatingFill < 2) { ++updatingFill; + qreal horizontalMargin = q->mirrored() ? rightMargin : leftMargin; + if (fill == item->parentItem()) { //child-parent - setItemPos(QPointF(leftMargin, topMargin)); + setItemPos(QPointF(horizontalMargin, topMargin)); } else if (fill->parentItem() == item->parentItem()) { //siblings - setItemPos(QPointF(fill->x()+leftMargin, fill->y()+topMargin)); + setItemPos(QPointF(fill->x()+horizontalMargin, fill->y()+topMargin)); } QGraphicsItemPrivate *fillPrivate = QGraphicsItemPrivate::get(fill); setItemSize(QSizeF(fillPrivate->width()-leftMargin-rightMargin, fillPrivate->height()-topMargin-bottomMargin)); @@ -199,18 +202,21 @@ void QDeclarativeAnchorsPrivate::fillChanged() void QDeclarativeAnchorsPrivate::centerInChanged() { + Q_Q(QDeclarativeAnchors); 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) + hCenterOffset, + 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) + hCenterOffset, + QPointF p(centerIn->x() + hcenter(centerIn) - hcenter(item) + effectiveHCenterOffset, centerIn->y() + vcenter(centerIn) - vcenter(item) + vCenterOffset); setItemPos(p); } @@ -311,6 +317,13 @@ void QDeclarativeAnchors::componentComplete() d->componentComplete = true; } +bool QDeclarativeAnchors::mirrored() +{ + Q_D(QDeclarativeAnchors); + QGraphicsItemPrivate * itemPrivate = QGraphicsItemPrivate::get(d->item); + return itemPrivate->isDeclarativeItem ? static_cast<QDeclarativeItemPrivate *>(itemPrivate)->effectiveLayoutMirror : false; +} + void QDeclarativeAnchorsPrivate::setItemHeight(qreal v) { updatingMe = true; @@ -570,58 +583,94 @@ void QDeclarativeAnchorsPrivate::updateVerticalAnchors() } } +inline QDeclarativeAnchorLine::AnchorLine reverseAnchorLine(QDeclarativeAnchorLine::AnchorLine anchorLine) { + if (anchorLine == QDeclarativeAnchorLine::Left) { + return QDeclarativeAnchorLine::Right; + } else if (anchorLine == QDeclarativeAnchorLine::Right) { + return QDeclarativeAnchorLine::Left; + } else { + return anchorLine; + } +} + void QDeclarativeAnchorsPrivate::updateHorizontalAnchors() { + Q_Q(QDeclarativeAnchors); if (fill || centerIn || !isItemComplete()) return; - if (updatingHorizontalAnchor < 2) { + if (updatingHorizontalAnchor < 3) { ++updatingHorizontalAnchor; + qreal effectiveRightMargin, effectiveLeftMargin, effectiveHorizontalCenterOffset; + QDeclarativeAnchorLine effectiveLeft, effectiveRight, effectiveHorizontalCenter; + QDeclarativeAnchors::Anchor effectiveLeftAnchor, effectiveRightAnchor; + if (q->mirrored()) { + effectiveLeftAnchor = QDeclarativeAnchors::RightAnchor; + effectiveRightAnchor = QDeclarativeAnchors::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 = QDeclarativeAnchors::LeftAnchor; + effectiveRightAnchor = QDeclarativeAnchors::RightAnchor; + effectiveLeft = left; + effectiveRight = right; + effectiveHorizontalCenter = hCenter; + effectiveLeftMargin = leftMargin; + effectiveRightMargin = rightMargin; + effectiveHorizontalCenterOffset = hCenterOffset; + } + QGraphicsItemPrivate *itemPrivate = QGraphicsItemPrivate::get(item); - if (usedAnchors & QDeclarativeAnchors::LeftAnchor) { + if (usedAnchors & effectiveLeftAnchor) { //Handle stretching bool invalid = true; qreal width = 0.0; - if (usedAnchors & QDeclarativeAnchors::RightAnchor) { - invalid = calcStretch(left, right, leftMargin, -rightMargin, QDeclarativeAnchorLine::Left, width); + if (usedAnchors & effectiveRightAnchor) { + invalid = calcStretch(effectiveLeft, effectiveRight, effectiveLeftMargin, -effectiveRightMargin, QDeclarativeAnchorLine::Left, width); } else if (usedAnchors & QDeclarativeAnchors::HCenterAnchor) { - invalid = calcStretch(left, hCenter, leftMargin, hCenterOffset, QDeclarativeAnchorLine::Left, width); + invalid = calcStretch(effectiveLeft, effectiveHorizontalCenter, effectiveLeftMargin, effectiveHorizontalCenterOffset, QDeclarativeAnchorLine::Left, width); width *= 2; } if (!invalid) setItemWidth(width); //Handle left - if (left.item == item->parentItem()) { - setItemX(adjustedPosition(left.item, left.anchorLine) + leftMargin); - } else if (left.item->parentItem() == item->parentItem()) { - setItemX(position(left.item, left.anchorLine) + leftMargin); + 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 & QDeclarativeAnchors::RightAnchor) { + } else if (usedAnchors & effectiveRightAnchor) { //Handle stretching (left + right case is handled in updateLeftAnchor) if (usedAnchors & QDeclarativeAnchors::HCenterAnchor) { qreal width = 0.0; - bool invalid = calcStretch(hCenter, right, hCenterOffset, -rightMargin, + bool invalid = calcStretch(effectiveHorizontalCenter, effectiveRight, effectiveHorizontalCenterOffset, -effectiveRightMargin, QDeclarativeAnchorLine::Left, width); if (!invalid) setItemWidth(width*2); } //Handle right - if (right.item == item->parentItem()) { - setItemX(adjustedPosition(right.item, right.anchorLine) - itemPrivate->width() - rightMargin); - } else if (right.item->parentItem() == item->parentItem()) { - setItemX(position(right.item, right.anchorLine) - itemPrivate->width() - rightMargin); + 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 & QDeclarativeAnchors::HCenterAnchor) { //Handle hCenter - if (hCenter.item == item->parentItem()) { - setItemX(adjustedPosition(hCenter.item, hCenter.anchorLine) - hcenter(item) + hCenterOffset); - } else if (hCenter.item->parentItem() == item->parentItem()) { - setItemX(position(hCenter.item, hCenter.anchorLine) - hcenter(item) + hCenterOffset); + 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 :) diff --git a/src/declarative/graphicsitems/qdeclarativeanchors_p.h b/src/declarative/graphicsitems/qdeclarativeanchors_p.h index d2c0a89..388d6b9 100644 --- a/src/declarative/graphicsitems/qdeclarativeanchors_p.h +++ b/src/declarative/graphicsitems/qdeclarativeanchors_p.h @@ -79,6 +79,7 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeAnchors : public QObject 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: QDeclarativeAnchors(QObject *parent=0); @@ -163,6 +164,8 @@ public: void classBegin(); void componentComplete(); + bool mirrored(); + Q_SIGNALS: void leftChanged(); void rightChanged(); @@ -181,9 +184,11 @@ Q_SIGNALS: void verticalCenterOffsetChanged(); void horizontalCenterOffsetChanged(); void baselineOffsetChanged(); + Q_REVISION(1) void mirroredChanged(); private: friend class QDeclarativeItem; + friend class QDeclarativeItemPrivate; friend class QDeclarativeGraphicsWidget; Q_DISABLE_COPY(QDeclarativeAnchors) Q_DECLARE_PRIVATE(QDeclarativeAnchors) diff --git a/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h b/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h index c4508e0..d8d2f15 100644 --- a/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeanchors_p_p.h @@ -133,6 +133,7 @@ public: bool checkVAnchorValid(QDeclarativeAnchorLine anchor) const; bool calcStretch(const QDeclarativeAnchorLine &edge1, const QDeclarativeAnchorLine &edge2, qreal offset1, qreal offset2, QDeclarativeAnchorLine::AnchorLine line, qreal &stretch); + bool isMirrored() const; void updateHorizontalAnchors(); void updateVerticalAnchors(); void fillChanged(); diff --git a/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp b/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp index 016b87d..8cc8165 100644 --- a/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeanimatedimage.cpp @@ -231,9 +231,18 @@ void QDeclarativeAnimatedImage::load() { Q_D(QDeclarativeAnimatedImage); + QDeclarativeImageBase::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); @@ -245,7 +254,8 @@ void QDeclarativeAnimatedImage::load() delete d->_movie; d->_movie = 0; d->status = Error; - emit statusChanged(d->status); + if (d->status != oldStatus) + emit statusChanged(d->status); return; } connect(d->_movie, SIGNAL(stateChanged(QMovie::MovieState)), @@ -262,20 +272,25 @@ void QDeclarativeAnimatedImage::load() d->setPixmap(d->_movie->currentPixmap()); d->status = Ready; d->progress = 1.0; - emit statusChanged(d->status); - emit sourceChanged(d->url); - emit progressChanged(d->progress); + 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))); } - emit statusChanged(d->status); } #define ANIMATEDIMAGE_MAXIMUM_REDIRECT_RECURSION 16 diff --git a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp index c03c624..8f37e90 100644 --- a/src/declarative/graphicsitems/qdeclarativeborderimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeborderimage.cpp @@ -149,6 +149,20 @@ QT_BEGIN_NAMESPACE \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. +*/ QDeclarativeBorderImage::QDeclarativeBorderImage(QDeclarativeItem *parent) : QDeclarativeImageBase(*(new QDeclarativeBorderImagePrivate), parent) { @@ -200,6 +214,15 @@ QDeclarativeBorderImage::~QDeclarativeBorderImage() */ /*! + \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 diff --git a/src/declarative/graphicsitems/qdeclarativeflickable.cpp b/src/declarative/graphicsitems/qdeclarativeflickable.cpp index 55b05b9..511f789 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable.cpp +++ b/src/declarative/graphicsitems/qdeclarativeflickable.cpp @@ -143,7 +143,7 @@ QDeclarativeFlickablePrivate::QDeclarativeFlickablePrivate() , stealMouse(false), pressed(false), interactive(true), calcVelocity(false) , deceleration(500), maxVelocity(2000), reportedVelocitySmoothing(100) , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(600) - , vTime(0), visibleArea(0) + , fixupMode(Normal), vTime(0), visibleArea(0) , flickableDirection(QDeclarativeFlickable::AutoFlickDirection) , boundsBehavior(QDeclarativeFlickable::DragAndOvershootBounds) { @@ -219,6 +219,7 @@ void QDeclarativeFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal { Q_Q(QDeclarativeFlickable); qreal maxDistance = -1; + data.fixingUp = false; bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds; // -ve velocity means list is moving up if (velocity > 0) { @@ -288,24 +289,45 @@ void QDeclarativeFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal if (data.move.value() > minExtent || maxExtent > minExtent) { timeline.reset(data.move); if (data.move.value() != minExtent) { - if (fixupDuration) { - 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); - } else { + 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); - if (fixupDuration) { - 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); - } else { + 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; + } } } + fixupMode = Normal; vTime = timeline.time(); } @@ -688,6 +710,12 @@ void QDeclarativeFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEven vData.velocity = 0; hData.dragStartOffset = 0; vData.dragStartOffset = 0; + hData.dragMinBound = q->minXExtent(); + vData.dragMinBound = q->minYExtent(); + hData.dragMaxBound = q->maxXExtent(); + vData.dragMaxBound = q->maxYExtent(); + hData.fixingUp = false; + vData.fixingUp = false; lastPos = QPoint(); QDeclarativeItemPrivate::start(lastPosTime); pressPos = event->pos(); @@ -716,8 +744,8 @@ void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent if (!vMoved) vData.dragStartOffset = dy; qreal newY = dy + vData.pressPos - vData.dragStartOffset; - const qreal minY = q->minYExtent(); - const qreal maxY = q->maxYExtent(); + const qreal minY = vData.dragMinBound; + const qreal maxY = vData.dragMaxBound; if (newY > minY) newY = minY + (newY - minY) / 2; if (newY < maxY && maxY - minY <= 0) @@ -748,8 +776,8 @@ void QDeclarativeFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent if (!hMoved) hData.dragStartOffset = dx; qreal newX = dx + hData.pressPos - hData.dragStartOffset; - const qreal minX = q->minXExtent(); - const qreal maxX = q->maxXExtent(); + const qreal minX = hData.dragMinBound; + const qreal maxX = hData.dragMaxBound; if (newX > minX) newX = minX + (newX - minX) / 2; if (newX < maxX && maxX - minX <= 0) @@ -1065,10 +1093,8 @@ void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, } // Make sure that we're entirely in view. if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; d->fixupX(); - d->fixupDuration = oldDuration; } } if (newGeometry.height() != oldGeometry.height()) { @@ -1080,10 +1106,8 @@ void QDeclarativeFlickable::geometryChanged(const QRectF &newGeometry, } // Make sure that we're entirely in view. if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; d->fixupY(); - d->fixupDuration = oldDuration; } } @@ -1258,10 +1282,11 @@ void QDeclarativeFlickable::setContentWidth(qreal w) d->contentItem->setWidth(w); // Make sure that we're entirely in view. if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; + d->fixupX(); + } else if (!d->pressed && d->hData.fixingUp) { + d->fixupMode = QDeclarativeFlickablePrivate::ExtentChanged; d->fixupX(); - d->fixupDuration = oldDuration; } emit contentWidthChanged(); d->updateBeginningEnd(); @@ -1285,10 +1310,11 @@ void QDeclarativeFlickable::setContentHeight(qreal h) d->contentItem->setHeight(h); // Make sure that we're entirely in view. if (!d->pressed && !d->movingHorizontally && !d->movingVertically) { - int oldDuration = d->fixupDuration; - d->fixupDuration = 0; + d->fixupMode = QDeclarativeFlickablePrivate::Immediate; + d->fixupY(); + } else if (!d->pressed && d->vData.fixingUp) { + d->fixupMode = QDeclarativeFlickablePrivate::ExtentChanged; d->fixupY(); - d->fixupDuration = oldDuration; } emit contentHeightChanged(); d->updateBeginningEnd(); @@ -1297,11 +1323,10 @@ void QDeclarativeFlickable::setContentHeight(qreal h) /*! \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. - \bold {This method was added in QtQuick 1.1.} - This does not scale the contents of the Flickable - it only resizes the \l contentWidth and \l contentHeight. @@ -1333,11 +1358,10 @@ void QDeclarativeFlickable::resizeContent(qreal w, qreal h, QPointF center) /*! \qmlmethod Flickable::returnToBounds() \preliminary + \since Quick 1.1 Ensures the content is within legal bounds. - \bold {This method was added in QtQuick 1.1.} - This may be called to ensure that the content is within legal bounds after manually positioning the content. */ @@ -1406,8 +1430,10 @@ bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) 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())) { + 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) { @@ -1454,12 +1480,12 @@ bool QDeclarativeFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event) break; } grabber = qobject_cast<QDeclarativeItem*>(s->mouseGrabberItem()); - if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) { + if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) { d->clearDelayedPress(); grabMouse(); } - return stealThisEvent || d->delayedPressEvent; + return stealThisEvent || d->delayedPressEvent || disabledItem; } else if (d->lastPosTime.isValid()) { d->lastPosTime.invalidate(); } @@ -1662,6 +1688,7 @@ void QDeclarativeFlickable::movementXEnding() emit movementEnded(); } } + d->hData.fixingUp = false; } void QDeclarativeFlickable::movementYEnding() @@ -1684,6 +1711,7 @@ void QDeclarativeFlickable::movementYEnding() emit movementEnded(); } } + d->vData.fixingUp = false; } void QDeclarativeFlickablePrivate::updateVelocity() diff --git a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h index 1b6081c..38a5eb3 100644 --- a/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativeflickable_p_p.h @@ -94,17 +94,21 @@ public: struct AxisData { AxisData(QDeclarativeFlickablePrivate *fp, void (QDeclarativeFlickablePrivate::*func)(qreal)) : move(fp, func), viewSize(-1), smoothVelocity(fp), atEnd(false), atBeginning(true) + , fixingUp(false) {} QDeclarativeTimeLineValueProxy<QDeclarativeFlickablePrivate> move; qreal viewSize; qreal pressPos; qreal dragStartOffset; + qreal dragMinBound; + qreal dragMaxBound; qreal velocity; qreal flickTarget; QDeclarativeFlickablePrivate::Velocity smoothVelocity; bool atEnd : 1; bool atBeginning : 1; + bool fixingUp : 1; }; void flickX(qreal velocity); @@ -161,6 +165,9 @@ public: int pressDelay; int fixupDuration; + enum FixupMode { Normal, Immediate, ExtentChanged }; + FixupMode fixupMode; + static void fixupY_callback(void *); static void fixupX_callback(void *); diff --git a/src/declarative/graphicsitems/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index dc4196d..546f75e 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview.cpp +++ b/src/declarative/graphicsitems/qdeclarativegridview.cpp @@ -68,19 +68,60 @@ public: } ~FxGridItem() {} - qreal rowPos() const { return (view->flow() == QDeclarativeGridView::LeftToRight ? item->y() : item->x()); } - qreal colPos() const { return (view->flow() == QDeclarativeGridView::LeftToRight ? item->x() : item->y()); } + qreal rowPos() const { + qreal rowPos = 0; + if (view->flow() == QDeclarativeGridView::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() == QDeclarativeGridView::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 { - return view->flow() == QDeclarativeGridView::LeftToRight - ? item->y() + view->cellHeight() - 1 - : item->x() + view->cellWidth() - 1; + if (view->flow() == QDeclarativeGridView::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->flow() == QDeclarativeGridView::LeftToRight) { - item->setPos(QPointF(col, row)); + if (view->effectiveLayoutDirection() == Qt::RightToLeft) { + if (view->flow() == QDeclarativeGridView::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 { - item->setPos(QPointF(row, col)); + if (view->flow() == QDeclarativeGridView::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() && @@ -101,10 +142,12 @@ class QDeclarativeGridViewPrivate : public QDeclarativeFlickablePrivate public: QDeclarativeGridViewPrivate() - : currentItem(0), flow(QDeclarativeGridView::LeftToRight) + : currentItem(0), layoutDirection(Qt::LeftToRight), flow(QDeclarativeGridView::LeftToRight) , visibleIndex(0) , currentIndex(-1) , cellWidth(100), cellHeight(100), columns(1), requestedIndex(-1), itemCount(0) - , highlightRangeStart(0), highlightRangeEnd(0), highlightRange(QDeclarativeGridView::NoHighlightRange) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightRangeStartValid(false), highlightRangeEndValid(false) + , highlightRange(QDeclarativeGridView::NoHighlightRange) , highlightComponent(0), highlight(0), trackedItem(0) , moveReason(Other), buffer(0), highlightXAnimator(0), highlightYAnimator(0) , highlightMoveDuration(150) @@ -144,35 +187,71 @@ public: return 0; } + bool isRightToLeftTopToBottom() const { + Q_Q(const QDeclarativeGridView); + return flow == QDeclarativeGridView::TopToBottom && q->effectiveLayoutDirection() == Qt::RightToLeft; + } + + void regenerate() { + Q_Q(QDeclarativeGridView); + if (q->isComponentComplete()) { + clear(); + updateGrid(); + q->refill(); + updateCurrent(currentIndex); + } + } + + void mirrorChange() { + Q_Q(QDeclarativeGridView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); + } + qreal position() const { Q_Q(const QDeclarativeGridView); return flow == QDeclarativeGridView::LeftToRight ? q->contentY() : q->contentX(); } void setPosition(qreal pos) { Q_Q(QDeclarativeGridView); - if (flow == QDeclarativeGridView::LeftToRight) + if (flow == QDeclarativeGridView::LeftToRight) { q->QDeclarativeFlickable::setContentY(pos); - else - q->QDeclarativeFlickable::setContentX(pos); + q->QDeclarativeFlickable::setContentX(0); + } else { + if (q->effectiveLayoutDirection() == Qt::LeftToRight) + q->QDeclarativeFlickable::setContentX(pos); + else + q->QDeclarativeFlickable::setContentX(-pos-size()); + q->QDeclarativeFlickable::setContentY(0); + } } int size() const { Q_Q(const QDeclarativeGridView); return flow == QDeclarativeGridView::LeftToRight ? q->height() : q->width(); } - qreal startPosition() const { + qreal originPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) pos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); return pos; } - qreal endPosition() const { + 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(); } @@ -227,7 +306,7 @@ public: } FxGridItem *firstVisibleItem() const { - const qreal pos = position(); + const qreal pos = isRightToLeftTopToBottom() ? -position()-size() : position(); for (int i = 0; i < visibleItems.count(); ++i) { FxGridItem *item = visibleItems.at(i); if (item->index != -1 && item->endRowPos() > pos) @@ -237,15 +316,12 @@ public: } int lastVisibleIndex() const { - int lastIndex = -1; - for (int i = visibleItems.count()-1; i >= 0; --i) { - FxGridItem *gridItem = visibleItems.at(i); - if (gridItem->index != -1) { - lastIndex = gridItem->index; - break; - } + for (int i = 0; i < visibleItems.count(); ++i) { + FxGridItem *item = visibleItems.at(i); + if (item->index != -1) + return item->index; } - return lastIndex; + return -1; } // Map a model index to visibleItems list index. @@ -271,8 +347,15 @@ public: pos += rowSize()/2; snapPos = visibleItems.first()->rowPos() - visibleIndex / columns * rowSize(); snapPos = pos - fmodf(pos - snapPos, qreal(rowSize())); - qreal maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); - qreal minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + qreal maxExtent; + qreal minExtent; + if (isRightToLeftTopToBottom()) { + maxExtent = q->minXExtent(); + minExtent = q->maxXExtent(); + } else { + maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + } if (snapPos > maxExtent) snapPos = maxExtent; if (snapPos < minExtent) @@ -363,6 +446,7 @@ public: QList<FxGridItem*> visibleItems; QHash<QDeclarativeItem*,int> unrequestedItems; FxGridItem *currentItem; + Qt::LayoutDirection layoutDirection; QDeclarativeGridView::Flow flow; int visibleIndex; int currentIndex; @@ -373,6 +457,8 @@ public: int itemCount; qreal highlightRangeStart; qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; QDeclarativeGridView::HighlightRangeMode highlightRange; QDeclarativeComponent *highlightComponent; FxGridItem *highlight; @@ -554,7 +640,7 @@ void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create while (visibleItems.count() > 1 && (item = visibleItems.first()) - && item->endRowPos() < bufferFrom - rowSize()*(item->colPos()/colSize()+1)/(columns+1)) { + && 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(); @@ -596,12 +682,14 @@ void QDeclarativeGridViewPrivate::refill(qreal from, qreal to, bool doBuffer) void QDeclarativeGridViewPrivate::updateGrid() { Q_Q(QDeclarativeGridView); + columns = (int)qMax((flow == QDeclarativeGridView::LeftToRight ? q->width() : q->height()) / colSize(), qreal(1.)); if (isValid()) { if (flow == QDeclarativeGridView::LeftToRight) q->setContentHeight(endPosition() - startPosition()); else - q->setContentWidth(endPosition() - startPosition()); + q->setContentWidth(lastPosition() - originPosition()); + setPosition(0); } } @@ -669,10 +757,14 @@ void QDeclarativeGridViewPrivate::updateUnrequestedPositions() { QHash<QDeclarativeItem*,int>::const_iterator it; for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) { + QDeclarativeItem *item = it.key(); if (flow == QDeclarativeGridView::LeftToRight) { - it.key()->setPos(QPointF(colPosAt(*it), rowPosAt(*it))); + item->setPos(QPointF(colPosAt(*it), rowPosAt(*it))); } else { - it.key()->setPos(QPointF(rowPosAt(*it), colPosAt(*it))); + if (isRightToLeftTopToBottom()) + item->setPos(QPointF(-rowPosAt(*it)-item->width(), colPosAt(*it))); + else + item->setPos(QPointF(rowPosAt(*it), colPosAt(*it))); } } } @@ -837,23 +929,30 @@ void QDeclarativeGridViewPrivate::updateFooter() } } 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 = endPosition(); + qreal endPos = lastPosition(); if (lastVisibleIndex() == model->count()-1) { - footer->setPosition(0, endPos); + footer->setPosition(colOffset, endPos + rowOffset); } else { - qreal visiblePos = position() + q->height(); - if (endPos <= visiblePos || footer->endRowPos() < endPos) - footer->setPosition(0, endPos); + 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 == QDeclarativeGridView::LeftToRight - ? header->item->height() - : header->item->width(); + endPos += flow == QDeclarativeGridView::LeftToRight ? header->item->height() : header->item->width(); } - footer->setPosition(0, endPos); + footer->setPosition(colOffset, endPos); } } } @@ -883,16 +982,27 @@ void QDeclarativeGridViewPrivate::updateHeader() } } 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 = startPosition(); + qreal startPos = originPosition(); if (visibleIndex == 0) { - header->setPosition(0, startPos - headerSize()); + header->setPosition(colOffset, startPos + rowOffset); } else { - if (position() <= startPos || header->rowPos() > startPos - headerSize()) - header->setPosition(0, startPos - headerSize()); + 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(0, 0); + header->setPosition(colOffset, 0); } } } @@ -912,76 +1022,107 @@ void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m || (flow == QDeclarativeGridView::LeftToRight && &data == &hData)) return; - int oldDuration = fixupDuration; - fixupDuration = moveReason == Mouse ? fixupDuration : 0; + 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 != QDeclarativeGridView::NoSnap) { - FxGridItem *topItem = snapItemAt(position()+highlightRangeStart); - FxGridItem *bottomItem = snapItemAt(position()+highlightRangeEnd); + qreal tempPosition = isRightToLeftTopToBottom() ? -position()-size() : position(); + FxGridItem *topItem = snapItemAt(tempPosition+highlightStart); + FxGridItem *bottomItem = snapItemAt(tempPosition+highlightEnd); qreal pos; if (topItem && bottomItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { - qreal topPos = qMin(topItem->rowPos() - highlightRangeStart, -maxExtent); - qreal bottomPos = qMax(bottomItem->rowPos() - highlightRangeEnd, -minExtent); + 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) { - if (topItem->index == 0 && header && position()+highlightRangeStart < header->rowPos()+headerSize()/2) - pos = header->rowPos() - highlightRangeStart; - else - pos = qMax(qMin(topItem->rowPos() - highlightRangeStart, -maxExtent), -minExtent); + 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) { - pos = qMax(qMin(bottomItem->rowPos() - highlightRangeStart, -maxExtent), -minExtent); + if (isRightToLeftTopToBottom()) + pos = qMax(qMin(-bottomItem->rowPos() + highlightStart - size(), -maxExtent), -minExtent); + else + pos = qMax(qMin(bottomItem->rowPos() - highlightStart, -maxExtent), -minExtent); } else { QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); - fixupDuration = oldDuration; return; } if (currentItem && haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { updateHighlight(); qreal currPos = currentItem->rowPos(); - if (pos < currPos + rowSize() - highlightRangeEnd) - pos = currPos + rowSize() - highlightRangeEnd; - if (pos > currPos - highlightRangeStart) - pos = currPos - highlightRangeStart; + 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 (fixupDuration) + if (fixupMode != Immediate) { timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else + data.fixingUp = true; + } else { timeline.set(data.move, -pos); + } vTime = timeline.time(); } } else if (haveHighlightRange && highlightRange == QDeclarativeGridView::StrictlyEnforceRange) { if (currentItem) { updateHighlight(); qreal pos = currentItem->rowPos(); - qreal viewPos = position(); - if (viewPos < pos + rowSize() - highlightRangeEnd) - viewPos = pos + rowSize() - highlightRangeEnd; - if (viewPos > pos - highlightRangeStart) - viewPos = pos - highlightRangeStart; - + 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 (fixupDuration) + if (fixupMode != Immediate) { timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else + data.fixingUp = true; + } else { timeline.set(data.move, -viewPos); + } } vTime = timeline.time(); } } else { QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); } - fixupDuration = oldDuration; + fixupMode = Normal; } void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity) { Q_Q(QDeclarativeGridView); + data.fixingUp = false; moveReason = Mouse; if ((!haveHighlightRange || highlightRange != QDeclarativeGridView::StrictlyEnforceRange) && snapMode == QDeclarativeGridView::NoSnap) { @@ -989,12 +1130,14 @@ void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal m return; } qreal maxDistance = 0; - // -ve velocity means list is moving up + 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 == QDeclarativeGridView::SnapOneRow) { - if (FxGridItem *item = firstVisibleItem()) - maxDistance = qAbs(item->rowPos() + data.move.value()); + if (FxGridItem *item = firstVisibleItem()) { + maxDistance = qAbs(item->rowPos() + dataValue); + } } else { maxDistance = qAbs(minExtent - data.move.value()); } @@ -1004,8 +1147,8 @@ void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal m } else { if (data.move.value() > maxExtent) { if (snapMode == QDeclarativeGridView::SnapOneRow) { - qreal pos = snapPosAt(-data.move.value()) + rowSize(); - maxDistance = qAbs(pos + data.move.value()); + qreal pos = snapPosAt(-dataValue) + (isRightToLeftTopToBottom() ? 0 : rowSize()); + maxDistance = qAbs(pos + dataValue); } else { maxDistance = qAbs(maxExtent - data.move.value()); } @@ -1013,7 +1156,10 @@ void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal m if (snapMode == QDeclarativeGridView::NoSnap && highlightRange != QDeclarativeGridView::StrictlyEnforceRange) data.flickTarget = maxExtent; } + bool overShoot = boundsBehavior == QDeclarativeFlickable::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; @@ -1032,7 +1178,9 @@ void QDeclarativeGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal m dist = qMin(dist, maxDistance); if (v > 0) dist = -dist; - data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; + 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 @@ -1224,6 +1372,13 @@ QVariant QDeclarativeGridView::model() const return d->modelVariant; } +// For internal use +int QDeclarativeGridView::modelCount() const +{ + Q_D(const QDeclarativeGridView); + return d->model->count(); +} + void QDeclarativeGridView::setModel(const QVariant &model) { Q_D(QDeclarativeGridView); @@ -1549,6 +1704,7 @@ qreal QDeclarativeGridView::preferredHighlightBegin() const void QDeclarativeGridView::setPreferredHighlightBegin(qreal start) { Q_D(QDeclarativeGridView); + d->highlightRangeStartValid = true; if (d->highlightRangeStart == start) return; d->highlightRangeStart = start; @@ -1556,6 +1712,16 @@ void QDeclarativeGridView::setPreferredHighlightBegin(qreal start) emit preferredHighlightBeginChanged(); } +void QDeclarativeGridView::resetPreferredHighlightBegin() +{ + Q_D(QDeclarativeGridView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + qreal QDeclarativeGridView::preferredHighlightEnd() const { Q_D(const QDeclarativeGridView); @@ -1565,6 +1731,7 @@ qreal QDeclarativeGridView::preferredHighlightEnd() const void QDeclarativeGridView::setPreferredHighlightEnd(qreal end) { Q_D(QDeclarativeGridView); + d->highlightRangeEndValid = true; if (d->highlightRangeEnd == end) return; d->highlightRangeEnd = end; @@ -1572,6 +1739,16 @@ void QDeclarativeGridView::setPreferredHighlightEnd(qreal end) emit preferredHighlightEndChanged(); } +void QDeclarativeGridView::resetPreferredHighlightEnd() +{ + Q_D(QDeclarativeGridView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + QDeclarativeGridView::HighlightRangeMode QDeclarativeGridView::highlightRangeMode() const { Q_D(const QDeclarativeGridView); @@ -1588,6 +1765,60 @@ void QDeclarativeGridView::setHighlightRangeMode(HighlightRangeMode mode) 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 QDeclarativeGridView::layoutDirection() const +{ + Q_D(const QDeclarativeGridView); + return d->layoutDirection; +} + +void QDeclarativeGridView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarativeGridView); + 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 QDeclarativeGridView::effectiveLayoutDirection() const +{ + Q_D(const QDeclarativeGridView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} /*! \qmlproperty enumeration GridView::flow @@ -1620,10 +1851,7 @@ void QDeclarativeGridView::setFlow(Flow flow) } setContentX(0); setContentY(0); - d->clear(); - d->updateGrid(); - refill(); - d->updateCurrent(d->currentIndex); + d->regenerate(); emit flowChanged(); } } @@ -1890,11 +2118,23 @@ void QDeclarativeGridView::viewportMoved() if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { // reposition highlight qreal pos = d->highlight->rowPos(); - qreal viewPos = d->position(); - if (pos > viewPos + d->highlightRangeEnd - d->rowSize()) - pos = viewPos + d->highlightRangeEnd - d->rowSize(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; + 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 @@ -1956,11 +2196,27 @@ qreal QDeclarativeGridView::minXExtent() const if (d->flow == QDeclarativeGridView::LeftToRight) return QDeclarativeFlickable::minXExtent(); qreal extent = -d->startPosition(); - if (d->header && d->visibleItems.count()) - extent += d->header->item->width(); + 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 += d->highlightRangeStart; - extent = qMax(extent, -(d->rowPosAt(0) + d->rowSize() - d->highlightRangeEnd)); + extent += highlightStart; + extent = qMax(extent, -(endPositionFirstItem - highlightEnd)); } return extent; } @@ -1971,17 +2227,38 @@ qreal QDeclarativeGridView::maxXExtent() const if (d->flow == QDeclarativeGridView::LeftToRight) return QDeclarativeFlickable::maxXExtent(); qreal extent; + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition; + 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; + if (d->model && d->model->count()) + lastItemPosition = d->rowPosAt(d->model->count()-1); + } if (!d->model || !d->model->count()) { extent = 0; - } 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 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->footer) - extent -= d->footer->item->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; @@ -2094,15 +2371,30 @@ void QDeclarativeGridView::moveCurrentIndexLeft() const int count = d->model ? d->model->count() : 0; if (!count) return; - if (d->flow == QDeclarativeGridView::LeftToRight) { - if (currentIndex() > 0 || d->wrap) { - int index = currentIndex() - 1; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); + + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QDeclarativeGridView::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 (currentIndex() >= d->columns || d->wrap) { - int index = currentIndex() - d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : count-1); + if (d->flow == QDeclarativeGridView::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); + } } } } @@ -2122,15 +2414,30 @@ void QDeclarativeGridView::moveCurrentIndexRight() const int count = d->model ? d->model->count() : 0; if (!count) return; - if (d->flow == QDeclarativeGridView::LeftToRight) { - if (currentIndex() < count - 1 || d->wrap) { - int index = currentIndex() + 1; - setCurrentIndex((index >= 0 && index < count) ? index : 0); + + if (effectiveLayoutDirection() == Qt::LeftToRight) { + if (d->flow == QDeclarativeGridView::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 (currentIndex() < count - d->columns || d->wrap) { - int index = currentIndex()+d->columns; - setCurrentIndex((index >= 0 && index < count) ? index : 0); + if (d->flow == QDeclarativeGridView::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); + } } } } @@ -2147,16 +2454,24 @@ void QDeclarativeGridViewPrivate::positionViewAtIndex(int index, int mode) if (layoutScheduled) layout(); - qreal pos = position(); + qreal pos = isRightToLeftTopToBottom() ? -position() - size() : position(); FxGridItem *item = visibleItem(idx); - qreal maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + qreal maxExtent; + if (flow == QDeclarativeGridView::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<FxGridItem*> oldVisible = visibleItems; visibleItems.clear(); visibleIndex = idx - idx % columns; - maxExtent = flow == QDeclarativeGridView::LeftToRight ? -q->maxYExtent() : -q->maxXExtent(); + if (flow == QDeclarativeGridView::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) @@ -2197,8 +2512,13 @@ void QDeclarativeGridViewPrivate::positionViewAtIndex(int index, int mode) if (itemPos < pos) pos = itemPos; } + pos = qMin(pos, maxExtent); - qreal minExtent = flow == QDeclarativeGridView::LeftToRight ? -q->minYExtent() : -q->minXExtent(); + qreal minExtent; + if (flow == QDeclarativeGridView::LeftToRight) + minExtent = -q->minYExtent(); + else + minExtent = isRightToLeftTopToBottom() ? q->maxXExtent()-size() : -q->minXExtent(); pos = qMax(pos, minExtent); moveReason = QDeclarativeGridViewPrivate::Other; q->cancelFlick(); @@ -2250,6 +2570,7 @@ void QDeclarativeGridView::positionViewAtIndex(int index, int 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. @@ -2337,32 +2658,43 @@ void QDeclarativeGridView::trackedPositionChanged() return; if (d->moveReason == QDeclarativeGridViewPrivate::SetIndex) { const qreal trackedPos = d->trackedItem->rowPos(); - const qreal viewPos = d->position(); + 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 + d->highlightRangeEnd - d->rowSize()) - pos = trackedPos - d->highlightRangeEnd + d->rowSize(); - if (trackedPos < pos + d->highlightRangeStart) - pos = trackedPos - d->highlightRangeStart; + if (trackedPos > pos + highlightEnd - d->rowSize()) + pos = trackedPos - highlightEnd + d->rowSize(); + if (trackedPos < pos + highlightStart) + pos = trackedPos - highlightStart; } else { - if (trackedPos < d->startPosition() + d->highlightRangeStart) { + if (trackedPos < d->startPosition() + highlightStart) { pos = d->startPosition(); - } else if (d->trackedItem->endRowPos() > d->endPosition() - d->size() + d->highlightRangeEnd) { + } 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 + d->highlightRangeStart) { - pos = trackedPos - d->highlightRangeStart; - } else if (trackedPos > viewPos + d->highlightRangeEnd - d->rowSize()) { - pos = trackedPos - d->highlightRangeEnd + d->rowSize(); + 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 = d->currentItem->rowPos() < trackedPos ? trackedPos : d->currentItem->rowPos(); + 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()) { @@ -2430,7 +2762,8 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) modelIndex = d->visibleIndex; } - int to = d->buffer+d->position()+d->size()-1; + 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()) { @@ -2448,10 +2781,7 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) } } } else if (d->itemCount == 0 && d->header) { - if (d->flow == QDeclarativeGridView::LeftToRight) - rowPos = d->headerSize(); - else - colPos = d->headerSize(); + rowPos = d->headerSize(); } // Update the indexes of the following visible items. @@ -2504,10 +2834,10 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) if (d->currentItem) { d->currentItem->index = d->currentIndex; d->currentItem->setPosition(d->colPosAt(d->currentIndex), d->rowPosAt(d->currentIndex)); - } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->updateCurrent(0); } emit currentIndexChanged(); + } else if (d->itemCount == 0 && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) { + setCurrentIndex(0); } // everything is in order now - emit add() signal @@ -2574,6 +2904,8 @@ void QDeclarativeGridView::itemsRemoved(int modelIndex, int count) d->currentIndex = -1; if (d->itemCount) d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + else + emit currentIndexChanged(); } // update visibleIndex @@ -2756,7 +3088,10 @@ void QDeclarativeGridView::animStopped() void QDeclarativeGridView::refill() { Q_D(QDeclarativeGridView); - d->refill(d->position(), d->position()+d->size()-1); + if (d->isRightToLeftTopToBottom()) + d->refill(-d->position()-d->size()+1, -d->position()); + else + d->refill(d->position(), d->position()+d->size()-1); } diff --git a/src/declarative/graphicsitems/qdeclarativegridview_p.h b/src/declarative/graphicsitems/qdeclarativegridview_p.h index e68a9ba..4d99a14 100644 --- a/src/declarative/graphicsitems/qdeclarativegridview_p.h +++ b/src/declarative/graphicsitems/qdeclarativegridview_p.h @@ -69,11 +69,13 @@ class Q_AUTOTEST_EXPORT QDeclarativeGridView : public QDeclarativeFlickable 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) - Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged) + 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) @@ -95,6 +97,7 @@ public: ~QDeclarativeGridView(); QVariant model() const; + int modelCount() const; void setModel(const QVariant &); QDeclarativeComponent *delegate() const; @@ -122,9 +125,15 @@ public: 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; @@ -184,6 +193,8 @@ Q_SIGNALS: void modelChanged(); void delegateChanged(); void flowChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) void effectiveLayoutDirectionChanged(); void keyNavigationWrapsChanged(); void cacheBufferChanged(); void snapModeChanged(); diff --git a/src/declarative/graphicsitems/qdeclarativeimage.cpp b/src/declarative/graphicsitems/qdeclarativeimage.cpp index 2c9bde5..ed5d5fc 100644 --- a/src/declarative/graphicsitems/qdeclarativeimage.cpp +++ b/src/declarative/graphicsitems/qdeclarativeimage.cpp @@ -147,8 +147,8 @@ void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap) /*! \qmlproperty enumeration Image::fillMode - Set this property to define what happens when the image set for the item is smaller - than the size of the item. + 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 @@ -234,6 +234,9 @@ void QDeclarativeImagePrivate::setPixmap(const QPixmap &pixmap) \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} */ QDeclarativeImage::FillMode QDeclarativeImage::fillMode() const @@ -386,14 +389,16 @@ void QDeclarativeImage::updatePaintedGeometry() if (d->fillMode == PreserveAspectFit) { if (!d->pix.width() || !d->pix.height()) return; - qreal widthScale = width() / qreal(d->pix.width()); - qreal heightScale = height() / qreal(d->pix.height()); + 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 = width(); + d->paintedWidth = w; d->paintedHeight = widthScale * qreal(d->pix.height()); } else if(heightScale < widthScale) { d->paintedWidth = heightScale * qreal(d->pix.width()); - d->paintedHeight = height(); + d->paintedHeight = h; } if (widthValid() && !heightValid()) { setImplicitHeight(d->paintedHeight); diff --git a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp index 471c87f..2de3ba0 100644 --- a/src/declarative/graphicsitems/qdeclarativeimagebase.cpp +++ b/src/declarative/graphicsitems/qdeclarativeimagebase.cpp @@ -130,7 +130,7 @@ QSize QDeclarativeImageBase::sourceSize() const int width = d->sourcesize.width(); int height = d->sourcesize.height(); - return QSize(width != -1 ? width : implicitWidth(), height != -1 ? height : implicitHeight()); + return QSize(width != -1 ? width : d->pix.width(), height != -1 ? height : d->pix.height()); } bool QDeclarativeImageBase::cache() const diff --git a/src/declarative/graphicsitems/qdeclarativeitem.cpp b/src/declarative/graphicsitems/qdeclarativeitem.cpp index 468b5cb..6602dda 100644 --- a/src/declarative/graphicsitems/qdeclarativeitem.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitem.cpp @@ -39,12 +39,12 @@ ** ****************************************************************************/ -#include "private/qdeclarativeitem_p.h" #include "qdeclarativeitem.h" #include "private/qdeclarativeevents_p_p.h" #include <private/qdeclarativeengine_p.h> #include <private/qgraphicsitem_p.h> +#include <QtDeclarative/private/qdeclarativeitem_p.h> #include <qdeclarativeengine.h> #include <qdeclarativeopenmetaobject_p.h> @@ -615,19 +615,28 @@ void QDeclarativeKeyNavigationAttached::keyPressed(QKeyEvent *event, bool post) return; } + bool mirror = false; switch(event->key()) { - case Qt::Key_Left: - if (d->left) { - setFocusNavigation(d->left, "left"); + 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 (d->right) { - setFocusNavigation(d->right, "right"); + } + 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"); @@ -669,16 +678,19 @@ void QDeclarativeKeyNavigationAttached::keyReleased(QKeyEvent *event, bool post) return; } + bool mirror = false; switch(event->key()) { case Qt::Key_Left: - if (d->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 (d->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) { @@ -731,6 +743,171 @@ void QDeclarativeKeyNavigationAttached::setFocusNavigation(QDeclarativeItem *cur } /*! + \qmlclass LayoutMirroring QDeclarativeLayoutMirroringAttached + \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. +*/ + +QDeclarativeLayoutMirroringAttached::QDeclarativeLayoutMirroringAttached(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"); +} + +QDeclarativeLayoutMirroringAttached * QDeclarativeLayoutMirroringAttached::qmlAttachedProperties(QObject *object) +{ + return new QDeclarativeLayoutMirroringAttached(object); +} + +bool QDeclarativeLayoutMirroringAttached::enabled() const +{ + return itemPrivate ? itemPrivate->effectiveLayoutMirror : false; +} + +void QDeclarativeLayoutMirroringAttached::setEnabled(bool enabled) +{ + if (!itemPrivate) + return; + + itemPrivate->isMirrorImplicit = false; + if (enabled != itemPrivate->effectiveLayoutMirror) { + itemPrivate->setLayoutMirror(enabled); + if (itemPrivate->inheritMirrorFromItem) + itemPrivate->resolveLayoutMirror(); + } +} + +void QDeclarativeLayoutMirroringAttached::resetEnabled() +{ + if (itemPrivate && !itemPrivate->isMirrorImplicit) { + itemPrivate->isMirrorImplicit = true; + itemPrivate->resolveLayoutMirror(); + } +} + +bool QDeclarativeLayoutMirroringAttached::childrenInherit() const +{ + return itemPrivate ? itemPrivate->inheritMirrorFromItem : false; +} + +void QDeclarativeLayoutMirroringAttached::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 QDeclarativeKeysAttached \ingroup qml-basic-interaction-elements \since 4.7 @@ -1397,6 +1574,11 @@ QDeclarativeKeysAttached *QDeclarativeKeysAttached::qmlAttachedProperties(QObjec \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. + */ /*! @@ -2154,6 +2336,8 @@ QDeclarativeAnchorLine QDeclarativeItemPrivate::baseline() const \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. @@ -2211,6 +2395,8 @@ QDeclarativeAnchorLine QDeclarativeItemPrivate::baseline() const 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}. @@ -2812,6 +2998,7 @@ QVariant QDeclarativeItem::itemChange(GraphicsItemChange change, Q_D(QDeclarativeItem); switch (change) { case ItemParentHasChanged: + d->resolveLayoutMirror(); emit parentChanged(parentItem()); d->parentNotifier.notify(); break; diff --git a/src/declarative/graphicsitems/qdeclarativeitem_p.h b/src/declarative/graphicsitems/qdeclarativeitem_p.h index 4303c0a..dae581c 100644 --- a/src/declarative/graphicsitems/qdeclarativeitem_p.h +++ b/src/declarative/graphicsitems/qdeclarativeitem_p.h @@ -77,6 +77,7 @@ QT_BEGIN_NAMESPACE class QNetworkReply; class QDeclarativeItemKeyFilter; +class QDeclarativeLayoutMirroringAttached; //### merge into private? class QDeclarativeContents : public QObject, public QDeclarativeItemChangeListener @@ -125,8 +126,10 @@ public: _stateGroup(0), origin(QDeclarativeItem::Center), widthValid(false), heightValid(false), componentComplete(true), keepMouse(false), - smooth(false), transformOriginDirty(true), doneEventPreHandler(false), keyHandler(0), - mWidth(0), mHeight(0), mImplicitWidth(0), mImplicitHeight(0), hadSubFocusItem(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; @@ -134,7 +137,6 @@ public: QGraphicsItem::ItemHasNoContents | QGraphicsItem::ItemIsFocusable | QGraphicsItem::ItemNegativeZStacksBehindParent); - } void init(QDeclarativeItem *parent) @@ -143,11 +145,17 @@ public: 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); @@ -162,6 +170,10 @@ public: virtual void implicitWidthChanged(); virtual void implicitHeightChanged(); + void resolveLayoutMirror(); + void setImplicitLayoutMirror(bool mirror, bool inherit); + void setLayoutMirror(bool mirror); + QDeclarativeListProperty<QObject> data(); QDeclarativeListProperty<QObject> resources(); @@ -272,6 +284,11 @@ public: 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; @@ -280,6 +297,8 @@ public: qreal mImplicitWidth; qreal mImplicitHeight; + QDeclarativeLayoutMirroringAttached* attachedLayoutDirection; + bool hadSubFocusItem; QPointF computeTransformOrigin() const; @@ -326,6 +345,8 @@ public: virtual void focusChanged(bool); + virtual void mirrorChange() {}; + static qint64 consistentTime; static void setConsistentTime(qint64 t); static void start(QElapsedTimer &); @@ -423,6 +444,31 @@ private: void setFocusNavigation(QDeclarativeItem *currentItem, const char *dir); }; +class QDeclarativeLayoutMirroringAttached : 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 QDeclarativeLayoutMirroringAttached(QObject *parent = 0); + + bool enabled() const; + void setEnabled(bool); + void resetEnabled(); + + bool childrenInherit() const; + void setChildrenInherit(bool); + + static QDeclarativeLayoutMirroringAttached *qmlAttachedProperties(QObject *); +Q_SIGNALS: + void enabledChanged(); + void childrenInheritChanged(); +private: + friend class QDeclarativeItemPrivate; + QDeclarativeItemPrivate *itemPrivate; +}; + class QDeclarativeKeysAttachedPrivate : public QObjectPrivate { public: @@ -572,5 +618,7 @@ QML_DECLARE_TYPE(QDeclarativeKeysAttached) QML_DECLARE_TYPEINFO(QDeclarativeKeysAttached, QML_HAS_ATTACHED_PROPERTIES) QML_DECLARE_TYPE(QDeclarativeKeyNavigationAttached) QML_DECLARE_TYPEINFO(QDeclarativeKeyNavigationAttached, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QDeclarativeLayoutMirroringAttached) +QML_DECLARE_TYPEINFO(QDeclarativeLayoutMirroringAttached, QML_HAS_ATTACHED_PROPERTIES) #endif // QDECLARATIVEITEM_P_H diff --git a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp index 3c8f64e..c4a9030 100644 --- a/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp +++ b/src/declarative/graphicsitems/qdeclarativeitemsmodule.cpp @@ -198,6 +198,7 @@ void QDeclarativeItemModule::defineModule() qmlRegisterRevision<QDeclarativeImplicitSizeItem,1>("QtQuick",1,1); qmlRegisterRevision<QDeclarativeImplicitSizePaintedItem,0>("QtQuick",1,0); qmlRegisterRevision<QDeclarativeImplicitSizePaintedItem,1>("QtQuick",1,1); + qmlRegisterUncreatableType<QDeclarativeLayoutMirroringAttached>("QtQuick",1,1,"LayoutMirroring", QDeclarativeLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties")); #ifndef QT_NO_IMPORT_QT47_QML #ifdef QT_NO_MOVIE diff --git a/src/declarative/graphicsitems/qdeclarativelistview.cpp b/src/declarative/graphicsitems/qdeclarativelistview.cpp index 338cb58..6ae1ddc 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview.cpp +++ b/src/declarative/graphicsitems/qdeclarativelistview.cpp @@ -100,13 +100,21 @@ public: } ~FxListItem() {} qreal position() const { - if (section) - return (view->orientation() == QDeclarativeListView::Vertical ? section->y() : section->x()); - else - return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x()); + if (section) { + if (view->orientation() == QDeclarativeListView::Vertical) + return section->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x()); + } else { + return itemPosition(); + } } + qreal itemPosition() const { - return (view->orientation() == QDeclarativeListView::Vertical ? item->y() : item->x()); + if (view->orientation() == QDeclarativeListView::Vertical) + return item->y(); + else + return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x()); } qreal size() const { if (section) @@ -123,9 +131,13 @@ public: return 0.0; } qreal endPosition() const { - return (view->orientation() == QDeclarativeListView::Vertical - ? item->y() + (item->height() >= 1.0 ? item->height() : 1) - : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1; + if (view->orientation() == QDeclarativeListView::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() == QDeclarativeListView::Vertical) { @@ -135,11 +147,19 @@ public: } item->setY(pos); } else { - if (section) { - section->setX(pos); - pos += section->width(); + 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); } - item->setX(pos); } } void setSize(qreal size) { @@ -168,10 +188,11 @@ class QDeclarativeListViewPrivate : public QDeclarativeFlickablePrivate public: QDeclarativeListViewPrivate() - : currentItem(0), orient(QDeclarativeListView::Vertical) + : currentItem(0), orient(QDeclarativeListView::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) @@ -204,7 +225,7 @@ public: } FxListItem *firstVisibleItem() const { - const qreal pos = position(); + const qreal pos = isRightToLeft() ? -position()-size() : position(); for (int i = 0; i < visibleItems.count(); ++i) { FxListItem *item = visibleItems.at(i); if (item->index != -1 && item->endPosition() > pos) @@ -214,7 +235,7 @@ public: } FxListItem *nextVisibleItem() const { - const qreal pos = position(); + const qreal pos = isRightToLeft() ? -position()-size() : position(); bool foundFirst = false; for (int i = 0; i < visibleItems.count(); ++i) { FxListItem *item = visibleItems.at(i); @@ -248,23 +269,65 @@ public: return 0; } + void regenerate() { + Q_Q(QDeclarativeListView); + 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(QDeclarativeListView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); + } + + bool isRightToLeft() const { + Q_Q(const QDeclarativeListView); + return orient == QDeclarativeListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft; + } + qreal position() const { Q_Q(const QDeclarativeListView); return orient == QDeclarativeListView::Vertical ? q->contentY() : q->contentX(); } + void setPosition(qreal pos) { Q_Q(QDeclarativeListView); - if (orient == QDeclarativeListView::Vertical) + if (orient == QDeclarativeListView::Vertical) { q->QDeclarativeFlickable::setContentY(pos); - else - q->QDeclarativeFlickable::setContentX(pos); + } else { + if (isRightToLeft()) + q->QDeclarativeFlickable::setContentX(-pos-size()); + else + q->QDeclarativeFlickable::setContentX(pos); + } } qreal size() const { Q_Q(const QDeclarativeListView); return orient == QDeclarativeListView::Vertical ? q->height() : q->width(); } - qreal startPosition() const { + qreal originPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) { pos = (*visibleItems.constBegin())->position(); @@ -274,7 +337,7 @@ public: return pos; } - qreal endPosition() const { + qreal lastPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) { int invisibleCount = visibleItems.count() - visibleIndex; @@ -291,6 +354,14 @@ public: return pos; } + qreal startPosition() const { + return isRightToLeft() ? -lastPosition()-1 : originPosition(); + } + + qreal endPosition() const { + return isRightToLeft() ? -originPosition()-1 : lastPosition(); + } + qreal positionAt(int modelIndex) const { if (FxListItem *item = visibleItem(modelIndex)) return item->position(); @@ -312,6 +383,7 @@ public: else idx = visibleItems.at(idx)->index; int count = modelIndex - idx - 1; + return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1; } } @@ -368,7 +440,7 @@ public: } else if (pos > endPos) return endPos + qRound((pos - endPos) / averageSize) * averageSize; } - return qRound((pos - startPosition()) / averageSize) * averageSize + startPosition(); + return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition(); } FxListItem *snapItemAt(qreal pos) { @@ -484,6 +556,7 @@ public: QHash<QDeclarativeItem*,int> unrequestedItems; FxListItem *currentItem; QDeclarativeListView::Orientation orient; + Qt::LayoutDirection layoutDirection; qreal visiblePos; int visibleIndex; qreal averageSize; @@ -492,6 +565,8 @@ public: int itemCount; qreal highlightRangeStart; qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; QDeclarativeComponent *highlightComponent; FxListItem *highlight; FxListItem *trackedItem; @@ -669,7 +744,6 @@ void QDeclarativeListViewPrivate::refill(qreal from, qreal to, bool doBuffer) if (visibleItems.at(i)->index != -1) modelIndex = visibleItems.at(i)->index + 1; } - bool changed = false; FxListItem *item = 0; qreal pos = itemEnd + 1; @@ -815,8 +889,12 @@ void QDeclarativeListViewPrivate::updateUnrequestedPositions() 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()) - item->setX(positionAt(*it)); + if (item->x() + item->width() > pos && item->x() < pos + q->width()) { + if (isRightToLeft()) + item->setX(-positionAt(*it)-item->width()); + else + item->setX(positionAt(*it)); + } } } } @@ -907,7 +985,9 @@ void QDeclarativeListViewPrivate::updateHighlight() createHighlight(); if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) { // auto-update highlight - highlightPosAnimator->to = currentItem->itemPosition(); + highlightPosAnimator->to = isRightToLeft() + ? -currentItem->itemPosition()-currentItem->itemSize() + : currentItem->itemPosition(); highlightSizeAnimator->to = currentItem->itemSize(); if (orient == QDeclarativeListView::Vertical) { if (highlight->item->width() == 0) @@ -942,7 +1022,7 @@ void QDeclarativeListViewPrivate::createSection(FxListItem *listItem) } else { QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q)); context->setContextProperty(QLatin1String("section"), listItem->attached->m_section); - QObject *nobj = sectionCriteria->delegate()->create(context); + QObject *nobj = sectionCriteria->delegate()->beginCreate(context); if (nobj) { QDeclarative_setParent_noEvent(context, nobj); listItem->section = qobject_cast<QDeclarativeItem *>(nobj); @@ -956,6 +1036,7 @@ void QDeclarativeListViewPrivate::createSection(FxListItem *listItem) } else { delete context; } + sectionCriteria->delegate()->completeCreate(); } listItem->setPosition(pos); } else { @@ -1124,7 +1205,7 @@ void QDeclarativeListViewPrivate::updateFooter() } if (footer) { if (visibleItems.count()) { - qreal endPos = endPosition() + 1; + qreal endPos = lastPosition() + 1; if (lastVisibleIndex() == model->count()-1) { footer->setPosition(endPos); } else { @@ -1164,7 +1245,7 @@ void QDeclarativeListViewPrivate::updateHeader() } if (header) { if (visibleItems.count()) { - qreal startPos = startPosition(); + qreal startPos = originPosition(); if (visibleIndex == 0) { header->setPosition(startPos - header->size()); } else { @@ -1197,56 +1278,83 @@ void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m return; correctFlick = false; - int oldDuration = fixupDuration; - fixupDuration = moveReason == Mouse ? fixupDuration : 0; + 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 == QDeclarativeListView::StrictlyEnforceRange) { + if (currentItem && haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange + && moveReason != QDeclarativeListViewPrivate::SetIndex) { updateHighlight(); qreal pos = currentItem->itemPosition(); - qreal viewPos = position(); - if (viewPos < pos + currentItem->itemSize() - highlightRangeEnd) - viewPos = pos + currentItem->itemSize() - highlightRangeEnd; - if (viewPos > pos - highlightRangeStart) - viewPos = pos - highlightRangeStart; + 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 (fixupDuration) + if (fixupMode != Immediate) { timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else + data.fixingUp = true; + } else { timeline.set(data.move, -viewPos); + } } vTime = timeline.time(); - } else if (snapMode != QDeclarativeListView::NoSnap) { - FxListItem *topItem = snapItemAt(position()+highlightRangeStart); - FxListItem *bottomItem = snapItemAt(position()+highlightRangeEnd); + } else if (snapMode != QDeclarativeListView::NoSnap && moveReason != QDeclarativeListViewPrivate::SetIndex) { + qreal tempPosition = isRightToLeft() ? -position()-size() : position(); + FxListItem *topItem = snapItemAt(tempPosition+highlightStart); + FxListItem *bottomItem = snapItemAt(tempPosition+highlightEnd); qreal pos; - if (topItem) { - if (topItem->index == 0 && header && position()+highlightRangeStart < header->position()+header->size()/2) - pos = header->position() - highlightRangeStart; + 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(topItem->position() - highlightRangeStart, -maxExtent), -minExtent); - } else if (bottomItem) { - pos = qMax(qMin(bottomItem->position() - highlightRangeStart, -maxExtent), -minExtent); + pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent); } else { QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); - fixupDuration = oldDuration; return; } qreal dist = qAbs(data.move + pos); if (dist > 0) { timeline.reset(data.move); - if (fixupDuration) + if (fixupMode != Immediate) { timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2); - else + data.fixingUp = true; + } else { timeline.set(data.move, -pos); + } vTime = timeline.time(); } } else { QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent); } - fixupDuration = oldDuration; + fixupMode = Normal; } void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, @@ -1254,6 +1362,7 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m { Q_Q(QDeclarativeListView); + data.fixingUp = false; moveReason = Mouse; if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) { correctFlick = true; @@ -1261,12 +1370,13 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m 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 == QDeclarativeListView::SnapOneItem) { - if (FxListItem *item = firstVisibleItem()) - maxDistance = qAbs(item->position() + data.move.value()); + if (FxListItem *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem()) + maxDistance = qAbs(item->position() + dataValue); } else { maxDistance = qAbs(minExtent - data.move.value()); } @@ -1276,8 +1386,8 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m } else { if (data.move.value() > maxExtent) { if (snapMode == QDeclarativeListView::SnapOneItem) { - if (FxListItem *item = nextVisibleItem()) - maxDistance = qAbs(item->position() + data.move.value()); + if (FxListItem *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem()) + maxDistance = qAbs(item->position() + dataValue); } else { maxDistance = qAbs(maxExtent - data.move.value()); } @@ -1285,7 +1395,10 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange) data.flickTarget = maxExtent; } + bool overShoot = boundsBehavior == QDeclarativeFlickable::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. @@ -1310,7 +1423,9 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m if (v > 0) dist = -dist; if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) { - data.flickTarget = -snapPosAt(-(data.move.value() - highlightRangeStart) + dist) + highlightRangeStart; + 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(v, vSize); @@ -1343,6 +1458,7 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m data.flickTarget -= overshootDist; } } + timeline.reset(data.move); timeline.accel(data.move, v, accel, maxDistance + overshootDist); timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); @@ -1362,8 +1478,11 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m } else { // reevaluate the target boundary. qreal newtarget = data.flickTarget; - if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) - newtarget = -snapPosAt(-(data.flickTarget - highlightRangeStart)) + highlightRangeStart; + if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::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) @@ -1381,6 +1500,7 @@ void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal m fixup(data, minExtent, maxExtent); return; } + timeline.reset(data.move); timeline.accelDistance(data.move, v, -dist); timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this)); @@ -1879,6 +1999,7 @@ qreal QDeclarativeListView::preferredHighlightBegin() const void QDeclarativeListView::setPreferredHighlightBegin(qreal start) { Q_D(QDeclarativeListView); + d->highlightRangeStartValid = true; if (d->highlightRangeStart == start) return; d->highlightRangeStart = start; @@ -1886,6 +2007,16 @@ void QDeclarativeListView::setPreferredHighlightBegin(qreal start) emit preferredHighlightBeginChanged(); } +void QDeclarativeListView::resetPreferredHighlightBegin() +{ + Q_D(QDeclarativeListView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + qreal QDeclarativeListView::preferredHighlightEnd() const { Q_D(const QDeclarativeListView); @@ -1895,6 +2026,7 @@ qreal QDeclarativeListView::preferredHighlightEnd() const void QDeclarativeListView::setPreferredHighlightEnd(qreal end) { Q_D(QDeclarativeListView); + d->highlightRangeEndValid = true; if (d->highlightRangeEnd == end) return; d->highlightRangeEnd = end; @@ -1902,6 +2034,16 @@ void QDeclarativeListView::setPreferredHighlightEnd(qreal end) emit preferredHighlightEndChanged(); } +void QDeclarativeListView::resetPreferredHighlightEnd() +{ + Q_D(QDeclarativeListView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const { Q_D(const QDeclarativeListView); @@ -1980,15 +2122,63 @@ void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orie setContentHeight(-1); setFlickableDirection(HorizontalFlick); } - d->clear(); - d->setPosition(0); - refill(); + d->regenerate(); emit orientationChanged(); - d->updateCurrent(d->currentIndex); } } /*! + \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 QDeclarativeListView::layoutDirection() const +{ + Q_D(const QDeclarativeListView); + return d->layoutDirection; +} + +void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QDeclarativeListView); + 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 QDeclarativeListView::effectiveLayoutDirection() const +{ + Q_D(const QDeclarativeListView); + 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. @@ -2359,11 +2549,23 @@ void QDeclarativeListView::viewportMoved() if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) { // reposition highlight qreal pos = d->highlight->position(); - qreal viewPos = d->position(); - if (pos > viewPos + d->highlightRangeEnd - d->highlight->size()) - pos = viewPos + d->highlightRangeEnd - d->highlight->size(); - if (pos < viewPos + d->highlightRangeStart) - pos = viewPos + d->highlightRangeStart; + 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)); @@ -2401,13 +2603,15 @@ void QDeclarativeListView::viewportMoved() 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 = QDeclarativeListViewPrivate::BufferBefore; + d->bufferMode = d->isRightToLeft() + ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::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 = QDeclarativeListViewPrivate::BufferAfter; + d->bufferMode = d->isRightToLeft() + ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter; } } d->inFlickCorrection = false; @@ -2445,7 +2649,8 @@ qreal QDeclarativeListView::maxYExtent() const return height(); if (d->maxExtentDirty) { if (!d->model || !d->model->count()) { - d->maxExtent = 0; + 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) @@ -2470,11 +2675,34 @@ qreal QDeclarativeListView::minXExtent() const return QDeclarativeFlickable::minXExtent(); if (d->minExtentDirty) { d->minExtent = -d->startPosition(); - if (d->header) - d->minExtent += d->header->size(); + + 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 += d->highlightRangeStart; - d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1)); + d->minExtent += highlightStart; + d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1)); } d->minExtentDirty = false; } @@ -2488,23 +2716,46 @@ qreal QDeclarativeListView::maxXExtent() const if (d->orient == QDeclarativeListView::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()) { - d->maxExtent = 0; + if (!d->isRightToLeft()) + d->maxExtent = d->header ? -d->header->size() : 0; + d->maxExtent += width(); } 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)); + 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->footer) - d->maxExtent -= d->footer->size(); - qreal minX = minXExtent(); - if (d->maxExtent > minX) - d->maxExtent = minX; + 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; } @@ -2516,7 +2767,8 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) return; if (d->model && d->model->count() && d->interactive) { - if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Left) + if ((!d->isRightToLeft() && event->key() == Qt::Key_Left) + || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right) || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) { if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) { decrementCurrentIndex(); @@ -2526,7 +2778,8 @@ void QDeclarativeListView::keyPressEvent(QKeyEvent *event) event->accept(); return; } - } else if ((d->orient == QDeclarativeListView::Horizontal && event->key() == Qt::Key_Right) + } else if ((!d->isRightToLeft() && event->key() == Qt::Key_Right) + || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left) || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) { if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) { incrementCurrentIndex(); @@ -2548,6 +2801,11 @@ void QDeclarativeListView::geometryChanged(const QRectF &newGeometry, Q_D(QDeclarativeListView); d->maxExtentDirty = true; d->minExtentDirty = true; + if (d->isRightToLeft() && d->orient == Qt::Horizontal) { + // maintain position relative to the right edge + int dx = newGeometry.width() - oldGeometry.width(); + setContentX(contentX() - dx); + } QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry); } @@ -2603,9 +2861,14 @@ void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode) if (layoutScheduled) layout(); - qreal pos = position(); + qreal pos = isRightToLeft() ? -position() - size() : position(); FxListItem *item = visibleItem(idx); - qreal maxExtent = orient == QDeclarativeListView::Vertical ? -q->maxYExtent() : -q->maxXExtent(); + qreal maxExtent; + if (orient == QDeclarativeListView::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 @@ -2648,7 +2911,12 @@ void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode) pos = itemPos; } pos = qMin(pos, maxExtent); - qreal minExtent = orient == QDeclarativeListView::Vertical ? -q->minYExtent() : -q->minXExtent(); + qreal minExtent; + if (orient == QDeclarativeListView::Vertical) { + minExtent = -q->minYExtent(); + } else { + minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent(); + } pos = qMax(pos, minExtent); moveReason = QDeclarativeListViewPrivate::Other; q->cancelFlick(); @@ -2708,6 +2976,7 @@ void QDeclarativeListView::positionViewAtIndex(int index, int 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. @@ -2797,13 +3066,18 @@ void QDeclarativeListView::updateSections() roles << d->sectionCriteria->property().toUtf8(); d->model->setWatchedRoles(roles); d->updateSections(); + if (d->itemCount) + d->layout(); } } void QDeclarativeListView::refill() { Q_D(QDeclarativeListView); - d->refill(d->position(), d->position()+d->size()-1); + if (d->isRightToLeft()) + d->refill(-d->position()-d->size()+1, -d->position()); + else + d->refill(d->position(), d->position()+d->size()-1); } void QDeclarativeListView::trackedPositionChanged() @@ -2818,26 +3092,37 @@ void QDeclarativeListView::trackedPositionChanged() trackedPos -= d->currentItem->sectionSize(); trackedSize += d->currentItem->sectionSize(); } - const qreal viewPos = d->position(); + 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 + d->highlightRangeEnd - d->trackedItem->size()) - pos = trackedPos - d->highlightRangeEnd + d->trackedItem->size(); - if (trackedPos < pos + d->highlightRangeStart) - pos = trackedPos - d->highlightRangeStart; + 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() + d->highlightRangeStart) { + if (trackedPos < d->startPosition() + highlightStart) { pos = d->startPosition(); - } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + d->highlightRangeEnd) { + } 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 + d->highlightRangeStart) { - pos = trackedPos - d->highlightRangeStart; - } else if (trackedPos > viewPos + d->highlightRangeEnd - trackedSize) { - pos = trackedPos - d->highlightRangeEnd + trackedSize; + if (trackedPos < viewPos + highlightStart) { + pos = trackedPos - highlightStart; + } else if (trackedPos > viewPos + highlightEnd - trackedSize) { + pos = trackedPos - highlightEnd + trackedSize; } } } @@ -2874,7 +3159,9 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) d->updateUnrequestedIndexes(); d->moveReason = QDeclarativeListViewPrivate::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) @@ -2883,7 +3170,7 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) // 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+d->position()+d->size()-1) { + && 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 { @@ -2928,7 +3215,7 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) // Insert items before the visible item. int insertionIdx = index; int i = 0; - int from = d->position() - d->buffer; + int from = tempPos - d->buffer; for (i = count-1; i >= 0 && pos > from; --i) { if (!addedVisible) { d->scheduleLayout(); @@ -2958,7 +3245,7 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) } } else { int i = 0; - int to = d->buffer+d->position()+d->size()-1; + int to = d->buffer+tempPos+d->size()-1; for (i = 0; i < count && pos <= to; ++i) { if (!addedVisible) { d->scheduleLayout(); @@ -2985,10 +3272,10 @@ void QDeclarativeListView::itemsInserted(int modelIndex, int count) if (d->currentItem) { d->currentItem->index = d->currentIndex; d->currentItem->setPosition(d->currentItem->position() + diff); - } else if (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared)) { - d->updateCurrent(0); } 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) { @@ -3069,6 +3356,8 @@ void QDeclarativeListView::itemsRemoved(int modelIndex, int count) d->currentIndex = -1; if (d->itemCount) d->updateCurrent(qMin(modelIndex, d->itemCount-1)); + else + emit currentIndexChanged(); } // update visibleIndex @@ -3234,11 +3523,8 @@ void QDeclarativeListView::itemsChanged(int, int) void QDeclarativeListView::modelReset() { Q_D(QDeclarativeListView); - d->clear(); - d->setPosition(0); - refill(); d->moveReason = QDeclarativeListViewPrivate::SetIndex; - d->updateCurrent(d->currentIndex); + d->regenerate(); if (d->highlight && d->currentItem) { if (d->autoHighlight) d->highlight->setPosition(d->currentItem->position()); @@ -3254,10 +3540,14 @@ void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item) if (d->requestedIndex != index) { item->setParentItem(contentItem()); d->unrequestedItems.insert(item, index); - if (d->orient == QDeclarativeListView::Vertical) + if (d->orient == QDeclarativeListView::Vertical) { item->setY(d->positionAt(index)); - else - item->setX(d->positionAt(index)); + } else { + if (d->isRightToLeft()) + item->setX(-d->positionAt(index)-item->width()); + else + item->setX(d->positionAt(index)); + } } } diff --git a/src/declarative/graphicsitems/qdeclarativelistview_p.h b/src/declarative/graphicsitems/qdeclarativelistview_p.h index 265f4bd..3b12225 100644 --- a/src/declarative/graphicsitems/qdeclarativelistview_p.h +++ b/src/declarative/graphicsitems/qdeclarativelistview_p.h @@ -107,12 +107,14 @@ class Q_AUTOTEST_EXPORT QDeclarativeListView : public QDeclarativeFlickable 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) - Q_PROPERTY(qreal preferredHighlightEnd READ preferredHighlightEnd WRITE setPreferredHighlightEnd NOTIFY preferredHighlightEndChanged) + 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(QDeclarativeViewSection *section READ sectionCriteria CONSTANT) @@ -158,9 +160,11 @@ public: qreal preferredHighlightBegin() const; void setPreferredHighlightBegin(qreal); + void resetPreferredHighlightBegin(); qreal preferredHighlightEnd() const; void setPreferredHighlightEnd(qreal); + void resetPreferredHighlightEnd(); qreal spacing() const; void setSpacing(qreal spacing); @@ -169,6 +173,10 @@ public: Orientation orientation() const; void setOrientation(Orientation); + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection); + Qt::LayoutDirection effectiveLayoutDirection() const; + bool isWrapEnabled() const; void setWrapEnabled(bool); @@ -220,6 +228,8 @@ Q_SIGNALS: void countChanged(); void spacingChanged(); void orientationChanged(); + Q_REVISION(1) void layoutDirectionChanged(); + Q_REVISION(1) void effectiveLayoutDirectionChanged(); void currentIndexChanged(); void currentSectionChanged(); void highlightMoveSpeedChanged(); diff --git a/src/declarative/graphicsitems/qdeclarativepathview.cpp b/src/declarative/graphicsitems/qdeclarativepathview.cpp index 4e401e9..778b8b9 100644 --- a/src/declarative/graphicsitems/qdeclarativepathview.cpp +++ b/src/declarative/graphicsitems/qdeclarativepathview.cpp @@ -796,6 +796,7 @@ void QDeclarativePathView::setPreferredHighlightBegin(qreal start) return; d->highlightRangeStart = start; d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + refill(); emit preferredHighlightBeginChanged(); } @@ -812,6 +813,7 @@ void QDeclarativePathView::setPreferredHighlightEnd(qreal end) return; d->highlightRangeEnd = end; d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + refill(); emit preferredHighlightEndChanged(); } diff --git a/src/declarative/graphicsitems/qdeclarativepincharea.cpp b/src/declarative/graphicsitems/qdeclarativepincharea.cpp index eae83f6..9bc3d8d 100644 --- a/src/declarative/graphicsitems/qdeclarativepincharea.cpp +++ b/src/declarative/graphicsitems/qdeclarativepincharea.cpp @@ -300,9 +300,8 @@ void QDeclarativePinchArea::updatePinch() d->stealMouse = false; setKeepMouseGrab(false); d->inPinch = false; - const qreal rotationAngle = d->pinchStartAngle - d->pinchLastAngle; QPointF pinchCenter = mapFromScene(d->sceneLastCenter); - QDeclarativePinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, rotationAngle); + QDeclarativePinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation); pe.setStartCenter(d->pinchStartCenter); pe.setPreviousCenter(pinchCenter); pe.setPreviousAngle(d->pinchLastAngle); @@ -349,6 +348,7 @@ void QDeclarativePinchArea::updatePinch() d->pinchStartAngle = angle; d->pinchLastScale = 1.0; d->pinchLastAngle = angle; + d->pinchRotation = 0.0; d->lastPoint1 = d->touchPoints.at(0).pos(); d->lastPoint2 = d->touchPoints.at(1).pos(); QDeclarativePinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0); @@ -380,11 +380,14 @@ void QDeclarativePinchArea::updatePinch() } } else if (d->pinchStartDist > 0) { qreal scale = dist / d->pinchStartDist; - qreal rotationAngle = d->pinchStartAngle - angle; - if (rotationAngle > 180) - rotationAngle -= 360; + qreal da = d->pinchLastAngle - angle; + if (da > 180) + da -= 360; + else if (da < -180) + da += 360; + d->pinchRotation += da; QPointF pinchCenter = mapFromScene(sceneCenter); - QDeclarativePinchEvent pe(pinchCenter, scale, angle, rotationAngle); + QDeclarativePinchEvent pe(pinchCenter, scale, angle, d->pinchRotation); pe.setStartCenter(d->pinchStartCenter); pe.setPreviousCenter(mapFromScene(d->sceneLastCenter)); pe.setPreviousAngle(d->pinchLastAngle); @@ -422,7 +425,7 @@ void QDeclarativePinchArea::updatePinch() } if (d->pinchStartRotation >= pinch()->minimumRotation() && d->pinchStartRotation <= pinch()->maximumRotation()) { - qreal r = rotationAngle + d->pinchStartRotation; + qreal r = d->pinchRotation + d->pinchStartRotation; r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation()); pinch()->target()->setRotation(r); } diff --git a/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h b/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h index 5641e35..cd5cf2a 100644 --- a/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativepincharea_p_p.h @@ -68,7 +68,9 @@ class QDeclarativePinchAreaPrivate : public QDeclarativeItemPrivate public: QDeclarativePinchAreaPrivate() : absorb(true), stealMouse(false), inPinch(false) - , pinchRejected(false), pinch(0), pinchStartDist(0) + , pinchRejected(false), pinch(0), pinchStartDist(0), pinchStartScale(1.0) + , pinchLastScale(1.0), pinchStartRotation(0.0), pinchStartAngle(0.0) + , pinchLastAngle(0.0), pinchRotation(0.0) { } @@ -97,6 +99,7 @@ public: qreal pinchStartRotation; qreal pinchStartAngle; qreal pinchLastAngle; + qreal pinchRotation; QPointF sceneStartCenter; QPointF pinchStartCenter; QPointF sceneLastCenter; diff --git a/src/declarative/graphicsitems/qdeclarativepositioners.cpp b/src/declarative/graphicsitems/qdeclarativepositioners.cpp index 27a1301..8a9bdb3 100644 --- a/src/declarative/graphicsitems/qdeclarativepositioners.cpp +++ b/src/declarative/graphicsitems/qdeclarativepositioners.cpp @@ -385,7 +385,7 @@ void QDeclarativeBasePositioner::finishApplyTransitions() Items with a width or height of 0 will not be positioned. - \sa Row, Grid, Flow, {declarative/positioners/addandremove}{Positioners example} + \sa Row, Grid, Flow, {declarative/positioners}{Positioners example} */ /*! \qmlproperty Transition Column::add @@ -425,7 +425,7 @@ void QDeclarativeBasePositioner::finishApplyTransitions() } \endqml - \sa add, {declarative/positioners/addandremove}{Positioners example} + \sa add, {declarative/positioners}{Positioners example} */ /*! \qmlproperty int Column::spacing @@ -528,7 +528,7 @@ void QDeclarativeColumn::reportConflictingAnchors() Items with a width or height of 0 will not be positioned. - \sa Column, Grid, Flow, {declarative/positioners/addandremove}{Positioners example} + \sa Column, Grid, Flow, {declarative/positioners}{Positioners example} */ /*! \qmlproperty Transition Row::add @@ -567,7 +567,7 @@ void QDeclarativeColumn::reportConflictingAnchors() } \endqml - \sa add, {declarative/positioners/addandremove}{Positioners example} + \sa add, {declarative/positioners}{Positioners example} */ /*! \qmlproperty int Row::spacing @@ -578,12 +578,14 @@ void QDeclarativeColumn::reportConflictingAnchors() \sa Grid::spacing */ QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent) -: QDeclarativeBasePositioner(Horizontal, parent), m_layoutDirection(Qt::LeftToRight) +: QDeclarativeBasePositioner(Horizontal, parent) { } /*! \qmlproperty enumeration Row::layoutDirection + \since Quick 1.1 + This property holds the layoutDirection of the row. Possible values: @@ -595,24 +597,43 @@ QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent) the right anchor remains to the right of the row. \endlist - \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/positioners/layoutdirection}{Layout directions example} + \sa Grid::layoutDirection, Flow::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} */ Qt::LayoutDirection QDeclarativeRow::layoutDirection() const { - return m_layoutDirection; + return QDeclarativeBasePositionerPrivate::getLayoutDirection(this); } void QDeclarativeRow::setLayoutDirection(Qt::LayoutDirection layoutDirection) { - if (m_layoutDirection != layoutDirection) { - m_layoutDirection = layoutDirection; + QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate* >(QDeclarativeBasePositionerPrivate::get(this)); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; 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 QDeclarativeRow::effectiveLayoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + void QDeclarativeRow::doPositioning(QSizeF *contentSize) { + QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this)); int hoffset = 0; QList<int> hoffsets; @@ -621,7 +642,7 @@ void QDeclarativeRow::doPositioning(QSizeF *contentSize) if (!child.item || !child.isVisible) continue; - if(m_layoutDirection == Qt::LeftToRight){ + if(d->isLeftToRight()){ if(child.item->x() != hoffset) positionX(hoffset, child); }else{ @@ -636,7 +657,7 @@ void QDeclarativeRow::doPositioning(QSizeF *contentSize) contentSize->setWidth(hoffset - spacing()); - if(m_layoutDirection == Qt::LeftToRight) + if(d->isLeftToRight()) return; //Right to Left layout @@ -732,7 +753,7 @@ void QDeclarativeRow::reportConflictingAnchors() Items with a width or height of 0 will not be positioned. - \sa Flow, Row, Column, {declarative/positioners/addandremove}{Positioners example} + \sa Flow, Row, Column, {declarative/positioners}{Positioners example} */ /*! \qmlproperty Transition Grid::add @@ -770,7 +791,7 @@ void QDeclarativeRow::reportConflictingAnchors() } \endqml - \sa add, {declarative/positioners/addandremove}{Positioners example} + \sa add, {declarative/positioners}{Positioners example} */ /*! \qmlproperty int Grid::spacing @@ -790,7 +811,7 @@ void QDeclarativeRow::reportConflictingAnchors() \sa rows, columns */ QDeclarativeGrid::QDeclarativeGrid(QDeclarativeItem *parent) : - QDeclarativeBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight), m_layoutDirection(Qt::LeftToRight) + QDeclarativeBasePositioner(Both, parent), m_rows(-1), m_columns(-1), m_flow(LeftToRight) { } @@ -859,38 +880,58 @@ void QDeclarativeGrid::setFlow(Flow flow) /*! \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 beginning - from the top, left anchor. The flow direction is dependent - on the \l Grid::flow property. - \o Qt.RightToLeft - Items are positioned beginning from the - top, right anchor. The flow direction is dependent on the + \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/positioners/layoutdirection}{Layout directions example} + \sa Flow::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} */ Qt::LayoutDirection QDeclarativeGrid::layoutDirection() const { - return m_layoutDirection; + return QDeclarativeBasePositionerPrivate::getLayoutDirection(this); } void QDeclarativeGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection) { - if (m_layoutDirection != layoutDirection) { - m_layoutDirection = layoutDirection; + QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this)); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; prePositioning(); emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); } } -void QDeclarativeGrid::doPositioning(QSizeF *contentSize) +/*! + \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 QDeclarativeGrid::effectiveLayoutDirection() const { + return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this); +} +void QDeclarativeGrid::doPositioning(QSizeF *contentSize) +{ + QDeclarativeBasePositionerPrivate *d = static_cast<QDeclarativeBasePositionerPrivate*>(QDeclarativeBasePositionerPrivate::get(this)); int c = m_columns; int r = m_rows; //Is allocating the extra QPODVector too much overhead? @@ -980,7 +1021,7 @@ void QDeclarativeGrid::doPositioning(QSizeF *contentSize) end = widthSum; int xoffset=0; - if(m_layoutDirection == Qt::RightToLeft) + if(!d->isLeftToRight()) xoffset=end; int yoffset=0; int curRow =0; @@ -988,7 +1029,7 @@ void QDeclarativeGrid::doPositioning(QSizeF *contentSize) for (int i = 0; i < visibleItems.count(); ++i) { const PositionedItem &child = visibleItems.at(i); int childXOffset = xoffset; - if(m_layoutDirection == Qt::RightToLeft) + if(!d->isLeftToRight()) childXOffset -= QGraphicsItemPrivate::get(child.item)->width(); if((child.item->x()!=childXOffset)||(child.item->y()!=yoffset)){ positionX(childXOffset, child); @@ -996,7 +1037,7 @@ void QDeclarativeGrid::doPositioning(QSizeF *contentSize) } if (m_flow == LeftToRight) { - if(m_layoutDirection == Qt::LeftToRight) + if(d->isLeftToRight()) xoffset+=maxColWidth[curCol]+spacing(); else xoffset-=maxColWidth[curCol]+spacing(); @@ -1004,7 +1045,7 @@ void QDeclarativeGrid::doPositioning(QSizeF *contentSize) curCol%=c; if (!curCol){ yoffset+=maxRowHeight[curRow]+spacing(); - if(m_layoutDirection == Qt::LeftToRight) + if(d->isLeftToRight()) xoffset=0; else xoffset=end; @@ -1017,7 +1058,7 @@ void QDeclarativeGrid::doPositioning(QSizeF *contentSize) curRow++; curRow%=r; if (!curRow){ - if(m_layoutDirection == Qt::LeftToRight) + if(d->isLeftToRight()) xoffset+=maxColWidth[curCol]+spacing(); else xoffset-=maxColWidth[curCol]+spacing(); @@ -1096,7 +1137,7 @@ void QDeclarativeGrid::reportConflictingAnchors() Items with a width or height of 0 will not be positioned. - \sa Column, Row, Grid, {declarative/positioners/addandremove}{Positioners example} + \sa Column, Row, Grid, {declarative/positioners}{Positioners example} */ /*! \qmlproperty Transition Flow::add @@ -1135,7 +1176,7 @@ void QDeclarativeGrid::reportConflictingAnchors() } \endqml - \sa add, {declarative/positioners/addandremove}{Positioners example} + \sa add, {declarative/positioners}{Positioners example} */ /*! \qmlproperty int Flow::spacing @@ -1152,12 +1193,10 @@ class QDeclarativeFlowPrivate : public QDeclarativeBasePositionerPrivate public: QDeclarativeFlowPrivate() - : QDeclarativeBasePositionerPrivate(), flow(QDeclarativeFlow::LeftToRight), - layoutDirection(Qt::LeftToRight) + : QDeclarativeBasePositionerPrivate(), flow(QDeclarativeFlow::LeftToRight) {} QDeclarativeFlow::Flow flow; - Qt::LayoutDirection layoutDirection; }; QDeclarativeFlow::QDeclarativeFlow(QDeclarativeItem *parent) @@ -1201,20 +1240,22 @@ void QDeclarativeFlow::setFlow(Flow flow) /*! \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 beginning - from the top, left anchor. The flow direction is dependent - on the \l Flow::flow property. - \o Qt.RightToLeft - Items are positioned beginning from the - top, right anchor. The flow direction is dependent on the + \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/positioners/layoutdirection}{Layout directions example} + \sa Grid::layoutDirection, Row::layoutDirection, {declarative/righttoleft/layoutdirection}{Layout directions example} */ Qt::LayoutDirection QDeclarativeFlow::layoutDirection() const @@ -1230,9 +1271,26 @@ void QDeclarativeFlow::setLayoutDirection(Qt::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 QDeclarativeFlow::effectiveLayoutDirection() const +{ + return QDeclarativeBasePositionerPrivate::getEffectiveLayoutDirection(this); +} + void QDeclarativeFlow::doPositioning(QSizeF *contentSize) { Q_D(QDeclarativeFlow); @@ -1262,7 +1320,7 @@ void QDeclarativeFlow::doPositioning(QSizeF *contentSize) } } - if(d->layoutDirection == Qt::LeftToRight){ + if(d->isLeftToRight()){ if(child.item->x() != hoffset) positionX(hoffset, child); }else{ @@ -1285,7 +1343,7 @@ void QDeclarativeFlow::doPositioning(QSizeF *contentSize) } } - if(d->layoutDirection == Qt::LeftToRight) + if(d->isLeftToRight()) return; int end; diff --git a/src/declarative/graphicsitems/qdeclarativepositioners_p.h b/src/declarative/graphicsitems/qdeclarativepositioners_p.h index 55d8fa1..214c04f 100644 --- a/src/declarative/graphicsitems/qdeclarativepositioners_p.h +++ b/src/declarative/graphicsitems/qdeclarativepositioners_p.h @@ -130,20 +130,22 @@ class Q_AUTOTEST_EXPORT QDeclarativeRow: public QDeclarativeBasePositioner { 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: QDeclarativeRow(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: - Qt::LayoutDirection m_layoutDirection; Q_DISABLE_COPY(QDeclarativeRow) }; @@ -154,7 +156,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeGrid : public QDeclarativeBasePositioner 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: QDeclarativeGrid(QDeclarativeItem *parent=0); @@ -171,12 +173,14 @@ public: 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); @@ -186,7 +190,6 @@ private: int m_rows; int m_columns; Flow m_flow; - Qt::LayoutDirection m_layoutDirection; Q_DISABLE_COPY(QDeclarativeGrid) }; @@ -194,8 +197,9 @@ class QDeclarativeFlowPrivate; class Q_AUTOTEST_EXPORT QDeclarativeFlow: public QDeclarativeBasePositioner { Q_OBJECT - Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged REVISION 1) 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: QDeclarativeFlow(QDeclarativeItem *parent=0); @@ -206,10 +210,11 @@ public: 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); diff --git a/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h b/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h index df105c6..e80129d 100644 --- a/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativepositioners_p_p.h @@ -75,7 +75,7 @@ public: QDeclarativeBasePositionerPrivate() : spacing(0), type(QDeclarativeBasePositioner::None) , moveTransition(0), addTransition(0), queuedPositioning(false) - , doingPositioning(false), anchorConflict(false) + , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) { } @@ -100,6 +100,9 @@ public: bool doingPositioning : 1; bool anchorConflict : 1; + Qt::LayoutDirection layoutDirection; + + void schedulePositioning() { Q_Q(QDeclarativeBasePositioner); @@ -109,6 +112,18 @@ public: } } + void mirrorChange() { + Q_Q(QDeclarativeBasePositioner); + if (type != QDeclarativeBasePositioner::Vertical) + q->prePositioning(); + } + bool isLeftToRight() const { + if (type == QDeclarativeBasePositioner::Vertical) + return true; + else + return effectiveLayoutMirror ? layoutDirection == Qt::RightToLeft : layoutDirection == Qt::LeftToRight; + } + virtual void itemSiblingOrderChanged(QDeclarativeItem* other) { Q_UNUSED(other); @@ -139,6 +154,19 @@ public: Q_Q(QDeclarativeBasePositioner); q->positionedItems.removeOne(QDeclarativeBasePositioner::PositionedItem(item)); } + + static Qt::LayoutDirection getLayoutDirection(const QDeclarativeBasePositioner *positioner) + { + return positioner->d_func()->layoutDirection; + } + + static Qt::LayoutDirection getEffectiveLayoutDirection(const QDeclarativeBasePositioner *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 diff --git a/src/declarative/graphicsitems/qdeclarativerectangle.cpp b/src/declarative/graphicsitems/qdeclarativerectangle.cpp index 7d36524..b3235ef 100644 --- a/src/declarative/graphicsitems/qdeclarativerectangle.cpp +++ b/src/declarative/graphicsitems/qdeclarativerectangle.cpp @@ -70,7 +70,7 @@ QT_BEGIN_NAMESPACE void QDeclarativePen::setColor(const QColor &c) { _color = c; - _valid = _color.alpha() ? true : false; + _valid = (_color.alpha() && _width >= 1) ? true : false; emit penChanged(); } @@ -80,7 +80,7 @@ void QDeclarativePen::setWidth(int w) return; _width = w; - _valid = (_width < 1) ? false : true; + _valid = (_color.alpha() && _width >= 1) ? true : false; emit penChanged(); } diff --git a/src/declarative/graphicsitems/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 049169e..fdc1a71 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -50,7 +50,6 @@ #include <QTextLayout> #include <QTextLine> #include <QTextDocument> -#include <QTextCursor> #include <QGraphicsSceneMouseEvent> #include <QPainter> #include <QAbstractTextDocumentLayout> @@ -97,13 +96,12 @@ DEFINE_BOOL_CONFIG_OPTION(enableImageCache, QML_ENABLE_TEXT_IMAGE_CACHE); QString QDeclarativeTextPrivate::elideChar = QString(0x2026); QDeclarativeTextPrivate::QDeclarativeTextPrivate() -: color((QRgb)0), style(QDeclarativeText::Normal), hAlign(QDeclarativeText::AlignLeft), +: color((QRgb)0), style(QDeclarativeText::Normal), hAlign(QDeclarativeText::AlignLeft), vAlign(QDeclarativeText::AlignTop), elideMode(QDeclarativeText::ElideNone), format(QDeclarativeText::AutoText), wrapMode(QDeclarativeText::NoWrap), lineHeight(1), - lineHeightMode(QDeclarativeText::ProportionalHeight), - lineCount(1), truncated(false), maximumLineCount(INT_MAX), + lineHeightMode(QDeclarativeText::ProportionalHeight), lineCount(1), truncated(false), maximumLineCount(INT_MAX), maximumLineCountValid(false), imageCacheDirty(true), updateOnComponentComplete(true), richText(false), singleline(false), - cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), naturalWidth(0), doc(0) + cacheAllTextAsImage(true), internalWidthUpdate(false), requireImplicitWidth(false), hAlignImplicit(true), rightToLeftText(false), naturalWidth(0), doc(0) { cacheAllTextAsImage = enableImageCache(); QGraphicsItemPrivate::acceptedMouseButtons = Qt::LeftButton; @@ -283,18 +281,26 @@ void QDeclarativeTextPrivate::updateSize() //setup instance of QTextLayout for all cases other than richtext if (!richText) { - size = setupTextLayout(); - if (layedOutTextSize != size) { + QRect textRect = setupTextLayout(); + if (layedOutTextRect.size() != textRect.size()) q->prepareGeometryChange(); - layedOutTextSize = size; - } + 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); + + QDeclarativeText::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QDeclarativeText::AlignLeft) + horizontalAlignment = QDeclarativeText::AlignRight; + else if (horizontalAlignment == QDeclarativeText::AlignRight) + horizontalAlignment = QDeclarativeText::AlignLeft; + } QTextOption option; - option.setAlignment((Qt::Alignment)int(hAlign | vAlign)); + option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign)); option.setWrapMode(QTextOption::WrapMode(wrapMode)); doc->setDefaultTextOption(option); if (requireImplicitWidth && q->widthValid()) { @@ -307,9 +313,9 @@ void QDeclarativeTextPrivate::updateSize() 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 != layedOutTextSize) { + if (dsize != layedOutTextRect.size()) { q->prepareGeometryChange(); - layedOutTextSize = dsize; + layedOutTextRect = QRect(QPoint(0,0), dsize); } size = QSize(int(doc->idealWidth()),dsize.height()); } @@ -344,45 +350,29 @@ void QDeclarativeTextPrivate::updateSize() Returns the size of the final text. This can be used to position the text vertically (the text is already absolutely positioned horizontally). */ -QSize QDeclarativeTextPrivate::setupTextLayout() +QRect QDeclarativeTextPrivate::setupTextLayout() { // ### text layout handling should be profiled and optimized as needed // what about QStackTextEngine engine(tmp, d->font.font()); QTextLayout textLayout(&engine); Q_Q(QDeclarativeText); layout.setCacheEnabled(true); - qreal height = 0; - qreal widthUsed = 0; qreal lineWidth = 0; - int visibleTextLength = 0; int visibleCount = 0; //set manual width - if ((wrapMode != QDeclarativeText::NoWrap || elideMode != QDeclarativeText::ElideNone) && q->widthValid()) + if (q->widthValid()) lineWidth = q->width(); QTextOption textOption = layout.textOption(); - if (hAlign == QDeclarativeText::AlignJustify) - textOption.setAlignment(Qt::Alignment(hAlign)); + textOption.setAlignment(Qt::Alignment(q->effectiveHAlign())); textOption.setWrapMode(QTextOption::WrapMode(wrapMode)); layout.setTextOption(textOption); - QDeclarativeText::HAlignment hAlignment = hAlign; - if(text.isRightToLeft()) { - if ((hAlign == QDeclarativeText::AlignLeft) || (hAlign == QDeclarativeText::AlignJustify)) { - hAlignment = QDeclarativeText::AlignRight; - } else if (hAlign == QDeclarativeText::AlignRight) { - hAlignment = QDeclarativeText::AlignLeft; - } else { - hAlignment = hAlign; - } - } - bool elideText = false; bool truncate = false; QFontMetrics fm(layout.font()); - qreal elideWidth = fm.width(elideChar); elidePos = QPointF(); if (requireImplicitWidth && q->widthValid()) { @@ -394,47 +384,47 @@ QSize QDeclarativeTextPrivate::setupTextLayout() break; } layout.endLayout(); - naturalWidth = 0; + QRectF br; for (int i = 0; i < layout.lineCount(); ++i) { QTextLine line = layout.lineAt(i); - naturalWidth = qMax(naturalWidth, line.naturalTextWidth()); + br = br.united(line.naturalTextRect()); } + naturalWidth = br.width(); } if (maximumLineCountValid) { layout.beginLayout(); if (!lineWidth) lineWidth = INT_MAX; - int y = 0; int linesLeft = maximumLineCount; + int visibleTextLength = 0; while (linesLeft > 0) { QTextLine line = layout.createLine(); if (!line.isValid()) break; visibleCount++; - line.setLineWidth(lineWidth); + if (lineWidth) + line.setLineWidth(lineWidth); visibleTextLength += line.textLength(); if (--linesLeft == 0) { if (visibleTextLength < text.length()) { truncate = true; if (elideMode==QDeclarativeText::ElideRight && q->widthValid()) { + qreal elideWidth = fm.width(elideChar); // Need to correct for alignment line.setLineWidth(lineWidth-elideWidth); - int x = line.naturalTextWidth(); - if (hAlignment == QDeclarativeText::AlignRight) { - x = q->width()-elideWidth; - } else if (hAlignment == QDeclarativeText::AlignHCenter) { - x = (q->width()+line.naturalTextWidth()-elideWidth)/2; + 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()); } - elidePos = QPointF(x, y + fm.ascent()); elideText = true; } } } - - y += line.height(); } layout.endLayout(); @@ -456,36 +446,23 @@ QSize QDeclarativeTextPrivate::setupTextLayout() layout.endLayout(); } + qreal height = 0; + QRectF br; for (int i = 0; i < layout.lineCount(); ++i) { QTextLine line = layout.lineAt(i); - widthUsed = qMax(widthUsed, line.naturalTextWidth()); + // 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 == QDeclarativeText::FixedHeight) ? lineHeight : line.height() * lineHeight; } + br.setHeight(height); - qreal layoutWidth = q->widthValid() ? q->width() : widthUsed; if (!q->widthValid()) - naturalWidth = layoutWidth; - - qreal x = 0; - for (int i = 0; i < layout.lineCount(); ++i) { - QTextLine line = layout.lineAt(i); - line.setPosition(QPointF(0, height)); - height += (lineHeightMode == QDeclarativeText::FixedHeight) ? lineHeight : line.height() * lineHeight; - - if (!cacheAllTextAsImage) { - if ((hAlignment == QDeclarativeText::AlignLeft) || (hAlignment == QDeclarativeText::AlignJustify)) { - x = 0; - } else if (hAlignment == QDeclarativeText::AlignRight) { - x = layoutWidth - line.naturalTextWidth(); - if (elideText && i == layout.lineCount()-1) - x -= elideWidth; // Correct for when eliding multilines - } else if (hAlignment == QDeclarativeText::AlignHCenter) { - x = (layoutWidth - line.naturalTextWidth()) / 2; - if (elideText && i == layout.lineCount()-1) - x -= elideWidth/2; // Correct for when eliding multilines - } - line.setPosition(QPointF(x, line.y())); - } - } + naturalWidth = br.width(); //Update the number of visible lines if (lineCount != visibleCount) { @@ -493,7 +470,7 @@ QSize QDeclarativeTextPrivate::setupTextLayout() emit q->lineCountChanged(); } - return QSize(qCeil(widthUsed), qCeil(height)); + return QRect(qRound(br.x()), qRound(br.y()), qCeil(br.width()), qCeil(br.height())); } /*! @@ -503,21 +480,7 @@ QSize QDeclarativeTextPrivate::setupTextLayout() QPixmap QDeclarativeTextPrivate::textLayoutImage(bool drawStyle) { //do layout - QSize size = layedOutTextSize; - - qreal x = 0; - for (int i = 0; i < layout.lineCount(); ++i) { - QTextLine line = layout.lineAt(i); - if ((hAlign == QDeclarativeText::AlignLeft) || (hAlign == QDeclarativeText::AlignJustify)) { - x = 0; - } else if (hAlign == QDeclarativeText::AlignRight) { - x = size.width() - line.naturalTextWidth(); - } else if (hAlign == QDeclarativeText::AlignHCenter) { - x = (size.width() - line.naturalTextWidth()) / 2; - } - line.setPosition(QPointF(x, line.y())); - } - + QSize size = layedOutTextRect.size(); //paint text QPixmap img(size); if (!size.isEmpty()) { @@ -530,7 +493,7 @@ QPixmap QDeclarativeTextPrivate::textLayoutImage(bool drawStyle) #ifdef Q_WS_MAC qt_applefontsmoothing_enabled = oldSmooth; #endif - drawTextLayout(&p, QPointF(0,0), drawStyle); + drawTextLayout(&p, QPointF(-layedOutTextRect.x(),0), drawStyle); } return img; } @@ -548,7 +511,7 @@ void QDeclarativeTextPrivate::drawTextLayout(QPainter *painter, const QPointF &p painter->setFont(font); layout.draw(painter, pos); if (!elidePos.isNull()) - painter->drawText(elidePos, elideChar); + painter->drawText(pos + elidePos, elideChar); } /*! @@ -953,14 +916,18 @@ void QDeclarativeText::setText(const QString &n) return; d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(n)); - if (d->richText && isComponentComplete()) { - d->ensureDoc(); - d->doc->setText(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); } @@ -1083,9 +1050,12 @@ void QDeclarativeText::setStyleColor(const QColor &color) /*! \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 top-left aligned. + 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 @@ -1095,6 +1065,11 @@ void QDeclarativeText::setStyleColor(const QColor &color) 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. */ QDeclarativeText::HAlignment QDeclarativeText::hAlign() const { @@ -1105,16 +1080,78 @@ QDeclarativeText::HAlignment QDeclarativeText::hAlign() const void QDeclarativeText::setHAlign(HAlignment align) { Q_D(QDeclarativeText); - if (d->hAlign == align) - return; + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) + d->updateLayout(); +} - if (isComponentComplete()) - prepareGeometryChange(); +void QDeclarativeText::resetHAlign() +{ + Q_D(QDeclarativeText); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) + d->updateLayout(); +} - d->hAlign = align; - d->updateLayout(); +QDeclarativeText::HAlignment QDeclarativeText::effectiveHAlign() const +{ + Q_D(const QDeclarativeText); + QDeclarativeText::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarativeText::AlignLeft: + effectiveAlignment = QDeclarativeText::AlignRight; + break; + case QDeclarativeText::AlignRight: + effectiveAlignment = QDeclarativeText::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarativeTextPrivate::setHAlign(QDeclarativeText::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarativeText); + if (hAlign != alignment || forceAlign) { + QDeclarativeText::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + + emit q->horizontalAlignmentChanged(hAlign); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QDeclarativeTextPrivate::determineHorizontalAlignment() +{ + Q_Q(QDeclarativeText); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QDeclarativeText::AlignRight : QDeclarativeText::AlignLeft); + } + return false; +} - emit horizontalAlignmentChanged(align); +void QDeclarativeTextPrivate::mirrorChange() +{ + Q_Q(QDeclarativeText); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarativeText::AlignRight || hAlign == QDeclarativeText::AlignLeft)) { + updateLayout(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } +} + +QTextDocument *QDeclarativeTextPrivate::textDocument() +{ + return doc; } QDeclarativeText::VAlignment QDeclarativeText::vAlign() const @@ -1168,6 +1205,7 @@ void QDeclarativeText::setWrapMode(WrapMode mode) /*! \qmlproperty int Text::lineCount + \since Quick 1.1 Returns the number of lines visible in the text item. @@ -1183,6 +1221,7 @@ int QDeclarativeText::lineCount() const /*! \qmlproperty bool Text::truncated + \since Quick 1.1 Returns true if the text has been truncated due to \l maximumLineCount or \l elide. @@ -1199,6 +1238,7 @@ bool QDeclarativeText::truncated() const /*! \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. @@ -1365,44 +1405,25 @@ QRectF QDeclarativeText::boundingRect() const { Q_D(const QDeclarativeText); - int w = width(); - int h = height(); - - int x = 0; - int y = 0; - - QSize size = d->layedOutTextSize; + QRect rect = d->layedOutTextRect; if (d->style != Normal) - size += QSize(2,2); + rect.adjust(-1, 0, 1, 2); // Could include font max left/right bearings to either side of rectangle. - switch (d->hAlign) { - case AlignLeft: - case AlignJustify: - x = 0; - break; - case AlignRight: - x = w - size.width(); - break; - case AlignHCenter: - x = (w - size.width()) / 2; - break; - } - + int h = height(); switch (d->vAlign) { case AlignTop: - y = 0; break; case AlignBottom: - y = h - size.height(); + rect.setY(h - rect.height()); break; case AlignVCenter: - y = (h - size.height()) / 2; + rect.setY((h - rect.height()) / 2); break; } - return QRectF(x,y,size.width(),size.height()); + return QRectF(rect); } /*! \internal */ @@ -1451,6 +1472,7 @@ qreal QDeclarativeText::paintedHeight() const /*! \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. @@ -1547,8 +1569,8 @@ void QDeclarativeText::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWid } else { QRectF bounds = boundingRect(); - bool needClip = clip() && (d->layedOutTextSize.width() > width() || - d->layedOutTextSize.height() > height()); + bool needClip = clip() && (d->layedOutTextRect.width() > width() || + d->layedOutTextRect.height() > height()); if (needClip) { p->save(); @@ -1580,7 +1602,11 @@ void QDeclarativeText::componentComplete() 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(); } } diff --git a/src/declarative/graphicsitems/qdeclarativetext_p.h b/src/declarative/graphicsitems/qdeclarativetext_p.h index b8835d1..a1153c2 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p.h @@ -69,7 +69,8 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeText : public QDeclarativeImplici 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 NOTIFY horizontalAlignmentChanged) + 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) @@ -133,6 +134,8 @@ public: HAlignment hAlign() const; void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; VAlignment vAlign() const; void setVAlign(VAlignment align); @@ -188,6 +191,7 @@ Q_SIGNALS: 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); diff --git a/src/declarative/graphicsitems/qdeclarativetext_p_p.h b/src/declarative/graphicsitems/qdeclarativetext_p_p.h index 36ae123..e3ab62a 100644 --- a/src/declarative/graphicsitems/qdeclarativetext_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetext_p_p.h @@ -76,6 +76,10 @@ public: void updateSize(); void updateLayout(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarativeText::HAlignment, bool forceAlign = false); + void mirrorChange(); + QTextDocument *textDocument(); QString text; QFont font; @@ -110,8 +114,10 @@ public: bool cacheAllTextAsImage:1; bool internalWidthUpdate:1; bool requireImplicitWidth:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; - QSize layedOutTextSize; + QRect layedOutTextRect; QSize paintedSize; qreal naturalWidth; virtual qreal implicitWidth() const; @@ -119,7 +125,7 @@ public: QPixmap textDocumentImage(bool drawStyle); QTextDocumentWithImageResources *doc; - QSize setupTextLayout(); + QRect setupTextLayout(); QPixmap textLayoutImage(bool drawStyle); void drawTextLayout(QPainter *p, const QPointF &pos, bool drawStyle); QDeclarativeTextLayout layout; diff --git a/src/declarative/graphicsitems/qdeclarativetextedit.cpp b/src/declarative/graphicsitems/qdeclarativetextedit.cpp index 7f383a6..2cb1c94 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextedit.cpp @@ -48,6 +48,7 @@ #include <QtCore/qmath.h> +#include <private/qtextengine_p.h> #include <QTextLayout> #include <QTextLine> #include <QTextDocument> @@ -249,6 +250,7 @@ void QDeclarativeTextEdit::setText(const QString &text) Q_D(QDeclarativeTextEdit); if (QDeclarativeTextEdit::text() == text) return; + d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text)); if (d->richText) { #ifndef QT_NO_TEXTHTMLPARSER @@ -457,9 +459,12 @@ void QDeclarativeTextEdit::setSelectedTextColor(const QColor &color) /*! \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 is top-left aligned. + 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 @@ -473,8 +478,13 @@ void QDeclarativeTextEdit::setSelectedTextColor(const QColor &color) \list \o TextEdit.AlignTop (default) \o TextEdit.AlignBottom - \c TextEdit.AlignVCenter + \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. */ QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::hAlign() const { @@ -482,15 +492,80 @@ QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::hAlign() const return d->hAlign; } -void QDeclarativeTextEdit::setHAlign(QDeclarativeTextEdit::HAlignment alignment) +void QDeclarativeTextEdit::setHAlign(HAlignment align) { Q_D(QDeclarativeTextEdit); - if (alignment == d->hAlign) - return; - d->hAlign = alignment; - d->updateDefaultTextOption(); - updateSize(); - emit horizontalAlignmentChanged(d->hAlign); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +void QDeclarativeTextEdit::resetHAlign() +{ + Q_D(QDeclarativeTextEdit); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + d->updateDefaultTextOption(); + updateSize(); + } +} + +QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::effectiveHAlign() const +{ + Q_D(const QDeclarativeTextEdit); + QDeclarativeTextEdit::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarativeTextEdit::AlignLeft: + effectiveAlignment = QDeclarativeTextEdit::AlignRight; + break; + case QDeclarativeTextEdit::AlignRight: + effectiveAlignment = QDeclarativeTextEdit::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarativeTextEditPrivate::setHAlign(QDeclarativeTextEdit::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarativeTextEdit); + if (hAlign != alignment || forceAlign) { + QDeclarativeTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QDeclarativeTextEditPrivate::determineHorizontalAlignment() +{ + Q_Q(QDeclarativeTextEdit); + if (hAlignImplicit && q->isComponentComplete()) { + bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText; + return setHAlign(alignToRight ? QDeclarativeTextEdit::AlignRight : QDeclarativeTextEdit::AlignLeft); + } + return false; +} + +void QDeclarativeTextEditPrivate::mirrorChange() +{ + Q_Q(QDeclarativeTextEdit); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarativeTextEdit::AlignRight || hAlign == QDeclarativeTextEdit::AlignLeft)) { + updateDefaultTextOption(); + q->updateSize(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } } QDeclarativeTextEdit::VAlignment QDeclarativeTextEdit::vAlign() const @@ -544,6 +619,7 @@ void QDeclarativeTextEdit::setWrapMode(WrapMode mode) /*! \qmlproperty int TextEdit::lineCount + \since Quick 1.1 Returns the total number of lines in the textEdit item. */ @@ -605,6 +681,22 @@ int QDeclarativeTextEdit::positionAt(int x, int y) const { Q_D(const QDeclarativeTextEdit); 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; } @@ -950,6 +1042,8 @@ void QDeclarativeTextEdit::componentComplete() Q_D(QDeclarativeTextEdit); QDeclarativePaintedItem::componentComplete(); if (d->dirty) { + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); updateSize(); d->dirty = false; } @@ -1134,6 +1228,7 @@ void QDeclarativeTextEditPrivate::focusChanged(bool hasFocus) /*! \qmlmethod void TextEdit::deselect() + \since Quick 1.1 Removes active text selection. */ @@ -1198,6 +1293,23 @@ void QDeclarativeTextEdit::select(int start, int end) 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 QDeclarativeTextEdit::isRightToLeft(int start, int end) +{ + Q_D(QDeclarativeTextEdit); + 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() @@ -1316,7 +1428,10 @@ Handles the given input method \a event. void QDeclarativeTextEdit::inputMethodEvent(QInputMethodEvent *event) { Q_D(QDeclarativeTextEdit); + const bool wasComposing = isInputMethodComposing(); d->control->processEvent(event, QPointF(0, -d->yoff)); + if (wasComposing != isInputMethodComposing()) + emit inputMethodComposingChanged(); } /*! @@ -1381,19 +1496,38 @@ void QDeclarativeTextEdit::updateImgCache(const QRectF &rf) /*! \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. - - \since QtQuick 1.1 */ - bool QDeclarativeTextEdit::canPaste() const { Q_D(const QDeclarativeTextEdit); 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 QDeclarativeTextEdit::isInputMethodComposing() const +{ + Q_D(const QDeclarativeTextEdit); + if (QTextLayout *layout = d->control->textCursor().block().layout()) + return layout->preeditAreaText().length() > 0; + return false; +} + void QDeclarativeTextEditPrivate::init() { Q_Q(QDeclarativeTextEdit); @@ -1429,6 +1563,7 @@ void QDeclarativeTextEditPrivate::init() #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(); @@ -1443,6 +1578,9 @@ void QDeclarativeTextEdit::q_textChanged() { Q_D(QDeclarativeTextEdit); d->text = text(); + d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft(); + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); updateSize(); updateTotalLines(); updateMicroFocus(); @@ -1607,9 +1745,18 @@ void QDeclarativeTextEdit::updateTotalLines() void QDeclarativeTextEditPrivate::updateDefaultTextOption() { + Q_Q(QDeclarativeTextEdit); QTextOption opt = document->defaultTextOption(); int oldAlignment = opt.alignment(); - opt.setAlignment((Qt::Alignment)(int)(hAlign | vAlign)); + + QDeclarativeTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign(); + if (rightToLeftText) { + if (horizontalAlignment == QDeclarativeTextEdit::AlignLeft) + horizontalAlignment = QDeclarativeTextEdit::AlignRight; + else if (horizontalAlignment == QDeclarativeTextEdit::AlignRight) + horizontalAlignment = QDeclarativeTextEdit::AlignLeft; + } + opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign)); QTextOption::WrapMode oldWrapMode = opt.wrapMode(); opt.setWrapMode(QTextOption::WrapMode(wrapMode)); diff --git a/src/declarative/graphicsitems/qdeclarativetextedit_p.h b/src/declarative/graphicsitems/qdeclarativetextedit_p.h index 7785a7a..25ca1e7 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextedit_p.h @@ -72,7 +72,8 @@ class Q_AUTOTEST_EXPORT QDeclarativeTextEdit : public QDeclarativeImplicitSizePa 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 NOTIFY horizontalAlignmentChanged) + 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) @@ -94,6 +95,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeTextEdit : public QDeclarativeImplicitSizePa 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: QDeclarativeTextEdit(QDeclarativeItem *parent=0); @@ -152,6 +154,8 @@ public: HAlignment hAlign() const; void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; VAlignment vAlign() const; void setVAlign(VAlignment align); @@ -215,6 +219,8 @@ public: QRectF boundingRect() const; + bool isInputMethodComposing() const; + Q_SIGNALS: void textChanged(const QString &); void paintedSizeChanged(); @@ -242,12 +248,15 @@ Q_SIGNALS: 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(); diff --git a/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h b/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h index 111cc02..36e1b51 100644 --- a/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextedit_p_p.h @@ -71,8 +71,8 @@ public: : color("black"), hAlign(QDeclarativeTextEdit::AlignLeft), vAlign(QDeclarativeTextEdit::AlignTop), imgDirty(true), dirty(false), richText(false), cursorVisible(false), focusOnPress(true), showInputPanelOnFocus(true), clickCausedFocus(false), persistentSelection(true), requireImplicitWidth(false), - textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), cursorComponent(0), cursor(0), - format(QDeclarativeTextEdit::AutoText), document(0), wrapMode(QDeclarativeTextEdit::NoWrap), + hAlignImplicit(true), rightToLeftText(false), textMargin(0.0), lastSelectionStart(0), lastSelectionEnd(0), + cursorComponent(0), cursor(0), format(QDeclarativeTextEdit::AutoText), document(0), wrapMode(QDeclarativeTextEdit::NoWrap), mouseSelectionMode(QDeclarativeTextEdit::SelectCharacters), selectByMouse(false), canPaste(false), yoff(0) { @@ -88,6 +88,9 @@ public: void updateDefaultTextOption(); void relayoutDocument(); void updateSelection(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarativeTextEdit::HAlignment, bool forceAlign = false); + void mirrorChange(); qreal implicitWidth() const; void focusChanged(bool); @@ -112,6 +115,8 @@ public: bool clickCausedFocus : 1; bool persistentSelection : 1; bool requireImplicitWidth:1; + bool hAlignImplicit:1; + bool rightToLeftText:1; qreal textMargin; int lastSelectionStart; int lastSelectionEnd; diff --git a/src/declarative/graphicsitems/qdeclarativetextinput.cpp b/src/declarative/graphicsitems/qdeclarativetextinput.cpp index 78f34db..af18c90 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextinput.cpp @@ -326,9 +326,12 @@ void QDeclarativeTextInput::setSelectedTextColor(const QColor &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 is left aligned. + 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 @@ -338,6 +341,11 @@ void QDeclarativeTextInput::setSelectedTextColor(const QColor &color) 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. */ QDeclarativeTextInput::HAlignment QDeclarativeTextInput::hAlign() const { @@ -348,12 +356,78 @@ QDeclarativeTextInput::HAlignment QDeclarativeTextInput::hAlign() const void QDeclarativeTextInput::setHAlign(HAlignment align) { Q_D(QDeclarativeTextInput); - if(align == d->hAlign) - return; - d->hAlign = align; - updateRect(); - d->updateHorizontalScroll(); - emit horizontalAlignmentChanged(d->hAlign); + bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror; + d->hAlignImplicit = false; + if (d->setHAlign(align, forceAlign) && isComponentComplete()) { + updateRect(); + d->updateHorizontalScroll(); + } +} + +void QDeclarativeTextInput::resetHAlign() +{ + Q_D(QDeclarativeTextInput); + d->hAlignImplicit = true; + if (d->determineHorizontalAlignment() && isComponentComplete()) { + updateRect(); + d->updateHorizontalScroll(); + } +} + +QDeclarativeTextInput::HAlignment QDeclarativeTextInput::effectiveHAlign() const +{ + Q_D(const QDeclarativeTextInput); + QDeclarativeTextInput::HAlignment effectiveAlignment = d->hAlign; + if (!d->hAlignImplicit && d->effectiveLayoutMirror) { + switch (d->hAlign) { + case QDeclarativeTextInput::AlignLeft: + effectiveAlignment = QDeclarativeTextInput::AlignRight; + break; + case QDeclarativeTextInput::AlignRight: + effectiveAlignment = QDeclarativeTextInput::AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + +bool QDeclarativeTextInputPrivate::setHAlign(QDeclarativeTextInput::HAlignment alignment, bool forceAlign) +{ + Q_Q(QDeclarativeTextInput); + if ((hAlign != alignment || forceAlign) && alignment <= QDeclarativeTextInput::AlignHCenter) { // justify not supported + QDeclarativeTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign(); + hAlign = alignment; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + return true; + } + return false; +} + +bool QDeclarativeTextInputPrivate::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 ? QDeclarativeTextInput::AlignRight : QDeclarativeTextInput::AlignLeft); + } + return false; +} + +void QDeclarativeTextInputPrivate::mirrorChange() +{ + Q_Q(QDeclarativeTextInput); + if (q->isComponentComplete()) { + if (!hAlignImplicit && (hAlign == QDeclarativeTextInput::AlignRight || hAlign == QDeclarativeTextInput::AlignLeft)) { + q->updateRect(); + updateHorizontalScroll(); + emit q->effectiveHorizontalAlignmentChanged(); + } + } } /*! @@ -482,6 +556,7 @@ QRect QDeclarativeTextInput::cursorRectangle() const Q_D(const QDeclarativeTextInput); QRect r = d->control->cursorRect(); r.setHeight(r.height()-1); // Make consistent with TextEdit (QLineControl inexplicably adds 1) + r.moveLeft(r.x() - d->hscroll); return r; } @@ -921,14 +996,22 @@ void QDeclarativeTextInput::moveCursor() QRectF QDeclarativeTextInput::positionToRectangle(int pos) const { Q_D(const QDeclarativeTextInput); + 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 QDeclarativeTextInput::positionAt(int x) const +{ + return positionAt(x, CursorBetweenCharacters); +} + /*! - \qmlmethod int TextInput::positionAt(int x) + \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 @@ -937,11 +1020,26 @@ QRectF QDeclarativeTextInput::positionToRectangle(int pos) const 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 QDeclarativeTextInput::positionAt(int x) const +int QDeclarativeTextInput::positionAt(int x, CursorPosition position) const { Q_D(const QDeclarativeTextInput); - return d->control->xToPos(x + d->hscroll); + 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 QDeclarativeTextInputPrivate::focusChanged(bool hasFocus) @@ -962,16 +1060,21 @@ void QDeclarativeTextInput::keyPressEvent(QKeyEvent* ev) keyPressPreHandler(ev); if (ev->isAccepted()) return; - if (((ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier) // Don't allow MacOSX up/down support, and we don't allow a completer. - || (((d->control->cursor() == 0 && ev->key() == Qt::Key_Left) - || (d->control->cursor() == d->control->text().length() - && ev->key() == Qt::Key_Right)) - && (d->lastSelectionStart == d->lastSelectionEnd))) - { - //ignore when moving off the end - //unless there is a selection, because then moving will do something (deselect) + + // 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{ + } else { d->control->processKeyEvent(ev); } if (!ev->isAccepted()) @@ -982,18 +1085,22 @@ void QDeclarativeTextInput::inputMethodEvent(QInputMethodEvent *ev) { Q_D(QDeclarativeTextInput); ev->ignore(); + const bool wasComposing = d->control->preeditAreaText().length() > 0; inputMethodPreHandler(ev); - if (ev->isAccepted()) - return; - if (d->control->isReadOnly()) { - ev->ignore(); - } else { - d->control->processInputMethodEvent(ev); - updateSize(); - d->updateHorizontalScroll(); + if (!ev->isAccepted()) { + if (d->control->isReadOnly()) { + ev->ignore(); + } else { + d->control->processInputMethodEvent(ev); + updateSize(); + d->updateHorizontalScroll(); + } } if (!ev->isAccepted()) QDeclarativePaintedItem::inputMethodEvent(ev); + + if (wasComposing != (d->control->preeditAreaText().length() > 0)) + emit inputMethodComposingChanged(); } /*! @@ -1047,7 +1154,7 @@ void QDeclarativeTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { Q_D(QDeclarativeTextInput); if (d->sendMouseEventToInputContext(event, QEvent::MouseMove)) - event->setAccepted(true); + return; if (d->selectByMouse) { if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance()) setKeepMouseGrab(true); @@ -1176,11 +1283,12 @@ void QDeclarativeTextInputPrivate::updateHorizontalScroll() int cix = qRound(control->cursorToX(control->cursor() + preeditLength)); QRect br(q->boundingRect().toRect()); int widthUsed = calculateTextWidth(); - Qt::Alignment va = QStyle::visualAlignment(control->layoutDirection(), QFlag(Qt::Alignment(hAlign))); + + QDeclarativeTextInput::HAlignment effectiveHAlign = q->effectiveHAlign(); if (autoScroll) { if (widthUsed <= br.width()) { // text fits in br; use hscroll for alignment - switch (va & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { + switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { case Qt::AlignRight: hscroll = widthUsed - br.width() - 1; break; @@ -1212,11 +1320,11 @@ void QDeclarativeTextInputPrivate::updateHorizontalScroll() hscroll = cix; } } else { - switch (va & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) { - case Qt::AlignRight: + switch (effectiveHAlign) { + case QDeclarativeTextInput::AlignRight: hscroll = q->width() - widthUsed; break; - case Qt::AlignHCenter: + case QDeclarativeTextInput::AlignHCenter: hscroll = (q->width() - widthUsed) / 2; break; default: @@ -1260,13 +1368,16 @@ QVariant QDeclarativeTextInput::inputMethodQuery(Qt::InputMethodQuery property) Q_D(const QDeclarativeTextInput); switch(property) { case Qt::ImMicroFocus: - return d->control->cursorRect(); + return cursorRectangle(); case Qt::ImFont: return font(); case Qt::ImCursorPosition: return QVariant(d->control->cursor()); case Qt::ImSurroundingText: - return QVariant(text()); + if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing()) + return QVariant(displayText()); + else + return QVariant(text()); case Qt::ImCurrentSelection: return QVariant(selectedText()); case Qt::ImMaximumTextLength: @@ -1285,6 +1396,7 @@ QVariant QDeclarativeTextInput::inputMethodQuery(Qt::InputMethodQuery property) /*! \qmlmethod void TextInput::deselect() + \since Quick 1.1 Removes active text selection. */ @@ -1305,6 +1417,23 @@ void QDeclarativeTextInput::selectAll() 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 QDeclarativeTextInput::isRightToLeft(int start, int end) +{ + Q_D(QDeclarativeTextInput); + 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() @@ -1466,6 +1595,13 @@ void QDeclarativeTextInput::setMouseSelectionMode(SelectionMode 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 QDeclarativeTextInput::canPaste() const { Q_D(const QDeclarativeTextInput); @@ -1534,38 +1670,41 @@ void QDeclarativeTextInput::moveCursorSelection(int pos, SelectionMode mode) anchor = d->control->selectionStart(); if (anchor < pos || (anchor == pos && cursor < pos)) { - QTextBoundaryFinder finder(QTextBoundaryFinder::Word, d->control->text()); + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); finder.setPosition(anchor); const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); - if (!(reasons & QTextBoundaryFinder::StartWord) - || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor)) { + if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord) + || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) { finder.toPreviousBoundary(); } - anchor = finder.position(); + anchor = finder.position() != -1 ? finder.position() : 0; finder.setPosition(pos); - if (!finder.isAtBoundary()) + if (pos > 0 && !finder.boundaryReasons()) finder.toNextBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : text.length(); - d->control->setSelection(anchor, finder.position() - anchor); + d->control->setSelection(anchor, cursor - anchor); } else if (anchor > pos || (anchor == pos && cursor > pos)) { - QTextBoundaryFinder finder(QTextBoundaryFinder::Word, d->control->text()); + const QString text = d->control->text(); + QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text); finder.setPosition(anchor); const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons(); - if (!(reasons & QTextBoundaryFinder::EndWord) - || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor)) { + if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord) + || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) { finder.toNextBoundary(); } - - anchor = finder.position(); + anchor = finder.position() != -1 ? finder.position() : text.length(); finder.setPosition(pos); - if (!finder.isAtBoundary()) + if (pos < text.length() && !finder.boundaryReasons()) finder.toPreviousBoundary(); + const int cursor = finder.position() != -1 ? finder.position() : 0; - d->control->setSelection(anchor, finder.position() - anchor); + d->control->setSelection(anchor, cursor - anchor); } } } @@ -1684,6 +1823,25 @@ void QDeclarativeTextInput::focusInEvent(QFocusEvent *event) QDeclarativePaintedItem::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 QDeclarativeTextInput::isInputMethodComposing() const +{ + Q_D(const QDeclarativeTextInput); + return d->control->preeditAreaText().length() > 0; +} + void QDeclarativeTextInputPrivate::init() { Q_Q(QDeclarativeTextInput); @@ -1708,9 +1866,12 @@ void QDeclarativeTextInputPrivate::init() q, SLOT(q_canPasteChanged())); q->connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged())); + canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0; #endif // QT_NO_CLIPBOARD q->connect(control, SIGNAL(updateMicroFocus()), q, SLOT(updateMicroFocus())); + q->connect(control, SIGNAL(displayTextChanged(QString)), + q, SLOT(updateRect())); q->updateSize(); oldValidity = control->hasAcceptableInput(); lastSelectionStart = 0; @@ -1718,6 +1879,7 @@ void QDeclarativeTextInputPrivate::init() QPalette p = control->palette(); selectedTextColor = p.color(QPalette::HighlightedText); selectionColor = p.color(QPalette::Highlight); + determineHorizontalAlignment(); } void QDeclarativeTextInput::cursorPosChanged() @@ -1765,6 +1927,7 @@ void QDeclarativeTextInput::q_textChanged() { Q_D(QDeclarativeTextInput); updateSize(); + d->determineHorizontalAlignment(); d->updateHorizontalScroll(); updateMicroFocus(); emit textChanged(); diff --git a/src/declarative/graphicsitems/qdeclarativetextinput_p.h b/src/declarative/graphicsitems/qdeclarativetextinput_p.h index e1e66a9..8c873b3 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextinput_p.h @@ -70,8 +70,8 @@ class Q_AUTOTEST_EXPORT QDeclarativeTextInput : public QDeclarativeImplicitSizeP 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 NOTIFY horizontalAlignmentChanged) - + 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) @@ -97,6 +97,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeTextInput : public QDeclarativeImplicitSizeP 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: QDeclarativeTextInput(QDeclarativeItem* parent=0); @@ -120,8 +121,14 @@ public: 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); @@ -146,6 +153,8 @@ public: HAlignment hAlign() const; void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; bool isReadOnly() const; void setReadOnly(bool); @@ -204,6 +213,8 @@ public: QRectF boundingRect() const; bool canPaste() const; + bool isInputMethodComposing() const; + Q_SIGNALS: void textChanged(); void cursorPositionChanged(); @@ -231,6 +242,8 @@ Q_SIGNALS: 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, @@ -251,6 +264,7 @@ public Q_SLOTS: 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(); diff --git a/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h b/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h index ab2838b..fd4da2e 100644 --- a/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h +++ b/src/declarative/graphicsitems/qdeclarativetextinput_p_p.h @@ -74,9 +74,9 @@ public: color((QRgb)0), style(QDeclarativeText::Normal), styleColor((QRgb)0), hAlign(QDeclarativeTextInput::AlignLeft), mouseSelectionMode(QDeclarativeTextInput::SelectCharacters), - hscroll(0), oldScroll(0), focused(false), focusOnPress(true), + hscroll(0), oldScroll(0), oldValidity(false), focused(false), focusOnPress(true), showInputPanelOnFocus(true), clickCausedFocus(false), cursorVisible(false), - autoScroll(true), selectByMouse(false), canPaste(false) + autoScroll(true), selectByMouse(false), canPaste(false), hAlignImplicit(true) { #ifdef Q_OS_SYMBIAN if (QSysInfo::symbianVersion() == QSysInfo::SV_SF_1 || QSysInfo::symbianVersion() == QSysInfo::SV_SF_3) { @@ -103,6 +103,9 @@ public: void startCreatingCursor(); void focusChanged(bool hasFocus); void updateHorizontalScroll(); + bool determineHorizontalAlignment(); + bool setHAlign(QDeclarativeTextInput::HAlignment, bool forceAlign = false); + void mirrorChange(); int calculateTextWidth(); bool sendMouseEventToInputContext(QGraphicsSceneMouseEvent *event, QEvent::Type eventType); @@ -125,17 +128,18 @@ public: int lastSelectionEnd; int oldHeight; int oldWidth; - bool oldValidity; int hscroll; int oldScroll; - bool focused; - bool focusOnPress; - bool showInputPanelOnFocus; - bool clickCausedFocus; - bool cursorVisible; - bool autoScroll; - bool selectByMouse; - bool canPaste; + 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; static inline QDeclarativeTextInputPrivate *get(QDeclarativeTextInput *t) { return t->d_func(); diff --git a/src/declarative/graphicsitems/qdeclarativetextlayout.cpp b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp index b24dd2c..31819f5 100644 --- a/src/declarative/graphicsitems/qdeclarativetextlayout.cpp +++ b/src/declarative/graphicsitems/qdeclarativetextlayout.cpp @@ -99,7 +99,6 @@ class DrawTextItemRecorder: public QPaintEngine needFreshCurrentItem = false; last.numChars += ti.num_chars; - last.numGlyphs += ti.glyphs.numGlyphs; } } @@ -111,7 +110,7 @@ class DrawTextItemRecorder: public QPaintEngine currentItem.font = ti.font(); currentItem.charOffset = charOffset; currentItem.numChars = ti.num_chars; - currentItem.numGlyphs = ti.glyphs.numGlyphs; + currentItem.numGlyphs = 0; currentItem.glyphOffset = glyphOffset; currentItem.positionOffset = positionOffset; currentItem.useBackendOptimizations = m_useBackendOptimizations; @@ -121,6 +120,8 @@ class DrawTextItemRecorder: public QPaintEngine m_inertText->items.append(currentItem); } + QStaticTextItem ¤tItem = m_inertText->items.last(); + QTransform matrix = m_untransformedCoordinates ? QTransform() : state->transform(); matrix.translate(position.x(), position.y()); @@ -129,18 +130,18 @@ class DrawTextItemRecorder: public QPaintEngine ti.fontEngine->getGlyphPositions(ti.glyphs, matrix, ti.flags, glyphs, positions); int size = glyphs.size(); - Q_ASSERT(size == ti.glyphs.numGlyphs); 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) * ti.glyphs.numGlyphs); + qMemCopy(glyphsDestination, glyphs.constData(), sizeof(glyph_t) * size); QFixedPoint *positionsDestination = m_inertText->positions.data() + positionOffset; - qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * ti.glyphs.numGlyphs); + qMemCopy(positionsDestination, positions.constData(), sizeof(QFixedPoint) * size); QChar *charsDestination = m_inertText->chars.data() + charOffset; qMemCopy(charsDestination, ti.chars, sizeof(QChar) * ti.num_chars); |