diff options
118 files changed, 6563 insertions, 718 deletions
diff --git a/dist/changes-4.7.3 b/dist/changes-4.7.3 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/dist/changes-4.7.3 diff --git a/doc/src/snippets/declarative/layoutmirroring.qml b/doc/src/snippets/declarative/layoutmirroring.qml new file mode 100644 index 0000000..23eecd6 --- /dev/null +++ b/doc/src/snippets/declarative/layoutmirroring.qml @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +//![0] +import QtQuick 1.1 + +Rectangle { + LayoutMirroring.enabled: true + LayoutMirroring.childrenInherit: true + width: 240; height: 50 + Row { + anchors { left: parent.left; margins: 5 } + y: 5; spacing: 5 + Repeater { + model: 5 + Rectangle { + color: "red" + opacity: (5-index) / 5 + width: 40; height: 40 + Text { + text: index+1 + anchors.centerIn: parent + } + } + } + } +} +//![0] diff --git a/doc/src/snippets/declarative/states/statechangescript.qml b/doc/src/snippets/declarative/states/statechangescript.qml index b885137..f490a97 100644 --- a/doc/src/snippets/declarative/states/statechangescript.qml +++ b/doc/src/snippets/declarative/states/statechangescript.qml @@ -37,6 +37,7 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ +import QtQuick 1.0 Item { //! [state and transition] diff --git a/examples/declarative/positioners/layoutdirection/layoutdirection.qml b/examples/declarative/positioners/layoutdirection/layoutdirection.qml index 3e23b15..080010e 100644 --- a/examples/declarative/positioners/layoutdirection/layoutdirection.qml +++ b/examples/declarative/positioners/layoutdirection/layoutdirection.qml @@ -41,10 +41,13 @@ import QtQuick 1.1 Rectangle { + property bool mirror + property int direction: Qt.application.layoutDirection + LayoutMirroring.enabled: mirror + LayoutMirroring.childrenInherit: true width: column.width + 100 height: column.height + 100 - property int direction: Qt.application.layoutDirection Column { id: column @@ -133,6 +136,23 @@ Rectangle { anchors.fill: parent } } + Rectangle { + height: 50; width: parent.width + color: mouseArea2.pressed ? "black" : "gray" + Text { + text: mirror ? "Mirrored" : "Normal" + color: "white" + font.pixelSize: 16 + anchors.centerIn: parent + } + MouseArea { + id: mouseArea2 + onClicked: { + mirror = !mirror; + } + anchors.fill: parent + } + } } Component { diff --git a/mkspecs/common/symbian/symbian.conf b/mkspecs/common/symbian/symbian.conf index 63db511..e3813a4 100644 --- a/mkspecs/common/symbian/symbian.conf +++ b/mkspecs/common/symbian/symbian.conf @@ -230,16 +230,41 @@ defineReplace(symbianRemoveSpecialCharacters) { # Determines translations that are Symbian supported defineTest(matchSymbianLanguages) { + # Qt language codes for which we need to parse .ts file SYMBIAN_MATCHED_LANGUAGES = + # List of translation files for matched languages SYMBIAN_MATCHED_TRANSLATIONS = + # List of Qt language codes for which we have no mapped Symbian code but we have a fallback code + # and therefore need to generate a mapping for in localize_deployment.prf. + # The fallback code means plain language code for languages that have both language and country codes. + # E.g. the fallback code for language "zh_CN" would be "zh". + SYMBIAN_UNMAPPED_LANGUAGES = + # List of handled Qt language codes to avoid duplicate Symbian language codes in case both + # unmapped language+country combination and its fallback code, or multiple unmapped language+country + # combinations that have same fallback code are included. + HANDLED_LANGUAGES = # Cannot parse .ts file for language here, so detect it from filename. # Allow two and three character language and country codes. for(translation, TRANSLATIONS) { language = $$replace(translation, "^(.*/)?[^/]+_(([^_]{2,3}_)?[^_]{2,3})\\.ts$", \\2) - contains(SYMBIAN_SUPPORTED_LANGUAGES, $$language) { - SYMBIAN_MATCHED_LANGUAGES += $$language - SYMBIAN_MATCHED_TRANSLATIONS += $$_PRO_FILE_PWD_/$$translation + !contains(HANDLED_LANGUAGES, $$language) { + HANDLED_LANGUAGES += $$language + contains(SYMBIAN_SUPPORTED_LANGUAGES, $$language) { + SYMBIAN_MATCHED_LANGUAGES += $$language + SYMBIAN_MATCHED_TRANSLATIONS += $$_PRO_FILE_PWD_/$$translation + } else { + # No direct mapping for specified language found. Check if a fallback language code can be used. + strippedLanguage = $$replace(language, "_.*$",) + contains(SYMBIAN_SUPPORTED_LANGUAGES, $$strippedLanguage):!contains(HANDLED_LANGUAGES, $$strippedLanguage) { + HANDLED_LANGUAGES += $$strippedLanguage + SYMBIAN_UNMAPPED_LANGUAGES += $$language + SYMBIAN_MATCHED_LANGUAGES += $$language + SYMBIAN_MATCHED_TRANSLATIONS += $$translation + SYMBIAN_LANGUAGE_FALLBACK.$$language = $$strippedLanguage + export(SYMBIAN_LANGUAGE_FALLBACK.$$language) + } + } } } @@ -247,6 +272,7 @@ defineTest(matchSymbianLanguages) { export(SYMBIAN_MATCHED_LANGUAGES) export(SYMBIAN_MATCHED_TRANSLATIONS) + export(SYMBIAN_UNMAPPED_LANGUAGES) } # Symbian pkg files that define multiple languages require a language specific string to be diff --git a/mkspecs/features/symbian/localize_deployment.prf b/mkspecs/features/symbian/localize_deployment.prf index 26a254b..185c713 100644 --- a/mkspecs/features/symbian/localize_deployment.prf +++ b/mkspecs/features/symbian/localize_deployment.prf @@ -101,6 +101,15 @@ isEmpty(SYMBIAN_MATCHED_LANGUAGES) { matchSymbianLanguages() } +# If there are translations that do not have Symbian language code defined for that exact +# language + country combination, but have Symbian language code defined for just the language, +# map the language + country combination to the same value as the plain language. +for(language, SYMBIAN_UNMAPPED_LANGUAGES) { + languageVar = SYMBIAN_LANG.$${language} + fallbackLanguageVar = SYMBIAN_LANG.$$eval(SYMBIAN_LANGUAGE_FALLBACK.$$language) + $$languageVar = $$eval($$fallbackLanguageVar) +} + !isEmpty(SYMBIAN_MATCHED_TRANSLATIONS) { # Generate dependencies to .ts files for pkg files template_pkg_target.depends += $$SYMBIAN_MATCHED_TRANSLATIONS diff --git a/mkspecs/symbian-sbsv2/flm/qt/qmake_clean.flm b/mkspecs/symbian-sbsv2/flm/qt/qmake_clean.flm index fe35e6e..c9a88fc 100644 --- a/mkspecs/symbian-sbsv2/flm/qt/qmake_clean.flm +++ b/mkspecs/symbian-sbsv2/flm/qt/qmake_clean.flm @@ -13,6 +13,6 @@ SINGLETON:=$(call sanitise,QMAKE_CLEAN_SINGLETON_$(EXTENSION_ROOT)) ifeq ($($(SINGLETON)),) # Prevent duplicate targets from being created $(SINGLETON):=1 -$(eval $(call GenerateStandardCleanTarget,$(CLEAN_FILES),'')) +$(eval $(call GenerateStandardCleanTarget,$(wildcard $(patsubst "%",%,$(CLEAN_FILES))))) endif diff --git a/qmake/generators/symbian/symmake_sbsv2.cpp b/qmake/generators/symbian/symmake_sbsv2.cpp index f94a63f..0fdef86 100644 --- a/qmake/generators/symbian/symmake_sbsv2.cpp +++ b/qmake/generators/symbian/symmake_sbsv2.cpp @@ -727,7 +727,10 @@ void SymbianSbsv2MakefileGenerator::writeBldInfExtensionRulesPart(QTextStream& t QStringList absoluteCleanFiles; foreach (QString cleanFile, cleanFiles) { QFileInfo fi(cleanFile); - absoluteCleanFiles << fi.absoluteFilePath(); + QString fileName = QLatin1String("\""); + fileName.append(fi.absoluteFilePath()); + fileName.append(QLatin1String("\"")); + absoluteCleanFiles << fileName; } t << "START EXTENSION qt/qmake_clean" << endl; t << "OPTION CLEAN_FILES " << absoluteCleanFiles.join(" ") << endl; 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 diff --git a/tests/auto/declarative/qdeclarativeanchors/data/fill.qml b/tests/auto/declarative/qdeclarativeanchors/data/fill.qml index 50fbbe0..ff19675 100644 --- a/tests/auto/declarative/qdeclarativeanchors/data/fill.qml +++ b/tests/auto/declarative/qdeclarativeanchors/data/fill.qml @@ -6,9 +6,9 @@ Rectangle { objectName: "filler" width: 50; height: 50; color: "blue" anchors.fill: parent; - anchors.leftMargin: 10; - anchors.rightMargin: 20; - anchors.topMargin: 30; - anchors.bottomMargin: 40; + anchors.leftMargin: 10; + anchors.rightMargin: 20; + anchors.topMargin: 30; + anchors.bottomMargin: 40; } } diff --git a/tests/auto/declarative/qdeclarativeanchors/data/margins.qml b/tests/auto/declarative/qdeclarativeanchors/data/margins.qml index dace9c0..685346a 100644 --- a/tests/auto/declarative/qdeclarativeanchors/data/margins.qml +++ b/tests/auto/declarative/qdeclarativeanchors/data/margins.qml @@ -6,8 +6,8 @@ Rectangle { objectName: "filler" width: 50; height: 50; color: "blue" anchors.fill: parent; - anchors.margins: 10 - anchors.leftMargin: 5 - anchors.topMargin: 6 + anchors.margins: 10 + anchors.leftMargin: 5 + anchors.topMargin: 6 } } diff --git a/tests/auto/declarative/qdeclarativeanchors/tst_qdeclarativeanchors.cpp b/tests/auto/declarative/qdeclarativeanchors/tst_qdeclarativeanchors.cpp index e880857..0442350 100644 --- a/tests/auto/declarative/qdeclarativeanchors/tst_qdeclarativeanchors.cpp +++ b/tests/auto/declarative/qdeclarativeanchors/tst_qdeclarativeanchors.cpp @@ -65,13 +65,10 @@ class tst_qdeclarativeanchors : public QObject public: tst_qdeclarativeanchors() {} - template<typename T> - T *findItem(QGraphicsObject *parent, const QString &id); - QGraphicsObject *findObject(QGraphicsObject *parent, const QString &objectName); - private slots: void basicAnchors(); void basicAnchorsQGraphicsWidget(); + void basicAnchorsRTL(); void loops(); void illegalSets(); void illegalSets_data(); @@ -82,16 +79,20 @@ private slots: void nullItem_data(); void crash1(); void centerIn(); + void centerInRTL(); void hvCenter(); + void hvCenterRTL(); void fill(); + void fillRTL(); void margins(); + void marginsRTL(); }; /* Find an item with the specified id. */ template<typename T> -T *tst_qdeclarativeanchors::findItem(QGraphicsObject *parent, const QString &objectName) +T *findItem(QGraphicsObject *parent, const QString &objectName) { const QMetaObject &mo = T::staticMetaObject; QList<QGraphicsItem *> children = parent->childItems(); @@ -110,7 +111,7 @@ T *tst_qdeclarativeanchors::findItem(QGraphicsObject *parent, const QString &obj return 0; } -QGraphicsObject *tst_qdeclarativeanchors::findObject(QGraphicsObject *parent, const QString &objectName) +QGraphicsObject *findObject(QGraphicsObject *parent, const QString &objectName) { QList<QGraphicsItem *> children = parent->childItems(); for (int i = 0; i < children.count(); ++i) { @@ -263,6 +264,105 @@ void tst_qdeclarativeanchors::basicAnchorsQGraphicsWidget() delete view; } +QDeclarativeItem* childItem(QDeclarativeItem *parentItem, const char * itemString) { + return findItem<QDeclarativeItem>(parentItem, QLatin1String(itemString)); +} + +qreal offsetMasterRTL(QDeclarativeItem *rootItem, const char * itemString) { + QDeclarativeItem* masterItem = findItem<QDeclarativeItem>(rootItem, QLatin1String("masterRect")); + return masterItem->width()+2*masterItem->x()-findItem<QDeclarativeItem>(rootItem, QLatin1String(itemString))->width(); +} + +qreal offsetParentRTL(QDeclarativeItem *rootItem, const char * itemString) { + return rootItem->width()+2*rootItem->x()-findItem<QDeclarativeItem>(rootItem, QLatin1String(itemString))->width(); +} + +void mirrorAnchors(QDeclarativeItem *item) { + QDeclarativeItemPrivate *itemPrivate = QDeclarativeItemPrivate::get(item); + itemPrivate->setLayoutMirror(true); +} + +void tst_qdeclarativeanchors::basicAnchorsRTL() +{ + QDeclarativeView *view = new QDeclarativeView; + view->setSource(QUrl::fromLocalFile(SRCDIR "/data/anchors.qml")); + + qApp->processEvents(); + + QDeclarativeItem* rootItem = qobject_cast<QDeclarativeItem*>(view->rootObject()); + foreach(QObject *child, rootItem->children()) { + bool mirrored = QDeclarativeItemPrivate::get(qobject_cast<QDeclarativeItem*>(child))->anchors()->property("mirrored").toBool(); + QCOMPARE(mirrored, false); + } + + foreach(QObject *child, rootItem->children()) + mirrorAnchors(qobject_cast<QDeclarativeItem*>(child)); + + foreach(QObject *child, rootItem->children()) { + bool mirrored = QDeclarativeItemPrivate::get(qobject_cast<QDeclarativeItem*>(child))->anchors()->property("mirrored").toBool(); + QCOMPARE(mirrored, true); + } + + //sibling horizontal + QCOMPARE(childItem(rootItem, "rect1")->x(), offsetMasterRTL(rootItem, "rect1")-26.0); + QCOMPARE(childItem(rootItem, "rect2")->x(), offsetMasterRTL(rootItem, "rect2")-122.0); + QCOMPARE(childItem(rootItem, "rect3")->x(), offsetMasterRTL(rootItem, "rect3")-74.0); + QCOMPARE(childItem(rootItem, "rect4")->x(), offsetMasterRTL(rootItem, "rect4")-16.0); + QCOMPARE(childItem(rootItem, "rect5")->x(), offsetMasterRTL(rootItem, "rect5")-112.0); + QCOMPARE(childItem(rootItem, "rect6")->x(), offsetMasterRTL(rootItem, "rect6")-64.0); + + //parent horizontal + QCOMPARE(childItem(rootItem, "rect7")->x(), offsetParentRTL(rootItem, "rect7")-0.0); + QCOMPARE(childItem(rootItem, "rect8")->x(), offsetParentRTL(rootItem, "rect8")-240.0); + QCOMPARE(childItem(rootItem, "rect9")->x(), offsetParentRTL(rootItem, "rect9")-120.0); + QCOMPARE(childItem(rootItem, "rect10")->x(), offsetParentRTL(rootItem, "rect10")+10.0); + QCOMPARE(childItem(rootItem, "rect11")->x(), offsetParentRTL(rootItem, "rect11")-230.0); + QCOMPARE(childItem(rootItem, "rect12")->x(), offsetParentRTL(rootItem, "rect12")-110.0); + + //vertical + QCOMPARE(childItem(rootItem, "rect13")->y(), 20.0); + QCOMPARE(childItem(rootItem, "rect14")->y(), 155.0); + + //stretch + QCOMPARE(childItem(rootItem, "rect15")->x(), offsetMasterRTL(rootItem, "rect15")-26.0); + QCOMPARE(childItem(rootItem, "rect15")->width(), 96.0); + QCOMPARE(childItem(rootItem, "rect16")->x(), offsetMasterRTL(rootItem, "rect16")-26.0); + QCOMPARE(childItem(rootItem, "rect16")->width(), 192.0); + QCOMPARE(childItem(rootItem, "rect17")->x(), offsetMasterRTL(rootItem, "rect17")+70.0); + QCOMPARE(childItem(rootItem, "rect17")->width(), 192.0); + + //vertical stretch + QCOMPARE(childItem(rootItem, "rect18")->y(), 20.0); + QCOMPARE(childItem(rootItem, "rect18")->height(), 40.0); + + //more parent horizontal + QCOMPARE(childItem(rootItem, "rect19")->x(), offsetParentRTL(rootItem, "rect19")-115.0); + QCOMPARE(childItem(rootItem, "rect20")->x(), offsetParentRTL(rootItem, "rect20")-235.0); + QCOMPARE(childItem(rootItem, "rect21")->x(), offsetParentRTL(rootItem, "rect21")+5.0); + + //centerIn + QCOMPARE(childItem(rootItem, "rect22")->x(), offsetMasterRTL(rootItem, "rect22")-69.0); + QCOMPARE(childItem(rootItem, "rect22")->y(), 5.0); + + //margins + QCOMPARE(childItem(rootItem, "rect23")->x(), offsetMasterRTL(rootItem, "rect23")-31.0); + QCOMPARE(childItem(rootItem, "rect23")->y(), 5.0); + QCOMPARE(childItem(rootItem, "rect23")->width(), 86.0); + QCOMPARE(childItem(rootItem, "rect23")->height(), 10.0); + + // offsets + QCOMPARE(childItem(rootItem, "rect24")->x(), offsetMasterRTL(rootItem, "rect24")-26.0); + QCOMPARE(childItem(rootItem, "rect25")->y(), 60.0); + QCOMPARE(childItem(rootItem, "rect26")->y(), 5.0); + + //baseline + QDeclarativeText *text1 = findItem<QDeclarativeText>(rootItem, QLatin1String("text1")); + QDeclarativeText *text2 = findItem<QDeclarativeText>(rootItem, QLatin1String("text2")); + QCOMPARE(text1->y(), text2->y()); + + delete view; +} + // mostly testing that we don't crash void tst_qdeclarativeanchors::loops() { @@ -514,6 +614,31 @@ void tst_qdeclarativeanchors::fill() delete view; } +void tst_qdeclarativeanchors::fillRTL() +{ + QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/fill.qml")); + + qApp->processEvents(); + QDeclarativeRectangle* rect = findItem<QDeclarativeRectangle>(view->rootObject(), QLatin1String("filler")); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + mirrorAnchors(rect); + + QCOMPARE(rect->x(), 0.0 + 20.0); + QCOMPARE(rect->y(), 0.0 + 30.0); + QCOMPARE(rect->width(), 200.0 - 10.0 - 20.0); + QCOMPARE(rect->height(), 200.0 - 30.0 - 40.0); + //Alter Offsets (tests QTBUG-6631) + rectPrivate->anchors()->setLeftMargin(20.0); + rectPrivate->anchors()->setRightMargin(0.0); + rectPrivate->anchors()->setBottomMargin(0.0); + rectPrivate->anchors()->setTopMargin(10.0); + QCOMPARE(rect->x(), 0.0 + 0.0); + QCOMPARE(rect->y(), 0.0 + 10.0); + QCOMPARE(rect->width(), 200.0 - 20.0); + QCOMPARE(rect->height(), 200.0 - 10.0); + + delete view; +} void tst_qdeclarativeanchors::centerIn() { QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/centerin.qml")); @@ -521,6 +646,7 @@ void tst_qdeclarativeanchors::centerIn() qApp->processEvents(); QDeclarativeRectangle* rect = findItem<QDeclarativeRectangle>(view->rootObject(), QLatin1String("centered")); QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + QCOMPARE(rect->x(), 75.0 + 10); QCOMPARE(rect->y(), 75.0 + 30); //Alter Offsets (tests QTBUG-6631) @@ -532,6 +658,27 @@ void tst_qdeclarativeanchors::centerIn() delete view; } + +void tst_qdeclarativeanchors::centerInRTL() +{ + QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/centerin.qml")); + + qApp->processEvents(); + QDeclarativeRectangle* rect = findItem<QDeclarativeRectangle>(view->rootObject(), QLatin1String("centered")); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + mirrorAnchors(rect); + + QCOMPARE(rect->x(), 75.0 - 10); + QCOMPARE(rect->y(), 75.0 + 30); + //Alter Offsets (tests QTBUG-6631) + rectPrivate->anchors()->setHorizontalCenterOffset(-20.0); + rectPrivate->anchors()->setVerticalCenterOffset(-10.0); + QCOMPARE(rect->x(), 75.0 + 20.0); + QCOMPARE(rect->y(), 75.0 - 10.0); + + delete view; +} + void tst_qdeclarativeanchors::hvCenter() { QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/hvCenter.qml")); @@ -539,12 +686,39 @@ void tst_qdeclarativeanchors::hvCenter() qApp->processEvents(); QDeclarativeRectangle* rect = findItem<QDeclarativeRectangle>(view->rootObject(), QLatin1String("centered")); QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + // test QTBUG-10999 QCOMPARE(rect->x(), 10.0); QCOMPARE(rect->y(), 19.0); + + rectPrivate->anchors()->setHorizontalCenterOffset(-5.0); + rectPrivate->anchors()->setVerticalCenterOffset(5.0); + QCOMPARE(rect->x(), 10.0 - 5.0); + QCOMPARE(rect->y(), 19.0 + 5.0); + delete view; } +void tst_qdeclarativeanchors::hvCenterRTL() +{ + QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/hvCenter.qml")); + + qApp->processEvents(); + QDeclarativeRectangle* rect = findItem<QDeclarativeRectangle>(view->rootObject(), QLatin1String("centered")); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + mirrorAnchors(rect); + + // test QTBUG-10999 + QCOMPARE(rect->x(), 10.0); + QCOMPARE(rect->y(), 19.0); + + rectPrivate->anchors()->setHorizontalCenterOffset(-5.0); + rectPrivate->anchors()->setVerticalCenterOffset(5.0); + QCOMPARE(rect->x(), 10.0 + 5.0); + QCOMPARE(rect->y(), 19.0 + 5.0); + + delete view; +} void tst_qdeclarativeanchors::margins() { QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/margins.qml")); @@ -568,6 +742,31 @@ void tst_qdeclarativeanchors::margins() delete view; } +void tst_qdeclarativeanchors::marginsRTL() +{ + QDeclarativeView *view = new QDeclarativeView(QUrl::fromLocalFile(SRCDIR "/data/margins.qml")); + + QDeclarativeRectangle* rect = findItem<QDeclarativeRectangle>(view->rootObject(), QLatin1String("filler")); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + mirrorAnchors(rect); + + QCOMPARE(rect->x(), 10.0); + QCOMPARE(rect->y(), 6.0); + QCOMPARE(rect->width(), 200.0 - 5.0 - 10.0); + QCOMPARE(rect->height(), 200.0 - 6.0 - 10.0); + + rectPrivate->anchors()->setTopMargin(0.0); + rectPrivate->anchors()->setMargins(20.0); + + QCOMPARE(rect->x(), 20.0); + QCOMPARE(rect->y(), 20.0); + QCOMPARE(rect->width(), 200.0 - 5.0 - 20.0); + QCOMPARE(rect->height(), 200.0 - 20.0 - 20.0); + + delete view; +} + + QTEST_MAIN(tst_qdeclarativeanchors) #include "tst_qdeclarativeanchors.moc" diff --git a/tests/auto/declarative/qdeclarativeanimatedimage/tst_qdeclarativeanimatedimage.cpp b/tests/auto/declarative/qdeclarativeanimatedimage/tst_qdeclarativeanimatedimage.cpp index 104ee15..7d1b807 100644 --- a/tests/auto/declarative/qdeclarativeanimatedimage/tst_qdeclarativeanimatedimage.cpp +++ b/tests/auto/declarative/qdeclarativeanimatedimage/tst_qdeclarativeanimatedimage.cpp @@ -46,6 +46,7 @@ #include <private/qdeclarativeimage_p.h> #include <private/qdeclarativeanimatedimage_p.h> #include <QSignalSpy> +#include <QtDeclarative/qdeclarativecontext.h> #include "../shared/testhttpserver.h" #include "../../../shared/util.h" @@ -76,6 +77,7 @@ private slots: void sourceSizeReadOnly(); void invalidSource(); void qtbug_16520(); + void progressAndStatusChanges(); private: QPixmap grabScene(QGraphicsScene *scene, int width, int height); @@ -333,6 +335,53 @@ void tst_qdeclarativeanimatedimage::qtbug_16520() delete anim; } +void tst_qdeclarativeanimatedimage::progressAndStatusChanges() +{ + TestHTTPServer server(14449); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data"); + + QDeclarativeEngine engine; + QString componentStr = "import QtQuick 1.0\nAnimatedImage { source: srcImage }"; + QDeclarativeContext *ctxt = engine.rootContext(); + ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/stickman.gif")); + QDeclarativeComponent component(&engine); + component.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeImage *obj = qobject_cast<QDeclarativeImage*>(component.create()); + QVERIFY(obj != 0); + QVERIFY(obj->status() == QDeclarativeImage::Ready); + QTRY_VERIFY(obj->progress() == 1.0); + + QSignalSpy sourceSpy(obj, SIGNAL(sourceChanged(const QUrl &))); + QSignalSpy progressSpy(obj, SIGNAL(progressChanged(qreal))); + QSignalSpy statusSpy(obj, SIGNAL(statusChanged(QDeclarativeImageBase::Status))); + + // Loading local file + ctxt->setContextProperty("srcImage", QUrl::fromLocalFile(SRCDIR "/data/colors.gif")); + QTRY_VERIFY(obj->status() == QDeclarativeImage::Ready); + QTRY_VERIFY(obj->progress() == 1.0); + QTRY_COMPARE(sourceSpy.count(), 1); + QTRY_COMPARE(progressSpy.count(), 0); + QTRY_COMPARE(statusSpy.count(), 0); + + // Loading remote file + ctxt->setContextProperty("srcImage", "http://127.0.0.1:14449/stickman.gif"); + QTRY_VERIFY(obj->status() == QDeclarativeImage::Loading); + QTRY_VERIFY(obj->progress() == 0.0); + QTRY_VERIFY(obj->status() == QDeclarativeImage::Ready); + QTRY_VERIFY(obj->progress() == 1.0); + QTRY_COMPARE(sourceSpy.count(), 2); + QTRY_VERIFY(progressSpy.count() > 1); + QTRY_COMPARE(statusSpy.count(), 2); + + ctxt->setContextProperty("srcImage", ""); + QTRY_VERIFY(obj->status() == QDeclarativeImage::Null); + QTRY_VERIFY(obj->progress() == 0.0); + QTRY_COMPARE(sourceSpy.count(), 3); + QTRY_VERIFY(progressSpy.count() > 2); + QTRY_COMPARE(statusSpy.count(), 3); +} + QTEST_MAIN(tst_qdeclarativeanimatedimage) #include "tst_qdeclarativeanimatedimage.moc" diff --git a/tests/auto/declarative/qdeclarativegridview/data/gridview-enforcerange.qml b/tests/auto/declarative/qdeclarativegridview/data/gridview-enforcerange.qml index 5719f43..69eaa47 100644 --- a/tests/auto/declarative/qdeclarativegridview/data/gridview-enforcerange.qml +++ b/tests/auto/declarative/qdeclarativegridview/data/gridview-enforcerange.qml @@ -1,4 +1,4 @@ -import QtQuick 1.0 +import QtQuick 1.1 Rectangle { width: 240 @@ -48,6 +48,8 @@ Rectangle { model: testModel delegate: myDelegate highlight: myHighlight + flow: (testTopToBottom == true) ? GridView.TopToBottom : GridView.LeftToRight + layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight preferredHighlightBegin: 100 preferredHighlightEnd: 100 highlightRangeMode: "StrictlyEnforceRange" diff --git a/tests/auto/declarative/qdeclarativegridview/data/gridview1.qml b/tests/auto/declarative/qdeclarativegridview/data/gridview1.qml index e4e699c..caa28d6 100644 --- a/tests/auto/declarative/qdeclarativegridview/data/gridview1.qml +++ b/tests/auto/declarative/qdeclarativegridview/data/gridview1.qml @@ -1,4 +1,4 @@ -import QtQuick 1.0 +import QtQuick 1.1 Rectangle { id: root @@ -55,7 +55,8 @@ Rectangle { height: 320 cellWidth: 80 cellHeight: 60 - flow: (testTopToBottom == false) ? "LeftToRight" : "TopToBottom" + flow: (testTopToBottom == false) ? GridView.LeftToRight : GridView.TopToBottom + layoutDirection: (testRightToLeft == true) ? Qt.RightToLeft : Qt.LeftToRight model: testModel delegate: myDelegate header: root.showHeader ? headerFooter : null diff --git a/tests/auto/declarative/qdeclarativegridview/data/mirroring.qml b/tests/auto/declarative/qdeclarativegridview/data/mirroring.qml new file mode 100644 index 0000000..54de16b --- /dev/null +++ b/tests/auto/declarative/qdeclarativegridview/data/mirroring.qml @@ -0,0 +1,43 @@ +// This example demonstrates how item positioning +// changes in right-to-left layout direction + +import QtQuick 1.1 + +Rectangle { + color: "lightgray" + width: 340 + height: 370 + + VisualItemModel { + id: itemModel + objectName: "itemModel" + Rectangle { + objectName: "item1" + height: 110; width: 120; color: "#FFFEF0" + Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + Rectangle { + objectName: "item2" + height: 130; width: 150; color: "#F0FFF7" + Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + Rectangle { + objectName: "item3" + height: 170; width: 190; color: "#F4F0FF" + Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + } + + GridView { + id: view + objectName: "view" + cellWidth: 190 + cellHeight: 170 + anchors.fill: parent + anchors.bottomMargin: 30 + model: itemModel + highlightRangeMode: "StrictlyEnforceRange" + flow: GridView.TopToBottom + flickDeceleration: 2000 + } +} diff --git a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp index 79189a7..5ced02b 100644 --- a/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp +++ b/tests/auto/declarative/qdeclarativegridview/tst_qdeclarativegridview.cpp @@ -46,6 +46,7 @@ #include <QtDeclarative/qdeclarativecomponent.h> #include <QtDeclarative/qdeclarativecontext.h> #include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/private/qdeclarativeitem_p.h> #include <QtDeclarative/private/qlistmodelinterface_p.h> #include <QtDeclarative/private/qdeclarativegridview_p.h> #include <QtDeclarative/private/qdeclarativetext_p.h> @@ -78,9 +79,12 @@ private slots: void componentChanges(); void modelChanges(); void positionViewAtIndex(); + void positionViewAtIndex_rightToLeft(); + void mirroring(); void snapping(); void resetModel(); void enforceRange(); + void enforceRange_rightToLeft(); void QTBUG_8456(); void manualHighlight(); void footer(); @@ -203,6 +207,7 @@ void tst_QDeclarativeGridView::items() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -251,6 +256,7 @@ void tst_QDeclarativeGridView::changed() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -284,6 +290,7 @@ void tst_QDeclarativeGridView::inserted() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -360,6 +367,7 @@ void tst_QDeclarativeGridView::removed() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -503,6 +511,7 @@ void tst_QDeclarativeGridView::moved() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -726,6 +735,58 @@ void tst_QDeclarativeGridView::currentIndex() QVERIFY(!gridview->highlightItem()); QVERIFY(!gridview->currentItem()); + gridview->setHighlightFollowsCurrentItem(true); + + gridview->setFlow(QDeclarativeGridView::LeftToRight); + gridview->setLayoutDirection(Qt::RightToLeft); + + qApp->setActiveWindow(canvas); +#ifdef Q_WS_X11 + // to be safe and avoid failing setFocus with window managers + qt_x11_wait_for_window_manager(canvas); +#endif + QTRY_VERIFY(canvas->hasFocus()); + QTRY_VERIFY(canvas->scene()->hasFocus()); + qApp->processEvents(); + + gridview->setCurrentIndex(35); + + QTest::keyClick(canvas, Qt::Key_Right); + QCOMPARE(gridview->currentIndex(), 34); + + QTest::keyClick(canvas, Qt::Key_Down); + QCOMPARE(gridview->currentIndex(), 37); + + QTest::keyClick(canvas, Qt::Key_Up); + QCOMPARE(gridview->currentIndex(), 34); + + QTest::keyClick(canvas, Qt::Key_Left); + QCOMPARE(gridview->currentIndex(), 35); + + + // turn off auto highlight + gridview->setHighlightFollowsCurrentItem(false); + QVERIFY(gridview->highlightFollowsCurrentItem() == false); + QVERIFY(gridview->highlightItem()); + hlPosX = gridview->highlightItem()->x(); + hlPosY = gridview->highlightItem()->y(); + + gridview->setCurrentIndex(5); + QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX); + QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY); + + // insert item before currentIndex + gridview->setCurrentIndex(28); + model.insertItem(0, "Foo", "1111"); + QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29); + + // check removing highlight by setting currentIndex to -1; + gridview->setCurrentIndex(-1); + + QCOMPARE(gridview->currentIndex(), -1); + QVERIFY(!gridview->highlightItem()); + QVERIFY(!gridview->currentItem()); + delete canvas; } @@ -774,6 +835,7 @@ void tst_QDeclarativeGridView::changeFlow() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -819,6 +881,44 @@ void tst_QDeclarativeGridView::changeFlow() QTRY_COMPARE(number->text(), model.number(i)); } + ctxt->setContextProperty("testRightToLeft", QVariant(true)); + + // Confirm items positioned correctly and indexes correct + itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 0; i < model.count() && i < itemCount; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width())); + QTRY_COMPARE(item->y(), qreal((i%5)*60)); + QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", i); + QTRY_VERIFY(name != 0); + QTRY_COMPARE(name->text(), model.name(i)); + QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", i); + QTRY_VERIFY(number != 0); + QTRY_COMPARE(number->text(), model.number(i)); + } + gridview->setContentX(100); + QTRY_COMPARE(gridview->contentX(), 100.); + ctxt->setContextProperty("testTopToBottom", QVariant(false)); + QTRY_COMPARE(gridview->contentX(), 0.); + + // Confirm items positioned correctly and indexes correct + itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 0; i < model.count() && i < itemCount; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80)); + QTRY_COMPARE(item->y(), qreal((i/3)*60)); + QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", i); + QTRY_VERIFY(name != 0); + QTRY_COMPARE(name->text(), model.name(i)); + QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", i); + QTRY_VERIFY(number != 0); + QTRY_COMPARE(number->text(), model.number(i)); + } + delete canvas; } @@ -879,6 +979,7 @@ void tst_QDeclarativeGridView::propertyChanges() QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged())); QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged())); + QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged())); QSignalSpy flowSpy(gridView, SIGNAL(flowChanged())); QTRY_COMPARE(gridView->isWrapEnabled(), true); @@ -905,6 +1006,38 @@ void tst_QDeclarativeGridView::propertyChanges() QTRY_COMPARE(cacheBufferSpy.count(),1); QTRY_COMPARE(flowSpy.count(),1); + gridView->setFlow(QDeclarativeGridView::LeftToRight); + QTRY_COMPARE(gridView->flow(), QDeclarativeGridView::LeftToRight); + + gridView->setWrapEnabled(true); + gridView->setCacheBuffer(5); + gridView->setLayoutDirection(Qt::RightToLeft); + + QTRY_COMPARE(gridView->isWrapEnabled(), true); + QTRY_COMPARE(gridView->cacheBuffer(), 5); + QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft); + + QTRY_COMPARE(keyNavigationWrapsSpy.count(),2); + QTRY_COMPARE(cacheBufferSpy.count(),2); + QTRY_COMPARE(layoutSpy.count(),1); + QTRY_COMPARE(flowSpy.count(),2); + + gridView->setWrapEnabled(true); + gridView->setCacheBuffer(5); + gridView->setLayoutDirection(Qt::RightToLeft); + + QTRY_COMPARE(keyNavigationWrapsSpy.count(),2); + QTRY_COMPARE(cacheBufferSpy.count(),2); + QTRY_COMPARE(layoutSpy.count(),1); + QTRY_COMPARE(flowSpy.count(),2); + + gridView->setFlow(QDeclarativeGridView::TopToBottom); + QTRY_COMPARE(gridView->flow(), QDeclarativeGridView::TopToBottom); + QTRY_COMPARE(flowSpy.count(),3); + + gridView->setFlow(QDeclarativeGridView::TopToBottom); + QTRY_COMPARE(flowSpy.count(),3); + delete canvas; } @@ -992,6 +1125,7 @@ void tst_QDeclarativeGridView::positionViewAtIndex() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); @@ -1185,6 +1319,7 @@ void tst_QDeclarativeGridView::snapping() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); ctxt->setContextProperty("testTopToBottom", QVariant(false)); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); qApp->processEvents(); @@ -1211,6 +1346,200 @@ void tst_QDeclarativeGridView::snapping() QCOMPARE(gridview->contentY(), 120.); delete canvas; + +} + +void tst_QDeclarativeGridView::mirroring() +{ + QDeclarativeView *canvasA = createView(); + canvasA->setSource(QUrl::fromLocalFile(SRCDIR "/data/mirroring.qml")); + QDeclarativeGridView *gridviewA = findItem<QDeclarativeGridView>(canvasA->rootObject(), "view"); + QTRY_VERIFY(gridviewA != 0); + + QDeclarativeView *canvasB = createView(); + canvasB->setSource(QUrl::fromLocalFile(SRCDIR "/data/mirroring.qml")); + QDeclarativeGridView *gridviewB = findItem<QDeclarativeGridView>(canvasB->rootObject(), "view"); + QTRY_VERIFY(gridviewA != 0); + qApp->processEvents(); + + QList<QString> objectNames; + objectNames << "item1" << "item2"; // << "item3" + + gridviewA->setProperty("layoutDirection", Qt::LeftToRight); + gridviewB->setProperty("layoutDirection", Qt::RightToLeft); + QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection()); + + // LTR != RTL + foreach(const QString objectName, objectNames) + QVERIFY(findItem<QDeclarativeItem>(gridviewA, objectName)->x() != findItem<QDeclarativeItem>(gridviewB, objectName)->x()); + + gridviewA->setProperty("layoutDirection", Qt::LeftToRight); + gridviewB->setProperty("layoutDirection", Qt::LeftToRight); + + // LTR == LTR + foreach(const QString objectName, objectNames) + QCOMPARE(findItem<QDeclarativeItem>(gridviewA, objectName)->x(), findItem<QDeclarativeItem>(gridviewB, objectName)->x()); + + QVERIFY(gridviewB->layoutDirection() == gridviewB->effectiveLayoutDirection()); + QDeclarativeItemPrivate::get(gridviewB)->setLayoutMirror(true); + QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection()); + + // LTR != LTR+mirror + foreach(const QString objectName, objectNames) + QVERIFY(findItem<QDeclarativeItem>(gridviewA, objectName)->x() != findItem<QDeclarativeItem>(gridviewB, objectName)->x()); + + gridviewA->setProperty("layoutDirection", Qt::RightToLeft); + + // RTL == LTR+mirror + foreach(const QString objectName, objectNames) + QCOMPARE(findItem<QDeclarativeItem>(gridviewA, objectName)->x(), findItem<QDeclarativeItem>(gridviewB, objectName)->x()); + + gridviewB->setProperty("layoutDirection", Qt::RightToLeft); + + // RTL != RTL+mirror + foreach(const QString objectName, objectNames) + QVERIFY(findItem<QDeclarativeItem>(gridviewA, objectName)->x() != findItem<QDeclarativeItem>(gridviewB, objectName)->x()); + + gridviewA->setProperty("layoutDirection", Qt::LeftToRight); + + // LTR == RTL+mirror + foreach(const QString objectName, objectNames) + QCOMPARE(findItem<QDeclarativeItem>(gridviewA, objectName)->x(), findItem<QDeclarativeItem>(gridviewB, objectName)->x()); + + delete canvasA; + delete canvasB; +} + +void tst_QDeclarativeGridView::positionViewAtIndex_rightToLeft() +{ + QDeclarativeView *canvas = createView(); + + TestModel model; + for (int i = 0; i < 40; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testTopToBottom", QVariant(true)); + ctxt->setContextProperty("testRightToLeft", QVariant(true)); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); + qApp->processEvents(); + + QDeclarativeGridView *gridview = findItem<QDeclarativeGridView>(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + + QDeclarativeItem *contentItem = gridview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + // Confirm items positioned correctly + int itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 0; i < model.count() && i < itemCount-1; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width())); + QTRY_COMPARE(item->y(), qreal((i%5)*60)); + } + + // Position on a currently visible item + gridview->positionViewAtIndex(6, QDeclarativeGridView::Beginning); + QTRY_COMPARE(gridview->contentX(), -320.); + + // Confirm items positioned correctly + itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width())); + QTRY_COMPARE(item->y(), qreal((i%5)*60)); + } + + // Position on an item beyond the visible items + gridview->positionViewAtIndex(21, QDeclarativeGridView::Beginning); + QTRY_COMPARE(gridview->contentX(), -560.); + + // Confirm items positioned correctly + itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width())); + QTRY_COMPARE(item->y(), qreal((i%5)*60)); + } + + // Position on an item that would leave empty space if positioned at the top + gridview->positionViewAtIndex(31, QDeclarativeGridView::Beginning); + QTRY_COMPARE(gridview->contentX(), -639.); + + // Confirm items positioned correctly + itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width())); + QTRY_COMPARE(item->y(), qreal((i%5)*60)); + } + + // Position at the beginning again + gridview->positionViewAtIndex(0, QDeclarativeGridView::Beginning); + QTRY_COMPARE(gridview->contentX(), -240.); + + // Confirm items positioned correctly + itemCount = findItems<QDeclarativeItem>(contentItem, "wrapper").count(); + for (int i = 0; i < model.count() && i < itemCount-1; ++i) { + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", i); + if (!item) qWarning() << "Item" << i << "not found"; + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width())); + QTRY_COMPARE(item->y(), qreal((i%5)*60)); + } + + // Position at End + gridview->positionViewAtIndex(30, QDeclarativeGridView::End); + QTRY_COMPARE(gridview->contentX(), -560.); + + // Position in Center + gridview->positionViewAtIndex(15, QDeclarativeGridView::Center); + QTRY_COMPARE(gridview->contentX(), -400.); + + // Ensure at least partially visible + gridview->positionViewAtIndex(15, QDeclarativeGridView::Visible); + QTRY_COMPARE(gridview->contentX(), -400.); + + gridview->setContentX(-555.); + gridview->positionViewAtIndex(15, QDeclarativeGridView::Visible); + QTRY_COMPARE(gridview->contentX(), -555.); + + gridview->setContentX(-239); + gridview->positionViewAtIndex(15, QDeclarativeGridView::Visible); + QTRY_COMPARE(gridview->contentX(), -320.); + + gridview->setContentX(-239); + gridview->positionViewAtIndex(20, QDeclarativeGridView::Visible); + QTRY_COMPARE(gridview->contentX(), -400.); + + gridview->setContentX(-640); + gridview->positionViewAtIndex(20, QDeclarativeGridView::Visible); + QTRY_COMPARE(gridview->contentX(), -560.); + + // Ensure completely visible + gridview->setContentX(-400); + gridview->positionViewAtIndex(20, QDeclarativeGridView::Contain); + QTRY_COMPARE(gridview->contentX(), -400.); + + gridview->setContentX(-315); + gridview->positionViewAtIndex(15, QDeclarativeGridView::Contain); + QTRY_COMPARE(gridview->contentX(), -320.); + + gridview->setContentX(-640); + gridview->positionViewAtIndex(20, QDeclarativeGridView::Contain); + QTRY_COMPARE(gridview->contentX(), -560.); + + delete canvas; } void tst_QDeclarativeGridView::resetModel() @@ -1264,9 +1593,12 @@ void tst_QDeclarativeGridView::enforceRange() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); + ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview-enforcerange.qml")); qApp->processEvents(); + QVERIFY(canvas->rootObject() != 0); QDeclarativeGridView *gridview = findItem<QDeclarativeGridView>(canvas->rootObject(), "grid"); QTRY_VERIFY(gridview != 0); @@ -1307,6 +1639,64 @@ void tst_QDeclarativeGridView::enforceRange() delete canvas; } +void tst_QDeclarativeGridView::enforceRange_rightToLeft() +{ + QDeclarativeView *canvas = createView(); + + TestModel model; + for (int i = 0; i < 30; i++) + model.addItem("Item" + QString::number(i), ""); + + QDeclarativeContext *ctxt = canvas->rootContext(); + ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(true)); + ctxt->setContextProperty("testTopToBottom", QVariant(true)); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview-enforcerange.qml")); + qApp->processEvents(); + QVERIFY(canvas->rootObject() != 0); + + QDeclarativeGridView *gridview = findItem<QDeclarativeGridView>(canvas->rootObject(), "grid"); + QTRY_VERIFY(gridview != 0); + + QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0); + QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0); + QTRY_COMPARE(gridview->highlightRangeMode(), QDeclarativeGridView::StrictlyEnforceRange); + + QDeclarativeItem *contentItem = gridview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + // view should be positioned at the top of the range. + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "wrapper", 0); + QTRY_VERIFY(item); + QTRY_COMPARE(gridview->contentX(), -100.); + QTRY_COMPARE(gridview->contentY(), 0.0); + + QDeclarativeText *name = findItem<QDeclarativeText>(contentItem, "textName", 0); + QTRY_VERIFY(name != 0); + QTRY_COMPARE(name->text(), model.name(0)); + QDeclarativeText *number = findItem<QDeclarativeText>(contentItem, "textNumber", 0); + QTRY_VERIFY(number != 0); + QTRY_COMPARE(number->text(), model.number(0)); + + // Check currentIndex is updated when contentItem moves + gridview->setContentX(-200); + QTRY_COMPARE(gridview->currentIndex(), 3); + + gridview->setCurrentIndex(7); + QTRY_COMPARE(gridview->contentX(), -300.); + QTRY_COMPARE(gridview->contentY(), 0.0); + + TestModel model2; + for (int i = 0; i < 5; i++) + model2.addItem("Item" + QString::number(i), ""); + + ctxt->setContextProperty("testModel", &model2); + QCOMPARE(gridview->count(), 5); + + delete canvas; +} + void tst_QDeclarativeGridView::QTBUG_8456() { QDeclarativeView *canvas = createView(); @@ -1475,6 +1865,7 @@ void tst_QDeclarativeGridView::indexAt() QDeclarativeContext *ctxt = canvas->rootContext(); ctxt->setContextProperty("testModel", &model); + ctxt->setContextProperty("testRightToLeft", QVariant(false)); ctxt->setContextProperty("testTopToBottom", QVariant(false)); canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml")); diff --git a/tests/auto/declarative/qdeclarativeitem/data/layoutmirroring.qml b/tests/auto/declarative/qdeclarativeitem/data/layoutmirroring.qml new file mode 100644 index 0000000..866b615 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeitem/data/layoutmirroring.qml @@ -0,0 +1,54 @@ +import QtQuick 1.1 + +Item { + property bool childrenInherit: true + Item { + objectName: "mirrored1" + LayoutMirroring.enabled: true + LayoutMirroring.childrenInherit: parent.childrenInherit + Item { + Item { + objectName: "notMirrored1" + LayoutMirroring.enabled: false + Item { + objectName: "inheritedMirror1" + } + } + Item { + objectName: "inheritedMirror2" + } + } + } + Item { + objectName: "mirrored2" + LayoutMirroring.enabled: true + LayoutMirroring.childrenInherit: false + Item { + objectName: "notMirrored2" + } + } + Item { + LayoutMirroring.enabled: true + LayoutMirroring.childrenInherit: true + Loader { + id: loader + } + } + states: State { + name: "newContent" + PropertyChanges { + target: loader + sourceComponent: component + } + } + Component { + id: component + Item { + objectName: "notMirrored3" + LayoutMirroring.enabled: false + Item { + objectName: "inheritedMirror3" + } + } + } +} diff --git a/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp b/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp index 137522d..52c9a72 100644 --- a/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp +++ b/tests/auto/declarative/qdeclarativeitem/tst_qdeclarativeitem.cpp @@ -65,7 +65,10 @@ private slots: void keys(); void keysProcessingOrder(); void keyNavigation(); + void keyNavigation_RightToLeft(); void keyNavigation_skipNotVisible(); + void layoutMirroring(); + void layoutMirroringIllegalParent(); void smooth(); void clip(); void mapCoordinates(); @@ -87,13 +90,33 @@ private slots: void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); void qtbug_16871(); - private: - template<typename T> - T *findItem(QGraphicsObject *parent, const QString &objectName); QDeclarativeEngine engine; }; +template<typename T> +T *findItem(QGraphicsObject *parent, const QString &objectName) +{ + if (!parent) + return 0; + + const QMetaObject &mo = T::staticMetaObject; + //qDebug() << parent->QGraphicsObject::children().count() << "children"; + for (int i = 0; i < parent->childItems().count(); ++i) { + QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i)); + if(!item) + continue; + //qDebug() << "try" << item; + if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) + return static_cast<T*>(item); + item = findItem<T>(item, objectName); + if (item) + return static_cast<T*>(item); + } + + return 0; +} + class KeysTestObject : public QObject { Q_OBJECT @@ -380,6 +403,165 @@ void tst_QDeclarativeItem::keysProcessingOrder() delete testObject; } +QDeclarativeItemPrivate *childPrivate(QGraphicsObject *rootItem, const char * itemString) +{ + QDeclarativeItem *item = findItem<QDeclarativeItem>(rootItem, QString(QLatin1String(itemString))); + QDeclarativeItemPrivate* itemPrivate = QDeclarativeItemPrivate::get(item); + return itemPrivate; +} + +QVariant childProperty(QGraphicsObject *rootItem, const char * itemString, const char * property) +{ + QDeclarativeItem *item = findItem<QDeclarativeItem>(rootItem, QString(QLatin1String(itemString))); + return item->property(property); +} + +bool anchorsMirrored(QGraphicsObject *rootItem, const char * itemString) +{ + QDeclarativeItem *item = findItem<QDeclarativeItem>(rootItem, QString(QLatin1String(itemString))); + QDeclarativeItemPrivate* itemPrivate = QDeclarativeItemPrivate::get(item); + return itemPrivate->anchors()->mirrored(); +} + +void tst_QDeclarativeItem::layoutMirroring() +{ + QDeclarativeView *canvas = new QDeclarativeView(0); + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/layoutmirroring.qml")); + canvas->show(); + + QDeclarativeItem *rootItem = qobject_cast<QDeclarativeItem*>(canvas->rootObject()); + QVERIFY(rootItem); + QDeclarativeItemPrivate *rootPrivate = QDeclarativeItemPrivate::get(rootItem); + QVERIFY(rootPrivate); + + QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "mirrored2")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "notMirrored2")->effectiveLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true); + + QCOMPARE(anchorsMirrored(rootItem, "mirrored1"), true); + QCOMPARE(anchorsMirrored(rootItem, "mirrored2"), true); + QCOMPARE(anchorsMirrored(rootItem, "notMirrored1"), false); + QCOMPARE(anchorsMirrored(rootItem, "notMirrored2"), false); + QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror1"), true); + QCOMPARE(anchorsMirrored(rootItem, "inheritedMirror2"), true); + + QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritedLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritedLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true); + + QCOMPARE(childPrivate(rootItem, "mirrored1")->isMirrorImplicit, false); + QCOMPARE(childPrivate(rootItem, "mirrored2")->isMirrorImplicit, false); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->isMirrorImplicit, false); + QCOMPARE(childPrivate(rootItem, "notMirrored2")->isMirrorImplicit, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->isMirrorImplicit, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->isMirrorImplicit, true); + + QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromParent, true); + QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromParent, false); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromParent, true); + QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromParent, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromParent, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromParent, true); + + QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritMirrorFromItem, true); + QCOMPARE(childPrivate(rootItem, "mirrored2")->inheritMirrorFromItem, false); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritMirrorFromItem, false); + QCOMPARE(childPrivate(rootItem, "notMirrored2")->inheritMirrorFromItem, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritMirrorFromItem, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritMirrorFromItem, false); + + // load dynamic content using Loader that needs to inherit mirroring + rootItem->setProperty("state", "newContent"); + QCOMPARE(childPrivate(rootItem, "notMirrored3")->effectiveLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->effectiveLayoutMirror, true); + + QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritedLayoutMirror, true); + + QCOMPARE(childPrivate(rootItem, "notMirrored3")->isMirrorImplicit, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->isMirrorImplicit, true); + + QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromParent, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror3")->inheritMirrorFromParent, true); + + QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false); + QCOMPARE(childPrivate(rootItem, "notMirrored3")->inheritMirrorFromItem, false); + + // disable inheritance + rootItem->setProperty("childrenInherit", false); + + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false); + + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, false); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, false); + + // re-enable inheritance + rootItem->setProperty("childrenInherit", true); + + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "mirrored1")->effectiveLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->effectiveLayoutMirror, false); + + QCOMPARE(childPrivate(rootItem, "inheritedMirror1")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "inheritedMirror2")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "mirrored1")->inheritedLayoutMirror, true); + QCOMPARE(childPrivate(rootItem, "notMirrored1")->inheritedLayoutMirror, true); + + // + // dynamic parenting + // + QDeclarativeItem *parentItem1 = new QDeclarativeItem(); + QDeclarativeItemPrivate::get(parentItem1)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true + QDeclarativeItemPrivate::get(parentItem1)->isMirrorImplicit = false; + QDeclarativeItemPrivate::get(parentItem1)->inheritMirrorFromItem = true; // LayoutMirroring.childrenInherit: true + QDeclarativeItemPrivate::get(parentItem1)->resolveLayoutMirror(); + + // inherit in constructor + QDeclarativeItem *childItem1 = new QDeclarativeItem(parentItem1); + QCOMPARE(QDeclarativeItemPrivate::get(childItem1)->effectiveLayoutMirror, true); + QCOMPARE(QDeclarativeItemPrivate::get(childItem1)->inheritMirrorFromParent, true); + + // inherit through a parent change + QDeclarativeItem *childItem2 = new QDeclarativeItem(); + QCOMPARE(QDeclarativeItemPrivate::get(childItem2)->effectiveLayoutMirror, false); + QCOMPARE(QDeclarativeItemPrivate::get(childItem2)->inheritMirrorFromParent, false); + childItem2->setParentItem(parentItem1); + QCOMPARE(QDeclarativeItemPrivate::get(childItem2)->effectiveLayoutMirror, true); + QCOMPARE(QDeclarativeItemPrivate::get(childItem2)->inheritMirrorFromParent, true); + + // stop inherting through a parent change + QDeclarativeItem *parentItem2 = new QDeclarativeItem(); + QDeclarativeItemPrivate::get(parentItem2)->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true + QDeclarativeItemPrivate::get(parentItem2)->resolveLayoutMirror(); + childItem2->setParentItem(parentItem2); + QCOMPARE(QDeclarativeItemPrivate::get(childItem2)->effectiveLayoutMirror, false); + QCOMPARE(QDeclarativeItemPrivate::get(childItem2)->inheritMirrorFromParent, false); + + delete parentItem1; + delete parentItem2; +} + +void tst_QDeclarativeItem::layoutMirroringIllegalParent() +{ + QDeclarativeComponent component(&engine); + component.setData("import QtQuick 1.1; QtObject { LayoutMirroring.enabled: true; LayoutMirroring.childrenInherit: true }", QUrl::fromLocalFile("")); + QTest::ignoreMessage(QtWarningMsg, "file::1:21: QML QtObject: LayoutDirection attached property only works with Items"); + QObject *object = component.create(); + QVERIFY(object != 0); +} + void tst_QDeclarativeItem::keyNavigation() { QDeclarativeView *canvas = new QDeclarativeView(0); @@ -460,6 +642,59 @@ void tst_QDeclarativeItem::keyNavigation() delete canvas; } +void tst_QDeclarativeItem::keyNavigation_RightToLeft() +{ + QDeclarativeView *canvas = new QDeclarativeView(0); + canvas->setFixedSize(240,320); + + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/keynavigationtest.qml")); + canvas->show(); + qApp->processEvents(); + + QDeclarativeItem *rootItem = qobject_cast<QDeclarativeItem*>(canvas->rootObject()); + QVERIFY(rootItem); + QDeclarativeItemPrivate* rootItemPrivate = QDeclarativeItemPrivate::get(rootItem); + + rootItemPrivate->effectiveLayoutMirror = true; // LayoutMirroring.mirror: true + rootItemPrivate->isMirrorImplicit = false; + rootItemPrivate->inheritMirrorFromItem = true; // LayoutMirroring.inherit: true + rootItemPrivate->resolveLayoutMirror(); + + QEvent wa(QEvent::WindowActivate); + QApplication::sendEvent(canvas, &wa); + QFocusEvent fe(QEvent::FocusIn); + QApplication::sendEvent(canvas, &fe); + + QDeclarativeItem *item = findItem<QDeclarativeItem>(canvas->rootObject(), "item1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + QVariant result; + QVERIFY(QMetaObject::invokeMethod(canvas->rootObject(), "verify", + Q_RETURN_ARG(QVariant, result))); + QVERIFY(result.toBool()); + + // right + QKeyEvent key(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier, "", false, 1); + QApplication::sendEvent(canvas, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QDeclarativeItem>(canvas->rootObject(), "item2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // left + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier, "", false, 1); + QApplication::sendEvent(canvas, &key); + QVERIFY(key.isAccepted()); + + item = findItem<QDeclarativeItem>(canvas->rootObject(), "item1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete canvas; +} + void tst_QDeclarativeItem::keyNavigation_skipNotVisible() { QDeclarativeView *canvas = new QDeclarativeView(0); @@ -1003,32 +1238,6 @@ void tst_QDeclarativeItem::qtbug_16871() delete o; } - -template<typename T> -T *tst_QDeclarativeItem::findItem(QGraphicsObject *parent, const QString &objectName) -{ - if (!parent) - return 0; - - const QMetaObject &mo = T::staticMetaObject; - //qDebug() << parent->QGraphicsObject::children().count() << "children"; - for (int i = 0; i < parent->childItems().count(); ++i) { - QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(parent->childItems().at(i)); - if(!item) - continue; - //qDebug() << "try" << item; - if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) - return static_cast<T*>(item); - item = findItem<T>(item, objectName); - if (item) - return static_cast<T*>(item); - } - - return 0; -} - - - QTEST_MAIN(tst_QDeclarativeItem) #include "tst_qdeclarativeitem.moc" diff --git a/tests/auto/declarative/qdeclarativelistview/data/rightToLeft.qml b/tests/auto/declarative/qdeclarativelistview/data/rightToLeft.qml new file mode 100644 index 0000000..1e92bb3 --- /dev/null +++ b/tests/auto/declarative/qdeclarativelistview/data/rightToLeft.qml @@ -0,0 +1,42 @@ +// This example demonstrates how item positioning +// changes in right-to-left layout direction + +import QtQuick 1.1 + +Rectangle { + color: "lightgray" + width: 640 + height: 320 + + VisualItemModel { + id: itemModel + objectName: "itemModel" + Rectangle { + objectName: "item1" + height: view.height; width: 100; color: "#FFFEF0" + Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + Rectangle { + objectName: "item2" + height: view.height; width: 200; color: "#F0FFF7" + Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + Rectangle { + objectName: "item3" + height: view.height; width: 240; color: "#F4F0FF" + Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + } + + ListView { + id: view + objectName: "view" + anchors.fill: parent + anchors.bottomMargin: 30 + model: itemModel + highlightRangeMode: "StrictlyEnforceRange" + orientation: ListView.Horizontal + flickDeceleration: 2000 + layoutDirection: Qt.RightToLeft + } +} diff --git a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp index e326136..a699bbe 100644 --- a/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp +++ b/tests/auto/declarative/qdeclarativelistview/tst_qdeclarativelistview.cpp @@ -45,6 +45,7 @@ #include <QtDeclarative/qdeclarativeengine.h> #include <QtDeclarative/qdeclarativecontext.h> #include <QtDeclarative/qdeclarativeexpression.h> +#include <QtDeclarative/private/qdeclarativeitem_p.h> #include <QtDeclarative/private/qdeclarativelistview_p.h> #include <QtDeclarative/private/qdeclarativetext_p.h> #include <QtDeclarative/private/qdeclarativevisualitemmodel_p.h> @@ -115,6 +116,8 @@ private slots: void onRemove_data(); void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); + void rightToLeft(); + void test_mirroring(); private: template <class T> void items(); @@ -1755,8 +1758,6 @@ void tst_QDeclarativeListView::manualHighlight() QDeclarativeView *canvas = new QDeclarativeView(0); canvas->setFixedSize(240,320); - QDeclarativeContext *ctxt = canvas->rootContext(); - QString filename(SRCDIR "/data/manual-highlight.qml"); canvas->setSource(QUrl::fromLocalFile(filename)); @@ -1887,8 +1888,6 @@ void tst_QDeclarativeListView::header() TestModel model; - QDeclarativeContext *ctxt = canvas->rootContext(); - canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header1.qml")); qApp->processEvents(); @@ -2358,6 +2357,108 @@ void tst_QDeclarativeListView::testQtQuick11Attributes_data() << ""; } +void tst_QDeclarativeListView::rightToLeft() +{ + QDeclarativeView *canvas = createView(); + canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml")); + qApp->processEvents(); + + QVERIFY(canvas->rootObject() != 0); + QDeclarativeListView *listview = findItem<QDeclarativeListView>(canvas->rootObject(), "view"); + QTRY_VERIFY(listview != 0); + + QDeclarativeItem *contentItem = listview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + QDeclarativeVisualItemModel *model = canvas->rootObject()->findChild<QDeclarativeVisualItemModel*>("itemModel"); + QTRY_VERIFY(model != 0); + + QTRY_VERIFY(model->count() == 3); + QTRY_COMPARE(listview->currentIndex(), 0); + + QDeclarativeItem *item = findItem<QDeclarativeItem>(contentItem, "item1"); + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), -100.0); + QCOMPARE(item->height(), listview->height()); + + QDeclarativeText *text = findItem<QDeclarativeText>(contentItem, "text1"); + QTRY_VERIFY(text); + QTRY_COMPARE(text->text(), QLatin1String("index: 0")); + + listview->setCurrentIndex(2); + + item = findItem<QDeclarativeItem>(contentItem, "item3"); + QTRY_VERIFY(item); + QTRY_COMPARE(item->x(), -540.0); + + text = findItem<QDeclarativeText>(contentItem, "text3"); + QTRY_VERIFY(text); + QTRY_COMPARE(text->text(), QLatin1String("index: 2")); + + delete canvas; +} + +void tst_QDeclarativeListView::test_mirroring() +{ + QDeclarativeView *canvasA = createView(); + canvasA->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml")); + QDeclarativeListView *listviewA = findItem<QDeclarativeListView>(canvasA->rootObject(), "view"); + QTRY_VERIFY(listviewA != 0); + + QDeclarativeView *canvasB = createView(); + canvasB->setSource(QUrl::fromLocalFile(SRCDIR "/data/rightToLeft.qml")); + QDeclarativeListView *listviewB = findItem<QDeclarativeListView>(canvasB->rootObject(), "view"); + QTRY_VERIFY(listviewA != 0); + qApp->processEvents(); + + QList<QString> objectNames; + objectNames << "item1" << "item2"; // << "item3" + + listviewA->setProperty("layoutDirection", Qt::LeftToRight); + listviewB->setProperty("layoutDirection", Qt::RightToLeft); + QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection()); + + // LTR != RTL + foreach(const QString objectName, objectNames) + QVERIFY(findItem<QDeclarativeItem>(listviewA, objectName)->x() != findItem<QDeclarativeItem>(listviewB, objectName)->x()); + + listviewA->setProperty("layoutDirection", Qt::LeftToRight); + listviewB->setProperty("layoutDirection", Qt::LeftToRight); + + // LTR == LTR + foreach(const QString objectName, objectNames) + QCOMPARE(findItem<QDeclarativeItem>(listviewA, objectName)->x(), findItem<QDeclarativeItem>(listviewB, objectName)->x()); + + QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection()); + QDeclarativeItemPrivate::get(listviewB)->setLayoutMirror(true); + QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection()); + + // LTR != LTR+mirror + foreach(const QString objectName, objectNames) + QVERIFY(findItem<QDeclarativeItem>(listviewA, objectName)->x() != findItem<QDeclarativeItem>(listviewB, objectName)->x()); + + listviewA->setProperty("layoutDirection", Qt::RightToLeft); + + // RTL == LTR+mirror + foreach(const QString objectName, objectNames) + QCOMPARE(findItem<QDeclarativeItem>(listviewA, objectName)->x(), findItem<QDeclarativeItem>(listviewB, objectName)->x()); + + listviewB->setProperty("layoutDirection", Qt::RightToLeft); + + // RTL != RTL+mirror + foreach(const QString objectName, objectNames) + QVERIFY(findItem<QDeclarativeItem>(listviewA, objectName)->x() != findItem<QDeclarativeItem>(listviewB, objectName)->x()); + + listviewA->setProperty("layoutDirection", Qt::LeftToRight); + + // LTR == RTL+mirror + foreach(const QString objectName, objectNames) + QCOMPARE(findItem<QDeclarativeItem>(listviewA, objectName)->x(), findItem<QDeclarativeItem>(listviewB, objectName)->x()); + + delete canvasA; + delete canvasB; +} + void tst_QDeclarativeListView::qListModelInterface_items() { items<TestModel>(); diff --git a/tests/auto/declarative/qdeclarativepositioners/data/grid-righttoleft.qml b/tests/auto/declarative/qdeclarativepositioners/data/grid-righttoleft.qml deleted file mode 100644 index 0ec1f37..0000000 --- a/tests/auto/declarative/qdeclarativepositioners/data/grid-righttoleft.qml +++ /dev/null @@ -1,41 +0,0 @@ -import QtQuick 1.1 - -Item { - width: 640 - height: 480 - Grid { - objectName: "grid" - columns: 3 - layoutDirection: Qt.RightToLeft - Rectangle { - objectName: "one" - color: "red" - width: 50 - height: 50 - } - Rectangle { - objectName: "two" - color: "green" - width: 20 - height: 50 - } - Rectangle { - objectName: "three" - color: "blue" - width: 50 - height: 20 - } - Rectangle { - objectName: "four" - color: "cyan" - width: 50 - height: 50 - } - Rectangle { - objectName: "five" - color: "magenta" - width: 10 - height: 10 - } - } -} diff --git a/tests/auto/declarative/qdeclarativepositioners/data/gridtest.qml b/tests/auto/declarative/qdeclarativepositioners/data/gridtest.qml index f3b17dd..929b726 100644 --- a/tests/auto/declarative/qdeclarativepositioners/data/gridtest.qml +++ b/tests/auto/declarative/qdeclarativepositioners/data/gridtest.qml @@ -1,9 +1,11 @@ -import QtQuick 1.0 +import QtQuick 1.1 Item { width: 640 height: 480 + property bool testRightToLeft: false Grid { + layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight objectName: "grid" columns: 3 Rectangle { @@ -21,7 +23,7 @@ Item { Rectangle { objectName: "three" color: "blue" - width: 50 + width: 30 height: 20 } Rectangle { diff --git a/tests/auto/declarative/qdeclarativepositioners/data/horizontal.qml b/tests/auto/declarative/qdeclarativepositioners/data/horizontal.qml index e1a9652..d35c02d 100644 --- a/tests/auto/declarative/qdeclarativepositioners/data/horizontal.qml +++ b/tests/auto/declarative/qdeclarativepositioners/data/horizontal.qml @@ -4,7 +4,6 @@ Item { width: 640 height: 480 property bool testRightToLeft: false - Row { objectName: "row" layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight diff --git a/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp b/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp index 40e533d..92ab722 100644 --- a/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp +++ b/tests/auto/declarative/qdeclarativepositioners/tst_qdeclarativepositioners.cpp @@ -45,6 +45,7 @@ #include <private/qdeclarativerectangle_p.h> #include <private/qdeclarativepositioners_p.h> #include <private/qdeclarativetransition_p.h> +#include <private/qdeclarativeitem_p.h> #include <qdeclarativeexpression.h> #include <QtGui/qgraphicswidget.h> #include "../../../shared/util.h" @@ -87,6 +88,7 @@ private slots: void test_flow_implicit_resize(); void test_conflictinganchors(); void test_vertical_qgraphicswidget(); + void test_mirroring(); void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); private: @@ -452,7 +454,7 @@ void tst_QDeclarativePositioners::test_grid() QDeclarativeGrid *grid = canvas->rootObject()->findChild<QDeclarativeGrid*>("grid"); QCOMPARE(grid->flow(), QDeclarativeGrid::LeftToRight); - QCOMPARE(grid->width(), 120.0); + QCOMPARE(grid->width(), 100.0); QCOMPARE(grid->height(), 100.0); delete canvas; @@ -494,7 +496,9 @@ void tst_QDeclarativePositioners::test_grid_topToBottom() void tst_QDeclarativePositioners::test_grid_rightToLeft() { - QDeclarativeView *canvas = createView(SRCDIR "/data/grid-righttoleft.qml"); + QDeclarativeView *canvas = createView(SRCDIR "/data/gridtest.qml"); + + canvas->rootObject()->setProperty("testRightToLeft", true); QDeclarativeRectangle *one = canvas->rootObject()->findChild<QDeclarativeRectangle*>("one"); QVERIFY(one != 0); @@ -507,20 +511,20 @@ void tst_QDeclarativePositioners::test_grid_rightToLeft() QDeclarativeRectangle *five = canvas->rootObject()->findChild<QDeclarativeRectangle*>("five"); QVERIFY(five != 0); - QCOMPARE(one->x(), 70.0); + QCOMPARE(one->x(), 50.0); QCOMPARE(one->y(), 0.0); - QCOMPARE(two->x(), 50.0); + QCOMPARE(two->x(), 30.0); QCOMPARE(two->y(), 0.0); QCOMPARE(three->x(), 0.0); QCOMPARE(three->y(), 0.0); - QCOMPARE(four->x(), 70.0); + QCOMPARE(four->x(), 50.0); QCOMPARE(four->y(), 50.0); - QCOMPARE(five->x(), 60.0); + QCOMPARE(five->x(), 40.0); QCOMPARE(five->y(), 50.0); QDeclarativeGrid *grid = canvas->rootObject()->findChild<QDeclarativeGrid*>("grid"); QCOMPARE(grid->layoutDirection(), Qt::RightToLeft); - QCOMPARE(grid->width(), 120.0); + QCOMPARE(grid->width(), 100.0); QCOMPARE(grid->height(), 100.0); delete canvas; @@ -1198,6 +1202,66 @@ void tst_QDeclarativePositioners::test_vertical_qgraphicswidget() delete canvas; } +void tst_QDeclarativePositioners::test_mirroring() +{ + QList<QString> qmlFiles; + qmlFiles << "horizontal.qml" << "gridtest.qml" << "flowtest.qml"; + QList<QString> objectNames; + objectNames << "one" << "two" << "three" << "four" << "five"; + + foreach(const QString qmlFile, qmlFiles) { + QDeclarativeView *canvasA = createView(QString(SRCDIR) + "/data/" + qmlFile); + QDeclarativeItem *rootA = qobject_cast<QDeclarativeItem*>(canvasA->rootObject()); + + QDeclarativeView *canvasB = createView(QString(SRCDIR) + "/data/" + qmlFile); + QDeclarativeItem *rootB = qobject_cast<QDeclarativeItem*>(canvasB->rootObject()); + + rootA->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft + + // LTR != RTL + foreach(const QString objectName, objectNames) { + // horizontal.qml only has three items + if (qmlFile == QString("horizontal.qml") && objectName == QString("four")) + break; + QDeclarativeItem *itemA = rootA->findChild<QDeclarativeItem*>(objectName); + QDeclarativeItem *itemB = rootB->findChild<QDeclarativeItem*>(objectName); + QVERIFY(itemA->x() != itemB->x()); + } + + QDeclarativeItemPrivate* rootPrivateB = QDeclarativeItemPrivate::get(rootB); + + rootPrivateB->effectiveLayoutMirror = true; // LayoutMirroring.enabled: true + rootPrivateB->isMirrorImplicit = false; + rootPrivateB->inheritMirrorFromItem = true; // LayoutMirroring.childrenInherit: true + rootPrivateB->resolveLayoutMirror(); + + // RTL == mirror + foreach(const QString objectName, objectNames) { + // horizontal.qml only has three items + if (qmlFile == QString("horizontal.qml") && objectName == QString("four")) + break; + QDeclarativeItem *itemA = rootA->findChild<QDeclarativeItem*>(objectName); + QDeclarativeItem *itemB = rootB->findChild<QDeclarativeItem*>(objectName); + QCOMPARE(itemA->x(), itemB->x()); + } + + rootA->setProperty("testRightToLeft", false); // layoutDirection: Qt.LeftToRight + rootB->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft + + // LTR == RTL + mirror + foreach(const QString objectName, objectNames) { + // horizontal.qml only has three items + if (qmlFile == QString("horizontal.qml") && objectName == QString("four")) + break; + QDeclarativeItem *itemA = rootA->findChild<QDeclarativeItem*>(objectName); + QDeclarativeItem *itemB = rootB->findChild<QDeclarativeItem*>(objectName); + QCOMPARE(itemA->x(), itemB->x()); + } + delete canvasA; + delete canvasB; + } +} + void tst_QDeclarativePositioners::testQtQuick11Attributes() { QFETCH(QString, code); diff --git a/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp b/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp index 56bed30..20e2640 100644 --- a/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp +++ b/tests/auto/declarative/qdeclarativestates/tst_qdeclarativestates.cpp @@ -120,6 +120,9 @@ private slots: void anchorChanges3(); void anchorChanges4(); void anchorChanges5(); + void anchorChangesRTL(); + void anchorChangesRTL2(); + void anchorChangesRTL3(); void anchorChangesCrash(); void anchorRewindBug(); void anchorRewindBug2(); @@ -813,6 +816,125 @@ void tst_qdeclarativestates::anchorChanges5() delete rect; } +void mirrorAnchors(QDeclarativeItem *item) { + QDeclarativeItemPrivate *itemPrivate = QDeclarativeItemPrivate::get(item); + itemPrivate->setLayoutMirror(true); +} + +qreal offsetRTL(QDeclarativeItem *anchorItem, QDeclarativeItem *item) { + return anchorItem->width()+2*anchorItem->x()-item->width(); +} + +void tst_qdeclarativestates::anchorChangesRTL() +{ + QDeclarativeEngine engine; + + QDeclarativeComponent rectComponent(&engine, SRCDIR "/data/anchorChanges1.qml"); + QDeclarativeRectangle *rect = qobject_cast<QDeclarativeRectangle*>(rectComponent.create()); + QVERIFY(rect != 0); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + + QDeclarativeRectangle *innerRect = qobject_cast<QDeclarativeRectangle*>(rect->findChild<QDeclarativeRectangle*>("MyRect")); + QVERIFY(innerRect != 0); + mirrorAnchors(innerRect); + + QDeclarativeListReference list(rect, "states"); + QDeclarativeState *state = qobject_cast<QDeclarativeState*>(list.at(0)); + QVERIFY(state != 0); + + qmlExecuteDeferred(state); + QDeclarativeAnchorChanges *aChanges = qobject_cast<QDeclarativeAnchorChanges*>(state->operationAt(0)); + QVERIFY(aChanges != 0); + + rectPrivate->setState("right"); + QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(150)); + QCOMPARE(aChanges->object(), qobject_cast<QDeclarativeItem*>(innerRect)); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->left().anchorLine, QDeclarativeAnchorLine::Invalid); //### was reset (how do we distinguish from not set at all) + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->right().item, rectPrivate->right().item); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->right().anchorLine, rectPrivate->right().anchorLine); + + rectPrivate->setState(""); + QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) -qreal(5)); + + delete rect; +} + +void tst_qdeclarativestates::anchorChangesRTL2() +{ + QDeclarativeEngine engine; + + QDeclarativeComponent rectComponent(&engine, SRCDIR "/data/anchorChanges2.qml"); + QDeclarativeRectangle *rect = qobject_cast<QDeclarativeRectangle*>(rectComponent.create()); + QVERIFY(rect != 0); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + + QDeclarativeRectangle *innerRect = qobject_cast<QDeclarativeRectangle*>(rect->findChild<QDeclarativeRectangle*>("MyRect")); + QVERIFY(innerRect != 0); + mirrorAnchors(innerRect); + + rectPrivate->setState("right"); + QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(150)); + + rectPrivate->setState(""); + QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(5)); + + delete rect; +} + +void tst_qdeclarativestates::anchorChangesRTL3() +{ + QDeclarativeEngine engine; + + QDeclarativeComponent rectComponent(&engine, SRCDIR "/data/anchorChanges3.qml"); + QDeclarativeRectangle *rect = qobject_cast<QDeclarativeRectangle*>(rectComponent.create()); + QVERIFY(rect != 0); + QDeclarativeItemPrivate *rectPrivate = QDeclarativeItemPrivate::get(rect); + + QDeclarativeRectangle *innerRect = qobject_cast<QDeclarativeRectangle*>(rect->findChild<QDeclarativeRectangle*>("MyRect")); + QVERIFY(innerRect != 0); + mirrorAnchors(innerRect); + + QDeclarativeItem *leftGuideline = qobject_cast<QDeclarativeItem*>(rect->findChild<QDeclarativeItem*>("LeftGuideline")); + QVERIFY(leftGuideline != 0); + + QDeclarativeItem *bottomGuideline = qobject_cast<QDeclarativeItem*>(rect->findChild<QDeclarativeItem*>("BottomGuideline")); + QVERIFY(bottomGuideline != 0); + + QDeclarativeListReference list(rect, "states"); + QDeclarativeState *state = qobject_cast<QDeclarativeState*>(list.at(0)); + QVERIFY(state != 0); + + qmlExecuteDeferred(state); + QDeclarativeAnchorChanges *aChanges = qobject_cast<QDeclarativeAnchorChanges*>(state->operationAt(0)); + QVERIFY(aChanges != 0); + + rectPrivate->setState("reanchored"); + QCOMPARE(aChanges->object(), qobject_cast<QDeclarativeItem*>(innerRect)); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->left().item, QDeclarativeItemPrivate::get(leftGuideline)->left().item); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->left().anchorLine, QDeclarativeItemPrivate::get(leftGuideline)->left().anchorLine); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->right().item, rectPrivate->right().item); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->right().anchorLine, rectPrivate->right().anchorLine); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->top().item, rectPrivate->top().item); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->top().anchorLine, rectPrivate->top().anchorLine); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->bottom().item, QDeclarativeItemPrivate::get(bottomGuideline)->bottom().item); + QCOMPARE(QDeclarativeItemPrivate::get(aChanges->object())->anchors()->bottom().anchorLine, QDeclarativeItemPrivate::get(bottomGuideline)->bottom().anchorLine); + + QCOMPARE(innerRect->x(), offsetRTL(leftGuideline, innerRect) - qreal(10)); + QCOMPARE(innerRect->y(), qreal(0)); + // between left side of parent and leftGuideline.x: 10, which has width 0 + QCOMPARE(innerRect->width(), qreal(10)); + QCOMPARE(innerRect->height(), qreal(150)); + + rectPrivate->setState(""); + QCOMPARE(innerRect->x(), offsetRTL(rect, innerRect) - qreal(0)); + QCOMPARE(innerRect->y(), qreal(10)); + // between right side of parent and left side of rightGuideline.x: 150, which has width 0 + QCOMPARE(innerRect->width(), qreal(50)); + QCOMPARE(innerRect->height(), qreal(190)); + + delete rect; +} + //QTBUG-9609 void tst_qdeclarativestates::anchorChangesCrash() { diff --git a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp index dbb822b..8841aa3 100644 --- a/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp +++ b/tests/auto/declarative/qdeclarativetext/tst_qdeclarativetext.cpp @@ -518,20 +518,112 @@ void tst_qdeclarativetext::horizontalAlignment_RightToLeft() QDeclarativeTextPrivate *textPrivate = QDeclarativeTextPrivate::get(text); QVERIFY(textPrivate != 0); - QVERIFY(textPrivate->layout.lineAt(0).x() > canvas->width()/2); + // implicit alignment should follow the reading direction of RTL text + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2); + + // explicitly left aligned text + text->setHAlign(QDeclarativeText::AlignLeft); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignLeft); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2); + + // explicitly right aligned text + text->setHAlign(QDeclarativeText::AlignRight); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2); + + // change to rich text + QString textString = text->text(); + text->setText(QString("<i>") + textString + QString("</i>")); + text->setTextFormat(QDeclarativeText::RichText); + text->resetHAlign(); + + // implicitly aligned rich text should follow the reading direction of text + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignLeft); + + // explicitly left aligned rich text + text->setHAlign(QDeclarativeText::AlignLeft); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignLeft); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignRight); - // "Right" aligned + // explicitly right aligned rich text text->setHAlign(QDeclarativeText::AlignRight); QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); - QVERIFY(textPrivate->layout.lineAt(0).x() < canvas->width()/2); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->textDocument()->defaultTextOption().alignment() & Qt::AlignLeft); - // Center aligned + text->setText(textString); + text->setTextFormat(QDeclarativeText::PlainText); + + // explicitly center aligned text->setHAlign(QDeclarativeText::AlignHCenter); QCOMPARE(text->hAlign(), QDeclarativeText::AlignHCenter); - QVERIFY(textPrivate->layout.lineAt(0).x() < canvas->width()/2); - QVERIFY(textPrivate->layout.lineAt(0).x() + textPrivate->layout.lineAt(0).width() > canvas->width()/2); + QCOMPARE(text->effectiveHAlign(), text->hAlign()); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().right() > canvas->width()/2); + + // reseted alignment should go back to following the text reading direction + text->resetHAlign(); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2); + + // mirror the text item + QDeclarativeItemPrivate::get(text)->setLayoutMirror(true); + + // mirrored implicit alignment should continue to follow the reading direction of the text + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); + QCOMPARE(text->effectiveHAlign(), QDeclarativeText::AlignRight); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2); + + // mirrored explicitly right aligned behaves as left aligned + text->setHAlign(QDeclarativeText::AlignRight); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); + QCOMPARE(text->effectiveHAlign(), QDeclarativeText::AlignLeft); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2); + + // mirrored explicitly left aligned behaves as right aligned + text->setHAlign(QDeclarativeText::AlignLeft); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignLeft); + QCOMPARE(text->effectiveHAlign(), QDeclarativeText::AlignRight); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() > canvas->width()/2); + + // disable mirroring + QDeclarativeItemPrivate::get(text)->setLayoutMirror(false); + text->resetHAlign(); + + // English text should be implicitly left aligned + text->setText("Hello world!"); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignLeft); + QVERIFY(textPrivate->layout.lineAt(0).naturalTextRect().left() < canvas->width()/2); + +#ifndef Q_OS_MAC // QTBUG-18040 + // empty text with implicit alignment follows the system locale-based + // keyboard input direction from QApplication::keyboardInputDirection + text->setText(""); + QCOMPARE(text->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ? + QDeclarativeText::AlignLeft : QDeclarativeText::AlignRight); + text->setHAlign(QDeclarativeText::AlignRight); + QCOMPARE(text->hAlign(), QDeclarativeText::AlignRight); +#endif delete canvas; + +#ifndef Q_OS_MAC // QTBUG-18040 + // alignment of Text with no text set to it + QString componentStr = "import QtQuick 1.0\nText {}"; + QDeclarativeComponent textComponent(&engine); + textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeText *textObject = qobject_cast<QDeclarativeText*>(textComponent.create()); + QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ? + QDeclarativeText::AlignLeft : QDeclarativeText::AlignRight); + delete textObject; +#endif } void tst_qdeclarativetext::verticalAlignment() diff --git a/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp b/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp index f642947..7aac76c 100644 --- a/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp +++ b/tests/auto/declarative/qdeclarativetextedit/tst_qdeclarativetextedit.cpp @@ -110,6 +110,8 @@ private slots: void persistentSelection(); void focusOnPress(); void selection(); + void isRightToLeft_data(); + void isRightToLeft(); void keySelection(); void moveCursorSelection_data(); void moveCursorSelection(); @@ -446,21 +448,109 @@ void tst_qdeclarativetextedit::hAlign_RightToLeft() QVERIFY(textEdit != 0); canvas->show(); + // implicit alignment should follow the reading direction of text + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); - // "Right" align + // explicitly left aligned + textEdit->setHAlign(QDeclarativeTextEdit::AlignLeft); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignLeft); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas->width()/2); + + // explicitly right aligned textEdit->setHAlign(QDeclarativeTextEdit::AlignRight); QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + + QString textString = textEdit->text(); + textEdit->setText(QString("<i>") + textString + QString("</i>")); + textEdit->resetHAlign(); + + // implicitly aligned rich text should follow the reading direction of RTL text + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign()); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + + // explicitly left aligned rich text + textEdit->setHAlign(QDeclarativeTextEdit::AlignLeft); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignLeft); + QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign()); QVERIFY(textEdit->positionToRectangle(0).x() < canvas->width()/2); - // Center align - // Note that position 0 is on the right-hand side + // explicitly right aligned rich text + textEdit->setHAlign(QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign()); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + + textEdit->setText(textString); + + // explicitly center aligned textEdit->setHAlign(QDeclarativeTextEdit::AlignHCenter); QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignHCenter); - QVERIFY(textEdit->positionToRectangle(0).x() - textEdit->width() < canvas->width()/2); QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + // reseted alignment should go back to following the text reading direction + textEdit->resetHAlign(); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + + // mirror the text item + QDeclarativeItemPrivate::get(textEdit)->setLayoutMirror(true); + + // mirrored implicit alignment should continue to follow the reading direction of the text + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), QDeclarativeTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + + // mirrored explicitly right aligned behaves as left aligned + textEdit->setHAlign(QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->effectiveHAlign(), QDeclarativeTextEdit::AlignLeft); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas->width()/2); + + // mirrored explicitly left aligned behaves as right aligned + textEdit->setHAlign(QDeclarativeTextEdit::AlignLeft); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignLeft); + QCOMPARE(textEdit->effectiveHAlign(), QDeclarativeTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + + // disable mirroring + QDeclarativeItemPrivate::get(textEdit)->setLayoutMirror(false); + textEdit->resetHAlign(); + + // English text should be implicitly left aligned + textEdit->setText("Hello world!"); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignLeft); + QVERIFY(textEdit->positionToRectangle(0).x() < canvas->width()/2); + +#ifndef Q_OS_MAC // QTBUG-18040 + // empty text with implicit alignment follows the system locale-based + // keyboard input direction from QApplication::keyboardInputDirection + textEdit->setText(""); + QCOMPARE(textEdit->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ? + QDeclarativeTextEdit::AlignLeft : QDeclarativeTextEdit::AlignRight); + if (QApplication::keyboardInputDirection() == Qt::LeftToRight) + QVERIFY(textEdit->positionToRectangle(0).x() < canvas->width()/2); + else + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); + textEdit->setHAlign(QDeclarativeTextEdit::AlignRight); + QCOMPARE(textEdit->hAlign(), QDeclarativeTextEdit::AlignRight); + QVERIFY(textEdit->positionToRectangle(0).x() > canvas->width()/2); +#endif + delete canvas; + +#ifndef Q_OS_MAC // QTBUG-18040 + // alignment of TextEdit with no text set to it + QString componentStr = "import QtQuick 1.0\nTextEdit {}"; + QDeclarativeComponent textComponent(&engine); + textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeTextEdit *textObject = qobject_cast<QDeclarativeTextEdit*>(textComponent.create()); + QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ? + QDeclarativeTextEdit::AlignLeft : QDeclarativeTextEdit::AlignRight); + delete textObject; +#endif } void tst_qdeclarativetextedit::vAlign() @@ -763,10 +853,70 @@ void tst_qdeclarativetextedit::selection() QVERIFY(textEditObject->selectedText().isNull()); } +void tst_qdeclarativetextedit::isRightToLeft_data() +{ + QTest::addColumn<QString>("text"); + QTest::addColumn<bool>("emptyString"); + QTest::addColumn<bool>("firstCharacter"); + QTest::addColumn<bool>("lastCharacter"); + QTest::addColumn<bool>("middleCharacter"); + QTest::addColumn<bool>("startString"); + QTest::addColumn<bool>("midString"); + QTest::addColumn<bool>("endString"); + + const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647}; + QTest::newRow("Empty") << "" << false << false << false << false << false << false << false; + QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false; + QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false; + QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true; + QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true; + QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false; +} + +void tst_qdeclarativetextedit::isRightToLeft() +{ + QFETCH(QString, text); + QFETCH(bool, emptyString); + QFETCH(bool, firstCharacter); + QFETCH(bool, lastCharacter); + QFETCH(bool, middleCharacter); + QFETCH(bool, startString); + QFETCH(bool, midString); + QFETCH(bool, endString); + + QDeclarativeTextEdit textEdit; + textEdit.setText(text); + + // first test that the right string is delivered to the QString::isRightToLeft() + QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft()); + QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft()); + if (text.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start."); + QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft()); + + // then test that the feature actually works + QCOMPARE(textEdit.isRightToLeft(0,0), emptyString); + QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter); + QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter); + QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter); + QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString); + QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString); + if (text.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start."); + QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString); +} + void tst_qdeclarativetextedit::keySelection() { QDeclarativeView *canvas = createView(SRCDIR "/data/navigation.qml"); canvas->show(); + QApplication::setActiveWindow(canvas); + QTest::qWaitForWindowShown(canvas); + QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas)); canvas->setFocus(); QVERIFY(canvas->rootObject() != 0); diff --git a/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp b/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp index a7a4e5e..796ac23 100644 --- a/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp +++ b/tests/auto/declarative/qdeclarativetextinput/tst_qdeclarativetextinput.cpp @@ -89,6 +89,8 @@ private slots: void font(); void color(); void selection(); + void isRightToLeft_data(); + void isRightToLeft(); void moveCursorSelection_data(); void moveCursorSelection(); void moveCursorSelectionSequence_data(); @@ -113,6 +115,7 @@ private slots: void cursorVisible(); void cursorRectangle(); void navigation(); + void navigation_RTL(); void copyAndPaste(); void canPasteEmpty(); void canPaste(); @@ -439,6 +442,63 @@ void tst_qdeclarativetextinput::selection() delete textinputObject; } +void tst_qdeclarativetextinput::isRightToLeft_data() +{ + QTest::addColumn<QString>("text"); + QTest::addColumn<bool>("emptyString"); + QTest::addColumn<bool>("firstCharacter"); + QTest::addColumn<bool>("lastCharacter"); + QTest::addColumn<bool>("middleCharacter"); + QTest::addColumn<bool>("startString"); + QTest::addColumn<bool>("midString"); + QTest::addColumn<bool>("endString"); + + const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647}; + QTest::newRow("Empty") << "" << false << false << false << false << false << false << false; + QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false; + QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false; + QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true; + QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true; + QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false; +} + +void tst_qdeclarativetextinput::isRightToLeft() +{ + QFETCH(QString, text); + QFETCH(bool, emptyString); + QFETCH(bool, firstCharacter); + QFETCH(bool, lastCharacter); + QFETCH(bool, middleCharacter); + QFETCH(bool, startString); + QFETCH(bool, midString); + QFETCH(bool, endString); + + QDeclarativeTextInput textInput; + textInput.setText(text); + + // first test that the right string is delivered to the QString::isRightToLeft() + QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft()); + QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft()); + QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft()); + QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft()); + QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft()); + QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft()); + if (text.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start."); + QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft()); + + // then test that the feature actually works + QCOMPARE(textInput.isRightToLeft(0,0), emptyString); + QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter); + QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter); + QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter); + QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString); + QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString); + if (text.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start."); + QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString); +} + void tst_qdeclarativetextinput::moveCursorSelection_data() { QTest::addColumn<QString>("testStr"); @@ -991,18 +1051,93 @@ void tst_qdeclarativetextinput::horizontalAlignment_RightToLeft() QVERIFY(textInputPrivate != 0); QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); - // "Right" Align - textInput->setHAlign(QDeclarativeTextInput::AlignRight); + // implicit alignment should follow the reading direction of RTL text QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); + + // explicitly left aligned + textInput->setHAlign(QDeclarativeTextInput::AlignLeft); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignLeft); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); QVERIFY(-textInputPrivate->hscroll < canvas->width()/2); - // Center Align + // explicitly right aligned + textInput->setHAlign(QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignRight); + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); + + // explicitly center aligned textInput->setHAlign(QDeclarativeTextInput::AlignHCenter); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignHCenter); QVERIFY(-textInputPrivate->hscroll < canvas->width()/2); QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width() > canvas->width()/2); + // reseted alignment should go back to following the text reading direction + textInput->resetHAlign(); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); + + // mirror the text item + QDeclarativeItemPrivate::get(textInput)->setLayoutMirror(true); + + // mirrored implicit alignment should continue to follow the reading direction of the text + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); + + // explicitly right aligned behaves as left aligned + textInput->setHAlign(QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->effectiveHAlign(), QDeclarativeTextInput::AlignLeft); + QVERIFY(-textInputPrivate->hscroll < canvas->width()/2); + + // mirrored explicitly left aligned behaves as right aligned + textInput->setHAlign(QDeclarativeTextInput::AlignLeft); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignLeft); + QCOMPARE(textInput->effectiveHAlign(), QDeclarativeTextInput::AlignRight); + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); + + // disable mirroring + QDeclarativeItemPrivate::get(textInput)->setLayoutMirror(false); + QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign()); + textInput->resetHAlign(); + + // English text should be implicitly left aligned + textInput->setText("Hello world!"); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignLeft); + QVERIFY(-textInputPrivate->hscroll < canvas->width()/2); + +#ifndef Q_OS_MAC // QTBUG-18040 + // empty text with implicit alignment follows the system locale-based + // keyboard input direction from QApplication::keyboardInputDirection + textInput->setText(""); + QCOMPARE(textInput->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ? + QDeclarativeTextInput::AlignLeft : QDeclarativeTextInput::AlignRight); + if (QApplication::keyboardInputDirection() == Qt::LeftToRight) + QVERIFY(-textInputPrivate->hscroll < canvas->width()/2); + else + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); + textInput->setHAlign(QDeclarativeTextInput::AlignRight); + QCOMPARE(textInput->hAlign(), QDeclarativeTextInput::AlignRight); + QVERIFY(-textInputPrivate->hscroll > canvas->width()/2); +#endif + delete canvas; + +#ifndef Q_OS_MAC // QTBUG-18040 + // alignment of TextInput with no text set to it + QString componentStr = "import QtQuick 1.0\nTextInput {}"; + QDeclarativeComponent textComponent(&engine); + textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile("")); + QDeclarativeTextInput *textObject = qobject_cast<QDeclarativeTextInput*>(textComponent.create()); + QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ? + QDeclarativeTextInput::AlignLeft : QDeclarativeTextInput::AlignRight); + delete textObject; +#endif } void tst_qdeclarativetextinput::positionAt() @@ -1314,6 +1449,45 @@ void tst_qdeclarativetextinput::navigation() delete canvas; } +void tst_qdeclarativetextinput::navigation_RTL() +{ + QDeclarativeView *canvas = createView(SRCDIR "/data/navigation.qml"); + canvas->show(); + canvas->setFocus(); + + QVERIFY(canvas->rootObject() != 0); + + QDeclarativeTextInput *input = qobject_cast<QDeclarativeTextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("myInput"))); + + QVERIFY(input != 0); + const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647}; + input->setText(QString::fromUtf16(arabic_str, 11)); + + input->setCursorPosition(0); + QTRY_VERIFY(input->hasActiveFocus() == true); + + // move off + simulateKey(canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == false); + + // move back + simulateKey(canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == true); + + input->setCursorPosition(input->text().length()); + QVERIFY(input->hasActiveFocus() == true); + + // move off + simulateKey(canvas, Qt::Key_Left); + QVERIFY(input->hasActiveFocus() == false); + + // move back + simulateKey(canvas, Qt::Key_Right); + QVERIFY(input->hasActiveFocus() == true); + + delete canvas; +} + void tst_qdeclarativetextinput::copyAndPaste() { #ifndef QT_NO_CLIPBOARD diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.0.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.0.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.0.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.1.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.1.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.1.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.10.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.10.png Binary files differnew file mode 100644 index 0000000..1ccab41 --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.10.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.11.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.11.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.11.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.12.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.12.png Binary files differnew file mode 100644 index 0000000..f25bd7c --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.12.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.2.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.2.png Binary files differnew file mode 100644 index 0000000..f25bd7c --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.2.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.3.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.3.png Binary files differnew file mode 100644 index 0000000..dad1de4 --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.3.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.4.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.4.png Binary files differnew file mode 100644 index 0000000..cd4f23a --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.4.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.5.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.5.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.5.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.6.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.6.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.6.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.7.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.7.png Binary files differnew file mode 100644 index 0000000..f25bd7c --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.7.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.8.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.8.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.8.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.9.png b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.9.png Binary files differnew file mode 100644 index 0000000..160155e --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.9.png diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.qml b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.qml new file mode 100644 index 0000000..e858c11 --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/data/reanchor.qml @@ -0,0 +1,1499 @@ +import Qt.VisualTest 4.7 + +VisualTest { + Frame { + msec: 0 + } + Frame { + msec: 16 + image: "reanchor.0.png" + } + Frame { + msec: 32 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 48 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 64 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 80 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 96 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 112 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 128 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 144 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 160 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 176 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 192 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 208 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 224 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 240 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 256 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 272 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 288 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 304 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 320 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 336 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 352 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 368 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 384 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 400 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 416 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 432 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 448 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 464 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 480 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 496 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 512 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 528 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 544 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 560 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 576 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 592 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 608 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 624 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 640 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 656 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 672 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 688 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 704 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 720 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 736 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 752 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 768 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 784 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 800 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 816 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 832 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 848 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 864 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 880 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 896 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 912 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 928 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 944 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 960 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 976 + image: "reanchor.1.png" + } + Frame { + msec: 992 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1008 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1024 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1040 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1056 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Mouse { + type: 2 + button: 1 + buttons: 1 + x: 164; y: 196 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 1072 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1088 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1104 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1120 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1136 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Mouse { + type: 3 + button: 1 + buttons: 0 + x: 164; y: 196 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 1152 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 1168 + hash: "f7814217626627ce70ca0e9487354ba9" + } + Frame { + msec: 1184 + hash: "7825b2b77e441ca6f46dbca80c7fe602" + } + Frame { + msec: 1200 + hash: "0ac443a9946b0bcf8db768af7d16d51e" + } + Frame { + msec: 1216 + hash: "c943d5d46f0d527690f38a9c8bd7be51" + } + Frame { + msec: 1232 + hash: "38151db0c9964d33bcb2ff155ebd468c" + } + Frame { + msec: 1248 + hash: "0fb8c53587a95a12cced6d30018edec1" + } + Frame { + msec: 1264 + hash: "2c684a649652270a638aca41a80e327c" + } + Frame { + msec: 1280 + hash: "60dd5c448ef8b97ec13ad3140a584229" + } + Frame { + msec: 1296 + hash: "d564f28f9d528daca729db6fab163b6c" + } + Frame { + msec: 1312 + hash: "4c07b33632ec4f30ee31141099c15a88" + } + Frame { + msec: 1328 + hash: "9facfd27fa16ee9d493e7fb7bcfadbf8" + } + Frame { + msec: 1344 + hash: "fc0fbb8aac8f389841e615be1e7b06de" + } + Frame { + msec: 1360 + hash: "579c18fa201b5609276c761ffd42df33" + } + Frame { + msec: 1376 + hash: "5b3630c37acfc2599a5a8b2e11aaa34c" + } + Frame { + msec: 1392 + hash: "2c1ee8aca06dccf0d39287721bf76aa7" + } + Frame { + msec: 1408 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1424 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1440 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1456 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1472 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1488 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1504 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1520 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1536 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1552 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1568 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1584 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1600 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1616 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1632 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1648 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1664 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1680 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1696 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1712 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1728 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1744 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1760 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1776 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1792 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1808 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1824 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1840 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1856 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1872 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1888 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1904 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1920 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1936 + image: "reanchor.2.png" + } + Frame { + msec: 1952 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1968 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 1984 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2000 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2016 + hash: "c03bb338fff252a100b080366ac907b5" + } + Mouse { + type: 2 + button: 1 + buttons: 1 + x: 170; y: 120 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2032 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2048 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2064 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2080 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2096 + hash: "c03bb338fff252a100b080366ac907b5" + } + Mouse { + type: 3 + button: 1 + buttons: 0 + x: 170; y: 120 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2112 + hash: "c03bb338fff252a100b080366ac907b5" + } + Frame { + msec: 2128 + hash: "e9d7372c17ca1510eb15faff5d0794b2" + } + Frame { + msec: 2144 + hash: "60f897e2b9594c4b5c02ce2fbdf9ae3c" + } + Frame { + msec: 2160 + hash: "c35ead9a8e682e8f3c0a091d232310f7" + } + Frame { + msec: 2176 + hash: "272632b0568391022590edc09ea30e28" + } + Frame { + msec: 2192 + hash: "9d4cdb31b01e86a31627e3ff9bb64100" + } + Frame { + msec: 2208 + hash: "5ee65b0290721fe47508c6435c18554b" + } + Frame { + msec: 2224 + hash: "8dd65e1a9417318d793d2027de4fe6ae" + } + Frame { + msec: 2240 + hash: "bcce6d1fd7d2c1539ad9ac42c0552d5e" + } + Frame { + msec: 2256 + hash: "e01f5850113c178da3383406fe73d6e0" + } + Frame { + msec: 2272 + hash: "968fc6b2bf6b7d43e05254339cf6123f" + } + Frame { + msec: 2288 + hash: "30f25fdde31e13934e328fa1d2655ccb" + } + Frame { + msec: 2304 + hash: "f58a21e96037813c9dd7f933405c9b11" + } + Frame { + msec: 2320 + hash: "1fe42c887f2eaf7696fcf0b8b884d0fd" + } + Frame { + msec: 2336 + hash: "848a27b9e4f4c0bcc1a11d6dba7ce92b" + } + Frame { + msec: 2352 + hash: "ca92736257db83e39f54b04325201942" + } + Frame { + msec: 2368 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 2384 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Mouse { + type: 2 + button: 1 + buttons: 1 + x: 134; y: 106 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2400 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 2416 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 2432 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 2448 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 2464 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Mouse { + type: 3 + button: 1 + buttons: 0 + x: 134; y: 106 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2480 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 2496 + hash: "9082504eee5e0c3cbef9fd9545f09dcb" + } + Frame { + msec: 2512 + hash: "dbe5169edb4400c74841a8af64e0949f" + } + Frame { + msec: 2528 + hash: "d588405fc5e2423cdb954c5624172209" + } + Frame { + msec: 2544 + hash: "ed2b273ea36fb7d8feaca4d5dae72f81" + } + Frame { + msec: 2560 + hash: "5249e4824eb169b5ee3f7fb52fe09aa7" + } + Frame { + msec: 2576 + hash: "2838eff2a1a299c9e47cf78be99172ca" + } + Frame { + msec: 2592 + hash: "c47f6a937a4a6ef045159d7ba04de8af" + } + Frame { + msec: 2608 + hash: "fd3bc1b9ba2629bccb0fec04deffcdad" + } + Frame { + msec: 2624 + hash: "54c9b8599a32ac95aff324977b34f7e6" + } + Frame { + msec: 2640 + hash: "cc5652a05828146cdc9c9b8430f5f59c" + } + Frame { + msec: 2656 + hash: "ce5815fb51a4bd697a2fde46084e118b" + } + Frame { + msec: 2672 + hash: "01dfd2604263f1fd24382ce876af10f9" + } + Frame { + msec: 2688 + hash: "45ea282d20ee9e345eb2cac8c22c42e0" + } + Frame { + msec: 2704 + hash: "afd26ac9776e57c94e4b52ebfeb7206c" + } + Frame { + msec: 2720 + hash: "97aeed321d4d92cb1ec236d2a98fbe9b" + } + Mouse { + type: 4 + button: 1 + buttons: 1 + x: 134; y: 106 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2736 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 2752 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 2768 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 2784 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 2800 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Mouse { + type: 3 + button: 1 + buttons: 0 + x: 134; y: 106 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2816 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 2832 + hash: "81b8228c6aeefe8072b7704f11e6707e" + } + Frame { + msec: 2848 + hash: "617e416bf117a51b756c90321ebb1449" + } + Frame { + msec: 2864 + hash: "656d8d5d54c9ee137aceb519aff72cce" + } + Frame { + msec: 2880 + hash: "94ba3b6f558c010cdd32f54cce436388" + } + Mouse { + type: 2 + button: 1 + buttons: 1 + x: 134; y: 106 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 2896 + image: "reanchor.3.png" + } + Frame { + msec: 2912 + hash: "0bc822fdd4caac17aab80e8601d3a523" + } + Frame { + msec: 2928 + hash: "886d0407ac76d7344f7a314f07b3efff" + } + Frame { + msec: 2944 + hash: "eb6c46af5037f24348edbe0dda48fb62" + } + Frame { + msec: 2960 + hash: "1c578a1eeb67c6833241bcb3214f06fb" + } + Frame { + msec: 2976 + hash: "55f1631ef567217a5945b2a23c59b549" + } + Frame { + msec: 2992 + hash: "25fdd4d54ddb035b082dc3a0d0816114" + } + Mouse { + type: 3 + button: 1 + buttons: 0 + x: 134; y: 106 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 3008 + hash: "25fdd4d54ddb035b082dc3a0d0816114" + } + Frame { + msec: 3024 + hash: "efd61e7c1aaffec77bd3d2de6645b2c0" + } + Frame { + msec: 3040 + hash: "02ac5ca0fa7d2ec3903fccd5dc556fa5" + } + Frame { + msec: 3056 + hash: "daf52e45b8fc68f74e424554074678cc" + } + Frame { + msec: 3072 + hash: "9e2def87e83b0c4b9f26684665aa1e51" + } + Frame { + msec: 3088 + hash: "0e72fc762cc9a061e91692376d65d292" + } + Frame { + msec: 3104 + hash: "c5ac37e4a5250b35a4976bcb31505cca" + } + Frame { + msec: 3120 + hash: "eefe6bb7963c580c68198ee6098a36f4" + } + Frame { + msec: 3136 + hash: "7b78d77ac11b72d1fb827ebb66a04c8e" + } + Frame { + msec: 3152 + hash: "ce5815fb51a4bd697a2fde46084e118b" + } + Frame { + msec: 3168 + hash: "94ba3b6f558c010cdd32f54cce436388" + } + Frame { + msec: 3184 + hash: "61a56140e5a6a2bfcee5c6322b37e130" + } + Frame { + msec: 3200 + hash: "a67b22c0a966fe3fbe869497dc00960f" + } + Frame { + msec: 3216 + hash: "4edd212676ac93ae761039e80f989349" + } + Frame { + msec: 3232 + hash: "fea5797441d65625c400238f73d94807" + } + Frame { + msec: 3248 + hash: "23e9209ff0257343016cffdf7ea6571c" + } + Frame { + msec: 3264 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3280 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3296 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3312 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3328 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3344 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3360 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3376 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3392 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3408 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3424 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3440 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3456 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3472 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3488 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3504 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3520 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3536 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3552 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3568 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3584 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3600 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3616 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3632 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3648 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3664 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3680 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3696 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3712 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3728 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3744 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3760 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3776 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3792 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3808 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3824 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3840 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3856 + image: "reanchor.4.png" + } + Frame { + msec: 3872 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3888 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3904 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3920 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3936 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3952 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3968 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 3984 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4000 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4016 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4032 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4048 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4064 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4080 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4096 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4112 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4128 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4144 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4160 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4176 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Mouse { + type: 2 + button: 1 + buttons: 1 + x: 124; y: 113 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 4192 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4208 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4224 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4240 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4256 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Mouse { + type: 3 + button: 1 + buttons: 0 + x: 124; y: 113 + modifiers: 0 + sendToViewport: true + } + Frame { + msec: 4272 + hash: "3a1fc9be558078e35a9828e411847c19" + } + Frame { + msec: 4288 + hash: "81b8228c6aeefe8072b7704f11e6707e" + } + Frame { + msec: 4304 + hash: "617e416bf117a51b756c90321ebb1449" + } + Frame { + msec: 4320 + hash: "656d8d5d54c9ee137aceb519aff72cce" + } + Frame { + msec: 4336 + hash: "94ba3b6f558c010cdd32f54cce436388" + } + Frame { + msec: 4352 + hash: "5b0679ff3730cba4ac026e89c7811fbe" + } + Frame { + msec: 4368 + hash: "0bc822fdd4caac17aab80e8601d3a523" + } + Frame { + msec: 4384 + hash: "886d0407ac76d7344f7a314f07b3efff" + } + Frame { + msec: 4400 + hash: "eb6c46af5037f24348edbe0dda48fb62" + } + Frame { + msec: 4416 + hash: "1c578a1eeb67c6833241bcb3214f06fb" + } + Frame { + msec: 4432 + hash: "55f1631ef567217a5945b2a23c59b549" + } + Frame { + msec: 4448 + hash: "25fdd4d54ddb035b082dc3a0d0816114" + } + Frame { + msec: 4464 + hash: "295ea6ff4d3c2c7de0cfbc29b2bd2c38" + } + Frame { + msec: 4480 + hash: "26b978ab645c04731703bcf15ac34a11" + } + Frame { + msec: 4496 + hash: "0db4c2515b89506df51732c4b9bf75dc" + } + Frame { + msec: 4512 + hash: "3cf30f3a06e325e195a4a7dec1e04c01" + } + Frame { + msec: 4528 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4544 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4560 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4576 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4592 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4608 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4624 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4640 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4656 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4672 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4688 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4704 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4720 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4736 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4752 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4768 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4784 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4800 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4816 + image: "reanchor.5.png" + } + Frame { + msec: 4832 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4848 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4864 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4880 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4896 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4912 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4928 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4944 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4960 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4976 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 4992 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5008 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5024 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5040 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5056 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5072 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5088 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5104 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5120 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5136 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5152 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5168 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5184 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5200 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5216 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5232 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5248 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5264 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5280 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5296 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5312 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5328 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5344 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5360 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5376 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5392 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5408 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5424 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5440 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5456 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5472 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5488 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5504 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5520 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5536 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5552 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5568 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } + Frame { + msec: 5584 + hash: "0009d8bfdfaed2a4f05aacb7a7992234" + } +} diff --git a/tests/auto/declarative/qmlvisual/animation/reanchorRTL/reanchor.qml b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/reanchor.qml new file mode 100644 index 0000000..ba37737 --- /dev/null +++ b/tests/auto/declarative/qmlvisual/animation/reanchorRTL/reanchor.qml @@ -0,0 +1,69 @@ +import QtQuick 1.1 + +Rectangle { + id: container + width: 200; height: 200 + Rectangle { + id: myRect + anchors.layoutDirection: Qt.RightToLeft + objectName: "MyRect" + color: "green"; + anchors.left: parent.left + anchors.right: rightGuideline.left + anchors.top: topGuideline.top + anchors.bottom: container.bottom + } + Item { id: leftGuideline; x: 10 } + Item { id: rightGuideline; x: 150 } + Item { id: topGuideline; y: 10 } + Item { id: bottomGuideline; y: 150 } + Item { id: topGuideline2; y: 50 } + Item { id: bottomGuideline2; y: 175 } + MouseArea { + id: wholeArea + anchors.fill: parent + onClicked: { + if (container.state == "") { + container.state = "reanchored"; + } else if (container.state == "reanchored") { + container.state = "reanchored2"; + } else if (container.state == "reanchored2") + container.state = "reanchored"; + } + } + + states: [ State { + name: "reanchored" + AnchorChanges { + target: myRect; + anchors.left: leftGuideline.left + anchors.right: container.right + anchors.top: container.top + anchors.bottom: bottomGuideline.bottom + } + }, State { + name: "reanchored2" + AnchorChanges { + target: myRect; + anchors.left: undefined + anchors.right: undefined + anchors.top: topGuideline2.top + anchors.bottom: bottomGuideline2.bottom + } + }] + + transitions: Transition { + AnchorAnimation { } + } + + MouseArea { + width: 50; height: 50 + anchors.right: parent.right + anchors.bottom: parent.bottom + onClicked: { + container.state = ""; + } + } + + state: "reanchored" +} diff --git a/tests/auto/mediaobject/dummy/dummy.pro b/tests/auto/mediaobject/dummy/dummy.pro index 88b864b..c81411c 100644 --- a/tests/auto/mediaobject/dummy/dummy.pro +++ b/tests/auto/mediaobject/dummy/dummy.pro @@ -1,7 +1,7 @@ TEMPLATE = lib isEmpty(QT_MAJOR_VERSION) { - VERSION=4.6.4 + VERSION=4.7.3 } else { VERSION=$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION} } diff --git a/tests/auto/nativeimagehandleprovider/nativeimagehandleprovider.pro b/tests/auto/nativeimagehandleprovider/nativeimagehandleprovider.pro new file mode 100644 index 0000000..fb8ecb0 --- /dev/null +++ b/tests/auto/nativeimagehandleprovider/nativeimagehandleprovider.pro @@ -0,0 +1,6 @@ +load(qttest_p4) +SOURCES += tst_nativeimagehandleprovider.cpp +symbian { + LIBS += -lfbscli -lbitgdi + contains(QT_CONFIG, openvg): QT *= openvg +} diff --git a/tests/auto/nativeimagehandleprovider/tst_nativeimagehandleprovider.cpp b/tests/auto/nativeimagehandleprovider/tst_nativeimagehandleprovider.cpp new file mode 100644 index 0000000..3a315f2 --- /dev/null +++ b/tests/auto/nativeimagehandleprovider/tst_nativeimagehandleprovider.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtGui/private/qpixmapdata_p.h> +#include <QtGui/private/qnativeimagehandleprovider_p.h> +#include <QScopedPointer> +#include <QPixmap> +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) +#include <fbs.h> +#include <bitdev.h> +#include <QtOpenVG/private/qpixmapdata_vg_p.h> +#endif + +QPixmap pixmapFromNativeImageHandleProvider(QNativeImageHandleProvider *source) +{ +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) + if (!source) + return QPixmap(); + QScopedPointer<QPixmapData> pd(QPixmapData::create(0, 0, QPixmapData::PixmapType)); + pd->fromNativeType(source, QPixmapData::NativeImageHandleProvider); + return QPixmap(pd.take()); +#else + Q_UNUSED(source); + return QPixmap(); +#endif +} + +class DummyProvider : public QNativeImageHandleProvider +{ +public: + void get(void **handle, QString *type); + void release(void *handle, const QString &type); +}; + +void DummyProvider::get(void **handle, QString *type) +{ + *handle = (void *) 0x12345678; + *type = "some dummy type"; +} + +void DummyProvider::release(void *handle, const QString &type) +{ + Q_UNUSED(handle); + Q_UNUSED(type); +} + +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) +class BitmapProvider : public QNativeImageHandleProvider +{ +public: + BitmapProvider() : bmp(0), refCount(0), w(50), h(60) { } + void get(void **handle, QString *type); + void release(void *handle, const QString &type); + + CFbsBitmap *bmp; + int refCount, w, h; + void *returnedHandle; + QString returnedType; +}; + +void BitmapProvider::get(void **handle, QString *type) +{ + // There may not be a release() if the get() fails so don't bother with + // refcounting in such cases. + if (bmp) + ++refCount; + returnedType = QLatin1String("CFbsBitmap"); + returnedHandle = bmp; + *handle = returnedHandle; + *type = returnedType; +} + +void BitmapProvider::release(void *handle, const QString &type) +{ + if (handle == returnedHandle && type == returnedType && returnedHandle) { + --refCount; + } +} +#endif // symbian & openvg + +class tst_NativeImageHandleProvider : public QObject +{ + Q_OBJECT + +public: + tst_NativeImageHandleProvider() { } + +private slots: + void create(); + void bitmap(); + void hibernate(); +}; + +void tst_NativeImageHandleProvider::create() +{ + QPixmap pm = pixmapFromNativeImageHandleProvider(0); + QVERIFY(pm.isNull()); + QPixmap tmp(10, 20); + if (tmp.pixmapData()->classId() == QPixmapData::OpenVGClass) { + // Verify that null pixmap is properly returned when get() provides bogus results. + DummyProvider prov; + pm = pixmapFromNativeImageHandleProvider(&prov); + QVERIFY(pm.isNull()); + pm = QPixmap(); + } else { + QSKIP("Not openvg, skipping non-trivial tests", SkipSingle); + } +} + +void tst_NativeImageHandleProvider::bitmap() +{ +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) + QPixmap tmp(10, 20); + if (tmp.pixmapData()->classId() == QPixmapData::OpenVGClass) { + BitmapProvider prov; + + // This should fail because of null ptr. + QPixmap pm = pixmapFromNativeImageHandleProvider(&prov); + QVERIFY(pm.isNull()); + pm = QPixmap(); + QCOMPARE(prov.refCount, 0); + + prov.bmp = new CFbsBitmap; + QCOMPARE(prov.bmp->Create(TSize(prov.w, prov.h), EColor16MAP), KErrNone); + CFbsBitmapDevice *bitmapDevice = CFbsBitmapDevice::NewL(prov.bmp); + CBitmapContext *bitmapContext = 0; + QCOMPARE(bitmapDevice->CreateBitmapContext(bitmapContext), KErrNone); + TRgb symbianColor = TRgb(255, 200, 100); + bitmapContext->SetBrushColor(symbianColor); + bitmapContext->Clear(); + delete bitmapContext; + delete bitmapDevice; + + pm = pixmapFromNativeImageHandleProvider(&prov); + QVERIFY(!pm.isNull()); + QCOMPARE(pm.width(), prov.w); + QCOMPARE(pm.height(), prov.h); + QVERIFY(prov.refCount == 1); + QImage img = pm.toImage(); + QVERIFY(prov.refCount == 1); + QRgb pix = img.pixel(QPoint(1, 2)); + QCOMPARE(qRed(pix), symbianColor.Red()); + QCOMPARE(qGreen(pix), symbianColor.Green()); + QCOMPARE(qBlue(pix), symbianColor.Blue()); + + pm = QPixmap(); // should result in calling release + QCOMPARE(prov.refCount, 0); + delete prov.bmp; + } else { + QSKIP("Not openvg", SkipSingle); + } +#else + QSKIP("Not applicable", SkipSingle); +#endif +} + +void tst_NativeImageHandleProvider::hibernate() +{ +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) + QPixmap tmp(10, 20); + if (tmp.pixmapData()->classId() == QPixmapData::OpenVGClass) { + BitmapProvider prov; + prov.bmp = new CFbsBitmap; + QCOMPARE(prov.bmp->Create(TSize(prov.w, prov.h), EColor16MAP), KErrNone); + + QPixmap pm = pixmapFromNativeImageHandleProvider(&prov); + QCOMPARE(prov.refCount, 1); + + QVGPixmapData *vgpd = static_cast<QVGPixmapData *>(pm.pixmapData()); + vgpd->hibernate(); + QCOMPARE(prov.refCount, 0); + + // Calling toVGImage() may cause some warnings as we don't have a gui initialized, + // but the only thing we care about here is get() being called. + vgpd->toVGImage(); + QCOMPARE(prov.refCount, 1); + + pm = QPixmap(); + QCOMPARE(prov.refCount, 0); + delete prov.bmp; + } else { + QSKIP("Not openvg", SkipSingle); + } +#else + QSKIP("Not applicable", SkipSingle); +#endif +} + +int main(int argc, char *argv[]) +{ + QApplication::setGraphicsSystem("openvg"); + QApplication app(argc, argv); + tst_NativeImageHandleProvider tc; + return QTest::qExec(&tc, argc, argv); +} + +#include "tst_nativeimagehandleprovider.moc" diff --git a/tests/auto/other.pro b/tests/auto/other.pro index 512cd25..8819879 100644 --- a/tests/auto/other.pro +++ b/tests/auto/other.pro @@ -34,7 +34,8 @@ SUBDIRS=\ qvariant \ qwidget \ qworkspace \ - windowsmobile + windowsmobile \ + nativeimagehandleprovider contains(QT_CONFIG, OdfWriter):SUBDIRS += qzip qtextodfwriter mac: { diff --git a/tests/auto/qdesktopwidget/tst_qdesktopwidget.cpp b/tests/auto/qdesktopwidget/tst_qdesktopwidget.cpp index 0256b82..f66b849 100644 --- a/tests/auto/qdesktopwidget/tst_qdesktopwidget.cpp +++ b/tests/auto/qdesktopwidget/tst_qdesktopwidget.cpp @@ -127,7 +127,7 @@ void tst_QDesktopWidget::screenNumberForQWidget() QWidget widget; widget.show(); - QApplication::processEvents(); + QTest::qWaitForWindowShown(&widget); QVERIFY(widget.isVisible()); int widgetScreen = desktop.screenNumber(&widget); @@ -142,7 +142,9 @@ void tst_QDesktopWidget::screenNumberForQPoint() QRect allScreens; for (int i = 0; i < desktopWidget->numScreens(); ++i) { QRect screenGeometry = desktopWidget->screenGeometry(i); +#if !defined(Q_OS_SYMBIAN) QCOMPARE(desktopWidget->screenNumber(screenGeometry.center()), i); +#endif allScreens |= screenGeometry; } @@ -180,7 +182,6 @@ void tst_QDesktopWidget::screenGeometry() total = desktopWidget->screenGeometry(i); available = desktopWidget->availableGeometry(i); } - QVERIFY(total.contains(r)); } QTEST_MAIN(tst_QDesktopWidget) diff --git a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp index d446ca7..9ff086c 100644 --- a/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp +++ b/tests/auto/qgraphicsscene/tst_qgraphicsscene.cpp @@ -289,6 +289,7 @@ private slots: void taskQTBUG_7863_paintIntoCacheWithTransparentParts(); void taskQT_3674_doNotCrash(); void taskQTBUG_15977_renderWithDeviceCoordinateCache(); + void taskQTBUG_16401_focusItem(); }; void tst_QGraphicsScene::initTestCase() @@ -4680,5 +4681,35 @@ void tst_QGraphicsScene::taskQTBUG_15977_renderWithDeviceCoordinateCache() QCOMPARE(image, expected); } +void tst_QGraphicsScene::taskQTBUG_16401_focusItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + QGraphicsRectItem *rect = scene.addRect(0, 0, 100, 100); + rect->setFlag(QGraphicsItem::ItemIsFocusable); + + view.show(); + QTest::qWaitForWindowShown(&view); + QApplication::setActiveWindow(&view); + + QVERIFY(!scene.focusItem()); + + rect->setFocus(); + QCOMPARE(scene.focusItem(), rect); + QFocusEvent focusOut(QEvent::FocusOut); + QApplication::sendEvent(&view, &focusOut); + QVERIFY(!scene.focusItem()); + QFocusEvent focusIn(QEvent::FocusIn); + QApplication::sendEvent(&view, &focusIn); + QCOMPARE(scene.focusItem(), rect); + + rect->clearFocus(); + QVERIFY(!scene.focusItem()); + QApplication::sendEvent(&view, &focusOut); + QVERIFY(!scene.focusItem()); + QApplication::sendEvent(&view, &focusIn); + QVERIFY(!scene.focusItem()); +} + QTEST_MAIN(tst_QGraphicsScene) #include "tst_qgraphicsscene.moc" diff --git a/tests/auto/qpixmap/qpixmap.pro b/tests/auto/qpixmap/qpixmap.pro index 185ec1a..e73c130 100644 --- a/tests/auto/qpixmap/qpixmap.pro +++ b/tests/auto/qpixmap/qpixmap.pro @@ -25,6 +25,7 @@ wince*: { LIBS += -lfbscli.dll -lbitgdi.dll -lgdi.dll contains(QT_CONFIG, openvg) { LIBS += $$QMAKE_LIBS_OPENVG + QT *= openvg } } else { DEFINES += SRCDIR=\\\"$$PWD\\\" diff --git a/tests/auto/qpixmap/tst_qpixmap.cpp b/tests/auto/qpixmap/tst_qpixmap.cpp index a8f8b7b..480893a 100644 --- a/tests/auto/qpixmap/tst_qpixmap.cpp +++ b/tests/auto/qpixmap/tst_qpixmap.cpp @@ -68,6 +68,10 @@ #include <fbs.h> #include <gdi.h> #include <bitdev.h> +#if !defined(QT_NO_OPENVG) +#include <QtOpenVG/qvg.h> +#include <QtOpenVG/private/qpixmapdata_vg_p.h> +#endif #endif #ifdef Q_WS_X11 @@ -187,6 +191,10 @@ private slots: void toImageDeepCopy(); void loadAsBitmapOrPixmap(); + +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) + void vgImageReadBack(); +#endif }; static bool lenientCompare(const QPixmap &actual, const QPixmap &expected) @@ -1783,6 +1791,104 @@ void tst_QPixmap::toImageDeepCopy() QVERIFY(first != second); } +#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_OPENVG) +Q_OPENVG_EXPORT VGImage qPixmapToVGImage(const QPixmap& pixmap); +class FriendlyVGPixmapData : public QVGPixmapData +{ +public: + FriendlyVGPixmapData(PixelType type) : QVGPixmapData(type) { } + bool sourceIsNull() { return source.isNull(); } + friend QPixmap pixmapFromVGImage(VGImage image); +}; +QPixmap pixmapFromVGImage(VGImage image) +{ + if (image != VG_INVALID_HANDLE) { + int w = vgGetParameteri(image, VG_IMAGE_WIDTH); + int h = vgGetParameteri(image, VG_IMAGE_HEIGHT); + FriendlyVGPixmapData *pd = new FriendlyVGPixmapData(QPixmapData::PixmapType); + pd->resize(w, h); + pd->vgImage = image; + pd->recreate = false; + pd->prevSize = QSize(pd->w, pd->h); + return QPixmap(pd); + } + return QPixmap(); +} +class Content : public QWidget +{ +public: + void paintEvent(QPaintEvent *) { + QPainter painter(this); + QColor testPixel(qRgb(200, 150, 100)); + if (pm.isNull()) { // first phase: create a VGImage + painter.beginNativePainting(); + vgimage = vgCreateImage(VG_sARGB_8888_PRE, w, h, VG_IMAGE_QUALITY_FASTER); + QImage img(20, 10, QImage::Format_ARGB32_Premultiplied); + img.fill(qRgb(0, 0, 0)); + QPainter p(&img); + p.fillRect(0, 0, img.width(), img.height(), testPixel); + p.end(); + vgImageSubData(vgimage, img.bits(), img.bytesPerLine(), VG_sARGB_8888_PRE, 0, 0, img.width(), img.height()); + // Now the area 0,0 20x10 (in OpenVG coords) is filled with some color. + painter.endNativePainting(); + } else { // second phase: check if readback works + painter.drawPixmap(0, 0, pm); + // Drawing should not cause readback, this is important for performance; + noreadback_ok = static_cast<FriendlyVGPixmapData *>(pm.pixmapData())->sourceIsNull(); + // However toImage() requires readback. + QImage img = pm.toImage(); + readback_ok = img.width() == pm.width(); + readback_ok &= img.height() == pm.height(); + readback_ok &= !static_cast<FriendlyVGPixmapData *>(pm.pixmapData())->sourceIsNull(); + uint pix = img.pixel(1, 1); + content_ok = qRed(pix) == testPixel.red(); + content_ok &= qGreen(pix) == testPixel.green(); + content_ok &= qBlue(pix) == testPixel.blue(); + pix = img.pixel(img.width() - 1, img.height() - 1); + content_ok &= qRed(pix) == 0; + content_ok &= qGreen(pix) == 0; + content_ok &= qBlue(pix) == 0; + } + } + int w; + int h; + VGImage vgimage; + QPixmap pm; + bool noreadback_ok; + bool readback_ok; + bool content_ok; +}; +void tst_QPixmap::vgImageReadBack() +{ + QPixmap tmp(10, 20); + if (tmp.pixmapData()->classId() == QPixmapData::OpenVGClass) { + Content c; + c.w = 50; + c.h = 60; + c.vgimage = VG_INVALID_HANDLE; + c.noreadback_ok = c.readback_ok = c.content_ok = false; + c.showFullScreen(); + QTest::qWaitForWindowShown(&c); + QVERIFY(c.vgimage != VG_INVALID_HANDLE); + QPixmap pm = pixmapFromVGImage(c.vgimage); + QVERIFY(!pm.isNull()); + QCOMPARE(pm.width(), c.w); + QCOMPARE(pm.height(), c.h); + QVERIFY(qPixmapToVGImage(pm) == c.vgimage); + QVERIFY(static_cast<FriendlyVGPixmapData *>(pm.pixmapData())->sourceIsNull()); + c.pm = pm; + // Make sure the second phase in paintEvent is executed too. + c.hide(); + c.showFullScreen(); + QTest::qWaitForWindowShown(&c); + QVERIFY(c.noreadback_ok); + QVERIFY(c.readback_ok); + QVERIFY(c.content_ok); + } else { + QSKIP("Not using openvg graphicssystem", SkipSingle); + } +} +#endif // Symbian & OpenVG QTEST_MAIN(tst_QPixmap) #include "tst_qpixmap.moc" diff --git a/tests/auto/qtextblock/tst_qtextblock.cpp b/tests/auto/qtextblock/tst_qtextblock.cpp index 7b41874..cec3a6a 100644 --- a/tests/auto/qtextblock/tst_qtextblock.cpp +++ b/tests/auto/qtextblock/tst_qtextblock.cpp @@ -75,6 +75,7 @@ private slots: void fragmentOverBlockBoundaries(); void excludeParagraphSeparatorFragment(); void backwardsBlockIterator(); + void previousBlock_qtbug18026(); private: QTextDocument *doc; @@ -174,5 +175,11 @@ void tst_QTextBlock::backwardsBlockIterator() QCOMPARE(it.fragment().position(), 0); } +void tst_QTextBlock::previousBlock_qtbug18026() +{ + QTextBlock last = doc->end().previous(); + QVERIFY(last.isValid()); +} + QTEST_MAIN(tst_QTextBlock) #include "tst_qtextblock.moc" diff --git a/tools/assistant/tools/assistant/doc/assistant.qdocconf b/tools/assistant/tools/assistant/doc/assistant.qdocconf index 491f159..4bd3842 100644 --- a/tools/assistant/tools/assistant/doc/assistant.qdocconf +++ b/tools/assistant/tools/assistant/doc/assistant.qdocconf @@ -10,7 +10,7 @@ description = "Qt Assistant" HTML.{postheader,address} = "" HTML.footer = "<p /><address><hr /><div align=\"center\">\n" \ "<table width=\"100%\" cellspacing=\"0\" border=\"0\"><tr class=\"address\">\n" \ - "<td width=\"30%\" align=\"left\">Copyright © 2010 Nokia Corporation and/or its subsidiary(-ies)</td>\n" \ + "<td width=\"30%\" align=\"left\">Copyright © 2011 Nokia Corporation and/or its subsidiary(-ies)</td>\n" \ "<td width=\"40%\" align=\"center\">Trademarks</td>\n" \ "<td width=\"30%\" align=\"right\"><div align=\"right\">Qt 4.8.0</div></td>\n" \ "</tr></table></div></address>" diff --git a/tools/qdoc3/doc/files/qt.qdocconf b/tools/qdoc3/doc/files/qt.qdocconf index 44cfbc1..9b16233 100644 --- a/tools/qdoc3/doc/files/qt.qdocconf +++ b/tools/qdoc3/doc/files/qt.qdocconf @@ -22,7 +22,7 @@ edition.DesktopLight.groups = -graphicsview-api qhp.projects = Qt qhp.Qt.file = qt.qhp -qhp.Qt.namespace = com.trolltech.qt.472 +qhp.Qt.namespace = com.trolltech.qt.473 qhp.Qt.virtualFolder = qdoc qhp.Qt.indexTitle = Qt Reference Documentation qhp.Qt.indexRoot = @@ -36,9 +36,9 @@ qhp.Qt.extraFiles = classic.css \ images/dynamiclayouts-example.png \ images/stylesheet-coffee-plastique.png -qhp.Qt.filterAttributes = qt 4.7.2 qtrefdoc -qhp.Qt.customFilters.Qt.name = Qt 4.7.2 -qhp.Qt.customFilters.Qt.filterAttributes = qt 4.7.2 +qhp.Qt.filterAttributes = qt 4.7.3 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.7.3 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.7.3 qhp.Qt.subprojects = classes overviews examples qhp.Qt.subprojects.classes.title = Classes qhp.Qt.subprojects.classes.indexTitle = Qt's Classes diff --git a/tools/qdoc3/test/qt-project.qdocconf b/tools/qdoc3/test/qt-project.qdocconf index 3ed7cff..2da0e90 100644 --- a/tools/qdoc3/test/qt-project.qdocconf +++ b/tools/qdoc3/test/qt-project.qdocconf @@ -6,7 +6,7 @@ include(qt-defines.qdocconf) project = Qt description = Qt Reference Documentation url = http://qt.nokia.com/doc/4.7 -version = 4.7.2 +version = 4.7.3 sourceencoding = UTF-8 outputencoding = UTF-8 @@ -15,14 +15,14 @@ naturallanguage = en_US qhp.projects = Qt qhp.Qt.file = qt.qhp -qhp.Qt.namespace = com.trolltech.qt.472 +qhp.Qt.namespace = com.trolltech.qt.473 qhp.Qt.virtualFolder = qdoc qhp.Qt.indexTitle = Qt Reference Documentation qhp.Qt.indexRoot = -qhp.Qt.filterAttributes = qt 4.7.2 qtrefdoc -qhp.Qt.customFilters.Qt.name = Qt 4.7.2 -qhp.Qt.customFilters.Qt.filterAttributes = qt 4.7.2 +qhp.Qt.filterAttributes = qt 4.7.3 qtrefdoc +qhp.Qt.customFilters.Qt.name = Qt 4.7.3 +qhp.Qt.customFilters.Qt.filterAttributes = qt 4.7.3 qhp.Qt.subprojects = classes overviews examples qhp.Qt.subprojects.classes.title = Classes qhp.Qt.subprojects.classes.indexTitle = Qt's Classes |