v1.2.0: add split-flap board and nixie tube display modes with display style config
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Split-flap display mode — airport departure board style.
|
||||
* White characters on black cells with a flap split line;
|
||||
* changed characters flip with a cascading animation.
|
||||
* Pure QtQuick so it can be tested outside the plasmoid shell.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
|
||||
Row {
|
||||
id: root
|
||||
|
||||
// wired by the parent loader
|
||||
property string display: "--"
|
||||
property color trendColor: "#9e9e9e"
|
||||
|
||||
spacing: height * 0.06
|
||||
|
||||
property var chars: []
|
||||
property bool ready: false
|
||||
readonly property real cellFont: height * 0.72
|
||||
readonly property real cellWidth: cellFont * 0.80
|
||||
|
||||
Component.onCompleted: {
|
||||
chars = display.split("");
|
||||
ready = true;
|
||||
}
|
||||
|
||||
onDisplayChanged: {
|
||||
if (!ready) {
|
||||
return;
|
||||
}
|
||||
var nc = display.split("");
|
||||
var n = Math.max(chars.length, nc.length);
|
||||
for (var i = 0; i < n; i++) {
|
||||
var from = i < chars.length ? chars[i] : "";
|
||||
var to = i < nc.length ? nc[i] : "";
|
||||
if (from !== to) {
|
||||
var cell = rep.itemAt(i);
|
||||
if (cell) {
|
||||
cell.flipTo(to, i * 45);
|
||||
}
|
||||
}
|
||||
}
|
||||
chars = nc;
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: rep
|
||||
model: root.chars.length
|
||||
|
||||
delegate: Rectangle {
|
||||
id: cell
|
||||
|
||||
width: root.cellWidth
|
||||
height: root.height
|
||||
radius: 2
|
||||
color: "#0b0b0d"
|
||||
border.color: "#26262b"
|
||||
border.width: 1
|
||||
|
||||
// plain property (not a binding) so the flip animation
|
||||
// controls when the character actually changes
|
||||
property string ch: ""
|
||||
property string pendingCh: ""
|
||||
|
||||
Component.onCompleted: {
|
||||
ch = root.chars[index] !== undefined ? root.chars[index] : "";
|
||||
}
|
||||
|
||||
function flipTo(newCh, delay) {
|
||||
if (newCh === ch) {
|
||||
return;
|
||||
}
|
||||
pendingCh = newCh;
|
||||
delayTimer.interval = Math.max(0, delay);
|
||||
delayTimer.restart();
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayTimer
|
||||
interval: 0
|
||||
onTriggered: flipAnim.start()
|
||||
}
|
||||
|
||||
transform: Scale {
|
||||
id: scaleTransform
|
||||
origin.y: cell.height / 2
|
||||
yScale: 1
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: flipAnim
|
||||
NumberAnimation {
|
||||
target: scaleTransform
|
||||
property: "yScale"
|
||||
to: 0.06
|
||||
duration: 130
|
||||
easing.type: Easing.InQuad
|
||||
}
|
||||
ScriptAction {
|
||||
script: cell.ch = cell.pendingCh
|
||||
}
|
||||
NumberAnimation {
|
||||
target: scaleTransform
|
||||
property: "yScale"
|
||||
to: 1
|
||||
duration: 130
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: cell.ch
|
||||
color: "#f2f2f2"
|
||||
font.family: "DejaVu Sans Mono"
|
||||
font.pixelSize: root.cellFont
|
||||
}
|
||||
|
||||
// the flap split line across the middle
|
||||
Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
y: parent.height / 2 - 1
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "#000000"
|
||||
opacity: 0.55
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trend LED (green/red/grey) — boards are monochrome, so the
|
||||
// trend shows here instead of in the letter color
|
||||
Rectangle {
|
||||
width: root.height * 0.14
|
||||
height: width
|
||||
radius: width / 2
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: root.trendColor
|
||||
border.color: "#26262b"
|
||||
border.width: 1
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user