88 lines
2.5 KiB
QML
88 lines
2.5 KiB
QML
// The countdown ring, drawn with Canvas.
|
|
//
|
|
// A hero number inside a progress arc: the number is what you read, the arc is
|
|
// the glance. The arc colour is the only thing that says focus vs break.
|
|
import QtQuick
|
|
import QtQuick.Controls.Material
|
|
|
|
Item {
|
|
id: dial
|
|
property real progress: 0 // 0..1 elapsed
|
|
property string clockText: "00:00"
|
|
property string label: ""
|
|
property string sublabel: ""
|
|
property color accent: "#2a78d6"
|
|
property bool paused: false
|
|
|
|
implicitWidth: 260
|
|
implicitHeight: 260
|
|
|
|
onProgressChanged: canvas.requestPaint()
|
|
onAccentChanged: canvas.requestPaint()
|
|
onPausedChanged: canvas.requestPaint()
|
|
|
|
Canvas {
|
|
id: canvas
|
|
anchors.fill: parent
|
|
antialiasing: true
|
|
|
|
onPaint: {
|
|
var ctx = getContext("2d")
|
|
ctx.reset()
|
|
var centre = Math.min(width, height) / 2
|
|
var stroke = Math.max(10, centre * 0.11)
|
|
var radius = centre - stroke
|
|
|
|
ctx.lineCap = "round"
|
|
ctx.lineWidth = stroke
|
|
|
|
ctx.beginPath()
|
|
ctx.strokeStyle = Qt.rgba(0.5, 0.5, 0.5, 0.18)
|
|
ctx.arc(centre, centre, radius, 0, Math.PI * 2)
|
|
ctx.stroke()
|
|
|
|
var remaining = 1 - Math.max(0, Math.min(1, dial.progress))
|
|
if (remaining > 0) {
|
|
ctx.beginPath()
|
|
ctx.globalAlpha = dial.paused ? 0.45 : 1
|
|
ctx.strokeStyle = dial.accent
|
|
ctx.arc(centre, centre, radius, -Math.PI / 2, -Math.PI / 2 + remaining * Math.PI * 2)
|
|
ctx.stroke()
|
|
}
|
|
}
|
|
}
|
|
|
|
Column {
|
|
anchors.centerIn: parent
|
|
spacing: 4
|
|
width: parent.width * 0.72
|
|
|
|
Label {
|
|
width: parent.width
|
|
text: dial.label.toUpperCase()
|
|
font.pixelSize: 11
|
|
font.letterSpacing: 1.5
|
|
opacity: 0.65
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
Label {
|
|
width: parent.width
|
|
text: dial.clockText
|
|
font.pixelSize: Math.round(dial.width * 0.2)
|
|
font.weight: Font.Light
|
|
font.family: "Roboto Mono, monospace"
|
|
opacity: dial.paused ? 0.6 : 1
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
Label {
|
|
width: parent.width
|
|
text: dial.sublabel
|
|
visible: dial.sublabel !== ""
|
|
font.pixelSize: 13
|
|
opacity: 0.75
|
|
elide: Text.ElideRight
|
|
horizontalAlignment: Text.AlignHCenter
|
|
}
|
|
}
|
|
}
|