From b040eb8c47addb1de19123ede3888a0f4ec21d2f Mon Sep 17 00:00:00 2001 From: ArnoldCordewiner Date: Sat, 5 Sep 2026 20:51:05 +0200 Subject: [PATCH] v1.4.0: digit-only flaps with white euro text, nixie white euro + configurable flicker, mode-conditional config fields; fix stale derived-property reads in change handler --- README.md | 9 ++- bitcoin-price-1.4.0.plasmoid | Bin 0 -> 11332 bytes contents/config/main.xml | 6 ++ contents/ui/DisplayFlip.qml | 43 +++++++++-- contents/ui/DisplayNixie.qml | 132 +++++++++++++++++++++------------- contents/ui/configGeneral.qml | 13 ++++ contents/ui/main.qml | 9 +++ metadata.desktop | 2 +- 8 files changed, 154 insertions(+), 60 deletions(-) create mode 100644 bitcoin-price-1.4.0.plasmoid 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 0000000000000000000000000000000000000000..60874b48ffdf9f015aeba09b1059377240a5a9b2 GIT binary patch literal 11332 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 Bj#vNy literal 0 HcmV?d00001 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