From 8f8341f6598afaa44e0370a67c7379fab78dd34c Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Wed, 1 May 2024 10:52:30 -0400 Subject: [PATCH] qml: Introduce Tooltip for the BlockClock navbar button The Tooltip will appear when hovering over the BlockClock tab button in the desktop navigation bar. It will show the current state of IBD. --- src/Makefile.qt.include | 6 +- src/qml/bitcoin_qml.qrc | 4 ++ src/qml/components/BlockClock.qml | 64 +-------------------- src/qml/components/Tooltip.qml | 44 +++++++++++++++ src/qml/controls/CoreText.qml | 2 +- src/qml/controls/Theme.qml | 3 + src/qml/controls/utils.js | 66 ++++++++++++++++++++++ src/qml/imageprovider.cpp | 10 ++++ src/qml/pages/wallet/DesktopWallets.qml | 29 ++++++++++ src/qml/res/icons/tooltip-arrow-dark.png | Bin 0 -> 2096 bytes src/qml/res/icons/tooltip-arrow-light.png | Bin 0 -> 1264 bytes src/qml/res/src/tooltip-arrow-dark.svg | 4 ++ src/qml/res/src/tooltip-arrow-light.svg | 4 ++ 13 files changed, 172 insertions(+), 64 deletions(-) create mode 100644 src/qml/components/Tooltip.qml create mode 100644 src/qml/controls/utils.js create mode 100644 src/qml/res/icons/tooltip-arrow-dark.png create mode 100644 src/qml/res/icons/tooltip-arrow-light.png create mode 100644 src/qml/res/src/tooltip-arrow-dark.svg create mode 100644 src/qml/res/src/tooltip-arrow-light.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 67cedeeeec..e5ac06c98b 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -336,7 +336,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 @@ -360,6 +362,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 \ @@ -381,6 +384,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 0000000000000000000000000000000000000000..a9f17332b0e7aaf94ec22571a84406bbb2e92705 GIT binary patch literal 2096 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 ah<^Y#Yc2hjRP?_90000 + + + 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 @@ + + + +