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
14 changes: 9 additions & 5 deletions qml/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include <qml/models/banlistmodel.h>
#include <qml/models/bitcoinaddress.h>
#include <qml/models/bitcoinurimodel.h>
#include <qml/models/blockclockmodel.h>
#include <qml/models/bumptransactionmodel.h>
#include <qml/models/chainmodel.h>
#include <qml/models/debuglogmodel.h>
Expand Down Expand Up @@ -602,9 +603,11 @@ int QmlGuiMain(int argc, char* argv[])
AndroidNotifier android_notifier{node_model};
#endif

ChainModel chain_model{*chain};
chain_model.setCurrentNetworkName(QString::fromStdString(gArgs.GetChainTypeString()));
setupChainQSettings(&app, chain_model.currentNetworkName());
ChainModel chain_model{QString::fromStdString(gArgs.GetChainTypeString())};
BlockClockModel block_clock_model{[chain = chain.get()](qint64 period_start, qint64 period_end) {
return LoadBlockClockHistory(*chain, period_start, period_end);
}};
setupChainQSettings(&app, chain_model.networkName());
// Settings reset must happen before model instantiation so the models
// read clean defaults from QSettings.
if (gArgs.IsArgSet("-resetguisettings")) {
Expand All @@ -614,8 +617,8 @@ int QmlGuiMain(int argc, char* argv[])
settings.remove(QStringLiteral("fMinimizeOnClose"));
}

QObject::connect(&node_model, &NodeModel::setTimeRatioList, &chain_model, &ChainModel::setTimeRatioList);
QObject::connect(&node_model, &NodeModel::setTimeRatioListInitial, &chain_model, &ChainModel::setTimeRatioListInitial);
QObject::connect(&node_model, &NodeModel::blockTipTimeChanged, &block_clock_model, &BlockClockModel::recordBlockTime);
QObject::connect(&node_model, &NodeModel::chainStateReady, &block_clock_model, &BlockClockModel::initializeHistory);


DesktopWindowBehaviorModel desktop_window_behavior_model;
Expand Down Expand Up @@ -649,6 +652,7 @@ int QmlGuiMain(int argc, char* argv[])
engine->rootContext()->setContextProperty("networkStatusModel", &network_status_model);
engine->rootContext()->setContextProperty("nodeModel", &node_model);
engine->rootContext()->setContextProperty("chainModel", &chain_model);
engine->rootContext()->setContextProperty("blockClockModel", &block_clock_model);
engine->rootContext()->setContextProperty("peerTableModel", &peer_model);
engine->rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
engine->rootContext()->setContextProperty("banListModel", &ban_list_model);
Expand Down
89 changes: 57 additions & 32 deletions qml/components/BlockClock.qml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Window 2.15

import org.bitcoincore.qt 1.0

