diff options
author | Thiago Macieira <thiago.macieira@nokia.com> | 2011-03-11 10:21:11 (GMT) |
---|---|---|
committer | Thiago Macieira <thiago.macieira@nokia.com> | 2011-03-11 10:21:11 (GMT) |
commit | 649e65519bef38948a818f282e3022d034dc80a5 (patch) | |
tree | 9f4b1de9322e304efd003a1f8c03047327732557 /src | |
parent | bb7b62f3cb7aca178a9e5e65263d0a4a8d40829c (diff) | |
parent | fcdf5a5471b7cf1d2bc72855ed1f627c8d6f4fc4 (diff) | |
download | Qt-649e65519bef38948a818f282e3022d034dc80a5.zip Qt-649e65519bef38948a818f282e3022d034dc80a5.tar.gz Qt-649e65519bef38948a818f282e3022d034dc80a5.tar.bz2 |
Merge remote-tracking branch 'origin/4.7' into qt-master-from-4.7
Conflicts:
mkspecs/common/symbian/symbian.conf
src/s60installs/bwins/QtGuiu.def
src/s60installs/eabi/QtGuiu.def
Diffstat (limited to 'src')
63 files changed, 2696 insertions, 585 deletions
diff --git a/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri b/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri index b98617f..07754a7 100644 --- a/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri +++ b/src/3rdparty/webkit/WebKit/qt/qt_webkit_version.pri @@ -1,5 +1,5 @@ -QT_WEBKIT_VERSION = 4.7.2 +QT_WEBKIT_VERSION = 4.7.3 QT_WEBKIT_MAJOR_VERSION = 4 QT_WEBKIT_MINOR_VERSION = 7 -QT_WEBKIT_PATCH_VERSION = 2 +QT_WEBKIT_PATCH_VERSION = 3 QT_CONFIG += webkit diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 1879537..b75d66e 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -2506,7 +2506,10 @@ QT3_SUPPORT Q_CORE_EXPORT const char *qInstallPathSysconf(); #ifdef SYMBIAN_GRAPHICS_TRANSITION_EFFECTS_SIGNALING_AVAILABLE # define Q_SYMBIAN_TRANSITION_EFFECTS #endif +#endif +#ifdef SYMBIAN_WSERV_AND_CONE_MULTIPLE_SCREENS +#define Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS #endif //Symbian does not support data imports from a DLL diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 5e0d2a2..811a193 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -54,6 +54,11 @@ #include <private/qeventdispatcher_unix_p.h> #endif +#ifdef Q_OS_SYMBIAN +#include <hal.h> +#include <hal_data.h> +#endif + #include "qthreadstorage.h" #include "qthread_p.h" @@ -63,6 +68,12 @@ #include <sched.h> #include <errno.h> +// You only find these enumerations on Symbian^3 onwards, so we need to provide our own +// to remain compatible with older releases. They won't be called by pre-Sym^3 SDKs. + +// HALData::ENumCpus +#define QT_HALData_ENumCpus 119 + #ifdef Q_OS_BSD4 #include <sys/sysctl.h> #endif @@ -451,8 +462,20 @@ int QThread::idealThreadCount() // as of aug 2008 Integrity only supports one single core CPU cores = 1; #elif defined(Q_OS_SYMBIAN) - // ### TODO - Get the number of cores from HAL? when multicore architectures (SMP) are supported - cores = 1; + if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_3) { + TInt inumcpus; + TInt err; + err = HAL::Get((HALData::TAttribute)QT_HALData_ENumCpus, inumcpus); + if (err != KErrNone) { + cores = 1; + } else if ( inumcpus <= 0 ) { + cores = 1; + } else { + cores = inumcpus; + } + } else { + cores = 1; + } #elif defined(Q_OS_VXWORKS) // VxWorks # if defined(QT_VXWORKS_HAS_CPUSET) 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/qdeclarativegridview.cpp b/src/declarative/graphicsitems/qdeclarativegridview.cpp index 1d2ad1c..5c2f781 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); } } } @@ -914,21 +1024,46 @@ void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m 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); return; @@ -936,12 +1071,15 @@ void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m 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); @@ -957,12 +1095,12 @@ void QDeclarativeGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m 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 (fixupMode != Immediate) { @@ -992,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()); } @@ -1007,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()); } @@ -1016,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; @@ -1035,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 @@ -1227,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); @@ -1552,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; @@ -1559,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); @@ -1568,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; @@ -1575,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); @@ -1591,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 @@ -1623,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(); } } @@ -1893,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 @@ -1959,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; } @@ -1974,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; @@ -2097,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); + } } } } @@ -2125,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); + } } } } @@ -2150,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) @@ -2200,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(); @@ -2341,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()) { @@ -2434,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()) { @@ -2452,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. @@ -2512,6 +2838,8 @@ void QDeclarativeGridView::itemsInserted(int modelIndex, int count) d->updateCurrent(0); } emit currentIndexChanged(); + } else if (d->itemCount == 0 && d->currentIndex == -1) { + setCurrentIndex(0); } // everything is in order now - emit add() signal @@ -2760,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/qdeclarativeitem.cpp b/src/declarative/graphicsitems/qdeclarativeitem.cpp index ffef61b..1b1c476 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,152 @@ void QDeclarativeKeyNavigationAttached::setFocusNavigation(QDeclarativeItem *cur } /*! + \qmlclass LayoutMirroring QDeclarativeLayoutMirroringAttached + \since QtQuick 1.1 + \ingroup qml-utility-elements + \brief The LayoutMirroring is used for mirroring the Qt Quick application layouts. + + LayoutMirroring \l enabled property can be used to horizontally mirror \l {anchor-layout}{Item anchors}, + \l{Using QML Positioner and Repeater Items}{Positioner} elements and QML views like \l {GridView}{GridView} + and horizontal \l {ListView}{ListView}. Mirroring is a visual change, left anchors will become + right anchors and left-to-right positioner will instead position child items from right to left. + By default setting the \l enabled property to true only affects the item in question. You can set property + LayoutDirection \l childrenInherit to true if you want the item children also inherit the mirror setting. + If no attached property has been defined, mirroring is disabled. + + The following example shows mirroring in action. When \l enabled is set to true, left anchor + becomes right, and \l {Row}{Row} starts positioning items in a reverse order: + + \snippet doc/src/snippets/declarative/layoutmirroring.qml 0 + + Layout mirroring is useful when you need to support both left-to-right and right-to-left + layout versions of your application that target different language areas. Inheritance saves + you from having to mirror the layouts manually for each layout item in your application. Keep + in mind however that the mirroring does not affect the positioning done by modifying Item's x + co-ordinate directly, so even with the mirroring enabled you will often need to do some layout + fixes to support the other reading direction. Also, there are cases where you need to disable + mirroring of individual child items, either because mirroring is not the wanted behavior or + because the item already implements mirroring in some custom way. +*/ + +/*! + \qmlproperty bool LayoutMirroring::enabled + + Setting this property to true mirrors item's layout horizontally, whether the layout is done + using \l {anchor-layout}{anchors}, \l{Using QML Positioner and Repeater Items}{Positioners} + or as a QML view \l {GridView}{GridView} or \l {ListView}{ListView}. +*/ + +/*! + \qmlproperty bool LayoutMirroring::childrenInherit + + This property can be set to true if you want the item children + to inherit the item's mirror setting. +*/ + +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 +1555,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 \l {LayoutMirroring}{LayoutMirroring} attached property. + */ /*! @@ -2812,6 +2975,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 e369ba6..f9f1a48 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,49 @@ public: return 0; } + void regenerate() { + Q_Q(QDeclarativeListView); + if (q->isComponentComplete()) { + 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 +321,7 @@ public: return pos; } - qreal endPosition() const { + qreal lastPosition() const { qreal pos = 0; if (!visibleItems.isEmpty()) { int invisibleCount = visibleItems.count() - visibleIndex; @@ -291,6 +338,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 +367,7 @@ public: else idx = visibleItems.at(idx)->index; int count = modelIndex - idx - 1; + return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1; } } @@ -368,7 +424,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 +540,7 @@ public: QHash<QDeclarativeItem*,int> unrequestedItems; FxListItem *currentItem; QDeclarativeListView::Orientation orient; + Qt::LayoutDirection layoutDirection; qreal visiblePos; int visibleIndex; qreal averageSize; @@ -492,6 +549,8 @@ public: int itemCount; qreal highlightRangeStart; qreal highlightRangeEnd; + bool highlightRangeStartValid; + bool highlightRangeEndValid; QDeclarativeComponent *highlightComponent; FxListItem *highlight; FxListItem *trackedItem; @@ -669,7 +728,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 +873,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 +969,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) @@ -1125,7 +1189,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 { @@ -1165,7 +1229,7 @@ void QDeclarativeListViewPrivate::updateHeader() } if (header) { if (visibleItems.count()) { - qreal startPos = startPosition(); + qreal startPos = originPosition(); if (visibleIndex == 0) { header->setPosition(startPos - header->size()); } else { @@ -1200,14 +1264,30 @@ void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m correctFlick = false; fixupMode = moveReason == Mouse ? fixupMode : Immediate; - if (currentItem && haveHighlightRange && highlightRange == QDeclarativeListView::StrictlyEnforceRange) { + 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 + && 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()) { @@ -1219,17 +1299,26 @@ void QDeclarativeListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal m } } 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); return; @@ -1265,12 +1354,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()); } @@ -1280,8 +1370,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()); } @@ -1289,7 +1379,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. @@ -1314,7 +1407,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); @@ -1347,6 +1442,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)); @@ -1366,8 +1462,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) @@ -1385,6 +1484,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)); @@ -1883,6 +1983,7 @@ qreal QDeclarativeListView::preferredHighlightBegin() const void QDeclarativeListView::setPreferredHighlightBegin(qreal start) { Q_D(QDeclarativeListView); + d->highlightRangeStartValid = true; if (d->highlightRangeStart == start) return; d->highlightRangeStart = start; @@ -1890,6 +1991,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); @@ -1899,6 +2010,7 @@ qreal QDeclarativeListView::preferredHighlightEnd() const void QDeclarativeListView::setPreferredHighlightEnd(qreal end) { Q_D(QDeclarativeListView); + d->highlightRangeEndValid = true; if (d->highlightRangeEnd == end) return; d->highlightRangeEnd = end; @@ -1906,6 +2018,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); @@ -1984,15 +2106,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. @@ -2363,11 +2533,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)); @@ -2405,13 +2587,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; @@ -2474,11 +2658,29 @@ 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; + if (d->isRightToLeft()) { + if (d->model && d->model->count()) + endPositionFirstItem = d->positionAt(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->minExtent += d->footer->size(); + } else { + endPositionFirstItem = d->endPositionAt(0); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->header) + 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; } @@ -2492,23 +2694,43 @@ qreal QDeclarativeListView::maxXExtent() const if (d->orient == QDeclarativeListView::Vertical) return width(); if (d->maxExtentDirty) { + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition; + 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; } 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(); + if (d->isRightToLeft()) { + if (d->header) + 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; } @@ -2520,7 +2742,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(); @@ -2530,7 +2753,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(); @@ -2607,9 +2831,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 @@ -2652,7 +2881,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(); @@ -2810,7 +3044,10 @@ void QDeclarativeListView::updateSections() 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() @@ -2825,26 +3062,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; } } } @@ -2881,7 +3129,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) @@ -2890,7 +3140,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 { @@ -2935,7 +3185,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(); @@ -2965,7 +3215,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(); @@ -3241,11 +3491,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()); @@ -3261,10 +3508,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/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 c0be2a2..84dcec6 100644 --- a/src/declarative/graphicsitems/qdeclarativepositioners.cpp +++ b/src/declarative/graphicsitems/qdeclarativepositioners.cpp @@ -578,7 +578,7 @@ void QDeclarativeColumn::reportConflictingAnchors() \sa Grid::spacing */ QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent) -: QDeclarativeBasePositioner(Horizontal, parent), m_layoutDirection(Qt::LeftToRight) +: QDeclarativeBasePositioner(Horizontal, parent) { } @@ -601,20 +601,39 @@ QDeclarativeRow::QDeclarativeRow(QDeclarativeItem *parent) */ 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; @@ -623,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{ @@ -638,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 @@ -792,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) { } @@ -868,11 +887,11 @@ void QDeclarativeGrid::setFlow(Flow flow) 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 @@ -880,21 +899,39 @@ void QDeclarativeGrid::setFlow(Flow flow) */ 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? @@ -984,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; @@ -992,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); @@ -1000,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(); @@ -1008,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; @@ -1021,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(); @@ -1156,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) @@ -1212,11 +1247,11 @@ void QDeclarativeFlow::setFlow(Flow flow) 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 @@ -1236,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); @@ -1268,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{ @@ -1291,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/qdeclarativetext.cpp b/src/declarative/graphicsitems/qdeclarativetext.cpp index 12e1c0f..994c01e 100644 --- a/src/declarative/graphicsitems/qdeclarativetext.cpp +++ b/src/declarative/graphicsitems/qdeclarativetext.cpp @@ -49,7 +49,6 @@ #include <QTextLayout> #include <QTextLine> #include <QTextDocument> -#include <QTextCursor> #include <QGraphicsSceneMouseEvent> #include <QPainter> #include <QAbstractTextDocumentLayout> @@ -88,13 +87,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; @@ -260,18 +258,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()) { @@ -284,9 +290,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()); } @@ -321,45 +327,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()) { @@ -371,47 +361,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(); @@ -433,36 +423,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) { @@ -470,7 +447,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())); } /*! @@ -480,21 +457,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()) { @@ -507,7 +470,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; } @@ -525,7 +488,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); } /*! @@ -930,14 +893,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); } @@ -1060,9 +1027,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 @@ -1072,6 +1042,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 { @@ -1082,16 +1057,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 @@ -1345,44 +1382,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 */ @@ -1528,8 +1546,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(); @@ -1561,7 +1579,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 d3c5b82..babc020 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 @@ -967,6 +1042,8 @@ void QDeclarativeTextEdit::componentComplete() Q_D(QDeclarativeTextEdit); QDeclarativePaintedItem::componentComplete(); if (d->dirty) { + d->determineHorizontalAlignment(); + d->updateDefaultTextOption(); updateSize(); d->dirty = false; } @@ -1216,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() @@ -1484,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(); @@ -1648,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 612f9a9..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) @@ -153,6 +154,8 @@ public: HAlignment hAlign() const; void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; VAlignment vAlign() const; void setVAlign(VAlignment align); @@ -246,12 +249,14 @@ Q_SIGNALS: 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 29b1f6b..6c26fd3 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; + return true; + emit q->horizontalAlignmentChanged(alignment); + if (oldEffectiveHAlign != q->effectiveHAlign()) + emit q->effectiveHorizontalAlignmentChanged(); + } + 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(); + } + } } /*! @@ -986,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()) @@ -1204,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; @@ -1240,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: @@ -1334,6 +1414,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() @@ -1777,6 +1874,7 @@ void QDeclarativeTextInputPrivate::init() QPalette p = control->palette(); selectedTextColor = p.color(QPalette::HighlightedText); selectionColor = p.color(QPalette::Highlight); + determineHorizontalAlignment(); } void QDeclarativeTextInput::cursorPosChanged() @@ -1824,6 +1922,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 822d5e2..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) @@ -153,6 +153,8 @@ public: HAlignment hAlign() const; void setHAlign(HAlignment align); + void resetHAlign(); + HAlignment effectiveHAlign() const; bool isReadOnly() const; void setReadOnly(bool); @@ -241,6 +243,7 @@ Q_SIGNALS: 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, @@ -261,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/qml/qdeclarativescriptparser.cpp b/src/declarative/qml/qdeclarativescriptparser.cpp index e8f6b0d..ad03e10 100644 --- a/src/declarative/qml/qdeclarativescriptparser.cpp +++ b/src/declarative/qml/qdeclarativescriptparser.cpp @@ -776,7 +776,10 @@ bool ProcessAST::visit(AST::UiSourceElement *node) f = f->finish(); } - QString body = textAt(funDecl->lbraceToken, funDecl->rbraceToken); + AST::SourceLocation loc = funDecl->rparenToken; + loc.offset = loc.end(); + loc.startColumn += 1; + QString body = textAt(loc, funDecl->rbraceToken); slot.name = funDecl->name->asString().toUtf8(); slot.body = body; obj->dynamicSlots << slot; diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp index ecc8941..2bf744d 100644 --- a/src/gui/graphicsview/qgraphicsitem.cpp +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -5577,8 +5577,10 @@ void QGraphicsItemPrivate::setSubFocus(QGraphicsItem *rootItem, QGraphicsItem *s parent->d_ptr->subFocusItemChange(); } while (!parent->isPanel() && (parent = parent->d_ptr->parent) && (visible || !parent->d_ptr->visible)); - if (scene && !scene->isActive()) + if (scene && !scene->isActive()) { + scene->d_func()->passiveFocusItem = subFocusItem; scene->d_func()->lastFocusItem = subFocusItem; + } } /*! diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 77ccc8e..6ff1d38 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -306,6 +306,7 @@ QGraphicsScenePrivate::QGraphicsScenePrivate() rectAdjust(2), focusItem(0), lastFocusItem(0), + passiveFocusItem(0), tabFocusFirst(0), activePanel(0), lastActivePanel(0), @@ -630,6 +631,8 @@ void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item) focusItem = 0; if (item == lastFocusItem) lastFocusItem = 0; + if (item == passiveFocusItem) + passiveFocusItem = 0; if (item == activePanel) { // ### deactivate... activePanel = 0; @@ -2980,7 +2983,7 @@ void QGraphicsScene::removeItem(QGraphicsItem *item) QGraphicsItem *QGraphicsScene::focusItem() const { Q_D(const QGraphicsScene); - return isActive() ? d->focusItem : d->lastFocusItem; + return isActive() ? d->focusItem : d->passiveFocusItem; } /*! @@ -3054,6 +3057,7 @@ void QGraphicsScene::clearFocus() Q_D(QGraphicsScene); if (d->hasFocus) { d->hasFocus = false; + d->passiveFocusItem = d->focusItem; setFocusItem(0, Qt::OtherFocusReason); } } @@ -3757,9 +3761,9 @@ void QGraphicsScene::focusInEvent(QFocusEvent *focusEvent) focusEvent->ignore(); break; default: - if (d->lastFocusItem) { + if (d->passiveFocusItem) { // Set focus on the last focus item - setFocusItem(d->lastFocusItem, focusEvent->reason()); + setFocusItem(d->passiveFocusItem, focusEvent->reason()); } break; } @@ -3778,6 +3782,7 @@ void QGraphicsScene::focusOutEvent(QFocusEvent *focusEvent) { Q_D(QGraphicsScene); d->hasFocus = false; + d->passiveFocusItem = d->focusItem; setFocusItem(0, focusEvent->reason()); // Remove all popups when the scene loses focus. diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h index 2314e37..9460a4d 100644 --- a/src/gui/graphicsview/qgraphicsscene_p.h +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -150,6 +150,7 @@ public: quint32 rectAdjust; QGraphicsItem *focusItem; QGraphicsItem *lastFocusItem; + QGraphicsItem *passiveFocusItem; QGraphicsWidget *tabFocusFirst; QGraphicsItem *activePanel; QGraphicsItem *lastActivePanel; diff --git a/src/gui/image/image.pri b/src/gui/image/image.pri index a5e37f3..72738c9 100644 --- a/src/gui/image/image.pri +++ b/src/gui/image/image.pri @@ -31,7 +31,8 @@ HEADERS += \ image/qpixmapfilter_p.h \ image/qimagepixmapcleanuphooks_p.h \ image/qvolatileimage_p.h \ - image/qvolatileimagedata_p.h + image/qvolatileimagedata_p.h \ + image/qnativeimagehandleprovider_p.h SOURCES += \ image/qbitmap.cpp \ diff --git a/src/gui/image/qnativeimagehandleprovider_p.h b/src/gui/image/qnativeimagehandleprovider_p.h new file mode 100644 index 0000000..4e6ed38 --- /dev/null +++ b/src/gui/image/qnativeimagehandleprovider_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNATIVEIMAGEHANDLEPROVIDER_P_H +#define QNATIVEIMAGEHANDLEPROVIDER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class QNativeImageHandleProvider +{ +public: + virtual void get(void **handle, QString *type) = 0; + virtual void release(void *handle, const QString &type) = 0; +}; + +QT_END_NAMESPACE + +#endif // QNATIVEIMAGEHANDLEPROVIDER_P_H diff --git a/src/gui/image/qpixmap_s60.cpp b/src/gui/image/qpixmap_s60.cpp index ca5f133..fbdebf3 100644 --- a/src/gui/image/qpixmap_s60.cpp +++ b/src/gui/image/qpixmap_s60.cpp @@ -374,6 +374,8 @@ CFbsBitmap *QPixmap::toSymbianCFbsBitmap() const To be sure that QPixmap does not modify your original instance, you should make a copy of your \c CFbsBitmap before calling this function. If the CFbsBitmap is not valid this function will return a null QPixmap. + For performance reasons it is recommended to use a \a bitmap with a display + mode of EColor16MAP or EColor16MU whenever possible. \warning This function is only available on Symbian OS. diff --git a/src/gui/image/qpixmapdata_p.h b/src/gui/image/qpixmapdata_p.h index db5ff4a..6bbce09 100644 --- a/src/gui/image/qpixmapdata_p.h +++ b/src/gui/image/qpixmapdata_p.h @@ -72,7 +72,8 @@ public: enum NativeType { FbsBitmap, SgImage, - VolatileImage + VolatileImage, + NativeImageHandleProvider }; #endif enum ClassId { RasterClass, X11Class, MacClass, DirectFBClass, diff --git a/src/gui/image/qvolatileimagedata_symbian.cpp b/src/gui/image/qvolatileimagedata_symbian.cpp index 474d0ef..6e2909b 100644 --- a/src/gui/image/qvolatileimagedata_symbian.cpp +++ b/src/gui/image/qvolatileimagedata_symbian.cpp @@ -392,6 +392,9 @@ void QVolatileImageData::initWithBitmap(CFbsBitmap *source) } else if (needsCopy) { // Rasterize extended and compressed bitmaps. bitmap = rasterizeBitmap(source, EColor16MAP); + } else if (source->DisplayMode() == EGray2) { + // The pixels will be inverted, must make a copy. + bitmap = rasterizeBitmap(source, source->DisplayMode()); } else { // Efficient path: no pixel data copying. Just duplicate. This of course // means the original bitmap's data may get modified, but that's fine diff --git a/src/gui/inputmethod/qcoefepinputcontext_p.h b/src/gui/inputmethod/qcoefepinputcontext_p.h index 8c8ffd4..de3577f 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_p.h +++ b/src/gui/inputmethod/qcoefepinputcontext_p.h @@ -93,6 +93,9 @@ public: TCoeInputCapabilities inputCapabilities(); + void resetSplitViewWidget(bool keepInputWidget = false); + void ensureFocusWidgetVisible(QWidget *widget); + protected: void timerEvent(QTimerEvent *timerEvent); @@ -104,9 +107,11 @@ private: void queueInputCapabilitiesChanged(); bool needsInputPanel(); void commitTemporaryPreeditString(); + bool isWidgetVisible(QWidget *widget, int offset = 0); private Q_SLOTS: void ensureInputCapabilitiesChanged(); + void translateInputWidget(); // From MCoeFepAwareTextEditor public: @@ -155,9 +160,15 @@ private: QBasicTimer m_tempPreeditStringTimeout; bool m_hasTempPreeditString; + int m_splitViewResizeBy; + Qt::WindowStates m_splitViewPreviousWindowStates; + QRectF m_transformation; + friend class tst_QInputContext; }; +Q_GUI_EXPORT void qt_s60_setPartialScreenInputMode(bool enable); + QT_END_NAMESPACE #endif // QT_NO_IM diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp index 1bef64d..73aa982 100644 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp @@ -48,6 +48,8 @@ #include <qgraphicsscene.h> #include <qgraphicswidget.h> #include <qsymbianevent.h> +#include <qlayout.h> +#include <qdesktopwidget.h> #include <private/qcore_symbian_p.h> #include <fepitfr.h> @@ -67,8 +69,16 @@ // that support text selection. #define QT_EAknEditorFlagSelectionVisible 0x100000 +// EAknEditorFlagEnablePartialScreen is only valid from Sym^3 onwards. +#define QT_EAknEditorFlagEnablePartialScreen 0x200000 + QT_BEGIN_NAMESPACE +Q_GUI_EXPORT void qt_s60_setPartialScreenInputMode(bool enable) +{ + S60->partial_keyboard = enable; +} + QCoeFepInputContext::QCoeFepInputContext(QObject *parent) : QInputContext(parent), m_fepState(q_check_ptr(new CAknEdwinState)), // CBase derived object needs check on new @@ -80,13 +90,19 @@ QCoeFepInputContext::QCoeFepInputContext(QObject *parent) m_inlinePosition(0), m_formatRetriever(0), m_pointerHandler(0), - m_hasTempPreeditString(false) + m_hasTempPreeditString(false), + m_splitViewResizeBy(0), + m_splitViewPreviousWindowStates(Qt::WindowNoState) { m_fepState->SetObjectProvider(this); - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) - m_fepState->SetFlags(EAknEditorFlagDefault | QT_EAknEditorFlagSelectionVisible); - else - m_fepState->SetFlags(EAknEditorFlagDefault); + int defaultFlags = EAknEditorFlagDefault; + if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) { + if (S60->partial_keyboard) { + defaultFlags |= QT_EAknEditorFlagEnablePartialScreen; + } + defaultFlags |= QT_EAknEditorFlagSelectionVisible; + } + m_fepState->SetFlags(defaultFlags); m_fepState->SetDefaultInputMode( EAknEditorTextInputMode ); m_fepState->SetPermittedInputModes( EAknEditorAllInputModes ); m_fepState->SetDefaultCase( EAknEditorTextCase ); @@ -210,6 +226,21 @@ bool QCoeFepInputContext::filterEvent(const QEvent *event) return false; switch (event->type()) { + case QEvent::MouseButtonPress: + // Alphanumeric keypad doesn't like it when we click and text is still getting displayed + // It ignores the mouse event, so we need to commit and send a selection event (which will get triggered + // after the commit) + if (!m_preeditString.isEmpty()) { + commitCurrentString(false); + + int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); + + QList<QInputMethodEvent::Attribute> selectAttributes; + selectAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, 0, QVariant()); + QInputMethodEvent selectEvent(QLatin1String(""), selectAttributes); + sendEvent(selectEvent); + } + break; case QEvent::KeyPress: commitTemporaryPreeditString(); // fall through intended @@ -343,6 +374,164 @@ TCoeInputCapabilities QCoeFepInputContext::inputCapabilities() return TCoeInputCapabilities(m_textCapabilities, this, 0); } +void QCoeFepInputContext::resetSplitViewWidget(bool keepInputWidget) +{ + QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget); + + if (!gv) { + return; + } + + QSymbianControl *symControl = static_cast<QSymbianControl*>(gv->effectiveWinId()); + symControl->CancelLongTapTimer(); + + const bool alwaysResize = (gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff); + QWidget *windowToMove = gv->window(); + + bool userResize = gv->testAttribute(Qt::WA_Resized); + + windowToMove->setUpdatesEnabled(false); + + if (!alwaysResize) { + if (gv->scene()) { + disconnect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget())); + QGraphicsItem *rootItem; + foreach (QGraphicsItem *item, gv->scene()->items()) { + if (!item->parentItem()) { + rootItem = item; + break; + } + } + if (rootItem) + rootItem->resetTransform(); + } + } else { + if (m_splitViewResizeBy) + gv->resize(gv->rect().width(), m_splitViewResizeBy); + } + // Resizing might have led to widget losing its original windowstate. + // Restore previous window state. + + if (m_splitViewPreviousWindowStates != windowToMove->windowState()) + windowToMove->setWindowState(m_splitViewPreviousWindowStates); + + windowToMove->setUpdatesEnabled(true); + + gv->setAttribute(Qt::WA_Resized, userResize); //not a user resize + + m_splitViewResizeBy = 0; + if (!keepInputWidget) { + m_splitViewPreviousWindowStates = Qt::WindowNoState; + S60->splitViewLastWidget = 0; + } +} + +// Checks if a given widget is visible in the splitview rect. The offset +// parameter can be used to validate if moving widget upwards or downwards +// by the offset would make a difference for the visibility. + +bool QCoeFepInputContext::isWidgetVisible(QWidget *widget, int offset) +{ + bool visible = false; + if (widget) { + QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); + QWidget *window = QApplication::activeWindow(); + QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget); + if (gv && window) { + if (QGraphicsScene *scene = gv->scene()) { + if (QGraphicsItem *focusItem = scene->focusItem()) { + QPoint cursorPos = window->mapToGlobal(focusItem->cursor().pos()); + cursorPos.setY(cursorPos.y() + offset); + if (splitViewRect.contains(cursorPos)) { + visible = true; + } + } + } + } + } + return visible; +} + +// Ensure that the input widget is visible in the splitview rect. + +void QCoeFepInputContext::ensureFocusWidgetVisible(QWidget *widget) +{ + // Native side opening and closing its virtual keyboard when it changes the keyboard layout, + // has an adverse impact on long tap timer. Cancel the timer when splitview opens to avoid this. + QSymbianControl *symControl = static_cast<QSymbianControl*>(widget->effectiveWinId()); + symControl->CancelLongTapTimer(); + + // Graphicsviews that have vertical scrollbars should always be resized to the splitview area. + // Graphicsviews without scrollbars should be translated. + + QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget); + if (!gv) + return; + + const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff); + const bool moveWithinVisibleArea = (S60->splitViewLastWidget != 0); + + QWidget *windowToMove = gv ? gv : symControl->widget(); + if (!windowToMove->isWindow()) + windowToMove = windowToMove->window(); + if (!windowToMove) { + return; + } + + // When opening the keyboard (not moving within the splitview area), save the original + // window state. In some cases, ensuring input widget visibility might lead to window + // states getting changed. + + if (!moveWithinVisibleArea) { + S60->splitViewLastWidget = widget; + m_splitViewPreviousWindowStates = windowToMove->windowState(); + } + + int windowTop = widget->window()->pos().y(); + + const bool userResize = widget->testAttribute(Qt::WA_Resized); + + QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); + + + // When resizing a window widget, it will lose its maximized window state. + // Native applications hide statuspane in splitview state, so lets move to + // fullscreen mode. This makes available area slightly bigger, which helps usability + // and greatly reduces event passing in orientation switch cases, + // as the statuspane size is not changing. + + if (!(windowToMove->windowState() & Qt::WindowFullScreen)) { + windowToMove->setWindowState( + (windowToMove->windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowFullScreen); + } + + if (alwaysResize) { + windowToMove->setUpdatesEnabled(false); + if (!moveWithinVisibleArea) + m_splitViewResizeBy = widget->height(); + + windowTop = widget->geometry().top(); + widget->resize(widget->width(), splitViewRect.height() - windowTop); + + if (gv->scene()) { + const QRectF microFocusRect = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF(); + gv->ensureVisible(microFocusRect); + } + windowToMove->setUpdatesEnabled(true); + } else { + if (!moveWithinVisibleArea) { + // Check if the widget contains cursorPositionChanged signal and connect to it. + const char *signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged())).constData(); + int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal + 1); + if (index != -1) + connect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget())); + } + translateInputWidget(); + } + + widget->setAttribute(Qt::WA_Resized, userResize); //not a user resize +} + static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat, bool validStyleColor) { QTextCharFormat qFormat; @@ -474,10 +663,12 @@ void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints) m_fepState->SetPermittedCases(flags); ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateCaseModeUpdate); - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) - flags = QT_EAknEditorFlagSelectionVisible; - else - flags = 0; + flags = 0; + if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) { + if (S60->partial_keyboard) + flags |= QT_EAknEditorFlagEnablePartialScreen; + flags |= QT_EAknEditorFlagSelectionVisible; + } if (hints & ImhUppercaseOnly && !(hints & ImhLowercaseOnly) || hints & ImhLowercaseOnly && !(hints & ImhUppercaseOnly)) { flags |= EAknEditorFlagFixedCase; @@ -604,6 +795,47 @@ void QCoeFepInputContext::ensureInputCapabilitiesChanged() m_pendingInputCapabilitiesChanged = false; } +void QCoeFepInputContext::translateInputWidget() +{ + QGraphicsView *gv = qobject_cast<QGraphicsView *>(S60->splitViewLastWidget); + QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); + + QRectF cursor = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF(); + QPolygon cursorP = gv->mapFromScene(cursor); + QRectF vkbRect = QRectF(splitViewRect.bottomLeft(), qApp->desktop()->rect().bottomRight()); + if (cursor.isEmpty() || vkbRect.isEmpty()) + return; + + // Fetch root item (i.e. graphicsitem with no parent) + QGraphicsItem *rootItem; + foreach (QGraphicsItem *item, gv->scene()->items()) { + if (!item->parentItem()) { + rootItem = item; + break; + } + } + if (!rootItem) + return; + + m_transformation = (rootItem->transform().isTranslating()) ? QRectF(0,0, gv->width(), rootItem->transform().dy()) : QRectF(); + + // Do nothing if the cursor is visible in the splitview area. + if (splitViewRect.contains(cursorP.boundingRect())) + return; + + // New Y position should be ideally at the center of the splitview area. + // If that would expose unpainted canvas, limit the tranformation to the visible scene bottom. + + const qreal maxY = gv->sceneRect().bottom() - splitViewRect.bottom() + m_transformation.height(); + qreal dy = -(qMin(maxY, (cursor.bottom() - vkbRect.top() / 2))); + + // Do not allow transform above screen top. + if (m_transformation.height() + dy > 0) + return; + + rootItem->setTransform(QTransform::fromTranslate(0, dy), true); +} + void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText, TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/, MFepInlineTextFormatRetriever& aInlineTextFormatRetriever, diff --git a/src/gui/kernel/qapplication_p.h b/src/gui/kernel/qapplication_p.h index a6815a8..954c6de 100644 --- a/src/gui/kernel/qapplication_p.h +++ b/src/gui/kernel/qapplication_p.h @@ -639,6 +639,8 @@ public: int pressureSupported; int maxTouchPressure; QList<QTouchEvent::TouchPoint> appAllTouchPoints; + + bool useTranslucentEGLSurfaces; #endif private: diff --git a/src/gui/kernel/qapplication_s60.cpp b/src/gui/kernel/qapplication_s60.cpp index 586869a..3ce597b 100644 --- a/src/gui/kernel/qapplication_s60.cpp +++ b/src/gui/kernel/qapplication_s60.cpp @@ -96,6 +96,9 @@ QT_BEGIN_NAMESPACE // Goom Events through Window Server static const int KGoomMemoryLowEvent = 0x10282DBF; static const int KGoomMemoryGoodEvent = 0x20026790; +// Split view open/close events from AVKON +static const int KSplitViewOpenEvent = 0x2001E2C0; +static const int KSplitViewCloseEvent = 0x2001E2C1; #if defined(QT_DEBUG) static bool appNoGrab = false; // Grabbing enabled @@ -401,6 +404,7 @@ QSymbianControl::QSymbianControl(QWidget *w) , m_longTapDetector(0) , m_ignoreFocusChanged(0) , m_symbianPopupIsOpen(0) + , m_inExternalScreenOverride(false) { } @@ -408,9 +412,11 @@ void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) { if (!desktop) { - if (isWindowOwning || !qwidget->parentWidget()) - CreateWindowL(S60->windowGroup()); - else + if (isWindowOwning || !qwidget->parentWidget() + || qwidget->parentWidget()->windowType() == Qt::Desktop) { + RWindowGroup &wg(S60->windowGroup(qwidget)); + CreateWindowL(wg); + } else { /** * TODO: in order to avoid creating windows for all ancestors of * this widget up to the root window, the parameter passed to @@ -420,6 +426,7 @@ void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) * is created for a widget between this one and the root window. */ CreateWindowL(qwidget->parentWidget()->winId()); + } // Necessary in order to be able to track the activation status of // the control's window @@ -452,7 +459,7 @@ void QSymbianControl::ConstructL(bool isWindowOwning, bool desktop) windowPurpose = ETfxPurposeSplashScreenWindow; break; default: - windowPurpose = (isWindowOwning || !qwidget->parentWidget()) + windowPurpose = (isWindowOwning || !qwidget->parentWidget() || qwidget->parentWidget()->windowType() == Qt::Desktop) ? ETfxPurposeWindow : ETfxPurposeChildWindow; break; } @@ -983,14 +990,15 @@ TKeyResponse QSymbianControl::handleVirtualMouse(const TKeyEvent& keyEvent,TEven } } //clip to screen size (window server allows a sprite hotspot to be outside the screen) + int screenNumber = S60->screenNumberForWidget(qwidget); if (x < 0) x = 0; - else if (x >= S60->screenWidthInPixels) - x = S60->screenWidthInPixels - 1; + else if (x >= S60->screenWidthInPixelsForScreen[screenNumber]) + x = S60->screenWidthInPixelsForScreen[screenNumber] - 1; if (y < 0) y = 0; - else if (y >= S60->screenHeightInPixels) - y = S60->screenHeightInPixels - 1; + else if (y >= S60->screenHeightInPixelsForScreen[screenNumber]) + y = S60->screenHeightInPixelsForScreen[screenNumber] - 1; TPoint epos(x, y); TPoint cpos = epos - PositionRelativeToScreen(); fakeEvent.iPosition = cpos; @@ -1167,6 +1175,18 @@ void QSymbianControl::SizeChanged() QSize newSize(Size().iWidth, Size().iHeight); if (oldSize != newSize) { + const bool isFullscreen = qwidget->windowState() & Qt::WindowFullScreen; + const int screenNumber = S60->screenNumberForWidget(qwidget); + if (!m_inExternalScreenOverride && isFullscreen && screenNumber > 0) { + int screenWidth = S60->screenWidthInPixelsForScreen[screenNumber]; + int screenHeight = S60->screenHeightInPixelsForScreen[screenNumber]; + TSize screenSize(screenWidth, screenHeight); + if (screenWidth > 0 && screenHeight > 0 && screenSize != Size()) { + m_inExternalScreenOverride = true; + SetExtent(TPoint(0, 0), screenSize); + return; + } + } QRect cr = qwidget->geometry(); cr.setSize(newSize); qwidget->data->crect = cr; @@ -1189,6 +1209,8 @@ void QSymbianControl::SizeChanged() } } + m_inExternalScreenOverride = false; + // CCoeControl::SetExtent calls SizeChanged, but does not call // PositionChanged, so we call it here to ensure that the widget's // position is updated. @@ -1224,6 +1246,11 @@ void QSymbianControl::FocusChanged(TDrawNow /* aDrawNow */) if (m_ignoreFocusChanged || (qwidget->windowType() & Qt::WindowType_Mask) == Qt::Desktop) return; +#ifdef Q_WS_S60 + if (S60->splitViewLastWidget) + return; +#endif + // Popups never get focused, but still receive the FocusChanged when they are hidden. if (QApplicationPrivate::popupWidgets != 0 || (qwidget->windowType() & Qt::Popup) == Qt::Popup) @@ -1302,9 +1329,56 @@ void QSymbianControl::handleClientAreaChange() } } +bool QSymbianControl::isSplitViewWidget(QWidget *widget) { + bool returnValue = true; + //Ignore events sent to non-active windows, not visible widgets and not parents of input widget. + if (!qwidget->isActiveWindow() + || !qwidget->isVisible() + || !qwidget->isAncestorOf(widget)) { + + returnValue = false; + } + return returnValue; +} + void QSymbianControl::HandleResourceChange(int resourceType) { switch (resourceType) { + case KSplitViewCloseEvent: //intentional fall-through + case KSplitViewOpenEvent: { +#if !defined(QT_NO_IM) && defined(Q_WS_S60) + + //Fetch widget getting the text input + QWidget *widget = QWidget::keyboardGrabber(); + if (!widget) { + if (QApplicationPrivate::popupWidgets) { + widget = QApplication::activePopupWidget()->focusWidget(); + if (!widget) { + widget = QApplication::activePopupWidget(); + } + } else { + widget = QApplicationPrivate::focus_widget; + if (!widget) { + widget = qwidget; + } + } + } + if (widget) { + QCoeFepInputContext *ic = qobject_cast<QCoeFepInputContext *>(widget->inputContext()); + if (!ic) { + ic = qobject_cast<QCoeFepInputContext *>(qApp->inputContext()); + } + if (ic && isSplitViewWidget(widget)) { + if (resourceType == KSplitViewCloseEvent) { + ic->resetSplitViewWidget(); + } else { + ic->ensureFocusWidgetVisible(widget); + } + } + } +#endif // !defined(QT_NO_IM) && defined(Q_WS_S60) + } + break; case KInternalStatusPaneChange: handleClientAreaChange(); if (IsFocused() && IsVisible()) { @@ -1592,6 +1666,32 @@ void qt_init(QApplicationPrivate * /* priv */, int) QObject::connect(qApp, SIGNAL(aboutToQuit()), qApp, SLOT(_q_aboutToQuit())); #endif +#ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = true; + + const TUid KIvePropertyCat = {0x2726beef}; + enum TIvePropertyChipType { + EVCBCM2727B1 = 0x00000000, + EVCBCM2763A0 = 0x04000100, + EVCBCM2763B0 = 0x04000102, + EVCBCM2763C0 = 0x04000103, + EVCBCM2763C1 = 0x04000104, + EVCBCMUnknown = 0x7fffffff + }; + + TInt chipType = EVCBCMUnknown; + if (RProperty::Get(KIvePropertyCat, 0 /*chip type*/, chipType) == KErrNone) { + if (chipType == EVCBCM2727B1) { + // We have only 32MB GPU memory. Use raster surfaces + // for transparent TLWs. + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; + } + } else { + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; + } +#else + QApplicationPrivate::instance()->useTranslucentEGLSurfaces = false; +#endif /* ### Commented out for now as parameter handling not needed in SOS(yet). Code below will break testlib with -o flag int argc = priv->argc; @@ -1988,7 +2088,10 @@ int QApplicationPrivate::symbianProcessWsEvent(const QSymbianEvent *symbianEvent return 1; } break; - case EEventScreenDeviceChanged: + case EEventScreenDeviceChanged: // fallthrough +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + case EEventDisplayChanged: +#endif if (callSymbianEventFilters(symbianEvent)) return 1; if (S60) diff --git a/src/gui/kernel/qdesktopwidget_s60.cpp b/src/gui/kernel/qdesktopwidget_s60.cpp index 3653ae2..c3963f4 100644 --- a/src/gui/kernel/qdesktopwidget_s60.cpp +++ b/src/gui/kernel/qdesktopwidget_s60.cpp @@ -44,36 +44,67 @@ #include "qwidget_p.h" #include "qt_s60_p.h" #include <w32std.h> - -#include "hal.h" -#include "hal_data.h" +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) +#include <graphics/displaycontrol.h> +#endif QT_BEGIN_NAMESPACE -class QDesktopWidgetPrivate : public QWidgetPrivate +extern int qt_symbian_create_desktop_on_screen; + +class QSingleDesktopWidget : public QWidget +{ +public: + QSingleDesktopWidget(); + ~QSingleDesktopWidget(); +}; + +QSingleDesktopWidget::QSingleDesktopWidget() + : QWidget(0, Qt::Desktop) +{ +} + +QSingleDesktopWidget::~QSingleDesktopWidget() { + const QObjectList &childList = children(); + for (int i = childList.size(); i > 0 ;) { + --i; + childList.at(i)->setParent(0); + } +} +class QDesktopWidgetPrivate : public QWidgetPrivate +{ public: QDesktopWidgetPrivate(); ~QDesktopWidgetPrivate(); - static void init(QDesktopWidget *that); static void cleanup(); + static void init_sys(); static int screenCount; static int primaryScreen; static QVector<QRect> *rects; static QVector<QRect> *workrects; + static QVector<QWidget *> *screens; static int refcount; + +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + static MDisplayControl *displayControl; +#endif }; int QDesktopWidgetPrivate::screenCount = 1; int QDesktopWidgetPrivate::primaryScreen = 0; QVector<QRect> *QDesktopWidgetPrivate::rects = 0; QVector<QRect> *QDesktopWidgetPrivate::workrects = 0; +QVector<QWidget *> *QDesktopWidgetPrivate::screens = 0; int QDesktopWidgetPrivate::refcount = 0; +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) +MDisplayControl *QDesktopWidgetPrivate::displayControl = 0; +#endif QDesktopWidgetPrivate::QDesktopWidgetPrivate() { @@ -88,25 +119,55 @@ QDesktopWidgetPrivate::~QDesktopWidgetPrivate() void QDesktopWidgetPrivate::init(QDesktopWidget *that) { - Q_UNUSED(that); -// int screenCount=0; - - // ### TODO: Implement proper multi-display support - QDesktopWidgetPrivate::screenCount = 1; -// if (HAL::Get(0, HALData::EDisplayNumberOfScreens, screenCount) == KErrNone) -// QDesktopWidgetPrivate::screenCount = screenCount; -// else -// QDesktopWidgetPrivate::screenCount = 0; + // Note that on S^3 devices the screen count retrieved via RWsSession + // will always be 2 but the width and height for screen number 1 will + // be 0 as long as TV-out is not connected. + // + // On the other hand a valid size for screen 1 will be reported even + // after the cable is disconnected. In order to overcome this, we use + // MDisplayControl::NumberOfResolutions() to check if the display is + // valid or not. + + screenCount = S60->screenCount(); +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + if (displayControl) { + if (displayControl->NumberOfResolutions() < 1) + screenCount = 1; + } +#endif + if (screenCount < 1) { + qWarning("No screen available"); + screenCount = 1; + } rects = new QVector<QRect>(); workrects = new QVector<QRect>(); - - rects->resize(QDesktopWidgetPrivate::screenCount); - workrects->resize(QDesktopWidgetPrivate::screenCount); - - (*rects)[0].setRect(0, 0, S60->screenWidthInPixels, S60->screenHeightInPixels); - QRect wr = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); - (*workrects)[0].setRect(wr.x(), wr.y(), wr.width(), wr.height()); + screens = new QVector<QWidget *>(); + + rects->resize(screenCount); + workrects->resize(screenCount); + screens->resize(screenCount); + + for (int i = 0; i < screenCount; ++i) { + // All screens will have a position of (0, 0) as there is no true virtual desktop + // or pointer event support for multiple screens on Symbian. + QRect r(0, 0, + S60->screenWidthInPixelsForScreen[i], S60->screenHeightInPixelsForScreen[i]); + // Stop here if empty and ignore this screen. + if (r.isEmpty()) { + screenCount = i; + break; + } + (*rects)[i] = r; + QRect wr; + if (i == 0) + wr = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); + else + wr = rects->at(i); + (*workrects)[i].setRect(wr.x(), wr.y(), wr.width(), wr.height()); + (*screens)[i] = 0; + } + (*screens)[0] = that; } void QDesktopWidgetPrivate::cleanup() @@ -115,6 +176,27 @@ void QDesktopWidgetPrivate::cleanup() rects = 0; delete workrects; workrects = 0; + if (screens) { + // First item is the QDesktopWidget so skip it. + for (int i = 1; i < screens->count(); ++i) + delete screens->at(i); + } + delete screens; + screens = 0; +} + +void QDesktopWidgetPrivate::init_sys() +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + CWsScreenDevice *dev = S60->screenDevice(1); + if (dev) { + displayControl = static_cast<MDisplayControl *>( + dev->GetInterface(MDisplayControl::ETypeId)); + if (displayControl) { + displayControl->EnableDisplayChangeEvents(ETrue); + } + } +#endif } @@ -122,6 +204,7 @@ QDesktopWidget::QDesktopWidget() : QWidget(*new QDesktopWidgetPrivate, 0, Qt::Desktop) { setObjectName(QLatin1String("desktop")); + QDesktopWidgetPrivate::init_sys(); QDesktopWidgetPrivate::init(this); } @@ -131,7 +214,7 @@ QDesktopWidget::~QDesktopWidget() bool QDesktopWidget::isVirtualDesktop() const { - return true; + return false; } int QDesktopWidget::primaryScreen() const @@ -145,9 +228,23 @@ int QDesktopWidget::numScreens() const return QDesktopWidgetPrivate::screenCount; } -QWidget *QDesktopWidget::screen(int /* screen */) +static inline QWidget *newSingleDesktopWidget(int screen) { - return this; + qt_symbian_create_desktop_on_screen = screen; + QWidget *w = new QSingleDesktopWidget; + qt_symbian_create_desktop_on_screen = -1; + return w; +} + +QWidget *QDesktopWidget::screen(int screen) +{ + Q_D(QDesktopWidget); + if (screen < 0 || screen >= d->screenCount) + screen = d->primaryScreen; + if (!d->screens->at(screen) + || d->screens->at(screen)->windowType() != Qt::Desktop) + (*d->screens)[screen] = newSingleDesktopWidget(screen); + return (*d->screens)[screen]; } const QRect QDesktopWidget::availableGeometry(int screen) const @@ -168,14 +265,19 @@ const QRect QDesktopWidget::screenGeometry(int screen) const return d->rects->at(screen); } -int QDesktopWidget::screenNumber(const QWidget * /* widget */) const +int QDesktopWidget::screenNumber(const QWidget *widget) const { - return QDesktopWidgetPrivate::primaryScreen; + Q_D(const QDesktopWidget); + return widget + ? S60->screenNumberForWidget(widget) + : d->primaryScreen; } -int QDesktopWidget::screenNumber(const QPoint & /* point */) const +int QDesktopWidget::screenNumber(const QPoint &point) const { - return QDesktopWidgetPrivate::primaryScreen; + Q_UNUSED(point); + Q_D(const QDesktopWidget); + return d->primaryScreen; } void QDesktopWidget::resizeEvent(QResizeEvent *) @@ -203,6 +305,10 @@ void QDesktopWidget::resizeEvent(QResizeEvent *) if (oldrect != newrect) emit workAreaResized(j); } + + if (oldscreencount != d->screenCount) { + emit screenCountChanged(d->screenCount); + } } QT_END_NAMESPACE diff --git a/src/gui/kernel/qt_s60_p.h b/src/gui/kernel/qt_s60_p.h index 40697bf..3bb27c3 100644 --- a/src/gui/kernel/qt_s60_p.h +++ b/src/gui/kernel/qt_s60_p.h @@ -64,6 +64,7 @@ #include "qapplication.h" #include "qelapsedtimer.h" #include "QtCore/qthreadstorage.h" +#include "qwidget_p.h" #include <w32std.h> #include <coecntrl.h> #include <eikenv.h> @@ -84,6 +85,8 @@ QT_BEGIN_NAMESPACE // system events seems to start with 0x10 const TInt KInternalStatusPaneChange = 0x50000000; +static const int qt_symbian_max_screens = 4; + //this macro exists because EColor16MAP enum value doesn't exist in Symbian OS 9.2 #define Q_SYMBIAN_ECOLOR16MAP TDisplayMode(13) @@ -142,7 +145,10 @@ public: int avkonComponentsSupportTransparency : 1; int menuBeingConstructed : 1; int orientationSet : 1; + int partial_keyboard : 1; QApplication::QS60MainApplicationFactory s60ApplicationFactory; // typedef'ed pointer type + QPointer<QWidget> splitViewLastWidget; + static CEikButtonGroupContainer *cba; enum ScanCodeState { @@ -154,8 +160,14 @@ public: static inline void updateScreenSize(); inline RWsSession& wsSession(); + static inline int screenCount(); static inline RWindowGroup& windowGroup(); + static inline RWindowGroup& windowGroup(const QWidget *widget); + static inline RWindowGroup& windowGroup(int screenNumber); inline CWsScreenDevice* screenDevice(); + inline CWsScreenDevice* screenDevice(const QWidget *widget); + inline CWsScreenDevice* screenDevice(int screenNumber); + static inline int screenNumberForWidget(const QWidget *widget); static inline CCoeAppUi* appUi(); static inline CEikMenuBar* menuBar(); #ifdef Q_WS_S60 @@ -172,6 +184,11 @@ public: #ifdef Q_OS_SYMBIAN TTrapHandler *s60InstalledTrapHandler; #endif + + int screenWidthInPixelsForScreen[qt_symbian_max_screens]; + int screenHeightInPixelsForScreen[qt_symbian_max_screens]; + int screenWidthInTwipsForScreen[qt_symbian_max_screens]; + int screenHeightInTwipsForScreen[qt_symbian_max_screens]; }; Q_AUTOTEST_EXPORT QS60Data* qGlobalS60Data(); @@ -252,6 +269,7 @@ private: #ifdef QT_SYMBIAN_SUPPORTS_ADVANCED_POINTER void translateAdvancedPointerEvent(const TAdvancedPointerEvent *event); #endif + bool isSplitViewWidget(QWidget *widget); public: void handleClientAreaChange(); @@ -270,6 +288,8 @@ private: // Fader object used to fade everything except this menu and the CBA. TAknPopupFader popupFader; #endif + + bool m_inExternalScreenOverride : 1; }; inline QS60Data::QS60Data() @@ -297,6 +317,7 @@ inline QS60Data::QS60Data() avkonComponentsSupportTransparency(0), menuBeingConstructed(0), orientationSet(0), + partial_keyboard(0), s60ApplicationFactory(0) #ifdef Q_OS_SYMBIAN ,s60InstalledTrapHandler(0) @@ -320,6 +341,17 @@ inline void QS60Data::updateScreenSize() S60->defaultDpiY = S60->screenHeightInPixels / inches; inches = S60->screenWidthInTwips / (TReal)KTwipsPerInch; S60->defaultDpiX = S60->screenWidthInPixels / inches; + + int screens = S60->screenCount(); + for (int i = 0; i < screens; ++i) { + CWsScreenDevice *dev = S60->screenDevice(i); + mode = dev->CurrentScreenMode(); + dev->GetScreenModeSizeAndRotation(mode, params); + S60->screenWidthInPixelsForScreen[i] = params.iPixelSize.iWidth; + S60->screenHeightInPixelsForScreen[i] = params.iPixelSize.iHeight; + S60->screenWidthInTwipsForScreen[i] = params.iTwipsSize.iWidth; + S60->screenHeightInTwipsForScreen[i] = params.iTwipsSize.iHeight; + } } inline RWsSession& QS60Data::wsSession() @@ -330,11 +362,38 @@ inline RWsSession& QS60Data::wsSession() return tls.localData()->wsSession; } +inline int QS60Data::screenCount() +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + CCoeEnv *env = CCoeEnv::Static(); + if (env) { + return qMin(env->WsSession().NumberOfScreens(), qt_symbian_max_screens); + } +#endif + return 1; +} + inline RWindowGroup& QS60Data::windowGroup() { return CCoeEnv::Static()->RootWin(); } +inline RWindowGroup& QS60Data::windowGroup(const QWidget *widget) +{ + return windowGroup(screenNumberForWidget(widget)); +} + +inline RWindowGroup& QS60Data::windowGroup(int screenNumber) +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + RWindowGroup *wg = CCoeEnv::Static()->RootWin(screenNumber); + return wg ? *wg : windowGroup(); +#else + Q_UNUSED(screenNumber); + return windowGroup(); +#endif +} + inline CWsScreenDevice* QS60Data::screenDevice() { if(!tls.hasLocalData()) { @@ -343,6 +402,36 @@ inline CWsScreenDevice* QS60Data::screenDevice() return tls.localData()->screenDevice; } +inline CWsScreenDevice* QS60Data::screenDevice(const QWidget *widget) +{ + return screenDevice(screenNumberForWidget(widget)); +} + +inline CWsScreenDevice* QS60Data::screenDevice(int screenNumber) +{ +#if defined(Q_SYMBIAN_SUPPORTS_MULTIPLE_SCREENS) + CCoeEnv *env = CCoeEnv::Static(); + if (env) { + CWsScreenDevice *dev = env->ScreenDevice(screenNumber); + return dev ? dev : screenDevice(); + } else { + return screenDevice(); + } +#else + return screenDevice(); +#endif +} + +inline int QS60Data::screenNumberForWidget(const QWidget *widget) +{ + if (!widget) + return 0; + const QWidget *w = widget; + while (w->parentWidget()) + w = w->parentWidget(); + return qt_widget_private(const_cast<QWidget *>(w))->symbianScreenNumber; +} + inline CCoeAppUi* QS60Data::appUi() { return CCoeEnv::Static()-> AppUi(); diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index bf9f6f9..37d7147 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -310,6 +310,8 @@ QWidgetPrivate::QWidgetPrivate(int version) , needWindowChange(0) , window_event(0) , qd_hd(0) +#elif defined(Q_OS_SYMBIAN) + , symbianScreenNumber(0) #endif { if (!qApp) { @@ -1294,6 +1296,10 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) // programmer specified desktop widget xinfo = desktopWidget->d_func()->xinfo; } +#elif defined(Q_OS_SYMBIAN) + if (desktopWidget) { + symbianScreenNumber = qt_widget_private(desktopWidget)->symbianScreenNumber; + } #elif defined(Q_WS_QPA) if (desktopWidget) { int screen = desktopWidget->d_func()->topData()->screenIndex; diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index 1083a1f..dd9bb49 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -900,6 +900,7 @@ public: #elif defined(Q_OS_SYMBIAN) // <--------------------------------------------------------- SYMBIAN static QWidget *mouseGrabber; static QWidget *keyboardGrabber; + int symbianScreenNumber; // only valid for desktop widget and top-levels void s60UpdateIsOpaque(); void reparentChildren(); void registerTouchWindow(); diff --git a/src/gui/kernel/qwidget_s60.cpp b/src/gui/kernel/qwidget_s60.cpp index 85164d2..16f28c7 100644 --- a/src/gui/kernel/qwidget_s60.cpp +++ b/src/gui/kernel/qwidget_s60.cpp @@ -84,6 +84,8 @@ QWidget *QWidgetPrivate::mouseGrabber = 0; QWidget *QWidgetPrivate::keyboardGrabber = 0; CEikButtonGroupContainer *QS60Data::cba = 0; +int qt_symbian_create_desktop_on_screen = -1; + static bool isEqual(const QList<QAction*>& a, const QList<QAction*>& b) { if ( a.count() != b.count()) @@ -349,12 +351,18 @@ void QWidgetPrivate::create_sys(WId window, bool /* initializeWindow */, bool de int sh = clientRect.Height(); if (desktop) { - TSize screenSize = S60->screenDevice()->SizeInPixels(); + symbianScreenNumber = qMax(qt_symbian_create_desktop_on_screen, 0); + TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels(); data.crect.setRect(0, 0, screenSize.iWidth, screenSize.iHeight); q->setAttribute(Qt::WA_DontShowOnScreen); } else if (topLevel && !q->testAttribute(Qt::WA_Resized)){ int width = sw; int height = sh; + if (symbianScreenNumber > 0) { + TSize screenSize = S60->screenDevice(symbianScreenNumber)->SizeInPixels(); + width = screenSize.iWidth; + height = screenSize.iHeight; + } if (extra) { width = qMax(qMin(width, extra->maxw), extra->minw); height = qMax(qMin(height, extra->maxh), extra->minh); @@ -644,7 +652,7 @@ void QWidgetPrivate::raise_sys() // If toplevel widget, raise app to foreground if (q->isWindow()) - S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup().Identifier(), 0); + S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), 0); } } @@ -656,7 +664,7 @@ void QWidgetPrivate::lower_sys() if (q->internalWinId()) { // If toplevel widget, lower app to background if (q->isWindow()) - S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup().Identifier(), -1); + S60->wsSession().SetWindowGroupOrdinalPosition(S60->windowGroup(q).Identifier(), -1); else q->internalWinId()->DrawableWindow()->SetOrdinalPosition(-1); } @@ -726,6 +734,11 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) { Q_Q(QWidget); + if (parent && parent->windowType() == Qt::Desktop) { + symbianScreenNumber = qt_widget_private(parent)->symbianScreenNumber; + parent = 0; + } + bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) @@ -804,17 +817,21 @@ void QWidgetPrivate::s60UpdateIsOpaque() RWindow *const window = static_cast<RWindow *>(q->effectiveWinId()->DrawableWindow()); #ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE - window->SetSurfaceTransparency(!isOpaque); - extra->topextra->nativeWindowTransparencyEnabled = !isOpaque; -#else + if (QApplicationPrivate::instance()->useTranslucentEGLSurfaces) { + window->SetSurfaceTransparency(!isOpaque); + extra->topextra->nativeWindowTransparencyEnabled = !isOpaque; + return; + } +#endif if (!isOpaque) { const TDisplayMode displayMode = static_cast<TDisplayMode>(window->SetRequiredDisplayMode(EColor16MA)); if (window->SetTransparencyAlphaChannel() == KErrNone) { window->SetBackgroundColor(TRgb(255, 255, 255, 0)); extra->topextra->nativeWindowTransparencyEnabled = 1; - if (extra->topextra->backingStore.data() && - QApplicationPrivate::graphics_system_name == QLatin1String("openvg")) { + if (extra->topextra->backingStore.data() && ( + QApplicationPrivate::graphics_system_name == QLatin1String("openvg") + || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"))) { // Semi-transparent EGL surfaces aren't supported. We need to // recreate backing store to get translucent surface (raster surface). extra->topextra->backingStore.create(q); @@ -825,7 +842,6 @@ void QWidgetPrivate::s60UpdateIsOpaque() window->SetTransparentRegion(TRegionFix<1>()); extra->topextra->nativeWindowTransparencyEnabled = 0; } -#endif } void QWidgetPrivate::setWindowIcon_sys(bool forceReset) @@ -1041,7 +1057,7 @@ int QWidget::metric(PaintDeviceMetric m) const } else if (m == PdmHeight) { val = data->crect.height(); } else { - CWsScreenDevice *scr = S60->screenDevice(); + CWsScreenDevice *scr = S60->screenDevice(this); switch(m) { case PdmDpiX: case PdmPhysicalDpiX: @@ -1211,7 +1227,16 @@ void QWidget::setWindowState(Qt::WindowStates newstate) const bool cbaVisibilityHint = windowFlags() & Qt::WindowSoftkeysVisibleHint; if (newstate & Qt::WindowFullScreen && !cbaVisibilityHint) { setAttribute(Qt::WA_OutsideWSRange, false); - window->SetExtentToWholeScreen(); + if (d->symbianScreenNumber > 0) { + int w = S60->screenWidthInPixelsForScreen[d->symbianScreenNumber]; + int h = S60->screenHeightInPixelsForScreen[d->symbianScreenNumber]; + if (w <= 0 || h <= 0) + window->SetExtentToWholeScreen(); + else + window->SetExtent(TPoint(0, 0), TSize(w, h)); + } else { + window->SetExtentToWholeScreen(); + } } else if (newstate & Qt::WindowMaximized || ((newstate & Qt::WindowFullScreen) && cbaVisibilityHint)) { setAttribute(Qt::WA_OutsideWSRange, false); TRect maxExtent = qt_QRect2TRect(qApp->desktop()->availableGeometry(this)); diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp index 7f5a937..1fccfc9 100644 --- a/src/gui/math3d/qvector2d.cpp +++ b/src/gui/math3d/qvector2d.cpp @@ -60,6 +60,11 @@ QT_BEGIN_NAMESPACE The QVector2D class can also be used to represent vertices in 2D space. We therefore do not need to provide a separate vertex class. + \bold{Note:} By design values in the QVector2D instance are stored as \c float. + This means that on platforms where the \c qreal arguments to QVector2D + functions are represented by \c double values, it is possible to + lose precision. + \sa QVector3D, QVector4D, QQuaternion */ diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp index 2414b5f..7bf0400 100644 --- a/src/gui/math3d/qvector3d.cpp +++ b/src/gui/math3d/qvector3d.cpp @@ -63,6 +63,11 @@ QT_BEGIN_NAMESPACE The QVector3D class can also be used to represent vertices in 3D space. We therefore do not need to provide a separate vertex class. + \bold{Note:} By design values in the QVector3D instance are stored as \c float. + This means that on platforms where the \c qreal arguments to QVector3D + functions are represented by \c double values, it is possible to + lose precision. + \sa QVector2D, QVector4D, QQuaternion */ diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp index 74dedc4..23befc0 100644 --- a/src/gui/math3d/qvector4d.cpp +++ b/src/gui/math3d/qvector4d.cpp @@ -59,6 +59,11 @@ QT_BEGIN_NAMESPACE The QVector4D class can also be used to represent vertices in 4D space. We therefore do not need to provide a separate vertex class. + \bold{Note:} By design values in the QVector4D instance are stored as \c float. + This means that on platforms where the \c qreal arguments to QVector4D + functions are represented by \c double values, it is possible to + lose precision. + \sa QQuaternion, QVector2D, QVector3D */ diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 129bba5..e5d8abc 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -1143,6 +1143,11 @@ void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedReg return; } + // If there's no partial update support we always need + // to do a full repaint before flushing + if (!windowSurface->hasPartialUpdateSupport()) + fullUpdatePending = true; + // Nothing to repaint. if (!isDirty()) { qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset); diff --git a/src/gui/text/qtextobject.cpp b/src/gui/text/qtextobject.cpp index c5b6e14..0081550 100644 --- a/src/gui/text/qtextobject.cpp +++ b/src/gui/text/qtextobject.cpp @@ -1504,7 +1504,7 @@ QTextBlock QTextBlock::next() const */ QTextBlock QTextBlock::previous() const { - if (!isValid()) + if (!p) return QTextBlock(); return QTextBlock(p, p->blockMap().previous(n)); diff --git a/src/opengl/qgraphicssystem_gl.cpp b/src/opengl/qgraphicssystem_gl.cpp index 79911fb..3574756 100644 --- a/src/opengl/qgraphicssystem_gl.cpp +++ b/src/opengl/qgraphicssystem_gl.cpp @@ -53,6 +53,10 @@ #include "private/qwindowsurface_x11gl_p.h" #endif +#if defined(Q_OS_SYMBIAN) +#include <QtGui/private/qapplication_p.h> +#endif + QT_BEGIN_NAMESPACE extern QGLWidget *qt_gl_getShareWidget(); @@ -86,6 +90,14 @@ QWindowSurface *QGLGraphicsSystem::createWindowSurface(QWidget *widget) const } #endif +#if defined(Q_OS_SYMBIAN) + if (!QApplicationPrivate::instance()->useTranslucentEGLSurfaces) { + QWidgetPrivate *d = qt_widget_private(widget); + if (!d->isOpaque && widget->testAttribute(Qt::WA_TranslucentBackground)) + return d->createDefaultWindowSurface_sys(); + } +#endif + return new QGLWindowSurface(widget); } diff --git a/src/openvg/qpaintengine_vg.cpp b/src/openvg/qpaintengine_vg.cpp index f0f198f..3d50558 100644 --- a/src/openvg/qpaintengine_vg.cpp +++ b/src/openvg/qpaintengine_vg.cpp @@ -2333,6 +2333,7 @@ bool QVGPaintEngine::isDefaultClipRect(const QRect& rect) void QVGPaintEngine::clipEnabledChanged() { #if defined(QVG_SCISSOR_CLIP) + vgSeti(VG_MASKING, VG_FALSE); // disable mask fallback updateScissor(); #else Q_D(QVGPaintEngine); @@ -4018,6 +4019,8 @@ VGImageFormat qt_vg_image_to_vg_format(QImage::Format format) switch (format) { case QImage::Format_MonoLSB: return VG_BW_1; + case QImage::Format_Indexed8: + return VG_sL_8; case QImage::Format_ARGB32_Premultiplied: return VG_sARGB_8888_PRE; case QImage::Format_RGB32: @@ -4028,7 +4031,8 @@ VGImageFormat qt_vg_image_to_vg_format(QImage::Format format) return VG_sRGB_565; case QImage::Format_ARGB4444_Premultiplied: return VG_sARGB_4444; - default: break; + default: + break; } return VG_sARGB_8888; // XXX } diff --git a/src/openvg/qpaintengine_vg_p.h b/src/openvg/qpaintengine_vg_p.h index 85583cc..1e9103b 100644 --- a/src/openvg/qpaintengine_vg_p.h +++ b/src/openvg/qpaintengine_vg_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE -class QFixedPoint; +struct QFixedPoint; class QVGPaintEnginePrivate; class QPixmapData; class QVGEGLWindowSurfacePrivate; diff --git a/src/openvg/qpixmapdata_vg.cpp b/src/openvg/qpixmapdata_vg.cpp index c5da115..3f67c79 100644 --- a/src/openvg/qpixmapdata_vg.cpp +++ b/src/openvg/qpixmapdata_vg.cpp @@ -50,6 +50,7 @@ #include <QBuffer> #include <QImageReader> #include <QtGui/private/qimage_p.h> +#include <QtGui/private/qnativeimagehandleprovider_p.h> QT_BEGIN_NAMESPACE @@ -66,6 +67,10 @@ QVGPixmapData::QVGPixmapData(PixelType type) inImagePool = false; inLRU = false; failedToAlloc = false; +#if defined(Q_OS_SYMBIAN) + nativeImageHandleProvider = 0; + nativeImageHandle = 0; +#endif #if !defined(QT_NO_EGL) context = 0; qt_vg_register_pixmap(this); @@ -98,6 +103,10 @@ void QVGPixmapData::destroyImages() vgImage = VG_INVALID_HANDLE; vgImageOpacity = VG_INVALID_HANDLE; inImagePool = false; + +#if defined(Q_OS_SYMBIAN) + releaseNativeImageHandle(); +#endif } void QVGPixmapData::destroyImageAndContext() @@ -105,6 +114,8 @@ void QVGPixmapData::destroyImageAndContext() if (vgImage != VG_INVALID_HANDLE) { // We need to have a context current to destroy the image. #if !defined(QT_NO_EGL) + if (!context) + context = qt_vg_create_context(0, QInternal::Pixmap); if (context->isCurrent()) { destroyImages(); } else { @@ -118,6 +129,10 @@ void QVGPixmapData::destroyImageAndContext() #else destroyImages(); #endif + } else { +#if defined(Q_OS_SYMBIAN) + releaseNativeImageHandle(); +#endif } #if !defined(QT_NO_EGL) if (context) { @@ -243,10 +258,7 @@ void QVGPixmapData::fill(const QColor &color) { if (!isValid()) return; - - if (source.isNull()) - source = QVolatileImage(w, h, sourceFormat()); - + forceToImage(); if (source.depth() == 1) { // Pick the best approximate color in the image's colortable. int gray = qGray(color.rgba()); @@ -258,13 +270,11 @@ void QVGPixmapData::fill(const QColor &color) } else { source.fill(PREMUL(color.rgba())); } - - // Re-upload the image to VG the next time toVGImage() is called. - recreate = true; } bool QVGPixmapData::hasAlphaChannel() const { + ensureReadback(true); if (!source.isNull()) return source.hasAlphaChannel(); else @@ -273,6 +283,8 @@ bool QVGPixmapData::hasAlphaChannel() const void QVGPixmapData::setAlphaChannel(const QPixmap &alphaChannel) { + if (!isValid()) + return; forceToImage(); source.setAlphaChannel(alphaChannel); } @@ -281,12 +293,11 @@ QImage QVGPixmapData::toImage() const { if (!isValid()) return QImage(); - + ensureReadback(true); if (source.isNull()) { source = QVolatileImage(w, h, sourceFormat()); recreate = true; } - return source.toImage(); } @@ -345,6 +356,12 @@ VGImage QVGPixmapData::toVGImage() else if (recreate) cachedOpacity = -1.0f; // Force opacity image to be refreshed later. +#if defined(Q_OS_SYMBIAN) + if (recreate && nativeImageHandleProvider && !nativeImageHandle) { + createFromNativeImageHandleProvider(); + } +#endif + if (vgImage == VG_INVALID_HANDLE) { vgImage = QVGImagePool::instance()->createImageForPixmap (qt_vg_image_to_vg_format(source.format()), w, h, VG_IMAGE_QUALITY_FASTER, this); @@ -429,9 +446,16 @@ void QVGPixmapData::detachImageFromPool() void QVGPixmapData::hibernate() { - // If the image was imported (e.g, from an SgImage under Symbian), - // then we cannot copy it back to main memory for storage. - if (vgImage != VG_INVALID_HANDLE && source.isNull()) + // If the image was imported (e.g, from an SgImage under Symbian), then + // skip the hibernation, there is no sense in copying it back to main + // memory because the data is most likely shared between several processes. + bool skipHibernate = (vgImage != VG_INVALID_HANDLE && source.isNull()); +#if defined(Q_OS_SYMBIAN) + // However we have to proceed normally if the image was retrieved via + // a handle provider. + skipHibernate &= !nativeImageHandleProvider; +#endif + if (skipHibernate) return; forceToImage(); @@ -476,18 +500,47 @@ int QVGPixmapData::metric(QPaintDevice::PaintDeviceMetric metric) const } } -// Force the pixmap data to be backed by some valid data. +// Ensures that the pixmap is backed by some valid data and forces the data to +// be re-uploaded to the VGImage when toVGImage() is called next time. void QVGPixmapData::forceToImage() { if (!isValid()) return; + ensureReadback(false); + if (source.isNull()) source = QVolatileImage(w, h, sourceFormat()); recreate = true; } +void QVGPixmapData::ensureReadback(bool readOnly) const +{ + if (vgImage != VG_INVALID_HANDLE && source.isNull()) { + source = QVolatileImage(w, h, sourceFormat()); + source.beginDataAccess(); + vgGetImageSubData(vgImage, source.bits(), source.bytesPerLine(), + qt_vg_image_to_vg_format(source.format()), + 0, 0, w, h); + source.endDataAccess(); + if (readOnly) { + recreate = false; + } else { + // Once we did a readback, the original VGImage must be destroyed + // because it may be shared (e.g. created via SgImage) and a subsequent + // upload of the image data may produce unexpected results. + const_cast<QVGPixmapData *>(this)->destroyImages(); +#if defined(Q_OS_SYMBIAN) + // There is now an own copy of the data so drop the handle provider, + // otherwise toVGImage() would request the handle again, which is wrong. + nativeImageHandleProvider = 0; +#endif + recreate = true; + } + } +} + QImage::Format QVGPixmapData::sourceFormat() const { return QImage::Format_ARGB32_Premultiplied; diff --git a/src/openvg/qpixmapdata_vg_p.h b/src/openvg/qpixmapdata_vg_p.h index c4fd47f..15ff889 100644 --- a/src/openvg/qpixmapdata_vg_p.h +++ b/src/openvg/qpixmapdata_vg_p.h @@ -55,7 +55,7 @@ #include <QtGui/private/qpixmap_raster_p.h> #include <QtGui/private/qvolatileimage_p.h> -#include <private/qvg_p.h> +#include "qvg_p.h" #if defined(Q_OS_SYMBIAN) class RSGImage; @@ -76,6 +76,8 @@ void qt_vg_unregister_pixmap(QVGPixmapData *pd); void qt_vg_hibernate_pixmaps(QVGSharedContext *context); #endif +class QNativeImageHandleProvider; + class Q_OPENVG_EXPORT QVGPixmapData : public QPixmapData { public: @@ -126,11 +128,21 @@ public: // VGImage objects to reuse storage. virtual void reclaimImages(); + // If vgImage is valid but source is null, copies pixel data from GPU back + // into main memory and destroys vgImage. For a normal pixmap this function + // does nothing, however if the pixmap was created directly from a VGImage + // (e.g. via SgImage on Symbian) then by doing the readback this ensures + // that QImage-based functions can operate too. + virtual void ensureReadback(bool readOnly) const; + QSize size() const { return QSize(w, h); } #if defined(Q_OS_SYMBIAN) void* toNativeType(NativeType type); void fromNativeType(void* pixmap, NativeType type); + bool initFromNativeImageHandle(void *handle, const QString &type); + void createFromNativeImageHandleProvider(); + void releaseNativeImageHandle(); #endif protected: @@ -170,6 +182,12 @@ protected: mutable QEglContext *context; #endif +#if defined(Q_OS_SYMBIAN) + mutable QNativeImageHandleProvider *nativeImageHandleProvider; + void *nativeImageHandle; + QString nativeImageType; +#endif + void forceToImage(); QImage::Format sourceFormat() const; QImage::Format idealFormat(QImage *image, Qt::ImageConversionFlags flags) const; diff --git a/src/openvg/qvg_symbian.cpp b/src/openvg/qvg_symbian.cpp index 22cbb3c..2924d41 100644 --- a/src/openvg/qvg_symbian.cpp +++ b/src/openvg/qvg_symbian.cpp @@ -41,6 +41,7 @@ #include "qpixmapdata_vg_p.h" #include "qvgfontglyphcache_p.h" +#include <QtGui/private/qnativeimagehandleprovider_p.h> #include <private/qt_s60_p.h> #include <fbs.h> @@ -111,6 +112,58 @@ void QVGPixmapData::cleanup() source = QVolatileImage(); } +bool QVGPixmapData::initFromNativeImageHandle(void *handle, const QString &type) +{ + if (type == QLatin1String("RSgImage")) { + fromNativeType(handle, QPixmapData::SgImage); + return true; + } else if (type == QLatin1String("CFbsBitmap")) { + fromNativeType(handle, QPixmapData::FbsBitmap); + return true; + } + return false; +} + +void QVGPixmapData::createFromNativeImageHandleProvider() +{ + void *handle = 0; + QString type; + nativeImageHandleProvider->get(&handle, &type); + if (handle) { + if (initFromNativeImageHandle(handle, type)) { + nativeImageHandle = handle; + nativeImageType = type; + } else { + qWarning("QVGPixmapData: Unknown native image type '%s'", qPrintable(type)); + } + } else { + qWarning("QVGPixmapData: Native handle is null"); + } +} + +void QVGPixmapData::releaseNativeImageHandle() +{ + if (nativeImageHandleProvider && nativeImageHandle) { + nativeImageHandleProvider->release(nativeImageHandle, nativeImageType); + nativeImageHandle = 0; + nativeImageType = QString(); + } +} + +static inline bool conversionLessFormat(QImage::Format format) +{ + switch (format) { + case QImage::Format_RGB16: // EColor64K + case QImage::Format_RGB32: // EColor16MU + case QImage::Format_ARGB32: // EColor16MA + case QImage::Format_ARGB32_Premultiplied: // EColor16MAP + case QImage::Format_Indexed8: // EGray256, EColor256 + return true; + default: + return false; + } +} + void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) { if (type == QPixmapData::SgImage && pixmap) { @@ -127,7 +180,7 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) } is_null = (w <= 0 || h <= 0); - source = QVolatileImage(); // vgGetImageSubData() some day? + source = QVolatileImage(); // readback will be done later, only when needed recreate = false; prevSize = QSize(w, h); updateSerial(); @@ -139,9 +192,11 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) source = QVolatileImage(bitmap); // duplicates only, if possible if (source.isNull()) return; - // Here we may need to copy if the formats do not match. - // (e.g. for display modes other than EColor16MAP and EColor16MU) - source.ensureFormat(idealFormat(&source.imageRef(), Qt::AutoColor)); + if (!conversionLessFormat(source.format())) { + // Here we may need to copy if the formats do not match. + // (e.g. for display modes other than EColor16MAP and EColor16MU) + source.ensureFormat(idealFormat(&source.imageRef(), Qt::AutoColor)); + } recreate = true; } else if (type == QPixmapData::VolatileImage && pixmap) { QVolatileImage *img = static_cast<QVolatileImage *>(pixmap); @@ -149,6 +204,11 @@ void QVGPixmapData::fromNativeType(void* pixmap, NativeType type) source = *img; source.ensureFormat(idealFormat(&source.imageRef(), Qt::AutoColor)); recreate = true; + } else if (type == QPixmapData::NativeImageHandleProvider && pixmap) { + destroyImages(); + nativeImageHandleProvider = static_cast<QNativeImageHandleProvider *>(pixmap); + // Cannot defer the retrieval, we need at least the size right away. + createFromNativeImageHandleProvider(); } } @@ -216,6 +276,7 @@ void* QVGPixmapData::toNativeType(NativeType type) return reinterpret_cast<void*>(sgImage.take()); #endif } else if (type == QPixmapData::FbsBitmap && isValid()) { + ensureReadback(true); if (source.isNull()) { source = QVolatileImage(w, h, sourceFormat()); } diff --git a/src/openvg/qwindowsurface_vgegl.cpp b/src/openvg/qwindowsurface_vgegl.cpp index ca80886..866453f 100644 --- a/src/openvg/qwindowsurface_vgegl.cpp +++ b/src/openvg/qwindowsurface_vgegl.cpp @@ -768,6 +768,11 @@ bool QVGEGLWindowSurfaceDirect::scroll(QWidget *widget, const QRegion& area, int context->lazyDoneCurrent(); return true; } +#else + Q_UNUSED(widget); + Q_UNUSED(area); + Q_UNUSED(dx); + Q_UNUSED(dy); #endif return false; } diff --git a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp index 1da58e1..4b4f677 100644 --- a/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp +++ b/src/plugins/graphicssystems/openvg/qgraphicssystem_vg.cpp @@ -43,7 +43,7 @@ #include <QtOpenVG/private/qpixmapdata_vg_p.h> #include <QtOpenVG/private/qwindowsurface_vg_p.h> #include <QtOpenVG/private/qvgimagepool_p.h> -#if defined(Q_OS_SYMBIAN) && !defined(Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE) +#if defined(Q_OS_SYMBIAN) #include <QtGui/private/qwidget_p.h> #endif #include <QtGui/private/qapplication_p.h> @@ -70,10 +70,12 @@ QPixmapData *QVGGraphicsSystem::createPixmapData(QPixmapData::PixelType type) co QWindowSurface *QVGGraphicsSystem::createWindowSurface(QWidget *widget) const { -#if defined(Q_OS_SYMBIAN) && !defined(Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE) - QWidgetPrivate *d = qt_widget_private(widget); - if (!d->isOpaque && widget->testAttribute(Qt::WA_TranslucentBackground)) - return d->createDefaultWindowSurface_sys(); +#if defined(Q_OS_SYMBIAN) + if (!QApplicationPrivate::instance()->useTranslucentEGLSurfaces) { + QWidgetPrivate *d = qt_widget_private(widget); + if (!d->isOpaque && widget->testAttribute(Qt::WA_TranslucentBackground)) + return d->createDefaultWindowSurface_sys(); + } #endif return new QVGWindowSurface(widget); } diff --git a/src/s60installs/bwins/QtDeclarativeu.def b/src/s60installs/bwins/QtDeclarativeu.def index 8b23b63..5490f0d 100644 --- a/src/s60installs/bwins/QtDeclarativeu.def +++ b/src/s60installs/bwins/QtDeclarativeu.def @@ -1888,4 +1888,9 @@ EXPORTS ??1QDeclarativeRefCount@@UAE@XZ @ 1887 NONAME ; QDeclarativeRefCount::~QDeclarativeRefCount(void) ?addref@QDeclarativeRefCount@@QAEXXZ @ 1888 NONAME ; void QDeclarativeRefCount::addref(void) ?release@QDeclarativeRefCount@@QAEXXZ @ 1889 NONAME ; void QDeclarativeRefCount::release(void) + ?resolveLayoutMirror@QDeclarativeItemPrivate@@QAEXXZ @ 1890 NONAME ; void QDeclarativeItemPrivate::resolveLayoutMirror(void) + ?mirrorChange@QDeclarativeItemPrivate@@UAEXXZ @ 1891 NONAME ; void QDeclarativeItemPrivate::mirrorChange(void) + ?setLayoutMirror@QDeclarativeItemPrivate@@QAEX_N@Z @ 1892 NONAME ; void QDeclarativeItemPrivate::setLayoutMirror(bool) + ?setImplicitLayoutMirror@QDeclarativeItemPrivate@@QAEX_N0@Z @ 1893 NONAME ; void QDeclarativeItemPrivate::setImplicitLayoutMirror(bool, bool) + ?isMirrored@QDeclarativeItemPrivate@@QBE_NXZ @ 1894 NONAME ; bool QDeclarativeItemPrivate::isMirrored(void) const diff --git a/src/s60installs/bwins/QtOpenVGu.def b/src/s60installs/bwins/QtOpenVGu.def index 4767d93..18f576b 100644 --- a/src/s60installs/bwins/QtOpenVGu.def +++ b/src/s60installs/bwins/QtOpenVGu.def @@ -179,4 +179,8 @@ EXPORTS ?updateSerial@QVGPixmapData@@IAEXXZ @ 178 NONAME ; void QVGPixmapData::updateSerial(void) ?copy@QVGPixmapData@@UAEXPBVQPixmapData@@ABVQRect@@@Z @ 179 NONAME ; void QVGPixmapData::copy(class QPixmapData const *, class QRect const &) ?idealFormat@QVGPixmapData@@IBE?AW4Format@QImage@@PAV3@V?$QFlags@W4ImageConversionFlag@Qt@@@@@Z @ 180 NONAME ; enum QImage::Format QVGPixmapData::idealFormat(class QImage *, class QFlags<enum Qt::ImageConversionFlag>) const + ?ensureReadback@QVGPixmapData@@UBEX_N@Z @ 181 NONAME ; void QVGPixmapData::ensureReadback(bool) const + ?initFromNativeImageHandle@QVGPixmapData@@QAE_NPAXABVQString@@@Z @ 182 NONAME ; bool QVGPixmapData::initFromNativeImageHandle(void *, class QString const &) + ?createFromNativeImageHandleProvider@QVGPixmapData@@QAEXXZ @ 183 NONAME ; void QVGPixmapData::createFromNativeImageHandleProvider(void) + ?releaseNativeImageHandle@QVGPixmapData@@QAEXXZ @ 184 NONAME ; void QVGPixmapData::releaseNativeImageHandle(void) diff --git a/src/s60installs/eabi/QtDeclarativeu.def b/src/s60installs/eabi/QtDeclarativeu.def index 130e2d5..2849068 100644 --- a/src/s60installs/eabi/QtDeclarativeu.def +++ b/src/s60installs/eabi/QtDeclarativeu.def @@ -1930,4 +1930,7 @@ EXPORTS _ZN20QDeclarativeRefCountD2Ev @ 1929 NONAME _ZTI20QDeclarativeRefCount @ 1930 NONAME _ZTV20QDeclarativeRefCount @ 1931 NONAME + _ZN23QDeclarativeItemPrivate15setLayoutMirrorEb @ 1932 NONAME + _ZN23QDeclarativeItemPrivate19resolveLayoutMirrorEv @ 1933 NONAME + _ZN23QDeclarativeItemPrivate23setImplicitLayoutMirrorEbb @ 1934 NONAME diff --git a/src/s60installs/eabi/QtOpenVGu.def b/src/s60installs/eabi/QtOpenVGu.def index 40aac8a..25c53b8 100644 --- a/src/s60installs/eabi/QtOpenVGu.def +++ b/src/s60installs/eabi/QtOpenVGu.def @@ -209,4 +209,8 @@ EXPORTS _ZN13QVGPixmapData12updateSerialEv @ 208 NONAME _ZN13QVGPixmapData4copyEPK11QPixmapDataRK5QRect @ 209 NONAME _ZNK13QVGPixmapData11idealFormatEP6QImage6QFlagsIN2Qt19ImageConversionFlagEE @ 210 NONAME + _ZNK13QVGPixmapData14ensureReadbackEb @ 211 NONAME + _ZN13QVGPixmapData24releaseNativeImageHandleEv @ 212 NONAME + _ZN13QVGPixmapData25initFromNativeImageHandleEPvRK7QString @ 213 NONAME + _ZN13QVGPixmapData35createFromNativeImageHandleProviderEv @ 214 NONAME |