added two new themes, reworked Crypto.qml and Stats.qml, standardized the buttons for the modules, added a new Sidebar widget which contains new 3 modules
This commit is contained in:
@@ -40,6 +40,7 @@ ModuleChip {
|
||||
property string localVsCur: "usd"
|
||||
property int localDecimals: cfg.cryptoDecimals
|
||||
property int localRefreshSec: cfg.cryptoRefreshSec
|
||||
property int localRotateSec: cfg.cryptoRotateSec
|
||||
property bool localShowIcon: cfg.cryptoShowIcon
|
||||
|
||||
function resolveWithPersistence() {
|
||||
@@ -49,6 +50,7 @@ ModuleChip {
|
||||
root.activeCoinIndex = 0
|
||||
root.localDecimals = cfg.cryptoDecimals
|
||||
root.localRefreshSec = cfg.cryptoRefreshSec
|
||||
root.localRotateSec = cfg.cryptoRotateSec
|
||||
root.localShowIcon = cfg.cryptoShowIcon
|
||||
}
|
||||
|
||||
@@ -56,6 +58,7 @@ ModuleChip {
|
||||
const pairs = root.localPairs
|
||||
const decimals = Math.round(root.localDecimals)
|
||||
const refreshSec = Math.round(root.localRefreshSec)
|
||||
const rotateSec = Math.round(root.localRotateSec)
|
||||
const showIcon = root.localShowIcon
|
||||
|
||||
const pairsQml = "[" + pairs.map(p =>
|
||||
@@ -64,7 +67,8 @@ ModuleChip {
|
||||
|
||||
configWriter.writeInts([
|
||||
{ key: "cryptoDecimals", value: decimals },
|
||||
{ key: "cryptoRefreshSec", value: refreshSec }
|
||||
{ key: "cryptoRefreshSec", value: refreshSec },
|
||||
{ key: "cryptoRotateSec", value: rotateSec }
|
||||
])
|
||||
configWriter.writeBool("cryptoShowIcon", showIcon)
|
||||
configWriter.writeArray("cryptoPairs", pairsQml)
|
||||
@@ -87,6 +91,10 @@ ModuleChip {
|
||||
property bool loading: false
|
||||
property bool loadingVisible: false
|
||||
property bool hasError: false
|
||||
property var sparklineData: []
|
||||
property real change7d: 0
|
||||
property var _priceCache: ({})
|
||||
property int _fetchGen: 0
|
||||
|
||||
Timer {
|
||||
id: minLoadTimer
|
||||
@@ -140,66 +148,148 @@ ModuleChip {
|
||||
return val.slice(0, maxLen).replace(/[\x00-\x1f\x7f-\x9f]/g, "")
|
||||
}
|
||||
|
||||
function rotatePrev() {
|
||||
if (root.localPairs.length <= 1) return
|
||||
root.activeCoinIndex = (root.activeCoinIndex - 1 + root.localPairs.length) % root.localPairs.length
|
||||
root._applyFromCache()
|
||||
rotateTimer.restart()
|
||||
}
|
||||
|
||||
function rotateNext() {
|
||||
if (root.localPairs.length <= 1) return
|
||||
root.activeCoinIndex = (root.activeCoinIndex + 1) % root.localPairs.length
|
||||
root._applyFromCache()
|
||||
rotateTimer.restart()
|
||||
}
|
||||
|
||||
function _applyFromCache() {
|
||||
var key = root.localCoinId + "_" + root.localActiveCurrency
|
||||
var entry = root._priceCache[key]
|
||||
if (!entry) { console.log("Crypto: no cache entry for", key); return }
|
||||
console.log("Crypto: applying", key, "price=" + entry.price)
|
||||
price = entry.price
|
||||
change24h = entry.change24h
|
||||
high24h = entry.high24h
|
||||
low24h = entry.low24h
|
||||
marketCap = entry.marketCap
|
||||
volume24h = entry.volume24h
|
||||
rank = entry.rank
|
||||
circulatingSupply = entry.circulatingSupply
|
||||
ath = entry.ath
|
||||
athChange = entry.athChange
|
||||
coinSymbol = safeString(entry.symbol, 8, root.localCoinId).toUpperCase()
|
||||
coinName = safeString(entry.name, 50, "")
|
||||
lastUpdated = new Date().toLocaleTimeString(Qt.locale(), "HH:mm:ss")
|
||||
root.displayCurrency = root.localActiveCurrency
|
||||
sparklineData = entry.sparkline || []
|
||||
change7d = entry.change7d || 0
|
||||
hasError = false
|
||||
}
|
||||
|
||||
function fetchPrice() {
|
||||
if (loading) return
|
||||
loading = true
|
||||
hasError = false
|
||||
_fetchGen++
|
||||
var myGen = _fetchGen
|
||||
|
||||
const url =
|
||||
"https://api.coingecko.com/api/v3/coins/markets" +
|
||||
"?vs_currency=" + encodeURIComponent(localActiveCurrency || "usd") +
|
||||
"&ids=" + encodeURIComponent(localCoinId || "bitcoin") +
|
||||
"&order=market_cap_desc&per_page=1&page=1" +
|
||||
"&sparkline=false&price_change_percentage=24h"
|
||||
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.timeout = 15000
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState !== XMLHttpRequest.DONE) return
|
||||
loading = false
|
||||
if (xhr.status !== 200) { hasError = true; return }
|
||||
try {
|
||||
const text = xhr.responseText
|
||||
if (text.length > 65536) { hasError = true; return }
|
||||
const data = JSON.parse(text)
|
||||
if (!Array.isArray(data) || data.length === 0) { hasError = true; return }
|
||||
const coin = data[0]
|
||||
if (typeof coin !== "object" || coin === null) { hasError = true; return }
|
||||
price = safeNumber(coin.current_price, -1)
|
||||
change24h = safeNumber(coin.price_change_percentage_24h, 0)
|
||||
high24h = safeNumber(coin.high_24h, 0)
|
||||
low24h = safeNumber(coin.low_24h, 0)
|
||||
marketCap = safeNumber(coin.market_cap, 0)
|
||||
volume24h = safeNumber(coin.total_volume, 0)
|
||||
rank = Math.trunc(safeNumber(coin.market_cap_rank, 0))
|
||||
circulatingSupply = safeNumber(coin.circulating_supply, 0)
|
||||
ath = safeNumber(coin.ath, 0)
|
||||
athChange = safeNumber(coin.ath_change_percentage, 0)
|
||||
coinSymbol = safeString(coin.symbol, 8, localCoinId).toUpperCase()
|
||||
coinName = safeString(coin.name, 50, "")
|
||||
lastUpdated = new Date().toLocaleTimeString(Qt.locale(), "HH:mm:ss")
|
||||
root.displayCurrency = root.localActiveCurrency
|
||||
root.activeCoinIndex = (root.activeCoinIndex + 1) % Math.max(1, root.localPairs.length)
|
||||
} catch (_) {
|
||||
hasError = true
|
||||
}
|
||||
var coinGroups = {}
|
||||
for (var i = 0; i < root.localPairs.length; i++) {
|
||||
var p = root.localPairs[i]
|
||||
if (!coinGroups[p.currency]) coinGroups[p.currency] = []
|
||||
var coinList = coinGroups[p.currency]
|
||||
if (coinList.indexOf(p.coin) === -1) coinList.push(p.coin)
|
||||
}
|
||||
|
||||
var currencies = Object.keys(coinGroups)
|
||||
var pending = currencies.length
|
||||
|
||||
if (pending === 0) { loading = false; hasError = true; return }
|
||||
|
||||
for (var ci = 0; ci < currencies.length; ci++) {
|
||||
var cur = currencies[ci]
|
||||
var ids = coinGroups[cur].join(",")
|
||||
|
||||
var url =
|
||||
"https://api.coingecko.com/api/v3/coins/markets" +
|
||||
"?vs_currency=" + encodeURIComponent(cur || "usd") +
|
||||
"&ids=" + encodeURIComponent(ids) +
|
||||
"&order=market_cap_desc&per_page=250&page=1" +
|
||||
"&sparkline=true&price_change_percentage=24h,7d"
|
||||
|
||||
var xhr = new XMLHttpRequest()
|
||||
xhr._currency = cur
|
||||
xhr.timeout = 15000
|
||||
xhr.onreadystatechange = (function(xhr, cur, myGen) {
|
||||
return function() {
|
||||
if (myGen !== root._fetchGen) return
|
||||
if (xhr.readyState !== XMLHttpRequest.DONE) return
|
||||
if (xhr.status !== 200) { hasError = true }
|
||||
else {
|
||||
try {
|
||||
var text = xhr.responseText
|
||||
if (text.length <= 65536) {
|
||||
var data = JSON.parse(text)
|
||||
if (Array.isArray(data)) {
|
||||
for (var di = 0; di < data.length; di++) {
|
||||
var coin = data[di]
|
||||
var key = coin.id + "_" + cur
|
||||
console.log("Crypto: cache", key, "price=" + coin.current_price)
|
||||
root._priceCache[key] = {
|
||||
price: safeNumber(coin.current_price, -1),
|
||||
change24h: safeNumber(coin.price_change_percentage_24h, 0),
|
||||
high24h: safeNumber(coin.high_24h, 0),
|
||||
low24h: safeNumber(coin.low_24h, 0),
|
||||
marketCap: safeNumber(coin.market_cap, 0),
|
||||
volume24h: safeNumber(coin.total_volume, 0),
|
||||
rank: Math.trunc(safeNumber(coin.market_cap_rank, 0)),
|
||||
circulatingSupply: safeNumber(coin.circulating_supply, 0),
|
||||
ath: safeNumber(coin.ath, 0),
|
||||
athChange: safeNumber(coin.ath_change_percentage, 0),
|
||||
symbol: safeString(coin.symbol, 8, coin.id),
|
||||
name: safeString(coin.name, 50, ""),
|
||||
sparkline: (coin.sparkline_in_7d && Array.isArray(coin.sparkline_in_7d.price))
|
||||
? coin.sparkline_in_7d.price : [],
|
||||
change7d: safeNumber(coin.price_change_percentage_7d_in_currency, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
if (--pending <= 0) {
|
||||
loading = false
|
||||
root._applyFromCache()
|
||||
}
|
||||
}
|
||||
})(xhr, cur, myGen)
|
||||
|
||||
xhr.open("GET", url)
|
||||
xhr.setRequestHeader("Accept", "application/json")
|
||||
xhr.send()
|
||||
}
|
||||
xhr.open("GET", url)
|
||||
xhr.setRequestHeader("Accept", "application/json")
|
||||
xhr.send()
|
||||
}
|
||||
|
||||
Component.onCompleted: { resolveWithPersistence(); fetchPrice() }
|
||||
|
||||
Timer {
|
||||
id: rotateTimer
|
||||
interval: root.localRotateSec * 1000
|
||||
running: root.localRotateSec > 0 && root.localPairs.length > 1
|
||||
repeat: true
|
||||
onTriggered: root.rotateNext()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: refreshTimer
|
||||
interval: root.localRefreshSec * 1000
|
||||
running: true
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: root.fetchPrice()
|
||||
}
|
||||
|
||||
onLocalRefreshSecChanged: refreshTimer.restart()
|
||||
onLocalRotateSecChanged: rotateTimer.restart()
|
||||
|
||||
RowLayout {
|
||||
id: chipRow
|
||||
@@ -277,36 +367,64 @@ ModuleChip {
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 30
|
||||
radius: 10
|
||||
height: 36
|
||||
radius: 12
|
||||
color: t.cryptoFieldBg
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 3
|
||||
spacing: 3
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
component TabBtn: Rectangle {
|
||||
required property string label
|
||||
required property int tabIndex
|
||||
property string icon: ""
|
||||
Layout.fillWidth: true
|
||||
height: 24
|
||||
radius: 8
|
||||
height: 28
|
||||
radius: 9
|
||||
|
||||
readonly property bool active: cryptoPopup.activeTab === tabIndex
|
||||
|
||||
color: active
|
||||
? (t.accent)
|
||||
: (tm.containsMouse ? (t.cryptoFieldBgFocus) : "transparent")
|
||||
Behavior on color { ColorAnimation { duration: 130 } }
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
|
||||
Text {
|
||||
border.color: active ? Qt.rgba(1, 1, 1, 0.18) : "transparent"
|
||||
border.width: 1
|
||||
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||
|
||||
scale: tm.pressed ? 0.93 : 1.0
|
||||
Behavior on scale { NumberAnimation { duration: 90; easing.type: Easing.OutQuad } }
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
text: parent.label
|
||||
color: parent.active ? (t.cryptoTextOnAccent) : (t.cryptoPopupDim)
|
||||
font.pixelSize: 11
|
||||
font.bold: parent.active
|
||||
Behavior on color { ColorAnimation { duration: 130 } }
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: parent.parent.icon !== ""
|
||||
text: parent.parent.icon
|
||||
color: parent.parent.active ? (t.cryptoTextOnAccent) : (t.cryptoPopupDim)
|
||||
font.pixelSize: 11
|
||||
opacity: parent.parent.active ? 1.0 : 0.7
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
Behavior on opacity { NumberAnimation { duration: 150 } }
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: parent.parent.label
|
||||
color: parent.parent.active ? (t.cryptoTextOnAccent) : (t.cryptoPopupDim)
|
||||
font.pixelSize: 12
|
||||
font.bold: parent.parent.active
|
||||
font.letterSpacing: parent.parent.active ? 0.4 : 0.0
|
||||
Behavior on color { ColorAnimation { duration: 150 } }
|
||||
Behavior on font.letterSpacing { NumberAnimation { duration: 150 } }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: tm
|
||||
anchors.fill: parent; hoverEnabled: true
|
||||
@@ -315,8 +433,8 @@ ModuleChip {
|
||||
}
|
||||
}
|
||||
|
||||
TabBtn { label: "Price"; tabIndex: 0 }
|
||||
TabBtn { label: "Settings"; tabIndex: 1 }
|
||||
TabBtn { label: "Price"; icon: ""; tabIndex: 0 }
|
||||
TabBtn { label: "Settings"; icon: ""; tabIndex: 1 }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -490,6 +608,113 @@ ModuleChip {
|
||||
|
||||
Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator }
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Text {
|
||||
text: "7d Chart"
|
||||
color: t.cryptoPopupDim
|
||||
font.pixelSize: 12
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Rectangle {
|
||||
visible: root.sparklineData.length > 1
|
||||
width: badge7d.implicitWidth + 12
|
||||
height: 20; radius: 10
|
||||
color: root.change7d >= 0 ? (t.cryptoUpBg) : (t.cryptoDownBg)
|
||||
Text {
|
||||
id: badge7d
|
||||
anchors.centerIn: parent
|
||||
text: (root.change7d >= 0 ? "▲ +" : "▼ ") + root.change7d.toFixed(2) + "%"
|
||||
color: root.change7d >= 0 ? (t.cryptoUp) : (t.cryptoDown)
|
||||
font.pixelSize: 10
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Canvas {
|
||||
id: sparklineCanvas
|
||||
Layout.fillWidth: true
|
||||
height: 52
|
||||
|
||||
property var prices: root.sparklineData
|
||||
property bool isUp: root.change7d >= 0
|
||||
property string upColor: t.cryptoUp || "#4ade80"
|
||||
property string downColor: t.cryptoDown || "#f87171"
|
||||
|
||||
onPricesChanged: requestPaint()
|
||||
onIsUpChanged: requestPaint()
|
||||
onUpColorChanged: requestPaint()
|
||||
onDownColorChanged:requestPaint()
|
||||
|
||||
onPaint: {
|
||||
var ctx = getContext("2d")
|
||||
ctx.clearRect(0, 0, width, height)
|
||||
var pts = prices
|
||||
if (!pts || pts.length < 2) {
|
||||
ctx.fillStyle = Qt.rgba(1, 1, 1, 0.06)
|
||||
ctx.beginPath()
|
||||
ctx.roundedRect(0, 0, width, height, 6, 6)
|
||||
ctx.fill()
|
||||
return
|
||||
}
|
||||
var minP = pts[0], maxP = pts[0]
|
||||
for (var i = 1; i < pts.length; i++) {
|
||||
if (pts[i] < minP) minP = pts[i]
|
||||
if (pts[i] > maxP) maxP = pts[i]
|
||||
}
|
||||
var range = maxP - minP
|
||||
if (range === 0) range = 1
|
||||
var pad = 4, pw = width - pad * 2, ph = height - pad * 2
|
||||
var lineColor = isUp ? upColor : downColor
|
||||
|
||||
ctx.beginPath()
|
||||
for (var j = 0; j < pts.length; j++) {
|
||||
var x = pad + (j / (pts.length - 1)) * pw
|
||||
var y = pad + ph - ((pts[j] - minP) / range) * ph
|
||||
if (j === 0) ctx.moveTo(x, y)
|
||||
else ctx.lineTo(x, y)
|
||||
}
|
||||
ctx.lineTo(pad + pw, pad + ph)
|
||||
ctx.lineTo(pad, pad + ph)
|
||||
ctx.closePath()
|
||||
var grad = ctx.createLinearGradient(0, 0, 0, height)
|
||||
grad.addColorStop(0, Qt.rgba(
|
||||
isUp ? 0.29 : 0.97,
|
||||
isUp ? 0.87 : 0.44,
|
||||
isUp ? 0.50 : 0.44, 0.22))
|
||||
grad.addColorStop(1, Qt.rgba(0, 0, 0, 0))
|
||||
ctx.fillStyle = grad
|
||||
ctx.fill()
|
||||
|
||||
ctx.beginPath()
|
||||
for (var k = 0; k < pts.length; k++) {
|
||||
var lx = pad + (k / (pts.length - 1)) * pw
|
||||
var ly = pad + ph - ((pts[k] - minP) / range) * ph
|
||||
if (k === 0) ctx.moveTo(lx, ly)
|
||||
else ctx.lineTo(lx, ly)
|
||||
}
|
||||
ctx.strokeStyle = lineColor
|
||||
ctx.lineWidth = 1.5
|
||||
ctx.lineJoin = "round"
|
||||
ctx.stroke()
|
||||
|
||||
var lx2 = pad + pw
|
||||
var ly2 = pad + ph - ((pts[pts.length - 1] - minP) / range) * ph
|
||||
ctx.beginPath()
|
||||
ctx.arc(lx2, ly2, 3, 0, Math.PI * 2)
|
||||
ctx.fillStyle = lineColor
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator }
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -500,6 +725,50 @@ ModuleChip {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.localPairs.length > 1
|
||||
width: 26; height: 26; radius: 8
|
||||
color: prevMouse.containsMouse
|
||||
? (prevMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
|
||||
: "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "‹"
|
||||
color: t.cryptoIcon
|
||||
font.pixelSize: 14
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: prevMouse
|
||||
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.rotatePrev()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: root.localPairs.length > 1
|
||||
width: 26; height: 26; radius: 8
|
||||
color: nextMouse.containsMouse
|
||||
? (nextMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
|
||||
: "transparent"
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "›"
|
||||
color: t.cryptoIcon
|
||||
font.pixelSize: 14
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: nextMouse
|
||||
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.rotateNext()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: refreshBtn
|
||||
width: 26; height: 26; radius: 8
|
||||
@@ -540,6 +809,7 @@ ModuleChip {
|
||||
ColumnLayout {
|
||||
id: settingsContent
|
||||
width: tabContainer.width
|
||||
height: tabContainer.height
|
||||
x: cryptoPopup.activeTab === 1 ? 0 : tabContainer.width
|
||||
opacity: cryptoPopup.activeTab === 1 ? 1.0 : 0.0
|
||||
Behavior on x { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } }
|
||||
@@ -650,7 +920,7 @@ ModuleChip {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: "Pairs to cycle"
|
||||
@@ -661,11 +931,11 @@ ModuleChip {
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 4
|
||||
spacing: 8
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
height: 24
|
||||
height: 28
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
@@ -756,6 +1026,7 @@ ModuleChip {
|
||||
root.localPairs = arr
|
||||
if (root.activeCoinIndex >= arr.length)
|
||||
root.activeCoinIndex = 0
|
||||
root.fetchPrice()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -885,41 +1156,51 @@ ModuleChip {
|
||||
root.localPairs = root.localPairs.concat([{ coin: c, currency: cur }])
|
||||
addCoinInput.text = ""
|
||||
addCurrencyInput.text = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 30; height: 30; radius: 8
|
||||
color: addPairMouse.containsMouse
|
||||
? (addPairMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
|
||||
: (t.cryptoFieldBg)
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
Text {
|
||||
anchors.centerIn: parent; text: "+"
|
||||
font.pixelSize: 14; font.bold: true
|
||||
color: t.cryptoPopupText
|
||||
}
|
||||
MouseArea {
|
||||
id: addPairMouse
|
||||
anchors.fill: parent; hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const c = addCoinInput.text.trim().toLowerCase()
|
||||
const cur = addCurrencyInput.text.trim().toLowerCase() || root.localVsCur
|
||||
if (c !== "") {
|
||||
root.localPairs = root.localPairs.concat([{ coin: c, currency: cur }])
|
||||
addCoinInput.text = ""
|
||||
addCurrencyInput.text = ""
|
||||
root.fetchPrice()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
height: 32; radius: 9
|
||||
color: addPairMouse.containsMouse
|
||||
? (addPairMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
|
||||
: (t.cryptoApplyIdleBg)
|
||||
Behavior on color { ColorAnimation { duration: 130 } }
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "+ Add Pair"
|
||||
color: addPairMouse.containsMouse ? (t.cryptoTextOnAccent) : (t.cryptoPopupText)
|
||||
font.pixelSize: 12; font.bold: true
|
||||
Behavior on color { ColorAnimation { duration: 130 } }
|
||||
}
|
||||
MouseArea {
|
||||
id: addPairMouse
|
||||
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
const c = addCoinInput.text.trim().toLowerCase()
|
||||
const cur = addCurrencyInput.text.trim().toLowerCase() || root.localVsCur
|
||||
if (c !== "") {
|
||||
root.localPairs = root.localPairs.concat([{ coin: c, currency: cur }])
|
||||
addCoinInput.text = ""
|
||||
addCurrencyInput.text = ""
|
||||
root.fetchPrice()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Rectangle { Layout.fillWidth: true; height: 1; color: t.calSeparator }
|
||||
|
||||
StepRow {
|
||||
label: "Decimals in label"
|
||||
value: root.localDecimals
|
||||
@@ -1015,6 +1296,93 @@ ModuleChip {
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Rotate (seconds)"
|
||||
color: t.cryptoPopupDim
|
||||
font.pixelSize: 12
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 24; height: 24; radius: 7
|
||||
color: rotDecMouse.containsMouse
|
||||
? (rotDecMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
|
||||
: (t.cryptoFieldBg)
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
Text { anchors.centerIn: parent; text: "−"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText }
|
||||
MouseArea {
|
||||
id: rotDecMouse
|
||||
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: if (root.localRotateSec > 1) root.localRotateSec -= 1
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 54; height: 24; radius: 6
|
||||
color: rotateInput.activeFocus ? (t.cryptoFieldBgFocus) : (t.cryptoFieldBg)
|
||||
border.color: rotateInput.activeFocus ? (t.accent) : "transparent"
|
||||
border.width: 1
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
Behavior on border.color { ColorAnimation { duration: 120 } }
|
||||
|
||||
TextInput {
|
||||
id: rotateInput
|
||||
anchors { fill: parent; leftMargin: 6; rightMargin: 6; topMargin: 4; bottomMargin: 4 }
|
||||
text: root.localRotateSec.toString()
|
||||
color: t.cryptoPopupText
|
||||
font.pixelSize: 12
|
||||
font.bold: true
|
||||
font.family: "monospace"
|
||||
selectionColor: t.accent
|
||||
horizontalAlignment: TextInput.AlignHCenter
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
validator: IntValidator { bottom: 1; top: 600 }
|
||||
clip: true
|
||||
|
||||
onActiveFocusChanged: if (!activeFocus) {
|
||||
const v = parseInt(text)
|
||||
if (!isNaN(v) && v >= 1 && v <= 600)
|
||||
root.localRotateSec = v
|
||||
else
|
||||
text = root.localRotateSec.toString()
|
||||
}
|
||||
onAccepted: {
|
||||
const v = parseInt(text)
|
||||
if (!isNaN(v) && v >= 1 && v <= 600)
|
||||
root.localRotateSec = v
|
||||
else
|
||||
text = root.localRotateSec.toString()
|
||||
focus = false
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root
|
||||
function onLocalRotateSecChanged() {
|
||||
if (!rotateInput.activeFocus)
|
||||
rotateInput.text = root.localRotateSec.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 24; height: 24; radius: 7
|
||||
color: rotIncMouse.containsMouse
|
||||
? (rotIncMouse.pressed ? (t.accent) : (t.cryptoFieldBgFocus))
|
||||
: (t.cryptoFieldBg)
|
||||
Behavior on color { ColorAnimation { duration: 100 } }
|
||||
Text { anchors.centerIn: parent; text: "+"; font.pixelSize: 14; font.bold: true; color: t.cryptoPopupText }
|
||||
MouseArea {
|
||||
id: rotIncMouse
|
||||
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: if (root.localRotateSec < 600) root.localRotateSec += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToggleRow {
|
||||
label: "Show icon on bar"
|
||||
checked: root.localShowIcon
|
||||
@@ -1047,9 +1415,7 @@ ModuleChip {
|
||||
id: applyMouse
|
||||
anchors.fill: parent; hoverEnabled: true; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.price = -1
|
||||
root.activeCoinIndex = 0
|
||||
root.coinSymbol = root.localCoinId.toUpperCase().slice(0, 4)
|
||||
root.persistSettings()
|
||||
root.fetchPrice()
|
||||
cryptoPopup.activeTab = 0
|
||||
@@ -1057,30 +1423,6 @@ ModuleChip {
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "Reset to defaults"
|
||||
color: t.cryptoPopupDim
|
||||
font.pixelSize: 11
|
||||
font.underline: true
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent; cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.localPairs = Array.isArray(cfg.cryptoPairs) && cfg.cryptoPairs.length > 0 ? cfg.cryptoPairs : [{ coin: "bitcoin", currency: "usd" }]
|
||||
root.localVsCur = "usd"
|
||||
root.displayCurrency = root.localPairs.length > 0 ? root.localPairs[0].currency : "usd"
|
||||
root.activeCoinIndex = 0
|
||||
root.localDecimals = cfg.cryptoDecimals
|
||||
root.localRefreshSec = cfg.cryptoRefreshSec
|
||||
root.localShowIcon = cfg.cryptoShowIcon
|
||||
root.persistSettings()
|
||||
root.price = -1
|
||||
cryptoPopup.activeTab = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { height: 2 }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user