diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 9d59b4eb53..96416c04c3 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -337,7 +337,9 @@ QML_RES_ICONS = \
qml/res/icons/shutdown.png \
qml/res/icons/singlesig-wallet.png \
qml/res/icons/storage-dark.png \
- qml/res/icons/storage-light.png
+ qml/res/icons/storage-light.png \
+ qml/res/icons/tooltip-arrow-dark.png \
+ qml/res/icons/tooltip-arrow-light.png
QML_QRC_CPP = qml/qrc_bitcoin.cpp
QML_QRC = qml/bitcoin_qml.qrc
@@ -361,6 +363,7 @@ QML_RES_QML = \
qml/components/StorageSettings.qml \
qml/components/ThemeSettings.qml \
qml/components/TotalBytesIndicator.qml \
+ qml/components/Tooltip.qml \
qml/controls/ContinueButton.qml \
qml/controls/CoreText.qml \
qml/controls/ExternalLink.qml \
@@ -383,6 +386,7 @@ QML_RES_QML = \
qml/controls/TextButton.qml \
qml/controls/Theme.qml \
qml/controls/ToggleButton.qml \
+ qml/controls/utils.js \
qml/controls/ValueInput.qml \
qml/pages/initerrormessage.qml \
qml/pages/main.qml \
diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc
index 86372d3c4f..3f89b0dbf7 100644
--- a/src/qml/bitcoin_qml.qrc
+++ b/src/qml/bitcoin_qml.qrc
@@ -19,6 +19,7 @@
components/StorageSettings.qml
components/ThemeSettings.qml
components/TotalBytesIndicator.qml
+ components/Tooltip.qml
controls/ContinueButton.qml
controls/CoreText.qml
controls/ExternalLink.qml
@@ -41,6 +42,7 @@
controls/TextButton.qml
controls/Theme.qml
controls/ToggleButton.qml
+ controls/utils.js
controls/ValueInput.qml
pages/initerrormessage.qml
pages/main.qml
@@ -88,6 +90,8 @@
res/icons/singlesig-wallet.png
res/icons/storage-dark.png
res/icons/storage-light.png
+ res/icons/tooltip-arrow-dark.png
+ res/icons/tooltip-arrow-light.png
res/fonts/Inter-Regular.otf
diff --git a/src/qml/components/BlockClock.qml b/src/qml/components/BlockClock.qml
index 6e1c28f0a7..dcb2d2d7b6 100644
--- a/src/qml/components/BlockClock.qml
+++ b/src/qml/components/BlockClock.qml
@@ -10,6 +10,7 @@ import Qt.labs.settings 1.0
import org.bitcoincore.qt 1.0
import "../controls"
+import "../controls/utils.js" as Utils
Item {
id: root
@@ -28,7 +29,7 @@ Item {
property bool synced: nodeModel.verificationProgress > 0.999
property string syncProgress: formatProgressPercentage(nodeModel.verificationProgress * 100)
property bool paused: false
- property var syncState: formatRemainingSyncTime(nodeModel.remainingSyncTime)
+ property var syncState: Utils.formatRemainingSyncTime(nodeModel.remainingSyncTime)
property string syncTime: syncState.text
property bool estimating: syncState.estimating
@@ -234,65 +235,4 @@ Item {
return "0%"
}
}
-
- function formatRemainingSyncTime(milliseconds) {
- var minutes = Math.floor(milliseconds / 60000);
- var seconds = Math.floor((milliseconds % 60000) / 1000);
- var weeks = Math.floor(minutes / 10080);
- minutes %= 10080;
- var days = Math.floor(minutes / 1440);
- minutes %= 1440;
- var hours = Math.floor(minutes / 60);
- minutes %= 60;
- var result = "";
- var estimatingStatus = false;
-
- if (weeks > 0) {
- return {
- text: "~" + weeks + (weeks === 1 ? " week" : " weeks") + " left",
- estimating: false
- };
- }
- if (days > 0) {
- return {
- text: "~" + days + (days === 1 ? " day" : " days") + " left",
- estimating: false
- };
- }
- if (hours >= 5) {
- return {
- text: "~" + hours + (hours === 1 ? " hour" : " hours") + " left",
- estimating: false
- };
- }
- if (hours > 0) {
- return {
- text: "~" + hours + "h " + minutes + "m" + " left",
- estimating: false
- };
- }
- if (minutes >= 5) {
- return {
- text: "~" + minutes + (minutes === 1 ? " minute" : " minutes") + " left",
- estimating: false
- };
- }
- if (minutes > 0) {
- return {
- text: "~" + minutes + "m " + seconds + "s" + " left",
- estimating: false
- };
- }
- if (seconds > 0) {
- return {
- text: "~" + seconds + (seconds === 1 ? " second" : " seconds") + " left",
- estimating: false
- };
- } else {
- return {
- text: "Estimating",
- estimating: true
- };
- }
- }
}
diff --git a/src/qml/components/Tooltip.qml b/src/qml/components/Tooltip.qml
new file mode 100644
index 0000000000..4f00f4404d
--- /dev/null
+++ b/src/qml/components/Tooltip.qml
@@ -0,0 +1,44 @@
+// Copyright (c) 2024 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+import QtQuick 2.15
+import QtQuick.Controls 2.15
+
+import "../controls"
+
+Item {
+ id: root
+
+ property alias text: tooltipText.text
+
+ Rectangle {
+ id: tooltipBg
+ color: Theme.color.neutral0
+ border.color: Theme.color.neutral4
+ radius: 5
+ border.width: 1
+ width: tooltipText.width + 30
+ height: tooltipText.height + 20
+ anchors.top: arrow.bottom
+ anchors.right: arrow.right
+ anchors.rightMargin: -10
+ anchors.topMargin: -1
+ }
+
+ Image {
+ id: arrow
+ source: Theme.image.tooltipArrow
+ width: 22
+ height: 10
+ anchors.horizontalCenter: root.horizontalCenter
+ anchors.top: root.top
+ }
+
+ CoreText {
+ id: tooltipText
+ text: ""
+ wrapMode: Text.NoWrap
+ anchors.centerIn: tooltipBg
+ }
+}
diff --git a/src/qml/controls/CoreText.qml b/src/qml/controls/CoreText.qml
index 050f03b256..3857912614 100644
--- a/src/qml/controls/CoreText.qml
+++ b/src/qml/controls/CoreText.qml
@@ -8,7 +8,7 @@ import QtQuick.Controls 2.15
Text {
property bool bold: false
property bool wrap: true
- color: Theme.color.white
+ color: Theme.color.neutral9
font.family: "Inter"
font.styleName: bold ? "Semi Bold" : "Regular"
font.pixelSize: 13
diff --git a/src/qml/controls/Theme.qml b/src/qml/controls/Theme.qml
index df62e0a4ad..f57e152cbd 100644
--- a/src/qml/controls/Theme.qml
+++ b/src/qml/controls/Theme.qml
@@ -44,6 +44,7 @@ Control {
required property url blocktime
required property url network
required property url storage
+ required property url tooltipArrow
}
ColorSet {
@@ -115,6 +116,7 @@ Control {
blocktime: "image://images/blocktime-dark"
network: "image://images/network-dark"
storage: "image://images/storage-dark"
+ tooltipArrow: "qrc:/icons/tooltip-arrow-dark"
}
ImageSet {
@@ -122,6 +124,7 @@ Control {
blocktime: "image://images/blocktime-light"
network: "image://images/network-light"
storage: "image://images/storage-light"
+ tooltipArrow: "qrc:/icons/tooltip-arrow-light"
}
function toggleDark() {
diff --git a/src/qml/controls/utils.js b/src/qml/controls/utils.js
new file mode 100644
index 0000000000..bcbe9af35c
--- /dev/null
+++ b/src/qml/controls/utils.js
@@ -0,0 +1,66 @@
+// Copyright (c) 2024 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+// utils.js
+
+function formatRemainingSyncTime(milliseconds) {
+ var minutes = Math.floor(milliseconds / 60000);
+ var seconds = Math.floor((milliseconds % 60000) / 1000);
+ var weeks = Math.floor(minutes / 10080);
+ minutes %= 10080;
+ var days = Math.floor(minutes / 1440);
+ minutes %= 1440;
+ var hours = Math.floor(minutes / 60);
+ minutes %= 60;
+ var result = "";
+ var estimatingStatus = false;
+
+ if (weeks > 0) {
+ return {
+ text: "~" + weeks + (weeks === 1 ? " week" : " weeks") + " left",
+ estimating: false
+ };
+ }
+ if (days > 0) {
+ return {
+ text: "~" + days + (days === 1 ? " day" : " days") + " left",
+ estimating: false
+ };
+ }
+ if (hours >= 5) {
+ return {
+ text: "~" + hours + (hours === 1 ? " hour" : " hours") + " left",
+ estimating: false
+ };
+ }
+ if (hours > 0) {
+ return {
+ text: "~" + hours + "h " + minutes + "m" + " left",
+ estimating: false
+ };
+ }
+ if (minutes >= 5) {
+ return {
+ text: "~" + minutes + (minutes === 1 ? " minute" : " minutes") + " left",
+ estimating: false
+ };
+ }
+ if (minutes > 0) {
+ return {
+ text: "~" + minutes + "m " + seconds + "s" + " left",
+ estimating: false
+ };
+ }
+ if (seconds > 0) {
+ return {
+ text: "~" + seconds + (seconds === 1 ? " second" : " seconds") + " left",
+ estimating: false
+ };
+ } else {
+ return {
+ text: "Estimating",
+ estimating: true
+ };
+ }
+}
diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp
index 6a739a5aee..b3b041c9eb 100644
--- a/src/qml/imageprovider.cpp
+++ b/src/qml/imageprovider.cpp
@@ -132,5 +132,15 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
return QIcon(":/icons/storage-light").pixmap(requested_size);
}
+ if (id == "tooltip-arrow-dark") {
+ *size = requested_size;
+ return QIcon(":/icons/tooltip-arrow-dark").pixmap(requested_size);
+ }
+
+ if (id == "tooltip-arrow-light") {
+ *size = requested_size;
+ return QIcon(":/icons/tooltip-arrow-light").pixmap(requested_size);
+ }
+
return {};
}
diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml
index 3c5c20fc10..87b90e0166 100644
--- a/src/qml/pages/wallet/DesktopWallets.qml
+++ b/src/qml/pages/wallet/DesktopWallets.qml
@@ -5,8 +5,11 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
+
import org.bitcoincore.qt 1.0
+
import "../../controls"
+import "../../controls/utils.js" as Utils
import "../../components"
import "../node"
@@ -67,10 +70,36 @@ Page {
shorten: true
}
NavigationTab {
+ id: blockClockTabButton
Layout.preferredWidth: 30
Layout.rightMargin: 10
property int index: 3
ButtonGroup.group: navigationTabs
+
+ Tooltip {
+ id: blockClockTooltip
+ property var syncState: Utils.formatRemainingSyncTime(nodeModel.remainingSyncTime)
+ property bool synced: nodeModel.verificationProgress > 0.9999
+ property bool paused: nodeModel.pause
+ property bool connected: nodeModel.numOutboundPeers > 0
+
+ anchors.top: blockClockTabButton.bottom
+ anchors.topMargin: -5
+ anchors.horizontalCenter: blockClockTabButton.horizontalCenter
+
+ visible: blockClockTabButton.hovered
+ text: {
+ if (paused) {
+ qsTr("Paused")
+ } else if (connected && synced) {
+ qsTr("Blocktime\n" + Number(nodeModel.blockTipHeight).toLocaleString(Qt.locale(), 'f', 0))
+ } else if (connected){
+ qsTr("Downloading blocks\n" + syncState.text)
+ } else {
+ qsTr("Connecting")
+ }
+ }
+ }
}
NavigationTab {
iconSource: "image://images/gear-outline"
diff --git a/src/qml/res/icons/tooltip-arrow-dark.png b/src/qml/res/icons/tooltip-arrow-dark.png
new file mode 100644
index 0000000000..a9f17332b0
Binary files /dev/null and b/src/qml/res/icons/tooltip-arrow-dark.png differ
diff --git a/src/qml/res/icons/tooltip-arrow-light.png b/src/qml/res/icons/tooltip-arrow-light.png
new file mode 100644
index 0000000000..20f9a6d310
Binary files /dev/null and b/src/qml/res/icons/tooltip-arrow-light.png differ
diff --git a/src/qml/res/src/tooltip-arrow-dark.svg b/src/qml/res/src/tooltip-arrow-dark.svg
new file mode 100644
index 0000000000..91cd0c81be
--- /dev/null
+++ b/src/qml/res/src/tooltip-arrow-dark.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/qml/res/src/tooltip-arrow-light.svg b/src/qml/res/src/tooltip-arrow-light.svg
new file mode 100644
index 0000000000..fe61055eff
--- /dev/null
+++ b/src/qml/res/src/tooltip-arrow-light.svg
@@ -0,0 +1,4 @@
+