diff --git a/examples/quick/shapes/content/item18.qml b/examples/quick/shapes/content/item18.qml
new file mode 100644
index 0000000000..3774d19bc5
--- /dev/null
+++ b/examples/quick/shapes/content/item18.qml
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, 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 The Qt Company Ltd 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$
+**
+****************************************************************************/
+
+import QtQuick 2.15
+import QtQuick.Shapes 1.0
+
+Rectangle {
+ color: "lightGray"
+
+ Shape {
+ anchors.centerIn: parent
+ width: 200
+ height: 100
+
+ ShapePath {
+ id: capTest
+ strokeColor: "black"
+ strokeWidth: 1
+ fillColor: "black"
+
+ PathText { x: 0; y: 100; font.family: "Arial"; font.pixelSize: 150; text: "Qt!" }
+ }
+ }
+}
diff --git a/examples/quick/shapes/content/shapegallery.qml b/examples/quick/shapes/content/shapegallery.qml
index 86445e25c2..e5a7c51483 100644
--- a/examples/quick/shapes/content/shapegallery.qml
+++ b/examples/quick/shapes/content/shapegallery.qml
@@ -130,6 +130,10 @@ Rectangle {
name: "Tiger"
shapeUrl: "item17.qml"
}
+ ListElement {
+ name: "Text"
+ shapeUrl: "item18.qml"
+ }
}
property int gridSpacing: 10
diff --git a/examples/quick/shapes/shapes.pro b/examples/quick/shapes/shapes.pro
index ff6fa422fb..f99d941804 100644
--- a/examples/quick/shapes/shapes.pro
+++ b/examples/quick/shapes/shapes.pro
@@ -24,7 +24,8 @@ OTHER_FILES += content/main.qml \
content/item13.qml \
content/item14.qml \
content/item15.qml \
- content/item17.qml
+ content/item17.qml \
+ content/item18.qml
target.path = $$[QT_INSTALL_EXAMPLES]/quick/shapes
INSTALLS += target
diff --git a/examples/quick/shapes/shapes.qrc b/examples/quick/shapes/shapes.qrc
index e03c0e8a0a..6de463dd33 100644
--- a/examples/quick/shapes/shapes.qrc
+++ b/examples/quick/shapes/shapes.qrc
@@ -28,5 +28,6 @@
content/item14.qml
content/item15.qml
content/item17.qml
+ content/item18.qml
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index c855acc185..375d0265e8 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -301,6 +301,8 @@ void QQuickPath::pathElements_append(QQmlListProperty *proper
QQuickCurve *curve = qobject_cast(pathElement);
if (curve)
d->_pathCurves.append(curve);
+ else if (QQuickPathText *text = qobject_cast(pathElement))
+ d->_pathTexts.append(text);
else {
QQuickPathAttribute *attribute = qobject_cast(pathElement);
if (attribute && !d->_attributes.contains(attribute->name()))
@@ -329,6 +331,7 @@ void QQuickPath::pathElements_clear(QQmlListProperty *propert
d->_pathElements.clear();
d->_pathCurves.clear();
d->_pointCache.clear();
+ d->_pathTexts.clear();
}
void QQuickPath::interpolate(int idx, const QString &name, qreal value)
@@ -478,6 +481,8 @@ QPainterPath QQuickPath::createPath(const QPointF &startPoint, const QPointF &en
point.values[percentString] = percent->value();
interpolate(attributePoints, attributePoints.count() - 1, percentString, percent->value());
usesPercent = true;
+ } else if (QQuickPathText *text = qobject_cast(pathElement)) {
+ text->addToPath(path);
}
}
@@ -547,6 +552,9 @@ QPainterPath QQuickPath::createShapePath(const QPointF &startPoint, const QPoint
++index;
}
+ for (QQuickPathText *text : qAsConst(d->_pathTexts))
+ text->addToPath(path);
+
if (closed) {
QPointF end = path.currentPosition();
*closed = startX == end.x() && startY == end.y();
@@ -593,6 +601,8 @@ void QQuickPath::gatherAttributes()
for (QQuickPathElement *pathElement : qAsConst(d->_pathElements)) {
if (QQuickCurve *curve = qobject_cast(pathElement))
d->_pathCurves.append(curve);
+ else if (QQuickPathText *text = qobject_cast(pathElement))
+ d->_pathTexts.append(text);
else if (QQuickPathAttribute *attribute = qobject_cast(pathElement))
attributes.insert(attribute->name());
}
@@ -2619,6 +2629,219 @@ void QQuickPathMultiline::addToPath(QPainterPath &path, const QQuickPathData &)
}
}
+/*!
+ \qmltype PathText
+ \instantiates QQuickPathText
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Defines a string in a specified font.
+ \since QtQuick 2.15
+
+ This element defines the shape of a specified string in a specified font. The text's
+ baseline will be translated to the x and y coordinates, and the outlines from the font
+ will be added to the path accordingly.
+
+ \qml
+ PathText {
+ x: 0
+ y: font.pixelSize
+ font.family: "Arial"
+ font.pixelSize: 100
+ text: "Foobar"
+ }
+ \endqml
+
+ \sa Path, QPainterPath::setFillRule, PathPolyline, PathQuad, PathCubic, PathArc, PathAngleArc, PathCurve, PathSvg, PathMove
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::x
+
+ The horizontal position of the PathText's baseline.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::y
+
+ The vertical position of the PathText's baseline.
+
+ \note This property refers to the position of the baseline of the text, not the top of its bounding box. This may
+ cause some confusion, e.g. when using the PathText with Qt Quick Shapes. See \l FontMetrics for information on how to
+ get the ascent of a font, which can be used to translate the text into the expected position.
+*/
+
+/*!
+ \qmlproperty string QtQuick::PathText::text
+
+ The text for which this PathText should contain the outlines.
+*/
+
+/*!
+ \qmlproperty string QtQuick::PathText::font.family
+
+ Sets the family name of the font.
+
+ The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
+ If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
+ If the family isn't available a family will be set using the font matching algorithm.
+*/
+
+/*!
+ \qmlproperty string QtQuick::PathText::font.styleName
+
+ Sets the style name of the font.
+
+ The style name is case insensitive. If set, the font will be matched against style name instead
+ of the font properties \l font.weight, \l font.bold and \l font.italic.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.bold
+
+ Sets whether the font weight is bold.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::PathText::font.weight
+
+ Sets the font's weight.
+
+ The weight can be one of:
+ \list
+ \li Font.Thin
+ \li Font.Light
+ \li Font.ExtraLight
+ \li Font.Normal - the default
+ \li Font.Medium
+ \li Font.DemiBold
+ \li Font.Bold
+ \li Font.ExtraBold
+ \li Font.Black
+ \endlist
+
+ \qml
+ PathText { text: "Hello"; font.weight: Font.DemiBold }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.italic
+
+ Sets whether the font has an italic style.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.underline
+
+ Sets whether the text is underlined.
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.strikeout
+
+ Sets whether the font has a strikeout style.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::font.pointSize
+
+ Sets the font size in points. The point size must be greater than zero.
+*/
+
+/*!
+ \qmlproperty int QtQuick::PathText::font.pixelSize
+
+ Sets the font size in pixels.
+
+ Using this function makes the font device dependent.
+ Use \c pointSize to set the size of the font in a device independent manner.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::font.letterSpacing
+
+ Sets the letter spacing for the font.
+
+ Letter spacing changes the default spacing between individual letters in the font.
+ A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathText::font.wordSpacing
+
+ Sets the word spacing for the font.
+
+ Word spacing changes the default spacing between individual words.
+ A positive value increases the word spacing by a corresponding amount of pixels,
+ while a negative value decreases the inter-word spacing accordingly.
+*/
+
+/*!
+ \qmlproperty enumeration QtQuick::PathText::font.capitalization
+
+ Sets the capitalization for the text.
+
+ \list
+ \li Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
+ \li Font.AllUppercase - This alters the text to be rendered in all uppercase type.
+ \li Font.AllLowercase - This alters the text to be rendered in all lowercase type.
+ \li Font.SmallCaps - This alters the text to be rendered in small-caps type.
+ \li Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
+ \endlist
+
+ \qml
+ PathText { text: "Hello"; font.capitalization: Font.AllLowercase }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.kerning
+
+ Enables or disables the kerning OpenType feature when shaping the text. Disabling this may
+ improve performance when creating or changing the text, at the expense of some cosmetic
+ features. The default value is true.
+
+ \qml
+ PathText { text: "OATS FLAVOUR WAY"; font.kerning: false }
+ \endqml
+*/
+
+/*!
+ \qmlproperty bool QtQuick::PathText::font.preferShaping
+
+ Sometimes, a font will apply complex rules to a set of characters in order to
+ display them correctly. In some writing systems, such as Brahmic scripts, this is
+ required in order for the text to be legible, but in e.g. Latin script, it is merely
+ a cosmetic feature. Setting the \c preferShaping property to false will disable all
+ such features when they are not required, which will improve performance in most cases.
+
+ The default value is true.
+
+ \qml
+ PathText { text: "Some text"; font.preferShaping: false }
+ \endqml
+*/
+
+void QQuickPathText::updatePath() const
+{
+ if (!_path.isEmpty())
+ return;
+
+ _path.addText(_x, _y, _font, _text);
+
+ // Account for distance from baseline to top, since addText() takes baseline position
+ QRectF brect = _path.boundingRect();
+ _path.translate(0.0, -brect.y());
+}
+
+void QQuickPathText::addToPath(QPainterPath &path)
+{
+ if (_text.isEmpty())
+ return;
+ updatePath();
+ path.addPath(_path);
+}
+
QT_END_NAMESPACE
#include "moc_qquickpath_p.cpp"
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index ca0495a90d..159c46d13c 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -63,6 +63,7 @@ QT_REQUIRE_CONFIG(quick_path);
#include
#include
+#include
QT_BEGIN_NAMESPACE
@@ -598,6 +599,106 @@ public:
static QPointF sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle = nullptr);
};
+class Q_QUICK_PRIVATE_EXPORT QQuickPathText : public QQuickPathElement
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
+ Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
+ Q_PROPERTY(qreal width READ width NOTIFY changed)
+ Q_PROPERTY(qreal height READ height NOTIFY changed)
+ Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
+ QML_NAMED_ELEMENT(PathText)
+ QML_ADDED_IN_MINOR_VERSION(15)
+public:
+ QQuickPathText(QObject *parent=nullptr) : QQuickPathElement(parent)
+ {
+ connect(this, &QQuickPathText::xChanged, this, &QQuickPathElement::changed);
+ connect(this, &QQuickPathText::yChanged, this, &QQuickPathElement::changed);
+ connect(this, &QQuickPathText::textChanged, this, &QQuickPathElement::changed);
+ connect(this, &QQuickPathText::fontChanged, this, &QQuickPathElement::changed);
+
+ connect(this, &QQuickPathElement::changed, this, &QQuickPathText::invalidate);
+ }
+
+ void addToPath(QPainterPath &path);
+
+ qreal x() const { return _x; }
+ qreal y() const { return _y; }
+ QString text() const { return _text; }
+ QFont font() const { return _font; }
+
+ void setX(qreal x)
+ {
+ if (qFuzzyCompare(_x, x))
+ return;
+
+ _x = x;
+ Q_EMIT xChanged();
+ }
+
+ void setY(qreal y)
+ {
+ if (qFuzzyCompare(_y, y))
+ return;
+
+ _y = y;
+ Q_EMIT yChanged();
+ }
+
+ void setText(const QString &text)
+ {
+ if (text == _text)
+ return;
+
+ _text = text;
+ Q_EMIT textChanged();
+ }
+
+ void setFont(const QFont &font)
+ {
+ if (font == _font)
+ return;
+
+ _font = font;
+ Q_EMIT fontChanged();
+ }
+
+ qreal width() const
+ {
+ updatePath();
+ return _path.boundingRect().width();
+ }
+
+ qreal height() const
+ {
+ updatePath();
+ return _path.boundingRect().height();
+ }
+
+Q_SIGNALS:
+ void xChanged();
+ void yChanged();
+ void textChanged();
+ void fontChanged();
+
+private Q_SLOTS:
+ void invalidate()
+ {
+ _path.clear();
+ }
+
+private:
+ void updatePath() const;
+
+ QString _text;
+ qreal _x = qreal(0.0);
+ qreal _y = qreal(0.0);
+ QFont _font;
+
+ mutable QPainterPath _path;
+};
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickPathElement)
@@ -614,5 +715,6 @@ QML_DECLARE_TYPE(QQuickPathSvg)
QML_DECLARE_TYPE(QQuickPathPercent)
QML_DECLARE_TYPE(QQuickPathPolyline)
QML_DECLARE_TYPE(QQuickPath)
+QML_DECLARE_TYPE(QQuickPathText)
#endif // QQUICKPATH_H
diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h
index e26001ec77..5505b876c1 100644
--- a/src/quick/util/qquickpath_p_p.h
+++ b/src/quick/util/qquickpath_p_p.h
@@ -80,6 +80,7 @@ public:
QList _attributePoints;
QStringList _attributes;
QList _pathCurves;
+ QList _pathTexts;
mutable QQuickCachedBezier prevBez;
QQmlNullableValue startX;
QQmlNullableValue startY;