Expand All @@ -17,20 +18,33 @@ Item {
property real parentWidth: 600
property real parentHeight: 600
property bool showNetworkIndicator: true
// Backing models remain current while false; only presentation work stops.
property bool renderingActive: true
property var nodeModelRef: typeof nodeModel !== "undefined" ? nodeModel : null
property var chainModelRef: typeof chainModel !== "undefined" ? chainModel : null
property var blockClockModelRef: typeof blockClockModel !== "undefined" ? blockClockModel : null
property var networkStatusModelRef: typeof networkStatusModel !== "undefined" ? networkStatusModel : null
readonly property bool windowVisible: root.Window.window !== null &&
root.Window.window.visible &&
root.Window.window.visibility !== Window.Hidden &&
root.Window.window.visibility !== Window.Minimized
readonly property bool presentationActive: root.renderingActive && root.visible && root.windowVisible

width: dial.width
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: root.nodeModelRef !== null && root.nodeModelRef.numPeers > 0
property bool synced: root.nodeModelRef !== null && root.nodeModelRef.verificationProgress > 0.999
property bool headerSyncActive: root.nodeModelRef !== null && root.nodeModelRef.headerSyncActive
property string syncProgress: formatProgressPercentage((root.headerSyncActive ? root.nodeModelRef.headerSyncProgress : (root.nodeModelRef !== null ? root.nodeModelRef.verificationProgress : 0)) * 100)
readonly property bool connected: root.nodeModelRef !== null && root.nodeModelRef.numPeers > 0
// Completion is latched after Core leaves IBD and the active chain catches
// the best known header. verificationProgress only renders ongoing sync.
readonly property bool synced: root.nodeModelRef !== null && root.nodeModelRef.initialSyncComplete
readonly property bool headerSyncActive: root.nodeModelRef !== null && root.nodeModelRef.headerSyncActive
readonly property real syncProgressFraction: root.headerSyncActive
? root.nodeModelRef.headerSyncProgress
: (root.nodeModelRef !== null ? root.nodeModelRef.verificationProgress : 0)
readonly property string syncProgress: formatProgressPercentage(root.syncProgressFraction * 100, root.synced)
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
Expand All @@ -47,20 +61,23 @@ Item {

BlockClockDial {
id: dial
objectName: "blockClockDial"
anchors.horizontalCenter: root.horizontalCenter
scale: Theme.blockclocksize
width: {Math.max(Math.min(200, Math.min(root.parentWidth - 30, root.parentHeight - 30)),
Math.min((root.parentWidth * dial.scale), (root.parentHeight * dial.scale)))}
height: dial.width
penWidth: dial.width / 50
timeRatioList: root.chainModelRef !== null ? root.chainModelRef.timeRatioList : []
verificationProgress: root.nodeModelRef !== null ? root.nodeModelRef.verificationProgress : 0
currentTimeFraction: root.blockClockModelRef !== null ? root.blockClockModelRef.currentTimeFraction : 0
blockTimeFractions: root.blockClockModelRef !== null ? root.blockClockModelRef.blockTimeFractions : []
syncProgress: root.syncProgressFraction
paused: root.paused || root.faulted || root.offline
connected: root.connected && !root.offline
synced: root.synced
backgroundColor: Theme.color.neutral2
timeTickColor: Theme.color.neutral5
confirmationColors: Theme.color.confirmationColors
renderingActive: root.presentationActive

Behavior on backgroundColor {
ColorAnimation { duration: 150 }
Expand Down Expand Up @@ -97,50 +114,50 @@ Item {
}
}

TextMetrics {
id: subTextMetrics
font.family: "BitcoinCoreSans"
font.styleName: "Semi Bold"
font.pixelSize: Math.max(1, Math.round(dial.width * (9/100)))
text: subText.text
}

Label {
id: subText
objectName: "blockClockSubText"
anchors.top: mainText.bottom
property bool estimating: root.estimating
anchors.horizontalCenter: root.horizontalCenter
width: dial.width * (4/5)
horizontalAlignment: Text.AlignHCenter
font.family: "BitcoinCoreSans"
font.styleName: "Semi Bold"
font.pixelSize: dial.width * (9/100)
readonly property int desiredPixelSize: subTextMetrics.font.pixelSize
readonly property real desiredTextWidth: Math.max(subTextMetrics.width, subTextMetrics.advanceWidth)
font.pixelSize: subText.desiredTextWidth > subText.width
? Math.max(1, Math.floor(subText.desiredPixelSize * (subText.width - 2) / subText.desiredTextWidth))
: subText.desiredPixelSize
elide: Text.ElideRight
color: Theme.color.neutral4

Component.onCompleted: {
colorChanged.connect(function() {
if (!subText.estimating) {
themeChange.restart();
}
});

estimatingChanged.connect(function() {
if (subText.estimating) {
estimatingTime.start();
} else {
estimatingTime.stop();
}
});

subText.estimatingChanged();
}

ColorAnimation on color{
id: themeChange
target: subText
duration: 150
Behavior on color {
enabled: !subText.estimating
ColorAnimation { duration: 150 }
}

SequentialAnimation {
id: estimatingTime
objectName: "blockClockEstimatingAnimation"
running: root.presentationActive && subText.estimating
loops: Animation.Infinite
ColorAnimation { target: subText; property: "color"; from: subText.color; to: Theme.color.neutral6; duration: 1000 }
ColorAnimation { target: subText; property: "color"; from: Theme.color.neutral6; to: subText.color; duration: 1000 }
ColorAnimation { target: subText; property: "color"; from: Theme.color.neutral4; to: Theme.color.neutral6; duration: 1000 }
ColorAnimation { target: subText; property: "color"; from: Theme.color.neutral6; to: Theme.color.neutral4; duration: 1000 }
}

}

PeersIndicator {
objectName: "blockClockPeersIndicator"
anchors.top: subText.bottom
anchors.topMargin: dial.width / 10
anchors.horizontalCenter: root.horizontalCenter
Expand All @@ -149,6 +166,7 @@ Item {
indicatorDimensions: dial.width * (3/200)
indicatorSpacing: dial.width / 40
paused: root.paused || root.faulted
active: root.presentationActive
}

NetworkIndicator {
Expand Down Expand Up @@ -271,7 +289,14 @@ Item {
]


function formatProgressPercentage(progress) {
function formatProgressPercentage(progress, complete) {
if (complete === true) {
return "100%"
}
// Never present an estimate as complete while initial sync is pending.
if (progress >= 99.9) {
return "99.9%"
}
if (progress >= 1) {
return Math.round(progress) + "%"
} else if (progress >= 0.1) {
Expand Down
26 changes: 18 additions & 8 deletions qml/components/MiniBlockClock.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

import QtQuick 2.15
import QtQuick.Window 2.15

import org.bitcoincore.qt 1.0

Expand All @@ -14,12 +15,20 @@ Item {

property real iconSize: 18
property bool pageSelected: false
property bool connected: nodeModel.numPeers > 0
property bool synced: nodeModel.verificationProgress > 0.999
property bool paused: nodeModel.pause
property bool faulted: nodeModel.faulted
property bool renderingActive: true
property var nodeModelRef: typeof nodeModel !== "undefined" ? nodeModel : null
readonly property bool connected: root.nodeModelRef !== null && root.nodeModelRef.numPeers > 0
readonly property bool synced: root.nodeModelRef !== null && root.nodeModelRef.initialSyncComplete
property bool paused: root.nodeModelRef !== null && root.nodeModelRef.pause
property bool faulted: root.nodeModelRef !== null && root.nodeModelRef.faulted
property var networkStatusModelRef: typeof networkStatusModel !== "undefined" ? networkStatusModel : null
property var blockClockModelRef: typeof blockClockModel !== "undefined" ? blockClockModel : null
property bool offline: networkStatusModelRef !== null && networkStatusModelRef.networkOffline
readonly property bool windowVisible: root.Window.window !== null &&
root.Window.window.visible &&
root.Window.window.visibility !== Window.Hidden &&
root.Window.window.visibility !== Window.Minimized
readonly property bool presentationActive: root.renderingActive && root.visible && root.windowVisible

readonly property bool showOfflineState: !root.faulted && root.offline
readonly property bool showPausedState: root.paused && !root.faulted && !root.offline
Expand All @@ -40,16 +49,16 @@ Item {
width: implicitWidth
height: implicitHeight

readonly property var fullDialTimeRatioList: [1.0, 0.0]

BlockClockDial {
id: dial
objectName: "miniBlockClockDial"
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
currentTimeFraction: root.pageSelected ? 1.0 : (root.blockClockModelRef !== null ? root.blockClockModelRef.currentTimeFraction : 0)
blockTimeFractions: []
syncProgress: root.pageSelected ? 1.0 : (root.nodeModelRef !== null ? root.nodeModelRef.verificationProgress : 0)
connected: root.pageSelected || root.connected
synced: root.pageSelected || root.synced
paused: false
Expand All @@ -60,6 +69,7 @@ Item {
backgroundColor: Theme.color.neutral3
timeTickColor: "transparent"
confirmationColors: Theme.color.confirmationColors
renderingActive: root.presentationActive
}

Item {
Expand Down
2 changes: 1 addition & 1 deletion qml/components/NetworkIndicator.qml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Button {
bottomPadding: 2
leftPadding: 7
rightPadding: 7
state: show && root.chainModelRef !== null ? root.chainModelRef.currentNetworkName : "MAIN"
state: show && root.chainModelRef !== null ? root.chainModelRef.networkName : "MAIN"
contentItem: CoreText {
text: root.text
font.pixelSize: root.textSize
Expand Down
9 changes: 8 additions & 1 deletion qml/components/PeersIndicator.qml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

import QtQuick 2.15
import QtQuick.Window 2.15
import "../controls"

Row {
id: root
required property int numOutboundPeers
required property int maxNumOutboundPeers
required property bool paused
required property bool active
readonly property bool windowVisible: root.Window.window !== null &&
root.Window.window.visible &&
root.Window.window.visibility !== Window.Hidden &&
root.Window.window.visibility !== Window.Minimized
readonly property bool presentationActive: root.active && root.visible && root.windowVisible
property int size: 5
property real indicatorDimensions: 3
property real indicatorSpacing: 5
Expand All @@ -28,7 +35,7 @@ Row {
Behavior on opacity { OpacityAnimator { duration: 150 } }
SequentialAnimation on opacity {
loops: Animation.Infinite
running: numOutboundPeers === 0 && index === 0 && !root.paused
running: root.presentationActive && numOutboundPeers === 0 && index === 0 && !root.paused
SmoothedAnimation { to: 0; velocity: 2.2 }
SmoothedAnimation { to: 1; velocity: 2.2 }
}
Expand Down
Loading
Loading