Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions qml/bitcoin_qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<file>components/ExternalSignerReviewActions.qml</file>
<file>components/ExternalPopup.qml</file>
<file>components/FeeSelection.qml</file>
<file>components/MiniBlockClock.qml</file>
<file>components/MonospaceOutputView.qml</file>
<file>components/InfoBanner.qml</file>
<file>components/NetworkTrafficGraph.qml</file>
Expand Down
52 changes: 31 additions & 21 deletions qml/components/BlockClock.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,27 @@ import "../controls/utils.js" as Utils

Item {
id: root
objectName: "blockClock"
property real parentWidth: 600
property real parentHeight: 600
property bool showNetworkIndicator: true
property var nodeModelRef: typeof nodeModel !== "undefined" ? nodeModel : null
property var chainModelRef: typeof chainModel !== "undefined" ? chainModel : null

width: dial.width
height: dial.height + networkIndicator.height + networkIndicator.anchors.topMargin
height: dial.height + (networkIndicator.visible ? networkIndicator.height + networkIndicator.anchors.topMargin : 0)

property alias header: mainText.text
property alias headerSize: mainText.font.pixelSize
property alias subText: subText.text
property bool connected: nodeModel.numOutboundPeers > 0
property bool synced: nodeModel.verificationProgress > 0.999
property string syncProgress: formatProgressPercentage(nodeModel.verificationProgress * 100)
property bool paused: false
property var syncState: Utils.formatRemainingSyncTime(nodeModel.remainingSyncTime)
property bool connected: root.nodeModelRef !== null && root.nodeModelRef.numOutboundPeers > 0
property bool synced: root.nodeModelRef !== null && root.nodeModelRef.verificationProgress > 0.999
property string syncProgress: formatProgressPercentage((root.nodeModelRef !== null ? root.nodeModelRef.verificationProgress : 0) * 100)
property bool paused: root.nodeModelRef !== null && root.nodeModelRef.pause
property var syncState: Utils.formatRemainingSyncTime(root.nodeModelRef !== null ? root.nodeModelRef.remainingSyncTime : 0)
property string syncTime: syncState.text
property bool estimating: syncState.estimating
property bool faulted: nodeModel.faulted
property bool faulted: root.nodeModelRef !== null && root.nodeModelRef.faulted

activeFocusOnTab: true

Expand All @@ -48,11 +51,11 @@ Item {
Math.min((root.parentWidth * dial.scale), (root.parentHeight * dial.scale)))}
height: dial.width
penWidth: dial.width / 50
timeRatioList: chainModel.timeRatioList
verificationProgress: nodeModel.verificationProgress
timeRatioList: root.chainModelRef !== null ? root.chainModelRef.timeRatioList : []
verificationProgress: root.nodeModelRef !== null ? root.nodeModelRef.verificationProgress : 0
paused: root.paused || root.faulted
connected: root.connected
synced: nodeModel.verificationProgress > 0.999
synced: root.synced
backgroundColor: Theme.color.neutral2
timeTickColor: Theme.color.neutral5
confirmationColors: Theme.color.confirmationColors
Expand Down Expand Up @@ -139,39 +142,40 @@ Item {
anchors.top: subText.bottom
anchors.topMargin: dial.width / 10
anchors.horizontalCenter: root.horizontalCenter
numOutboundPeers: nodeModel.numOutboundPeers
maxNumOutboundPeers: nodeModel.maxNumOutboundPeers
numOutboundPeers: root.nodeModelRef !== null ? root.nodeModelRef.numOutboundPeers : 0
maxNumOutboundPeers: root.nodeModelRef !== null ? Math.max(root.nodeModelRef.maxNumOutboundPeers, 1) : 1
indicatorDimensions: dial.width * (3/200)
indicatorSpacing: dial.width / 40
paused: root.paused || root.faulted
}

NetworkIndicator {
id: networkIndicator
objectName: "blockClockNetworkIndicator"
show: root.showNetworkIndicator
chainModelRef: root.chainModelRef
anchors.top: dial.bottom
anchors.topMargin: networkIndicator.visible ? 30 : 0
anchors.horizontalCenter: root.horizontalCenter
}

