Skip to content

Commit 696c347

Browse files
committed
qt: add reusable AddressLabel component
Add an AddressLabel component that formats addresses in four-character groups with alternating colors. Make the address copyable and show inline copied feedback.
1 parent c238cef commit 696c347

2 files changed

Lines changed: 141 additions & 47 deletions

File tree

qml/components/AddressDetailRow.qml

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
import QtQuick 2.15
66
import QtQuick.Layouts 1.15
7-
import org.bitcoincore.qt 1.0
87

98
import "../controls"
109

@@ -19,25 +18,10 @@ Item {
1918
Layout.fillWidth: true
2019
implicitHeight: addressCol.implicitHeight + 20
2120

22-
function formatAddressRichText(addr) {
23-
if (!addr) return ""
24-
var primaryColor = Theme.color.neutral9
25-
var secondaryColor = Theme.color.neutral7
26-
var html = ""
27-
for (var i = 0; i < addr.length; i += 4) {
28-
var chunk = addr.substring(i, Math.min(i + 4, addr.length))
29-
var color = (Math.floor(i / 4) % 2 === 0) ? primaryColor : secondaryColor
30-
if (i > 0) html += " "
31-
html += "<nobr><font color=\"" + color + "\">" + chunk + "</font></nobr>"
32-
}
33-
return html
34-
}
35-
3621
ColumnLayout {
3722
id: addressCol
3823
anchors.left: parent.left
39-
anchors.right: copyIcon.left
40-
anchors.rightMargin: 10
24+
anchors.right: parent.right
4125
anchors.top: parent.top
4226
anchors.topMargin: 10
4327
spacing: 4
@@ -52,37 +36,10 @@ Item {
5236
lineHeightMode: Text.FixedHeight
5337
}
5438

55-
CoreText {
39+
AddressLabel {
5640
Layout.fillWidth: true
57-
horizontalAlignment: Text.AlignLeft
58-
color: Theme.color.neutral9
59-
font: Theme.text.monoBody.font
60-
lineHeight: Theme.text.monoBody.lineHeight
61-
lineHeightMode: Text.FixedHeight
62-
textFormat: Text.RichText
63-
text: root.formatAddressRichText(root.address)
64-
wrapMode: Text.WordWrap
65-
}
66-
}
67-
68-
Icon {
69-
id: copyIcon
70-
anchors.right: parent.right
71-
anchors.verticalCenter: parent.verticalCenter
72-
source: "qrc:/icons/copy"
73-
color: Theme.color.neutral9
74-
size: 24
75-
}
76-
77-
MouseArea {
78-
anchors.right: parent.right
79-
anchors.verticalCenter: parent.verticalCenter
80-
width: 44
81-
height: 44
82-
cursorShape: Qt.PointingHandCursor
83-
onClicked: {
84-
Clipboard.setText(root.address)
85-
root.copied()
41+
address: root.address
42+
onCopied: root.copied()
8643
}
8744
}
8845
}

qml/components/AddressLabel.qml

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) 2026 The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
import QtQuick 2.15
6+
import QtQuick.Controls 2.15
7+
import QtQuick.Layouts 1.15
8+
import org.bitcoincore.qt 1.0
9+
10+
import "../controls"
11+
12+
AbstractButton {
13+
id: root
14+
15+
property string address: ""
16+
property color primaryColor: Theme.color.neutral9
17+
property color secondaryColor: Theme.color.neutral7
18+
property var clipboard: Clipboard
19+
readonly property string formattedText: formatAddressRichText(address)
20+
readonly property bool showCopiedStatus: copiedResetTimer.running
21+
22+
signal copied()
23+
24+
function formatAddressRichText(value) {
25+
if (!value) return ""
26+
27+
var html = ""
28+
for (var i = 0; i < value.length; i += 4) {
29+
var chunk = value.substring(i, Math.min(i + 4, value.length))
30+
var color = (Math.floor(i / 4) % 2 === 0)
31+
? root.primaryColor
32+
: root.secondaryColor
33+
if (i > 0) html += " "
34+
html += "<nobr><font color=\"" + color + "\">" + chunk + "</font></nobr>"
35+
}
36+
return html
37+
}
38+
39+
function copy() {
40+
if (root.address === "" || !root.clipboard) return
41+
root.clipboard.setText(root.address)
42+
copiedResetTimer.restart()
43+
root.copied()
44+
}
45+
46+
hoverEnabled: AppMode.isDesktop
47+
enabled: address !== ""
48+
leftPadding: 8
49+
rightPadding: 8
50+
topPadding: 4
51+
bottomPadding: 4
52+
implicitHeight: content.implicitHeight + topPadding + bottomPadding
53+
Accessible.name: showCopiedStatus ? qsTr("Copied") : qsTr("Copy address")
54+
55+
HoverHandler {
56+
cursorShape: Qt.PointingHandCursor
57+
}
58+
59+
onClicked: copy()
60+
61+
contentItem: Item {
62+
id: content
63+
implicitHeight: Math.max(addressText.paintedHeight, copiedRow.implicitHeight)
64+
65+
CoreText {
66+
id: addressText
67+
objectName: root.objectName + "Value"
68+
anchors.left: parent.left
69+
anchors.right: parent.right
70+
anchors.top: parent.top
71+
height: paintedHeight
72+
horizontalAlignment: Text.AlignLeft
73+
verticalAlignment: Text.AlignTop
74+
font: Theme.text.monoBody.font
75+
lineHeight: Theme.text.monoBody.lineHeight
76+
lineHeightMode: Text.FixedHeight
77+
textFormat: Text.RichText
78+
text: root.formattedText
79+
wrapMode: Text.WordWrap
80+
opacity: root.showCopiedStatus ? 0 : 1
81+
82+
Behavior on opacity {
83+
NumberAnimation { duration: 150 }
84+
}
85+
}
86+
87+
RowLayout {
88+
id: copiedRow
89+
objectName: root.objectName + "CopiedStatus"
90+
anchors.centerIn: parent
91+
spacing: 6
92+
opacity: root.showCopiedStatus ? 1 : 0
93+
scale: root.showCopiedStatus ? 1 : 0.8
94+
95+
Behavior on opacity {
96+
NumberAnimation { duration: 150 }
97+
}
98+
99+
Behavior on scale {
100+
NumberAnimation {
101+
duration: 150
102+
easing.type: Easing.InOutCubic
103+
}
104+
}
105+
106+
Icon {
107+
Layout.alignment: Qt.AlignVCenter
108+
source: "qrc:/icons/copy"
109+
color: Theme.color.neutral9
110+
size: 20
111+
}
112+
113+
CoreText {
114+
Layout.alignment: Qt.AlignVCenter
115+
text: qsTr("Copied")
116+
color: Theme.color.neutral9
117+
font: Theme.text.body.font
118+
lineHeight: Theme.text.body.lineHeight
119+
lineHeightMode: Text.FixedHeight
120+
}
121+
}
122+
}
123+
124+
background: Rectangle {
125+
radius: 5
126+
color: root.hovered || root.down ? Theme.color.neutral2 : "transparent"
127+
128+
Behavior on color {
129+
ColorAnimation { duration: 150 }
130+
}
131+
}
132+
133+
Timer {
134+
id: copiedResetTimer
135+
interval: 1000
136+
}
137+
}

0 commit comments

Comments
 (0)