aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/quick/window/Splash.qml2
-rw-r--r--qtdeclarative.pro5
-rw-r--r--src/imports/dialogs/qquickabstractfiledialog.cpp9
-rw-r--r--src/imports/testlib/TestCase.qml11
-rw-r--r--src/imports/testlib/testcase.qdoc7
-rw-r--r--src/imports/widgets/qquickqfiledialog.cpp132
-rw-r--r--src/imports/widgets/qquickqfiledialog_p.h29
-rw-r--r--src/particles/qquickimageparticle.cpp10
-rw-r--r--src/qml/doc/src/qtqml-cpp.qdoc2
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp7
-rw-r--r--src/qml/qml/qqmlapplicationengine_p.h1
-rw-r--r--src/quick/doc/src/qtquick-cpp.qdoc1
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp237
-rw-r--r--tests/auto/quick/qquickcanvasitem/data/tst_context.qml77
14 files changed, 359 insertions, 171 deletions
diff --git a/examples/quick/window/Splash.qml b/examples/quick/window/Splash.qml
index 995e264f8b..629a52b6e4 100644
--- a/examples/quick/window/Splash.qml
+++ b/examples/quick/window/Splash.qml
@@ -60,7 +60,7 @@ Window {
Image {
id: splashImage
- source: "../../shared/images/qt-logo.png"
+ source: "../shared/images/qt-logo.png"
MouseArea {
anchors.fill: parent
onClicked: Qt.quit()
diff --git a/qtdeclarative.pro b/qtdeclarative.pro
index 8bc15a5662..02ba4a951d 100644
--- a/qtdeclarative.pro
+++ b/qtdeclarative.pro
@@ -1,2 +1,7 @@
CONFIG += tests_need_tools
load(qt_parts)
+
+ios {
+ log("The qtdeclarative module was disabled from the build because the dependency qtjsbackend/V8 is not ported to iOS.")
+ SUBDIRS=
+}
diff --git a/src/imports/dialogs/qquickabstractfiledialog.cpp b/src/imports/dialogs/qquickabstractfiledialog.cpp
index e415ebc7e4..3a0d5baa83 100644
--- a/src/imports/dialogs/qquickabstractfiledialog.cpp
+++ b/src/imports/dialogs/qquickabstractfiledialog.cpp
@@ -115,10 +115,9 @@ QUrl QQuickAbstractFileDialog::folder()
void QQuickAbstractFileDialog::setFolder(const QUrl &f)
{
- QString dir = f.path();
if (m_dlgHelper)
- m_dlgHelper->setDirectory(dir);
- m_options->setInitialDirectory(dir);
+ m_dlgHelper->setDirectory(f);
+ m_options->setInitialDirectory(f);
emit folderChanged();
}
@@ -159,7 +158,9 @@ QUrl QQuickAbstractFileDialog::fileUrl()
QList<QUrl> QQuickAbstractFileDialog::fileUrls()
{
- return m_dlgHelper->selectedFiles();
+ if (m_dlgHelper)
+ return m_dlgHelper->selectedFiles();
+ return QList<QUrl>();
}
void QQuickAbstractFileDialog::updateModes()
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml
index 0bec4cddf2..6e2c8e73ab 100644
--- a/src/imports/testlib/TestCase.qml
+++ b/src/imports/testlib/TestCase.qml
@@ -290,14 +290,21 @@ Item {
return qtest_results.grabImage(item);
}
- function tryCompare(obj, prop, value, timeout) {
+ function tryCompare(obj, prop, value, timeout, msg) {
if (arguments.length == 2) {
qtest_results.fail("A value is required for tryCompare",
util.callerFile(), util.callerLine())
throw new Error("QtQuickTest::fail")
}
+ if (timeout !== undefined && typeof(timeout) != "number") {
+ qtest_results.fail("timeout should be a number",
+ util.callerFile(), util.callerLine())
+ throw new Error("QtQuickTest::fail")
+ }
if (!timeout)
timeout = 5000
+ if (msg === undefined)
+ msg = "property " + prop
if (!qtest_compareInternal(obj[prop], value))
wait(0)
var i = 0
@@ -309,7 +316,7 @@ Item {
var act = qtest_results.stringify(actual)
var exp = qtest_results.stringify(value)
var success = qtest_compareInternal(actual, value)
- if (!qtest_results.compare(success, "property " + prop, act, exp, util.callerFile(), util.callerLine()))
+ if (!qtest_results.compare(success, msg, act, exp, util.callerFile(), util.callerLine()))
throw new Error("QtQuickTest::fail")
}
diff --git a/src/imports/testlib/testcase.qdoc b/src/imports/testlib/testcase.qdoc
index 56fcb24beb..dd1d9e5ad3 100644
--- a/src/imports/testlib/testcase.qdoc
+++ b/src/imports/testlib/testcase.qdoc
@@ -367,11 +367,12 @@
*/
/*!
- \qmlmethod TestCase::tryCompare(obj, property, expected, timeout = 5000)
+ \qmlmethod TestCase::tryCompare(obj, property, expected, timeout = 5000, message = "")
Fails the current test case if the specified \a property on \a obj
- is not the same as \a expected. The test will be retried multiple
- times until the \a timeout (in milliseconds) is reached.
+ is not the same as \a expected, and displays the optional \a message.
+ The test will be retried multiple times until the
+ \a timeout (in milliseconds) is reached.
This function is intended for testing applications where a property
changes value based on asynchronous events. Use compare() for testing
diff --git a/src/imports/widgets/qquickqfiledialog.cpp b/src/imports/widgets/qquickqfiledialog.cpp
index 7446887eb8..c3991b4f3c 100644
--- a/src/imports/widgets/qquickqfiledialog.cpp
+++ b/src/imports/widgets/qquickqfiledialog.cpp
@@ -50,68 +50,6 @@
QT_BEGIN_NAMESPACE
-class QFileDialogHelper : public QPlatformFileDialogHelper
-{
-public:
- QFileDialogHelper() :
- QPlatformFileDialogHelper()
- {
- connect(&m_dialog, SIGNAL(currentChanged(const QString&)), this, SIGNAL(currentChanged(const QString&)));
- connect(&m_dialog, SIGNAL(directoryEntered(const QString&)), this, SIGNAL(directoryEntered(const QString&)));
- connect(&m_dialog, SIGNAL(fileSelected(const QString&)), this, SIGNAL(fileSelected(const QString&)));
- connect(&m_dialog, SIGNAL(filesSelected(const QStringList&)), this, SIGNAL(filesSelected(const QStringList&)));
- connect(&m_dialog, SIGNAL(filterSelected(const QString&)), this, SIGNAL(filterSelected(const QString&)));
- connect(&m_dialog, SIGNAL(accepted()), this, SIGNAL(accept()));
- connect(&m_dialog, SIGNAL(rejected()), this, SIGNAL(reject()));
- }
-
- virtual bool defaultNameFilterDisables() const { return true; }
- virtual void setDirectory(const QUrl &dir) { m_dialog.setDirectoryUrl(dir); }
- virtual QUrl directory() const { return m_dialog.directoryUrl(); }
- virtual void selectFile(const QUrl &f) { m_dialog.selectUrl(f); }
- virtual QList<QUrl> selectedFiles() const;
-
- virtual void setFilter() {
- m_dialog.setWindowTitle(QPlatformFileDialogHelper::options()->windowTitle());
- if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::LookIn))
- m_dialog.setLabelText(m_dialog.LookIn, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::LookIn));
- if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::FileName))
- m_dialog.setLabelText(m_dialog.FileName, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::FileName));
- if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::FileType))
- m_dialog.setLabelText(m_dialog.FileType, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::FileType));
- if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::Accept))
- m_dialog.setLabelText(m_dialog.Accept, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::Accept));
- if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::Reject))
- m_dialog.setLabelText(m_dialog.Reject, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::Reject));
- m_dialog.setFilter(QPlatformFileDialogHelper::options()->filter());
- m_dialog.setNameFilters(QPlatformFileDialogHelper::options()->nameFilters());
- m_dialog.selectNameFilter(QPlatformFileDialogHelper::options()->initiallySelectedNameFilter());
- m_dialog.setFileMode(QFileDialog::FileMode(QPlatformFileDialogHelper::options()->fileMode()));
- m_dialog.setOptions((QFileDialog::Options)((int)(QPlatformFileDialogHelper::options()->options())));
- m_dialog.setAcceptMode(QFileDialog::AcceptMode(QPlatformFileDialogHelper::options()->acceptMode()));
- }
-
- virtual void selectNameFilter(const QString &f) { m_dialog.selectNameFilter(f); }
- virtual QString selectedNameFilter() const { return m_dialog.selectedNameFilter(); }
- virtual void exec() { m_dialog.exec(); }
-
- virtual bool show(Qt::WindowFlags f, Qt::WindowModality m, QWindow *parent) {
- m_dialog.winId();
- QWindow *window = m_dialog.windowHandle();
- Q_ASSERT(window);
- window->setTransientParent(parent);
- window->setFlags(f);
- m_dialog.setWindowModality(m);
- m_dialog.show();
- return m_dialog.isVisible();
- }
-
- virtual void hide() { m_dialog.hide(); }
-
-private:
- QFileDialog m_dialog;
-};
-
/*!
\qmltype QtFileDialog
\instantiates QQuickQFileDialog
@@ -190,8 +128,8 @@ QPlatformFileDialogHelper *QQuickQFileDialog::helper()
if (!m_dlgHelper) {
m_dlgHelper = new QFileDialogHelper();
- connect(m_dlgHelper, SIGNAL(directoryEntered(QString)), this, SIGNAL(folderChanged()));
- connect(m_dlgHelper, SIGNAL(filterSelected(QString)), this, SIGNAL(filterSelected()));
+ connect(m_dlgHelper, SIGNAL(directoryEntered(const QUrl &)), this, SIGNAL(folderChanged()));
+ connect(m_dlgHelper, SIGNAL(filterSelected(const QString &)), this, SIGNAL(filterSelected()));
connect(m_dlgHelper, SIGNAL(accept()), this, SLOT(accept()));
connect(m_dlgHelper, SIGNAL(reject()), this, SLOT(reject()));
}
@@ -199,9 +137,75 @@ QPlatformFileDialogHelper *QQuickQFileDialog::helper()
return m_dlgHelper;
}
+QFileDialogHelper::QFileDialogHelper() :
+ QPlatformFileDialogHelper()
+{
+ connect(&m_dialog, SIGNAL(currentChanged(const QString&)), this, SLOT(currentChanged(const QString&)));
+ connect(&m_dialog, SIGNAL(directoryEntered(const QString&)), this, SLOT(directoryEntered(const QString&)));
+ connect(&m_dialog, SIGNAL(fileSelected(const QString&)), this, SLOT(fileSelected(const QString&)));
+ connect(&m_dialog, SIGNAL(filesSelected(const QStringList&)), this, SLOT(filesSelected(const QStringList&)));
+ connect(&m_dialog, SIGNAL(filterSelected(const QString&)), this, SIGNAL(filterSelected(const QString&)));
+ connect(&m_dialog, SIGNAL(accepted()), this, SIGNAL(accept()));
+ connect(&m_dialog, SIGNAL(rejected()), this, SIGNAL(reject()));
+}
+
QList<QUrl> QFileDialogHelper::selectedFiles() const
{
return m_dialog.selectedUrls();
}
+void QFileDialogHelper::setFilter() {
+ m_dialog.setWindowTitle(QPlatformFileDialogHelper::options()->windowTitle());
+ if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::LookIn))
+ m_dialog.setLabelText(m_dialog.LookIn, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::LookIn));
+ if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::FileName))
+ m_dialog.setLabelText(m_dialog.FileName, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::FileName));
+ if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::FileType))
+ m_dialog.setLabelText(m_dialog.FileType, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::FileType));
+ if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::Accept))
+ m_dialog.setLabelText(m_dialog.Accept, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::Accept));
+ if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::Reject))
+ m_dialog.setLabelText(m_dialog.Reject, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::Reject));
+ m_dialog.setFilter(QPlatformFileDialogHelper::options()->filter());
+ m_dialog.setNameFilters(QPlatformFileDialogHelper::options()->nameFilters());
+ m_dialog.selectNameFilter(QPlatformFileDialogHelper::options()->initiallySelectedNameFilter());
+ m_dialog.setFileMode(QFileDialog::FileMode(QPlatformFileDialogHelper::options()->fileMode()));
+ m_dialog.setOptions((QFileDialog::Options)((int)(QPlatformFileDialogHelper::options()->options())));
+ m_dialog.setAcceptMode(QFileDialog::AcceptMode(QPlatformFileDialogHelper::options()->acceptMode()));
+}
+
+bool QFileDialogHelper::show(Qt::WindowFlags f, Qt::WindowModality m, QWindow *parent) {
+ m_dialog.winId();
+ QWindow *window = m_dialog.windowHandle();
+ Q_ASSERT(window);
+ window->setTransientParent(parent);
+ window->setFlags(f);
+ m_dialog.setWindowModality(m);
+ m_dialog.show();
+ return m_dialog.isVisible();
+}
+
+void QFileDialogHelper::currentChanged(const QString& path)
+{
+ emit QPlatformFileDialogHelper::currentChanged(QUrl::fromLocalFile(path));
+}
+
+void QFileDialogHelper::directoryEntered(const QString& path)
+{
+ emit QPlatformFileDialogHelper::directoryEntered(QUrl::fromLocalFile(path));
+}
+
+void QFileDialogHelper::fileSelected(const QString& path)
+{
+ emit QPlatformFileDialogHelper::fileSelected(QUrl::fromLocalFile(path));
+}
+
+void QFileDialogHelper::filesSelected(const QStringList& paths)
+{
+ QList<QUrl> pathUrls;
+ foreach (const QString &path, paths)
+ pathUrls << QUrl::fromLocalFile(path);
+ emit QPlatformFileDialogHelper::filesSelected(pathUrls);
+}
+
QT_END_NAMESPACE
diff --git a/src/imports/widgets/qquickqfiledialog_p.h b/src/imports/widgets/qquickqfiledialog_p.h
index 73067f796c..8bf7c73882 100644
--- a/src/imports/widgets/qquickqfiledialog_p.h
+++ b/src/imports/widgets/qquickqfiledialog_p.h
@@ -53,6 +53,7 @@
// We mean it.
//
+#include <QFileDialog>
#include "../dialogs/qquickabstractfiledialog_p.h"
QT_BEGIN_NAMESPACE
@@ -71,6 +72,34 @@ protected:
Q_DISABLE_COPY(QQuickQFileDialog)
};
+class QFileDialogHelper : public QPlatformFileDialogHelper
+{
+ Q_OBJECT
+public:
+ QFileDialogHelper();
+
+ bool defaultNameFilterDisables() const Q_DECL_OVERRIDE { return true; }
+ void setDirectory(const QUrl &dir) Q_DECL_OVERRIDE { m_dialog.setDirectoryUrl(dir); }
+ QUrl directory() const Q_DECL_OVERRIDE { return m_dialog.directoryUrl(); }
+ void selectFile(const QUrl &f) Q_DECL_OVERRIDE { m_dialog.selectUrl(f); }
+ QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE;
+ void setFilter() Q_DECL_OVERRIDE;
+ void selectNameFilter(const QString &f) Q_DECL_OVERRIDE { m_dialog.selectNameFilter(f); }
+ QString selectedNameFilter() const Q_DECL_OVERRIDE { return m_dialog.selectedNameFilter(); }
+ void exec() Q_DECL_OVERRIDE { m_dialog.exec(); }
+ bool show(Qt::WindowFlags f, Qt::WindowModality m, QWindow *parent) Q_DECL_OVERRIDE;
+ void hide() Q_DECL_OVERRIDE { m_dialog.hide(); }
+
+private slots:
+ void currentChanged(const QString& path);
+ void directoryEntered(const QString& path);
+ void fileSelected(const QString& path);
+ void filesSelected(const QStringList& paths);
+
+private:
+ QFileDialog m_dialog;
+};
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickQFileDialog *)
diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp
index 424352d8e8..c3e13f162f 100644
--- a/src/particles/qquickimageparticle.cpp
+++ b/src/particles/qquickimageparticle.cpp
@@ -365,7 +365,6 @@ public:
}
void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) {
- glFuncs->glActiveTexture(GL_TEXTURE0);
d->texture->bind();
program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
@@ -405,7 +404,7 @@ public:
QList<QByteArray> attributes() const {
return QList<QByteArray>() << "vPosTex" << "vData" << "vVec"
<< "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos";
- };
+ }
void initialize() {
QSGSimpleMaterialShader<SpriteMaterialData>::initialize();
@@ -496,7 +495,6 @@ public:
}
void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) {
- glFuncs->glActiveTexture(GL_TEXTURE0);
d->texture->bind();
program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
@@ -561,7 +559,6 @@ public:
}
void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) {
- glFuncs->glActiveTexture(GL_TEXTURE0);
d->texture->bind();
program()->setUniformValue(m_timestamp_id, (float) d->timestamp);
@@ -1677,7 +1674,6 @@ void QQuickImageParticle::spritesUpdate(qreal time)
if (frameAt < (datum->frameCount-1))
x2 += w;
- node->setFlag(QSGNode::OwnsGeometry, false);
SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
spriteVertices += datum->index*4;
for (int i=0; i<4; i++) {
@@ -1689,7 +1685,6 @@ void QQuickImageParticle::spritesUpdate(qreal time)
spriteVertices[i].animH = h;
spriteVertices[i].animProgress = progress;
}
- node->setFlag(QSGNode::OwnsGeometry, true);
}
}
}
@@ -1853,7 +1848,6 @@ void QQuickImageParticle::commit(int gIdx, int pIdx)
if (!node)
return;
QQuickParticleData* datum = m_system->groupData[gIdx]->data[pIdx];
- node->setFlag(QSGNode::OwnsGeometry, false);
SpriteVertex *spriteVertices = (SpriteVertex *) node->geometry()->vertexData();
DeformableVertex *deformableVertices = (DeformableVertex *) node->geometry()->vertexData();
ColoredVertex *coloredVertices = (ColoredVertex *) node->geometry()->vertexData();
@@ -2004,8 +1998,6 @@ void QQuickImageParticle::commit(int gIdx, int pIdx)
default:
break;
}
-
- node->setFlag(QSGNode::OwnsGeometry, true);
}
diff --git a/src/qml/doc/src/qtqml-cpp.qdoc b/src/qml/doc/src/qtqml-cpp.qdoc
index 74e59d2540..07aca29f7d 100644
--- a/src/qml/doc/src/qtqml-cpp.qdoc
+++ b/src/qml/doc/src/qtqml-cpp.qdoc
@@ -27,6 +27,8 @@
/*!
\module QtQml
\title Qt QML C++ Classes
+\ingroup modules
+\qtvariable qml
\brief The C++ API provided by the Qt QML module
To include the definitions of the module's classes, use the
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index 9181dad519..85aeaf5786 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -54,6 +54,10 @@ QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e)
QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate()
{
+}
+
+void QQmlApplicationEnginePrivate::cleanUp()
+{
qDeleteAll(objects);
#ifndef QT_NO_TRANSLATIONS
qDeleteAll(translators);
@@ -229,7 +233,8 @@ QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *p
*/
QQmlApplicationEngine::~QQmlApplicationEngine()
{
- //Instantiated root objects cleaned up in private class
+ Q_D(QQmlApplicationEngine);
+ d->cleanUp();//Instantiated root objects must be deleted before the engine
}
/*!
diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h
index db144af504..cc38c62c02 100644
--- a/src/qml/qml/qqmlapplicationengine_p.h
+++ b/src/qml/qml/qqmlapplicationengine_p.h
@@ -70,6 +70,7 @@ public:
QQmlApplicationEnginePrivate(QQmlEngine *e);
~QQmlApplicationEnginePrivate();
void init();
+ void cleanUp();
void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false);
void loadTranslations(const QUrl &rootFile);
diff --git a/src/quick/doc/src/qtquick-cpp.qdoc b/src/quick/doc/src/qtquick-cpp.qdoc
index 71234f142b..6ff52bfd4d 100644
--- a/src/quick/doc/src/qtquick-cpp.qdoc
+++ b/src/quick/doc/src/qtquick-cpp.qdoc
@@ -28,6 +28,7 @@
\module QtQuick
\title Qt Quick C++ Classes
\ingroup modules
+ \qtvariable quick
\brief The Qt Quick module provides classes for embedding Qt Quick
in Qt/C++ applications.
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index d59e65dfd4..d2014ded8c 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -198,57 +198,141 @@ QColor qt_color_from_string(const QV4::Value &name)
return QColor();
}
+static int qParseFontSizeFromToken(const QString &fontSizeToken, bool &ok)
+{
+ ok = false;
+ float size = fontSizeToken.trimmed().toFloat(&ok);
+ if (ok) {
+ return int(size);
+ }
+ qWarning().nospace() << "Context2D: A font size of " << fontSizeToken << " is invalid.";
+ return 0;
+}
+
+/*
+ Attempts to set the font size of \a font to \a fontSizeToken, returning
+ \c true if successful. If the font size is invalid, \c false is returned
+ and a warning is printed.
+*/
static bool qSetFontSizeFromToken(QFont &font, const QString &fontSizeToken)
{
const QString trimmedToken = fontSizeToken.trimmed();
- QString unit = trimmedToken.right(2);
- QString value = trimmedToken.left(fontSizeToken.size() - 2);
+ const QString unitStr = trimmedToken.right(2);
+ const QString value = trimmedToken.left(trimmedToken.size() - 2);
bool ok = false;
- float size = value.trimmed().toFloat(&ok);
- if (ok) {
- int intSize = int(size);
- if (unit.compare(QLatin1String("px")) == 0) {
- font.setPixelSize(intSize);
+ int size = 0;
+ if (unitStr == QStringLiteral("px")) {
+ size = qParseFontSizeFromToken(value, ok);
+ if (ok) {
+ font.setPixelSize(size);
return true;
- } else if (unit.compare(QLatin1String("pt")) == 0) {
- font.setPointSize(intSize);
+ }
+ } else if (unitStr == QStringLiteral("pt")) {
+ size = qParseFontSizeFromToken(value, ok);
+ if (ok) {
+ font.setPointSize(size);
return true;
}
+ } else {
+ qWarning().nospace() << "Context2D: Invalid font size unit in font string.";
}
- qWarning().nospace() << "Context2D: A font size of " << fontSizeToken << " is invalid.";
return false;
}
-static bool qSetFontFamilyFromToken(QFont &font, const QString &fontFamilyToken)
-{
- const QString trimmedToken = fontFamilyToken.trimmed();
- QFontDatabase fontDatabase;
- if (fontDatabase.hasFamily(trimmedToken)) {
- font.setFamily(trimmedToken);
- return true;
- } else {
- // Can't find a family matching this name; if it's a generic family,
- // try searching for the default family for it by using style hints.
- QFont tmp;
- int styleHint = -1;
- if (fontFamilyToken.compare(QLatin1String("serif")) == 0) {
- styleHint = QFont::Serif;
- } else if (fontFamilyToken.compare(QLatin1String("sans-serif")) == 0) {
- styleHint = QFont::SansSerif;
- } else if (fontFamilyToken.compare(QLatin1String("cursive")) == 0) {
- styleHint = QFont::Cursive;
- } else if (fontFamilyToken.compare(QLatin1String("monospace")) == 0) {
- styleHint = QFont::Monospace;
- } else if (fontFamilyToken.compare(QLatin1String("fantasy")) == 0) {
- styleHint = QFont::Fantasy;
+/*
+ Returns a list of all of the families in \a fontFamiliesString, where
+ each family is separated by spaces. Families with spaces in their name
+ must be quoted.
+*/
+static QStringList qExtractFontFamiliesFromString(const QString &fontFamiliesString)
+{
+ QStringList extractedFamilies;
+ int quoteIndex = -1;
+ QString currentFamily;
+ for (int index = 0; index < fontFamiliesString.size(); ++index) {
+ const QChar ch = fontFamiliesString.at(index);
+ if (ch == '"' || ch == '\'') {
+ if (quoteIndex == -1) {
+ quoteIndex = index;
+ } else {
+ if (ch == fontFamiliesString.at(quoteIndex)) {
+ // Found the matching quote. +1/-1 because we don't want the quote as part of the name.
+ const QString family = fontFamiliesString.mid(quoteIndex + 1, index - quoteIndex - 1);
+ extractedFamilies.push_back(family);
+ currentFamily.clear();
+ quoteIndex = -1;
+ } else {
+ qWarning().nospace() << "Context2D: Mismatched quote in font string.";
+ return QStringList();
+ }
+ }
+ } else if (ch == ' ' && quoteIndex == -1) {
+ // This is a space that's not within quotes...
+ if (!currentFamily.isEmpty()) {
+ // and there is a current family; consider it the end of the current family.
+ extractedFamilies.push_back(currentFamily);
+ currentFamily.clear();
+ } // else: ignore the space
+ } else {
+ currentFamily.push_back(ch);
+ }
+ }
+ if (!currentFamily.isEmpty()) {
+ if (quoteIndex == -1) {
+ // This is the end of the string, so add this family to our list.
+ extractedFamilies.push_back(currentFamily);
+ } else {
+ qWarning().nospace() << "Context2D: Unclosed quote in font string.";
+ return QStringList();
}
- if (styleHint != -1) {
- tmp.setStyleHint(static_cast<QFont::StyleHint>(styleHint));
- font.setFamily(tmp.defaultFamily());
+ }
+ if (extractedFamilies.isEmpty()) {
+ qWarning().nospace() << "Context2D: Missing or misplaced font family in font string"
+ << " (it must come after the font size).";
+ }
+ return extractedFamilies;
+}
+
+/*!
+ Tries to set a family on \a font using the families provided in \a fontFamilyTokens.
+
+ The list is ordered by preference, with the first family having the highest preference.
+ If the first family is invalid, the next family in the list is evaluated.
+ This process is repeated until a valid font is found (at which point the function
+ will return \c true and the family set on \a font) or there are no more
+ families left, at which point a warning is printed and \c false is returned.
+*/
+static bool qSetFontFamilyFromTokens(QFont &font, const QStringList &fontFamilyTokens)
+{
+ foreach (QString fontFamilyToken, fontFamilyTokens) {
+ QFontDatabase fontDatabase;
+ if (fontDatabase.hasFamily(fontFamilyToken)) {
+ font.setFamily(fontFamilyToken);
return true;
+ } else {
+ // Can't find a family matching this name; if it's a generic family,
+ // try searching for the default family for it by using style hints.
+ int styleHint = -1;
+ if (fontFamilyToken.compare(QLatin1String("serif")) == 0) {
+ styleHint = QFont::Serif;
+ } else if (fontFamilyToken.compare(QLatin1String("sans-serif")) == 0) {
+ styleHint = QFont::SansSerif;
+ } else if (fontFamilyToken.compare(QLatin1String("cursive")) == 0) {
+ styleHint = QFont::Cursive;
+ } else if (fontFamilyToken.compare(QLatin1String("monospace")) == 0) {
+ styleHint = QFont::Monospace;
+ } else if (fontFamilyToken.compare(QLatin1String("fantasy")) == 0) {
+ styleHint = QFont::Fantasy;
+ }
+ if (styleHint != -1) {
+ QFont tmp;
+ tmp.setStyleHint(static_cast<QFont::StyleHint>(styleHint));
+ font.setFamily(tmp.defaultFamily());
+ return true;
+ }
}
}
- qWarning().nospace() << "Context2D: The font family " << fontFamilyToken << " is invalid.";
+ qWarning("Context2D: The font families specified are invalid: %s", qPrintable(fontFamilyTokens.join(QString()).trimmed()));
return false;
}
@@ -275,18 +359,63 @@ if (!(usedTokens & token)) { \
See: http://www.w3.org/TR/css3-fonts/#font-prop
*/
static QFont qt_font_from_string(const QString& fontString, const QFont &currentFont) {
- const QStringList tokens = fontString.split(QLatin1Char(' '));
- if (tokens.size() < 2) {
- qWarning().nospace() << "Context2D: Insufficent amount of tokens in font string.";
+ if (fontString.isEmpty()) {
+ qWarning().nospace() << "Context2D: Font string is empty.";
+ return currentFont;
+ }
+
+ // We know that font-size must be specified and it must be before font-family
+ // (which could potentially have "px" or "pt" in its name), so extract it now.
+ int fontSizeEnd = fontString.indexOf(QStringLiteral("px"));
+ if (fontSizeEnd == -1)
+ fontSizeEnd = fontString.indexOf(QStringLiteral("pt"));
+ if (fontSizeEnd == -1) {
+ qWarning().nospace() << "Context2D: Invalid font size unit in font string.";
return currentFont;
}
+ int fontSizeStart = fontString.lastIndexOf(' ', fontSizeEnd);
+ if (fontSizeStart == -1) {
+ // The font size might be the first token in the font string, which is OK.
+ // Regardless, we'll find out if the font is invalid with qSetFontSizeFromToken().
+ fontSizeStart = 0;
+ } else {
+ // Don't want to take the leading space.
+ ++fontSizeStart;
+ }
+
+ // + 2 for the unit, +1 for the space that we require.
+ fontSizeEnd += 3;
+
QFont newFont;
- QStringList remainingTokens = tokens;
+ if (!qSetFontSizeFromToken(newFont, fontString.mid(fontSizeStart, fontSizeEnd - fontSizeStart)))
+ return currentFont;
+
+ // We don't want to parse the size twice, so remove it now.
+ QString remainingFontString = fontString;
+ remainingFontString.remove(fontSizeStart, fontSizeEnd - fontSizeStart);
+
+ // Next, we have to take any font families out, as QString::split() will ruin quoted family names.
+ const QString fontFamiliesString = remainingFontString.mid(fontSizeStart);
+ remainingFontString.chop(remainingFontString.length() - fontSizeStart);
+ QStringList fontFamilies = qExtractFontFamiliesFromString(fontFamiliesString);
+ if (fontFamilies.isEmpty()) {
+ return currentFont;
+ }
+ if (!qSetFontFamilyFromTokens(newFont, fontFamilies))
+ return currentFont;
+
+ // Now that we've removed the messy parts, we can split the font string on spaces.
+ const QString trimmedTokensStr = remainingFontString.trimmed();
+ if (trimmedTokensStr.isEmpty()) {
+ // No optional properties.
+ return newFont;
+ }
+ const QStringList tokens = trimmedTokensStr.split(QLatin1Char(' '));
+
int usedTokens = NoTokens;
// Optional properties can be in any order, but font-size and font-family must be last.
- while (remainingTokens.size() > 2) {
- const QString token = remainingTokens.takeFirst();
+ foreach (const QString token, tokens) {
if (token.compare(QLatin1String("normal")) == 0) {
if (!(usedTokens & FontStyle) || !(usedTokens & FontVariant) || !(usedTokens & FontWeight)) {
// Could be font-style, font-variant or font-weight.
@@ -330,19 +459,6 @@ static QFont qt_font_from_string(const QString& fontString, const QFont &current
return currentFont;
}
}
- if (remainingTokens.size() == 2) {
- // Order must be: font-size font-family.
- if (!qSetFontSizeFromToken(newFont, remainingTokens.first())) {
- return currentFont;
- }
- if (!qSetFontFamilyFromToken(newFont, remainingTokens.last())) {
- return currentFont;
- }
- return newFont;
- } else {
- qWarning().nospace() << "Context2D: Missing font-size and/or font-family tokens in font string.";
- return currentFont;
- }
return newFont;
}
@@ -2399,8 +2515,9 @@ QV4::Value QQuickJSContext2DPrototype::method_caretBlinkRate(QV4::SimpleCallCont
\li font-family: See \l {http://www.w3.org/TR/CSS2/fonts.html#propdef-font-family}
\endlist
- Note that font-size and font-family are mandatory and must be in the order
- they are shown in above.
+ \note The font-size and font-family properties are mandatory and must be in
+ the order they are shown in above. In addition, a font family with spaces in
+ its name must be quoted.
The default font value is "10px sans-serif".
*/
@@ -3648,10 +3765,12 @@ int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &
int offset = 0;
switch (value) {
case QQuickContext2D::Top:
+ case QQuickContext2D::Hanging:
break;
- case QQuickContext2D::Alphabetic:
case QQuickContext2D::Middle:
- case QQuickContext2D::Hanging:
+ offset = (metrics.ascent() >> 1) + metrics.height() - metrics.ascent();
+ break;
+ case QQuickContext2D::Alphabetic:
offset = metrics.ascent();
break;
case QQuickContext2D::Bottom:
diff --git a/tests/auto/quick/qquickcanvasitem/data/tst_context.qml b/tests/auto/quick/qquickcanvasitem/data/tst_context.qml
index ab351f0de8..b18250291e 100644
--- a/tests/auto/quick/qquickcanvasitem/data/tst_context.qml
+++ b/tests/auto/quick/qquickcanvasitem/data/tst_context.qml
@@ -111,6 +111,14 @@ Canvas {
{ string: "bold 12px sans-serif", expected: "sans-serif,-1,12,5,75,0,0,0,0,0" },
{ string: "0 12px sans-serif", expected: "sans-serif,-1,12,5,0,0,0,0,0,0" },
{ string: "small-caps 12px sans-serif", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ { string: "12px \"sans-serif\"", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ { string: "12px 'sans-serif'", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ // sans-serif will always be chosen, but this still tests that multiple families can be read.
+ { string: "12px 'sans-serif' 'cursive'", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ { string: "12px sans-serif 'cursive' monospace", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ { string: "12px sans-serif 'cursive' monospace ", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ { string: " 12px sans-serif 'cursive' monospace ", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" },
+ { string: " 12px sans-serif 'cursive' monospace ", expected: "sans-serif,-1,12,5,50,0,0,0,0,0" }
];
for (var i = 0; i < validFonts.length; ++i) {
ctx.font = validFonts[i].string;
@@ -125,38 +133,51 @@ Canvas {
var ctx = canvas.getContext("2d");
var originalFont = ctx.font;
- var i = 0;
- var insufficientQtyTokens = ["", "12px", "sans-serif"];
- for (i = 0; i < insufficientQtyTokens.length; ++i) {
- ignoreWarning("Context2D: Insufficent amount of tokens in font string.");
- ctx.font = insufficientQtyTokens[i];
- compare(ctx.font, originalFont);
- }
+ var fontStrings = [
+ "",
+ "12px",
+ "sans-serif",
+ "z12px sans-serif",
+ "1z2px sans-serif",
+ "12zpx sans-serif",
+ "12pxz sans-serif",
+ "sans-serif 12px",
+ "12px !@weeeeeeee!@!@Don'tNameYourFontThis",
+ "12px )(&*^^^%#$@*!!@#$JSPOR)",
+ "normal normal normal normal 12px sans-serif",
+ "normal normal bold bold 12px sans-serif",
+ "bold bold 12px sans-serif",
+ "12px 'cursive\"",
+ "12px 'cursive\" sans-serif",
+ "12px 'cursive"
+ ];
- var invalidFontSizes = ["z12px sans-serif", "1z2px sans-serif", "12zpx sans-serif",
- "12pzx sans-serif", "12pxz sans-serif", "sans-serif 12px"];
- for (i = 0; i < invalidFontSizes.length; ++i) {
- ignoreWarning("Context2D: A font size of \"" + invalidFontSizes[i].split(" ")[0] + "\" is invalid.");
- ctx.font = invalidFontSizes[i];
- compare(ctx.font, originalFont);
- }
+ var ignoredWarnings = [
+ "Context2D: Font string is empty.",
+ "Context2D: Missing or misplaced font family in font string (it must come after the font size).",
+ "Context2D: Invalid font size unit in font string.",
+ "Context2D: A font size of \"z12\" is invalid.",
+ "Context2D: A font size of \"1z2\" is invalid.",
+ "Context2D: A font size of \"12z\" is invalid.",
+ "Context2D: Invalid font size unit in font string.",
+ "Context2D: Missing or misplaced font family in font string (it must come after the font size).",
+ "Context2D: Unclosed quote in font string.",
+ "Context2D: The font families specified are invalid: )(&*^^^%#$@*!!@#$JSPOR)",
+ "Context2D: Duplicate token \"normal\" found in font string.",
+ "Context2D: Duplicate token \"bold\" found in font string.",
+ "Context2D: Duplicate token \"bold\" found in font string.",
+ "Context2D: Mismatched quote in font string.",
+ "Context2D: Mismatched quote in font string.",
+ "Context2D: Unclosed quote in font string."
+ ];
- var invalidFontFamilies = ["12px !@weeeeeeee!@!@Don'tNameYourFontThis", "12px )(&*^^^%#$@*!!@#$JSPOR)"];
- for (i = 0; i < invalidFontFamilies.length; ++i) {
- ignoreWarning("Context2D: The font family \"" + invalidFontFamilies[i].split(" ")[1] + "\" is invalid.");
- ctx.font = invalidFontFamilies[i];
- compare(ctx.font, originalFont);
- }
+ // Sanity check...
+ compare(ignoredWarnings.length, fontStrings.length);
- var duplicates = [
- { duplicate: "normal", string: "normal normal normal normal 12px sans-serif" },
- { duplicate: "bold", string: "normal normal bold bold 12px sans-serif" },
- { duplicate: "bold", string: "bold bold 12px sans-serif" }
- ];
- for (i = 0; i < duplicates.length; ++i) {
- ignoreWarning("Context2D: Duplicate token \"" + duplicates[i].duplicate + "\" found in font string.");
- ctx.font = duplicates[i].string;
+ for (var i = 0; i < fontStrings.length; ++i) {
+ ignoreWarning(ignoredWarnings[i]);
+ ctx.font = fontStrings[i];
compare(ctx.font, originalFont);
}
}