diff --git a/README.md b/README.md
index 5a8423f..a52e8c5 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,8 @@ Three display styles, configurable in the settings dialog:
| Style | Description |
|---|---|
| *Plain text* | Trend-colored text (green/red/grey) |
-| *Split-flap board* | Airport board: white characters on black flaps, cascading flip animation on price changes, trend LED |
-| *Nixie tubes* | Retro amber glowing digits in glass tubes (with a soft flicker), trend LED |
+| *Split-flap board* | Airport board: white digit flaps, white € sign in the default font, cascading flip animation on price changes (configurable duration), trend LED |
+| *Nixie tubes* | Retro amber glowing digits in glass tubes with configurable random flicker, white € sign in the default font, trend LED |
In the split-flap and nixie styles the digits keep their display color and the
trend (up/down/unchanged) is shown on a small LED next to the display.
@@ -35,7 +35,10 @@ representations.
5 minutes. A hint text in the config dialog explains this.
- **Split-flap cascade (seconds)** — default 3, range 1–15. Spreads the flip
animation of a price change evenly over this many seconds (applies to the
- split-flap display style).
+ split-flap display style). Only shown when that style is selected.
+- **Nixie flicker (0 = off)** — default 40, range 0–100. Random cathode-dropout
+ flicker of the nixie tubes; 0 disables it, higher values flicker more often
+ and deeper. Only shown when the nixie style is selected.
Configurable via right-click on the widget → *Configure Bitcoin Price…* → *General*.
diff --git a/bitcoin-price-1.4.0.plasmoid b/bitcoin-price-1.4.0.plasmoid
new file mode 100644
index 0000000..60874b4
Binary files /dev/null and b/bitcoin-price-1.4.0.plasmoid differ
diff --git a/contents/config/main.xml b/contents/config/main.xml
index a51a203..8a8367a 100644
--- a/contents/config/main.xml
+++ b/contents/config/main.xml
@@ -20,6 +20,12 @@
1
15
+
+
+ 40
+ 0
+ 100
+
5
diff --git a/contents/ui/DisplayFlip.qml b/contents/ui/DisplayFlip.qml
index 234ae85..01599a9 100644
--- a/contents/ui/DisplayFlip.qml
+++ b/contents/ui/DisplayFlip.qml
@@ -1,7 +1,9 @@
/*
* 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.
+ * Only digits are shown as flaps; the euro sign is rendered as plain
+ * text (default font, white) next to the board, and the trend shows
+ * on a small LED. Changed characters flip with a cascading animation
+ * spread over the configured number of seconds.
* Pure QtQuick so it can be tested outside the plasmoid shell.
*/
@@ -18,6 +20,23 @@ Row {
spacing: height * 0.06
+ function digitsOf(s) {
+ var out = "";
+ for (var i = 0; i < s.length; i++) {
+ var ch = s.charAt(i);
+ if ((ch >= "0" && ch <= "9") || ch === "-") {
+ out += ch;
+ }
+ }
+ return out;
+ }
+
+ // UI-only derived values (euro visibility); logic must not rely on these —
+ // QML gives no ordering guarantee between a change handler and the
+ // re-evaluation of dependent bindings.
+ readonly property string digitsOnly: digitsOf(display)
+ readonly property string boardText: digitsOnly.length > 0 ? digitsOnly : "--"
+
property var chars: []
property bool ready: false
readonly property real cellFont: height * 0.72
@@ -26,7 +45,7 @@ Row {
readonly property int flapDuration: 260
Component.onCompleted: {
- chars = display.split("");
+ chars = boardText.split("");
ready = true;
}
@@ -34,7 +53,10 @@ Row {
if (!ready) {
return;
}
- var nc = display.split("");
+ // compute from display directly: derived bindings may still be
+ // stale at this point (QML makes no ordering guarantee)
+ var digits = digitsOf(display);
+ var nc = (digits.length > 0 ? digits : "--").split("");
var n = Math.max(chars.length, nc.length);
// spread the changed cells evenly across flapSeconds
var changed = [];
@@ -141,8 +163,17 @@ Row {
}
}
- // trend LED (green/red/grey) — boards are monochrome, so the
- // trend shows here instead of in the letter color
+ // euro sign in the plain-text font, white
+ Text {
+ anchors.verticalCenter: parent.verticalCenter
+ text: "\u20AC"
+ visible: root.digitsOnly.length > 0
+ color: "#ffffff"
+ font.family: Qt.application.font.family
+ font.pixelSize: root.height * 0.66
+ }
+
+ // trend LED (green/red/grey)
Rectangle {
width: root.height * 0.14
height: width
diff --git a/contents/ui/DisplayNixie.qml b/contents/ui/DisplayNixie.qml
index b0a1481..fd7c112 100644
--- a/contents/ui/DisplayNixie.qml
+++ b/contents/ui/DisplayNixie.qml
@@ -1,7 +1,10 @@
/*
* Nixie tube display mode — retro amber glowing digits in glass tubes.
* Glow is faked with layered text copies, so no QtGraphicalEffects
- * dependency. Pure QtQuick: testable outside the plasmoid shell.
+ * dependency. Tubes flicker randomly (nixie cathode style); the
+ * flicker intensity is configurable (0 = off, 100 = nervous).
+ * The euro sign is rendered as plain text (default font, white).
+ * Pure QtQuick: testable outside the plasmoid shell.
*/
import QtQuick 2.15
@@ -12,12 +15,26 @@ Row {
// wired by the parent loader
property string display: "--"
property color trendColor: "#9e9e9e"
+ // 0 = no flicker, 100 = frequent and deep flicker
+ property int flickerIntensity: 40
spacing: height * 0.05
- readonly property string digits: display.replace(/[^0-9]/g, "")
+ 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
@@ -48,71 +65,86 @@ Row {
opacity: 0.06
}
- Text {
- id: glowText
- anchors.centerIn: parent
- text: root.digits[index]
- color: "#ffb648"
- font.family: "DejaVu Serif"
- font.pixelSize: root.glowFont
+ // digit + glow layers, flickered as one group
+ Item {
+ id: glowGroup
+ anchors.fill: parent
- // layered glow: two scaled copies behind the crisp digit
Text {
+ id: glowText
anchors.centerIn: parent
- text: parent.text
- color: "#ff8c00"
- opacity: 0.30
- font: parent.font
- scale: 1.24
+ 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
+ }
}
- 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
+ Timer {
+ id: flickTimer
+ repeat: false
+ running: root.flickerOn
+ onTriggered: {
+ flick.restart();
+ scheduleNext();
}
- // occasional gentle nixie flicker
- Timer {
- interval: 2200 + Math.random() * 3200
- repeat: false
- running: root.visible && root.digits.length > 0
- onTriggered: {
- flick.stop();
- flick.start();
- interval = 2200 + Math.random() * 3200;
- restart();
+ function scheduleNext() {
+ // intensity 40 -> ~4 s between flicks, 100 -> ~1.5 s
+ 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);
}
}
- SequentialAnimation {
- id: flick
- NumberAnimation {
- target: glowText
- property: "opacity"
- to: 0.82
- duration: 70
- }
- NumberAnimation {
- target: glowText
- property: "opacity"
- to: 1.0
- duration: 140
- }
+ PauseAnimation { duration: 40 + Math.random() * 140 }
+ NumberAnimation {
+ target: glowGroup
+ property: "opacity"
+ to: 1.0
+ duration: 90 + Math.random() * 160
+ easing.type: Easing.OutQuad
}
}
}
}
- // currency indicator, dim amber like an indicator lamp
+ // euro sign in the plain-text font, white
Text {
anchors.verticalCenter: parent.verticalCenter
text: "\u20AC"
- color: "#8a5a20"
- font.family: "DejaVu Serif"
- font.pixelSize: root.glowFont * 0.6
visible: root.digits.length > 0
+ color: "#ffffff"
+ font.family: Qt.application.font.family
+ font.pixelSize: root.height * 0.60
}
// error / empty state
diff --git a/contents/ui/configGeneral.qml b/contents/ui/configGeneral.qml
index 0b9f870..e8ec7e3 100644
--- a/contents/ui/configGeneral.qml
+++ b/contents/ui/configGeneral.qml
@@ -2,6 +2,7 @@
* Config page for nl.a14r.bitcoinprice.
* Follows the canonical Plasma 5 config dialog pattern:
* Kirigami.FormLayout + cfg_ prefixed property aliases.
+ * Mode-specific settings are only visible for the matching style.
*/
import QtQuick 2.15
@@ -15,6 +16,7 @@ Kirigami.FormLayout {
property alias cfg_displayMode: modeCombo.currentIndex
property alias cfg_refreshMinutes: intervalSpin.value
property alias cfg_flapSeconds: flapSpin.value
+ property alias cfg_nixieFlicker: flickSpin.value
Item {
Kirigami.FormData.isSection: true
@@ -28,6 +30,7 @@ Kirigami.FormLayout {
QQC2.SpinBox {
id: flapSpin
+ visible: modeCombo.currentIndex === 1
Kirigami.FormData.label: i18n("Split-flap cascade (seconds):")
from: 1
to: 15
@@ -35,6 +38,16 @@ Kirigami.FormLayout {
editable: true
}
+ QQC2.SpinBox {
+ id: flickSpin
+ visible: modeCombo.currentIndex === 2
+ Kirigami.FormData.label: i18n("Nixie flicker (0 = off):")
+ from: 0
+ to: 100
+ stepSize: 10
+ editable: true
+ }
+
QQC2.SpinBox {
id: intervalSpin
Kirigami.FormData.label: i18n("Refresh interval (minutes):")
diff --git a/contents/ui/main.qml b/contents/ui/main.qml
index fc6e1ad..6a51e46 100644
--- a/contents/ui/main.qml
+++ b/contents/ui/main.qml
@@ -28,6 +28,9 @@ Item {
// Seconds over which the split-flap display cascades to a new price
readonly property int flapSeconds: Math.max(1, plasmoid.configuration.flapSeconds)
+ // Nixie tube flicker intensity (0 = off, 100 = frequent and deep)
+ readonly property int nixieFlicker: Math.max(0, Math.min(100, plasmoid.configuration.nixieFlicker))
+
// 0 = plain text, 1 = split-flap board, 2 = nixie tubes
property int displayMode: 0
readonly property string modeSource: displayMode === 1 ? "DisplayFlip.qml"
@@ -143,6 +146,9 @@ Item {
if (item.flapSeconds !== undefined) {
item.flapSeconds = Qt.binding(function () { return root.flapSeconds; });
}
+ if (item.flickerIntensity !== undefined) {
+ item.flickerIntensity = Qt.binding(function () { return root.nixieFlicker; });
+ }
}
}
}
@@ -181,6 +187,9 @@ Item {
if (item.flapSeconds !== undefined) {
item.flapSeconds = Qt.binding(function () { return root.flapSeconds; });
}
+ if (item.flickerIntensity !== undefined) {
+ item.flickerIntensity = Qt.binding(function () { return root.nixieFlicker; });
+ }
}
}
diff --git a/metadata.desktop b/metadata.desktop
index 82bf150..99193d1 100644
--- a/metadata.desktop
+++ b/metadata.desktop
@@ -10,7 +10,7 @@ X-KDE-ServiceTypes=Plasma/Applet
X-KDE-PluginInfo-Author=Arnold Cordewiner
X-KDE-PluginInfo-Email=hermes@a14r.be
X-KDE-PluginInfo-Name=nl.a14r.bitcoinprice
-X-KDE-PluginInfo-Version=1.3.0
+X-KDE-PluginInfo-Version=1.4.0
X-KDE-PluginInfo-Website=https://gitea.a14r.be/hermes/plasma-bitcoin-price
X-KDE-PluginInfo-Category=Utilities
X-KDE-PluginInfo-License=MIT