diff options
Diffstat (limited to 'src/imports/components/RegularPolygonItem.qml')
-rw-r--r-- | src/imports/components/RegularPolygonItem.qml | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/src/imports/components/RegularPolygonItem.qml b/src/imports/components/RegularPolygonItem.qml new file mode 100644 index 0000000..0d51760 --- /dev/null +++ b/src/imports/components/RegularPolygonItem.qml @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Quick Studio Components. +** +** $QT_BEGIN_LICENSE:GPL$ +** 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. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.10 +import QtQuick.Shapes 1.0 + +/*! + \qmltype RegularPolygon + \inqmlmodule QtQuick.Studio.Components + \since QtQuick.Studio.Components 1.0 + \inherits Shape + + \brief A filled regular polygon with an optional border. +*/ + +Shape { + id: root + width: 200 + height: 200 + +/*! + The radius used to draw rounded corners. + + The default value is 10. + + If radius is non-zero, the corners will be rounded, otherwise they will + be sharp. The radius can also be specified separately for each corner by + using the \l bottomLeftRadius, \l bottomRightRadius, \l topLeftRadius, and + \l topRightRadius properties. +*/ + property int radius: 10 + +/*! + The gradient of the regular polygon fill color. + + By default, no gradient is enabled and the value is null. In this case, the + fill uses a solid color based on the value of \l fillColor. + + When set, \l fillColor is ignored and filling is done using one of the + \l ShapeGradient subtypes. + + \note The \l Gradient type cannot be used here. Rather, prefer using one of + the advanced subtypes, like \l LinearGradient. +*/ + property alias gradient: path.fillGradient + +/*! + The style of the regular polygon border. + + \value ShapePath.SolidLine + A solid line. This is the default value. + \value ShapePath.DashLine + Dashes separated by a few pixels. + The \l dashPattern property specifies the dash pattern. + + \sa Qt::PenStyle +*/ + property alias strokeStyle: path.strokeStyle + +/*! + The width of the border of the regular polygon. + + The default value is 4. + + A width of 1 creates a thin line. For no line, use a negative value or a + transparent color. + + \note The width of the regular polygon's border does not affect the geometry of + the regular polygon itself or its position relative to other items if anchors are + used. + + The border is rendered within the regular polygon's boundaries. +*/ + property alias strokeWidth: path.strokeWidth + +/*! + The color used to draw the border of the regular polygon. + + When set to \c transparent, no line is drawn. + + The default value is \c red. + + \sa QColor +*/ + property alias strokeColor: path.strokeColor + +/*! + The dash pattern of the regular polygon border specified as the dashes and the + gaps between them. + + The dash pattern is specified in units of the pen's width. That is, a dash + with the length 5 and width 10 is 50 pixels long. + + The default value is (4, 2), meaning a dash of 4 * \l strokeWidth pixels + followed by a space of 2 * \l strokeWidth pixels. + + \sa QPen::setDashPattern() +*/ + property alias dashPattern: path.dashPattern + + + property alias joinStyle: path.joinStyle + +/*! + The regular polygon fill color. + + A gradient for the fill can be specified by using \l gradient. If both a + color and a gradient are specified, the gradient is used. + + When set to \c transparent, no filling occurs. + + The default value is \c white. +*/ + property alias fillColor: path.fillColor + +/*! + The starting point of the dash pattern for the regular polygon border. + + The offset is measured in terms of the units used to specify the dash + pattern. For example, a pattern where each stroke is four units long, + followed by a gap of two units, will begin with the stroke when drawn + as a line. However, if the dash offset is set to 4.0, any line drawn + will begin with the gap. Values of the offset up to 4.0 will cause part + of the stroke to be drawn first, and values of the offset between 4.0 and + 6.0 will cause the line to begin with part of the gap. + + The default value is 0. + + \sa QPen::setDashOffset() +*/ + property alias dashOffset: path.dashOffset + +/*! + Number of sides on the polygon. +*/ + property int sideCount: 6 + + layer.enabled: root.antialiasing + layer.smooth: root.antialiasing + layer.textureSize: Qt.size(root.width * 2, root.height * 2) + + // This is used to make the bounding box of the item a bit bigger so it will draw sharp edges + // in case of large stroke width instead of cutting it off. + Item { + anchors.fill: parent + anchors.margins: -root.strokeWidth + } + + ShapePath { + id: path + + joinStyle: ShapePath.MiterJoin + strokeWidth: 4 + strokeColor: "red" + startX: 0 + startY: 0 + } + + onSideCountChanged: root.constructPolygon() + onRadiusChanged: { + // Only construct polygon if radius changed from 0 to 1 or vice versa. + if ((root.radius + root.__previousRadius) === 1) + root.constructPolygon() + + root.__previousRadius = root.radius + } + Component.onCompleted: root.constructPolygon() + + property real __centerX: root.width / 2 + property real __centerY: root.height / 2 + property real __radius: Math.min(root.width, root.height) / 2 + + property int __previousRadius: root.radius + + property int minRadius: 0 + property int maxRadius: root.__radius * Math.cos(root.toRadians(180.0 / root.sideCount)) + + property int __actualRadius: Math.max(root.minRadius, Math.min(root.maxRadius, root.radius)) + + function constructPolygon() { + root.clearPathElements() + + if (root.radius === 0) + root.constructNonRoundedPolygonPath() + else + root.constructRoundedPolygonPath() + } + + function toRadians(degrees) { + return degrees * (Math.PI / 180.0) + } + + function constructNonRoundedPolygonPath() { + for (var cornerNumber = 0; cornerNumber < root.sideCount; cornerNumber++) { + let angleToCorner = root.toRadians(cornerNumber * (360.0 / root.sideCount)) + + if (cornerNumber === 0) { + path.startX = Qt.binding(function() { + return root.__centerX + root.__radius * Math.cos(0) + }) + path.startY = Qt.binding(function() { + return root.__centerY + root.__radius * Math.sin(0) + }) + } else { + let pathLine = Qt.createQmlObject('import QtQuick 2.15; PathLine {}', path) + pathLine.x = Qt.binding(function() { + return root.__centerX + root.__radius * Math.cos(angleToCorner) + }) + pathLine.y = Qt.binding(function() { + return root.__centerY + root.__radius * Math.sin(angleToCorner) + }) + path.pathElements.push(pathLine) + } + } + + // Close the polygon + var pathLineClose = Qt.createQmlObject('import QtQuick 2.15; PathLine {}', path) + pathLineClose.x = Qt.binding(function() { return path.startX } ) + pathLineClose.y = Qt.binding(function() { return path.startY } ) + path.pathElements.push(pathLineClose) + } + + property real __halfInteriorCornerAngle: 90 - (180.0 / root.sideCount) + property real __halfCornerArcSweepAngle: 90 - root.__halfInteriorCornerAngle + property real __distanceToCornerArcCenter: root.__radius - root.__actualRadius / + Math.sin(root.toRadians(root.__halfInteriorCornerAngle)) + + function constructRoundedPolygonPath() { + for (var cornerNumber = 0; cornerNumber < root.sideCount; cornerNumber++) { + let angleToCorner = cornerNumber * (360.0 / root.sideCount) + + let pathArc = Qt.createQmlObject('import QtQuick 2.15; PathArc { + property real centerX; + property real centerY }', path) + pathArc.centerX = Qt.binding(function() { + return root.__centerX + root.__distanceToCornerArcCenter + * Math.cos(root.toRadians(angleToCorner)) + }) + pathArc.centerY = Qt.binding(function() { + return root.__centerY + root.__distanceToCornerArcCenter + * Math.sin(root.toRadians(angleToCorner)) + }) + pathArc.x = Qt.binding(function() { + return pathArc.centerX + root.__actualRadius + * (Math.cos(root.toRadians(angleToCorner + root.__halfCornerArcSweepAngle))) + }) + pathArc.y = Qt.binding(function() { + return pathArc.centerY + root.__actualRadius + * (Math.sin(root.toRadians(angleToCorner + root.__halfCornerArcSweepAngle))) + }) + pathArc.radiusX = Qt.binding(function() { return root.__actualRadius }) + pathArc.radiusY = Qt.binding(function() { return root.__actualRadius }) + + if (cornerNumber === 0) { + path.startX = Qt.binding(function() { + return pathArc.centerX + root.__actualRadius + * (Math.cos(root.toRadians(angleToCorner - root.__halfCornerArcSweepAngle))) + }) + path.startY = Qt.binding(function() { + return pathArc.centerY + root.__actualRadius + * (Math.sin(root.toRadians(angleToCorner - root.__halfCornerArcSweepAngle))) + }) + } else { + let pathLine = Qt.createQmlObject('import QtQuick 2.15; PathLine {}', path) + pathLine.x = Qt.binding(function() { + return pathArc.centerX + root.__actualRadius + * (Math.cos(root.toRadians(angleToCorner - root.__halfCornerArcSweepAngle))) + }) + pathLine.y = Qt.binding(function() { + return pathArc.centerY + root.__actualRadius + * (Math.sin(root.toRadians(angleToCorner - root.__halfCornerArcSweepAngle))) + }) + path.pathElements.push(pathLine) + } + + path.pathElements.push(pathArc) + } + + // Close the polygon + var pathLineClose = Qt.createQmlObject('import QtQuick 2.15; PathLine {}', path) + pathLineClose.x = Qt.binding(function() { return path.startX} ) + pathLineClose.y = Qt.binding(function() { return path.startY} ) + path.pathElements.push(pathLineClose) + } + + function clearPathElements() { + for (var i = 0; i !== path.pathElements.length; ++i) + path.pathElements[i].destroy() + + path.pathElements = [] + } +} |