MouseArea {
objectName: "blockClockToggleArea"
anchors.fill: dial
cursorShape: root.faulted ? Qt.ArrowCursor : Qt.PointingHandCursor
enabled: !root.faulted
onClicked: {
if (!root.faulted) {
root.paused = !root.paused
nodeModel.pause = root.paused
}
function click() {
root.togglePause()
}
onClicked: click()
FocusBorder {
visible: root.activeFocus
}
}

states: [
State {
name: "IBD"; when: !synced && !paused && connected
name: "IBD"; when: !faulted && !synced && !paused && connected
PropertyChanges {
target: root
header: root.syncProgress
Expand All @@ -180,10 +184,10 @@ Item {
},

State {
name: "BLOCKCLOCK"; when: synced && !paused && connected
name: "BLOCKCLOCK"; when: !faulted && synced && !paused && connected
PropertyChanges {
target: root
header: Number(nodeModel.blockTipHeight).toLocaleString(Qt.locale(), 'f', 0)
header: Number(root.nodeModelRef !== null ? root.nodeModelRef.blockTipHeight : 0).toLocaleString(Qt.locale(), 'f', 0)
subText: "Blocktime"
estimating: false
}
Expand Down Expand Up @@ -223,7 +227,7 @@ Item {
},

State {
name: "CONNECTING"; when: !paused && !connected
name: "CONNECTING"; when: !faulted && !paused && !connected
PropertyChanges {
target: root
header: qsTr("Connecting")
Expand Down Expand Up @@ -254,4 +258,10 @@ Item {
return "0%"
}
}

function togglePause() {
if (!root.faulted && root.nodeModelRef !== null) {
root.nodeModelRef.pause = !root.paused
}
}
}
158 changes: 158 additions & 0 deletions qml/components/MiniBlockClock.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// Copyright (c) 2026 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 org.bitcoincore.qt 1.0

import "../controls"

Item {
id: root
objectName: "miniBlockClock"

property real iconSize: 18
property bool pageSelected: false
property bool connected: nodeModel.numOutboundPeers > 0
property bool synced: nodeModel.verificationProgress > 0.999
property bool paused: nodeModel.pause
property bool faulted: nodeModel.faulted

readonly property bool showConnectingState: !root.paused && !root.faulted && !root.connected
readonly property bool showIbdState: !root.paused && !root.faulted && root.connected && !root.synced
readonly property bool showClockState: !root.paused && !root.faulted && root.connected && root.synced
readonly property bool showDialState: dial.visible
readonly property real strokeWidth: Math.max(1, root.width / 12)
readonly property color dialGlyphColor: root.pageSelected ? Theme.color.confirmationColors[5] : Theme.color.neutral9
readonly property color pausedGlyphColor: root.pageSelected ? Theme.color.confirmationColors[5] : Theme.color.neutral6

implicitWidth: iconSize
implicitHeight: iconSize
width: implicitWidth
height: implicitHeight

readonly property var fullDialTimeRatioList: [1.0, 0.0]

BlockClockDial {
id: dial
visible: root.showConnectingState || root.showIbdState || root.showClockState
anchors.fill: parent
penWidth: root.strokeWidth
connectingAnimationDelayMs: 0
timeRatioList: root.pageSelected ? root.fullDialTimeRatioList : chainModel.timeRatioList
verificationProgress: root.pageSelected ? 1.0 : nodeModel.verificationProgress
connected: root.pageSelected || root.connected
synced: root.pageSelected || root.synced
paused: false
animateDial: root.showConnectingState && !root.pageSelected
showTimeTicks: false
showBlockSegments: false
useGradientArcWhenSynced: !root.pageSelected
backgroundColor: Theme.color.neutral3
timeTickColor: "transparent"
confirmationColors: Theme.color.confirmationColors
}

Item {
visible: root.showDialState
anchors.centerIn: parent
width: parent.width * (8 / 18)
height: width

Rectangle {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
width: Math.max(1, parent.width * 0.25)
height: width
radius: width / 2
color: root.dialGlyphColor
}

Rectangle {
anchors.top: parent.top
anchors.topMargin: parent.height * (3.5 / 8)
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
height: Math.max(1, parent.height * (1.5 / 8))
radius: height / 2
color: root.dialGlyphColor
}

Rectangle {
anchors.top: parent.top
anchors.topMargin: parent.height * (6.5 / 8)
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width * 0.5
height: Math.max(1, parent.height * (1.5 / 8))
radius: height / 2
color: root.dialGlyphColor
}
}

Item {
visible: root.paused && !root.faulted
anchors.fill: parent

Rectangle {
anchors.centerIn: parent
width: parent.width - root.strokeWidth
height: width
radius: width / 2
color: "transparent"
border.width: root.strokeWidth
border.color: root.pausedGlyphColor
}

Rectangle {
anchors.centerIn: parent
anchors.horizontalCenterOffset: -parent.width * 0.12
width: Math.max(root.strokeWidth, parent.width * 0.11)
height: parent.height * 0.42
radius: width / 2
color: root.pausedGlyphColor
}

Rectangle {
anchors.centerIn: parent
anchors.horizontalCenterOffset: parent.width * 0.12
width: Math.max(root.strokeWidth, parent.width * 0.11)
height: parent.height * 0.42
radius: width / 2
color: root.pausedGlyphColor
}
}

Item {
visible: root.faulted
anchors.fill: parent

Rectangle {
anchors.centerIn: parent
width: parent.width - root.strokeWidth
height: width
radius: width / 2
color: "transparent"
border.width: root.strokeWidth
border.color: Theme.color.red
}

Rectangle {
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.height * 0.08
width: Math.max(root.strokeWidth, parent.width * 0.12)
height: parent.height * 0.34
radius: width / 2
color: Theme.color.red
}

Rectangle {
anchors.centerIn: parent
anchors.verticalCenterOffset: parent.height * 0.22
width: Math.max(root.strokeWidth, parent.width * 0.12)
height: width
radius: width / 2
color: Theme.color.red
}
}
}
3 changes: 2 additions & 1 deletion qml/components/NetworkIndicator.qml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ Button {
property color bgColor
property bool shorten: false
property bool show: true
property var chainModelRef: typeof chainModel !== "undefined" ? chainModel : null
property int textSize: 15
topPadding: 2
bottomPadding: 2
leftPadding: 7
rightPadding: 7
state: show ? chainModel.currentNetworkName : "MAIN"
state: show && root.chainModelRef !== null ? root.chainModelRef.currentNetworkName : "MAIN"
contentItem: CoreText {
text: root.text
font.pixelSize: root.textSize
Expand Down
Loading
Loading