make circular progresses use shape instead of canvas

This commit is contained in:
end-4
2025-08-03 18:12:44 +07:00
parent 839593b11e
commit 71d0ac4c5e
@@ -2,6 +2,7 @@
// License: LGPL-3.0 - A copy can be found in `licenses` folder of repo // License: LGPL-3.0 - A copy can be found in `licenses` folder of repo
import QtQuick import QtQuick
import QtQuick.Shapes
import qs.modules.common import qs.modules.common
/** /**
@@ -15,7 +16,7 @@ Item {
property real value: 0 property real value: 0
property color primaryColor: Appearance.m3colors.m3onSecondaryContainer property color primaryColor: Appearance.m3colors.m3onSecondaryContainer
property color secondaryColor: Appearance.colors.colSecondaryContainer property color secondaryColor: Appearance.colors.colSecondaryContainer
property real gapAngle: Math.PI / 9 property real gapAngle: 180 / 9
property bool fill: false property bool fill: false
property int fillOverflow: 2 property int fillOverflow: 2
property bool enableAnimation: true property bool enableAnimation: true
@@ -25,73 +26,66 @@ Item {
width: size width: size
height: size height: size
signal animationFinished(); property real degree: value * 360
property real centerX: root.width / 2
property real centerY: root.height / 2
property real arcRadius: root.size / 2 - root.lineWidth
property real startAngle: -90
Behavior on degree {
enabled: root.enableAnimation
NumberAnimation {
duration: root.animationDuration
easing.type: root.easingType
}
onValueChanged: {
canvas.degree = value * 360;
}
onPrimaryColorChanged: {
canvas.requestPaint();
}
onSecondaryColorChanged: {
canvas.requestPaint();
} }
Canvas { Loader {
id: canvas active: root.fill
property real degree: 0
anchors.fill: parent anchors.fill: parent
antialiasing: true
sourceComponent: Rectangle {
onDegreeChanged: { radius: 9999
requestPaint(); color: root.secondaryColor
} }
}
onPaint: { Shape {
var ctx = getContext("2d"); anchors.fill: parent
var x = root.width / 2; layer.enabled: true
var y = root.height / 2; layer.smooth: true
var radius = root.size / 2 - root.lineWidth; preferredRendererType: Shape.CurveRenderer
var startAngle = (Math.PI / 180) * 270; ShapePath {
var fullAngle = (Math.PI / 180) * (270 + 360); id: secondaryPath
var progressAngle = (Math.PI / 180) * (270 + degree); strokeColor: root.secondaryColor
var epsilon = 0.01; // Small angle in radians strokeWidth: root.lineWidth
capStyle: ShapePath.RoundCap
ctx.reset(); fillColor: "transparent"
if (root.fill) { PathAngleArc {
ctx.fillStyle = root.secondaryColor; centerX: root.centerX
ctx.beginPath(); centerY: root.centerY
ctx.arc(x, y, radius + fillOverflow, startAngle, fullAngle); radiusX: root.arcRadius
ctx.fill(); radiusY: root.arcRadius
startAngle: root.startAngle - root.gapAngle
sweepAngle: -(360 - root.degree - 2 * root.gapAngle)
} }
ctx.lineCap = 'round';
ctx.lineWidth = root.lineWidth;
// Secondary
ctx.beginPath();
ctx.arc(x, y, radius, progressAngle + gapAngle, fullAngle - gapAngle);
ctx.strokeStyle = root.secondaryColor;
ctx.stroke();
// Primary (value indication)
var endAngle = progressAngle + (value > 0 ? 0 : epsilon);
ctx.beginPath();
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.strokeStyle = root.primaryColor;
ctx.stroke();
} }
ShapePath {
Behavior on degree { id: primaryPath
enabled: root.enableAnimation strokeColor: root.primaryColor
NumberAnimation { strokeWidth: root.lineWidth
duration: root.animationDuration capStyle: ShapePath.RoundCap
easing.type: root.easingType fillColor: "transparent"
PathAngleArc {
centerX: root.centerX
centerY: root.centerY
radiusX: root.arcRadius
radiusY: root.arcRadius
startAngle: root.startAngle
sweepAngle: root.degree
} }
} }
} }
} }