Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b040eb8c47 | ||
|
|
bb9bed4184 |
@@ -10,11 +10,12 @@ 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.
|
||||
Display style changes apply immediately, without restarting the shell.
|
||||
|
||||
| Situation | Plain text color | Trend LED |
|
||||
|---|---|---|
|
||||
@@ -32,19 +33,25 @@ representations.
|
||||
- **Refresh interval (minutes)** — default 5, minimum **5**, maximum 1440.
|
||||
The 5-minute floor exists because the CoinGecko API allows one request per
|
||||
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). 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*.
|
||||
|
||||
## Installation
|
||||
|
||||
### Graphical
|
||||
Double-click `bitcoin-price-1.2.0.plasmoid` in Dolphin (Plasma installs it to
|
||||
Double-click `bitcoin-price-1.3.0.plasmoid` in Dolphin (Plasma installs it to
|
||||
`~/.local/share/plasma/plasmoids/` automatically). The latest build is attached
|
||||
to the [v1.2.0 release](../../releases/tag/v1.2.0).
|
||||
to the [v1.3.0 release](../../releases/tag/v1.2.0).
|
||||
|
||||
### Terminal
|
||||
```bash
|
||||
kpackagetool5 --type Plasma/Applet --upgrade bitcoin-price-1.2.0.plasmoid
|
||||
kpackagetool5 --type Plasma/Applet --upgrade bitcoin-price-1.3.0.plasmoid
|
||||
# Then restart the shell if needed:
|
||||
plasmashell --replace &
|
||||
```
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -14,6 +14,18 @@
|
||||
</choices>
|
||||
<default>0</default>
|
||||
</entry>
|
||||
<entry name="flapSeconds" type="Int">
|
||||
<label>Seconds over which the split-flap cascade spreads a price change</label>
|
||||
<default>3</default>
|
||||
<min>1</min>
|
||||
<max>15</max>
|
||||
</entry>
|
||||
<entry name="nixieFlicker" type="Int">
|
||||
<label>Nixie tube flicker intensity (0 = off, 100 = frequent and deep)</label>
|
||||
<default>40</default>
|
||||
<min>0</min>
|
||||
<max>100</max>
|
||||
</entry>
|
||||
<entry name="refreshMinutes" type="Int">
|
||||
<label>Refresh interval in minutes (minimum 5 because of the CoinGecko rate limit)</label>
|
||||
<default>5</default>
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -13,16 +15,37 @@ Row {
|
||||
// wired by the parent loader
|
||||
property string display: "--"
|
||||
property color trendColor: "#9e9e9e"
|
||||
// seconds over which the cascade to a new price is spread
|
||||
property int flapSeconds: 3
|
||||
|
||||
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
|
||||
readonly property real cellWidth: cellFont * 0.80
|
||||
// one flap open+close takes 260 ms; keep delays above that
|
||||
readonly property int flapDuration: 260
|
||||
|
||||
Component.onCompleted: {
|
||||
chars = display.split("");
|
||||
chars = boardText.split("");
|
||||
ready = true;
|
||||
}
|
||||
|
||||
@@ -30,17 +53,27 @@ 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 = [];
|
||||
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);
|
||||
changed.push([i, to]);
|
||||
}
|
||||
}
|
||||
var span = Math.max(1, flapSeconds) * 1000 - flapDuration;
|
||||
var step = changed.length > 1 ? Math.max(0, Math.floor(span / (changed.length - 1))) : 0;
|
||||
for (var j = 0; j < changed.length; j++) {
|
||||
var cell = rep.itemAt(changed[j][0]);
|
||||
if (cell) {
|
||||
cell.flipTo(changed[j][1], j * step);
|
||||
}
|
||||
}
|
||||
chars = nc;
|
||||
}
|
||||
@@ -130,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
|
||||
|
||||
@@ -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,6 +65,11 @@ Row {
|
||||
opacity: 0.06
|
||||
}
|
||||
|
||||
// digit + glow layers, flickered as one group
|
||||
Item {
|
||||
id: glowGroup
|
||||
anchors.fill: parent
|
||||
|
||||
Text {
|
||||
id: glowText
|
||||
anchors.centerIn: parent
|
||||
@@ -73,46 +95,56 @@ Row {
|
||||
font: parent.font
|
||||
scale: 1.10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// occasional gentle nixie flicker
|
||||
// nixie flicker: random dropout of the cathode glow
|
||||
Timer {
|
||||
interval: 2200 + Math.random() * 3200
|
||||
id: flickTimer
|
||||
repeat: false
|
||||
running: root.visible && root.digits.length > 0
|
||||
running: root.flickerOn
|
||||
onTriggered: {
|
||||
flick.stop();
|
||||
flick.start();
|
||||
interval = 2200 + Math.random() * 3200;
|
||||
restart();
|
||||
flick.restart();
|
||||
scheduleNext();
|
||||
}
|
||||
|
||||
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
|
||||
NumberAnimation {
|
||||
target: glowText
|
||||
property: "opacity"
|
||||
to: 0.82
|
||||
duration: 70
|
||||
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: glowText
|
||||
target: glowGroup
|
||||
property: "opacity"
|
||||
to: 1.0
|
||||
duration: 140
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -14,6 +15,8 @@ Kirigami.FormLayout {
|
||||
// The cfg_ prefix is what Plasma uses to read/write plasmoid.configuration
|
||||
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
|
||||
@@ -25,6 +28,26 @@ Kirigami.FormLayout {
|
||||
model: [i18n("Plain text"), i18n("Split-flap board"), i18n("Nixie tubes")]
|
||||
}
|
||||
|
||||
QQC2.SpinBox {
|
||||
id: flapSpin
|
||||
visible: modeCombo.currentIndex === 1
|
||||
Kirigami.FormData.label: i18n("Split-flap cascade (seconds):")
|
||||
from: 1
|
||||
to: 15
|
||||
stepSize: 1
|
||||
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):")
|
||||
|
||||
+37
-1
@@ -25,11 +25,32 @@ Item {
|
||||
|
||||
readonly property int refreshMinutes: Math.max(5, plasmoid.configuration.refreshMinutes)
|
||||
|
||||
// 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"
|
||||
: displayMode === 2 ? "DisplayNixie.qml"
|
||||
: "DisplayText.qml"
|
||||
|
||||
// Enum config values arrive either as an int index or as the choice
|
||||
// name, depending on the Plasma version — accept both.
|
||||
function modeFromConfig(v) {
|
||||
if (typeof v === "number") {
|
||||
return Math.max(0, Math.min(2, Math.round(v)));
|
||||
}
|
||||
if (v === "SplitFlap") {
|
||||
return 1;
|
||||
}
|
||||
if (v === "Nixie") {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
readonly property string apiUrl: "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=eur"
|
||||
|
||||
// -1 = down, 0 = unchanged / no previous reading yet, 1 = up
|
||||
@@ -72,7 +93,10 @@ Item {
|
||||
|
||||
Component.onCompleted: {
|
||||
try {
|
||||
displayMode = Math.max(0, Math.min(2, plasmoid.configuration.displayMode));
|
||||
// live binding: config changes from the dialog apply immediately
|
||||
displayMode = Qt.binding(function () {
|
||||
return modeFromConfig(plasmoid.configuration.displayMode);
|
||||
});
|
||||
} catch (e) {
|
||||
// outside a real plasmoid shell (tests): keep the default
|
||||
}
|
||||
@@ -119,6 +143,12 @@ Item {
|
||||
onLoaded: {
|
||||
item.display = Qt.binding(function () { return root.displayText; });
|
||||
item.trendColor = Qt.binding(function () { return root.priceColor; });
|
||||
if (item.flapSeconds !== undefined) {
|
||||
item.flapSeconds = Qt.binding(function () { return root.flapSeconds; });
|
||||
}
|
||||
if (item.flickerIntensity !== undefined) {
|
||||
item.flickerIntensity = Qt.binding(function () { return root.nixieFlicker; });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,6 +184,12 @@ Item {
|
||||
onLoaded: {
|
||||
item.display = Qt.binding(function () { return root.displayText; });
|
||||
item.trendColor = Qt.binding(function () { return root.priceColor; });
|
||||
if (item.flapSeconds !== undefined) {
|
||||
item.flapSeconds = Qt.binding(function () { return root.flapSeconds; });
|
||||
}
|
||||
if (item.flickerIntensity !== undefined) {
|
||||
item.flickerIntensity = Qt.binding(function () { return root.nixieFlicker; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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.2.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
|
||||
|
||||
Reference in New Issue
Block a user