187 lines
5.9 KiB
QML
187 lines
5.9 KiB
QML
/*
|
|
* Nixie tube display mode — retro amber glowing digits in glass tubes.
|
|
* Glow is faked with layered text copies, so no QtGraphicalEffects
|
|
* dependency. Tubes flicker randomly (nixie cathode style); the
|
|
* flicker intensity is configurable (0 = off, 100 = nervous).
|
|
* The euro sign is plain text (default font, white), followed by a
|
|
* trend arrow (green up / red down; unchanged keeps the last arrow).
|
|
* Pure QtQuick: testable outside the plasmoid shell.
|
|
*/
|
|
|
|
import QtQuick 2.15
|
|
|
|
Row {
|
|
id: root
|
|
|
|
// wired by the parent loader
|
|
property string display: "--"
|
|
// 1 = up, -1 = down, 0 = unchanged -> keep showing the last arrow
|
|
property int trendDir: 0
|
|
property int lastDir: 0
|
|
onTrendDirChanged: {
|
|
if (trendDir !== 0) {
|
|
lastDir = trendDir;
|
|
}
|
|
}
|
|
// 0 = no flicker, 100 = frequent and deep flicker
|
|
property int flickerIntensity: 40
|
|
|
|
spacing: height * 0.05
|
|
|
|
function digitsOf(s) {
|
|
var out = "";
|
|
for (var i = 0; i < s.length; i++) {
|
|
var ch = s.charAt(i);
|
|
if (ch >= "0" && ch <= "9") {
|
|
out += ch;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
readonly property string digits: digitsOf(display)
|
|
readonly property real tubeWidth: height * 0.52
|
|
readonly property real glowFont: height * 0.80
|
|
readonly property bool flickerOn: flickerIntensity > 0 && digits.length > 0
|
|
|
|
Repeater {
|
|
model: root.digits.length
|
|
|
|
delegate: Rectangle {
|
|
id: tube
|
|
|
|
width: root.tubeWidth
|
|
height: root.height
|
|
radius: width * 0.30
|
|
gradient: Gradient {
|
|
GradientStop { position: 0.0; color: "#3a2410" }
|
|
GradientStop { position: 0.5; color: "#241505" }
|
|
GradientStop { position: 1.0; color: "#160c03" }
|
|
}
|
|
border.color: "#4a2e12"
|
|
border.width: 1
|
|
|
|
// glass reflection strip
|
|
Rectangle {
|
|
anchors.top: parent.top
|
|
anchors.topMargin: parent.height * 0.06
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
width: parent.width * 0.5
|
|
height: parent.height * 0.16
|
|
radius: height / 2
|
|
color: "#ffffff"
|
|
opacity: 0.06
|
|
}
|
|
|
|
// digit + glow layers, flickered as one group
|
|
Item {
|
|
id: glowGroup
|
|
anchors.fill: parent
|
|
|
|
Text {
|
|
id: glowText
|
|
anchors.centerIn: parent
|
|
text: root.digits[index]
|
|
color: "#ffb648"
|
|
font.family: "DejaVu Serif"
|
|
font.pixelSize: root.glowFont
|
|
|
|
// layered glow: two scaled copies behind the crisp digit
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: parent.text
|
|
color: "#ff8c00"
|
|
opacity: 0.30
|
|
font: parent.font
|
|
scale: 1.24
|
|
}
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: parent.text
|
|
color: "#ff9d1e"
|
|
opacity: 0.50
|
|
font: parent.font
|
|
scale: 1.10
|
|
}
|
|
}
|
|
}
|
|
|
|
// nixie flicker: random dropout of the cathode glow.
|
|
// repeat: true keeps the cycle alive; each trigger reschedules
|
|
// the next interval (intensity 40 -> ~4 s between flicks,
|
|
// 100 -> ~1.5 s).
|
|
Timer {
|
|
id: flickTimer
|
|
repeat: true
|
|
running: root.flickerOn
|
|
interval: 1500
|
|
onTriggered: {
|
|
flick.restart();
|
|
scheduleNext();
|
|
}
|
|
|
|
function scheduleNext() {
|
|
var base = 6000 - root.flickerIntensity * 45;
|
|
interval = Math.max(700, base * (0.6 + Math.random() * 0.8));
|
|
}
|
|
|
|
Component.onCompleted: scheduleNext()
|
|
}
|
|
|
|
SequentialAnimation {
|
|
id: flick
|
|
ScriptAction {
|
|
script: {
|
|
var range = root.flickerIntensity / 100 * 0.75;
|
|
glowGroup.opacity = 1 - (0.15 + Math.random() * range);
|
|
}
|
|
}
|
|
PauseAnimation { duration: 40 + Math.random() * 140 }
|
|
NumberAnimation {
|
|
target: glowGroup
|
|
property: "opacity"
|
|
to: 1.0
|
|
duration: 90 + Math.random() * 160
|
|
easing.type: Easing.OutQuad
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// breathing room between the tubes and the currency sign
|
|
Item {
|
|
width: root.height * 0.22
|
|
height: 1
|
|
}
|
|
|
|
// euro sign in the plain-text font, white
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "\u20AC"
|
|
visible: root.digits.length > 0
|
|
color: "#ffffff"
|
|
font.family: Qt.application.font.family
|
|
font.pixelSize: root.height * 0.85
|
|
}
|
|
|
|
// trend arrow: green up / red down; unchanged keeps the last arrow
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: root.lastDir > 0 ? "\u25B2" : "\u25BC"
|
|
visible: root.digits.length > 0 && root.lastDir !== 0
|
|
color: root.lastDir > 0 ? "#2ecc71" : "#e74c3c"
|
|
font.family: Qt.application.font.family
|
|
font.pixelSize: root.height * 0.45
|
|
}
|
|
|
|
// error / empty state
|
|
Text {
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "--"
|
|
color: "#7a4e1c"
|
|
font.family: "DejaVu Serif"
|
|
font.pixelSize: root.glowFont * 0.8
|
|
visible: root.digits.length === 0
|
|
}
|
|
}
|