-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPixelText.qml
More file actions
137 lines (119 loc) · 4.65 KB
/
Copy pathPixelText.qml
File metadata and controls
137 lines (119 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import QtQuick
import "pixelfont.js" as PixelFont
// Draws text as a grid of hard-edged squares. A Canvas is used rather than a
// Repeater of Rectangles because a single line of the date already needs a few
// hundred cells, and hundreds of scene-graph nodes per label is a real cost for
// something that only redraws once a second.
Item {
id: root
property string text: ""
// Size of one lit pixel and the dead space around it. Keeping them separate
// is what gives the glyphs their visible grid.
property real cell: 3
property real gap: 1
property color color: "#f2f2f2"
// When set, the unlit pixels of the glyph box are drawn faintly, which reads
// as a dormant LED matrix behind the digits.
property color offColor: "transparent"
property int tracking: 1
property bool animated: false
property int rollDuration: 320
readonly property real step: cell + gap
readonly property int rowCount: PixelFont.ROWS
// Text currently drawn at rest. During a roll it holds the outgoing string
// while `text` holds the incoming one.
property string shownText: ""
property string rollTarget: ""
property real rollPhase: 1
implicitWidth: Math.max(1, PixelFont.columns(text, tracking) * step - gap)
implicitHeight: rowCount * step - gap
onTextChanged: {
if (!animated || shownText === "") {
rollAnimation.stop()
shownText = text
rollPhase = 1
canvas.requestPaint()
return
}
if (shownText === text) return
// A change arriving mid-roll settles the previous one instantly, so the
// outgoing glyph is never two generations stale.
if (rollAnimation.running) {
rollAnimation.stop()
shownText = rollTarget
}
rollTarget = text
rollPhase = 0
rollAnimation.start()
}
onRollPhaseChanged: canvas.requestPaint()
onColorChanged: canvas.requestPaint()
onOffColorChanged: canvas.requestPaint()
onCellChanged: canvas.requestPaint()
onGapChanged: canvas.requestPaint()
NumberAnimation {
id: rollAnimation
target: root
property: "rollPhase"
from: 0
to: 1
duration: root.rollDuration
easing.type: Easing.OutCubic
onFinished: root.shownText = root.rollTarget
}
Canvas {
id: canvas
anchors.fill: parent
onWidthChanged: requestPaint()
onHeightChanged: requestPaint()
onPaint: {
let ctx = getContext("2d")
ctx.reset()
let step = root.step
let cell = root.cell
let boxHeight = root.rowCount * step
let incoming = root.text
let outgoing = root.shownText
let phase = root.rollPhase
let showOff = root.offColor.a > 0
// Snapping the offset to whole rows keeps every pixel on the grid
// during the roll, so the transition still looks like a pixel
// display rather than a smoothly sliding bitmap.
let shift = Math.round(phase * root.rowCount) * step
let x = 0
for (let i = 0; i < incoming.length; i++) {
let ch = incoming.charAt(i)
let glyph = PixelFont.glyph(ch)
let cols = glyph[0].length
let previous = i < outgoing.length ? outgoing.charAt(i) : ch
if (showOff) {
ctx.fillStyle = root.offColor
for (let r = 0; r < root.rowCount; r++)
for (let c = 0; c < cols; c++)
ctx.fillRect(x + c * step, r * step, cell, cell)
}
ctx.fillStyle = root.color
if (phase >= 1 || previous === ch) {
paintGlyph(ctx, glyph, x, 0, step, cell)
} else {
ctx.save()
ctx.beginPath()
ctx.rect(x - 1, -1, cols * step + 2, boxHeight + 2)
ctx.clip()
paintGlyph(ctx, PixelFont.glyph(previous), x, -shift, step, cell)
paintGlyph(ctx, glyph, x, boxHeight - shift, step, cell)
ctx.restore()
}
x += (cols + root.tracking) * step
}
}
function paintGlyph(ctx, glyph, x, y, step, cell) {
for (let r = 0; r < glyph.length; r++) {
let row = glyph[r]
for (let c = 0; c < row.length; c++)
if (row.charAt(c) === "1")
ctx.fillRect(x + c * step, y + r * step, cell, cell)
}
}
}
}