Skip to content

Commit a4f4263

Browse files
qml, control: new IPAddressValueInput
Adding a new improved ValueInput control to handle both IP address and port values combined in the same field with the correct visual formatting (255.255.255.255:65535). In this iteration, validation for this control is implemented using both a function within the control itself focused solely on ensuring correct formatting and a simple regex validator for the valid input characters. Future versions will see integration with network model classes, enabling parsing of IP addresses and ports and additional checks, such as those outlined in doc/p2p-bad-ports.md.
1 parent 9db473a commit a4f4263

File tree

3 files changed

+98
-6
lines changed

3 files changed

+98
-6
lines changed

src/qml/bitcoin_qml.qrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<file>controls/Header.qml</file>
2727
<file>controls/Icon.qml</file>
2828
<file>controls/InformationPage.qml</file>
29+
<file>controls/IPAddressValueInput.qml</file>
2930
<file>controls/NavButton.qml</file>
3031
<file>controls/PageIndicator.qml</file>
3132
<file>controls/NavigationBar.qml</file>

src/qml/components/ProxySettings.qml

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,25 @@ ColumnLayout {
4343
Layout.fillWidth: true
4444
header: qsTr("IP and Port")
4545
errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.")
46+
showErrorText: !loadedItem.isValidIPPort(loadedItem.description)
4647
state: !defaultProxyEnable.loadedItem.checked ? "DISABLED" : "ACTIVE"
47-
actionItem: ValueInput {
48+
actionItem: IPAddressValueInput {
4849
parentState: defaultProxy.state
4950
description: "127.0.0.1:9050"
51+
activeFocusOnTab: true
5052
onEditingFinished: {
51-
defaultProxy.forceActiveFocus()
53+
if (isValidIPPort(text)) {
54+
defaultProxy.forceActiveFocus()
55+
defaultProxy.showErrorText = false
56+
} else {
57+
defaultProxy.showErrorText = true
58+
}
5259
}
5360
}
54-
onClicked: loadedItem.forceActiveFocus()
61+
onClicked: {
62+
loadedItem.filled = true
63+
loadedItem.forceActiveFocus()
64+
}
5565
}
5666
Separator { Layout.fillWidth: true }
5767
Header {
@@ -90,15 +100,24 @@ ColumnLayout {
90100
Layout.fillWidth: true
91101
header: qsTr("IP and Port")
92102
errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.")
103+
showErrorText: !loadedItem.isValidIPPort(loadedItem.description)
93104
state: !torProxyEnable.loadedItem.checked ? "DISABLED" : "ACTIVE"
94-
actionItem: ValueInput {
105+
actionItem: IPAddressValueInput {
95106
parentState: torProxy.state
96107
description: "127.0.0.1:9050"
97108
onEditingFinished: {
98-
torProxy.forceActiveFocus()
109+
if (isValidIPPort(text)) {
110+
torProxy.forceActiveFocus()
111+
torProxy.showErrorText = false
112+
} else {
113+
torProxy.showErrorText = true
114+
}
99115
}
100116
}
101-
onClicked: loadedItem.forceActiveFocus()
117+
onClicked: {
118+
loadedItem.filled = true
119+
loadedItem.forceActiveFocus()
120+
}
102121
}
103122
Separator { Layout.fillWidth: true }
104123
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) 2024 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+
8+
TextInput {
9+
id: root
10+
required property string parentState
11+
property string description: ""
12+
property bool filled: false
13+
property int descriptionSize: 18
14+
property color textColor: root.filled ? Theme.color.neutral9 : Theme.color.neutral5
15+
enabled: true
16+
state: root.parentState
17+
validator: RegExpValidator { regExp: /[0-9.:]*/ } // Allow only digits, dots, and colons
18+
selectByMouse: true // Enable text selection with the mouse
19+
20+
maximumLength: 21
21+
22+
states: [
23+
State {
24+
name: "ACTIVE"
25+
PropertyChanges { target: root; textColor: Theme.color.orange }
26+
},
27+
State {
28+
name: "HOVER"
29+
PropertyChanges {
30+
target: root
31+
textColor: root.filled ? Theme.color.orangeLight1 : Theme.color.neutral5
32+
}
33+
},
34+
State {
35+
name: "DISABLED"
36+
PropertyChanges {
37+
target: root
38+
enabled: false
39+
textColor: Theme.color.neutral4
40+
}
41+
}
42+
]
43+
44+
font.family: "Inter"
45+
font.styleName: "Regular"
46+
font.pixelSize: root.descriptionSize
47+
color: root.textColor
48+
text: root.description
49+
horizontalAlignment: Text.AlignRight
50+
wrapMode: Text.WordWrap
51+
52+
Behavior on color {
53+
ColorAnimation { duration: 150 }
54+
}
55+
56+
function isValidIPPort(input)
57+
{
58+
var parts = input.split(":");
59+
if (parts.length !== 2) return false;
60+
if (parts[1].length === 0) return false; // port part is empty
61+
var ipAddress = parts[0];
62+
var ipAddressParts = ipAddress.split(".");
63+
if (ipAddressParts.length !== 4) return false;
64+
for (var i = 0; (i < ipAddressParts.length); i++) {
65+
if (ipAddressParts[i].length === 0) return false; // ip group number part is empty
66+
if (parseInt(ipAddressParts[i]) > 255) return false;
67+
}
68+
var port = parseInt(parts[1]);
69+
if (port < 1 || port > 65535) return false;
70+
return true;
71+
}
72+
}

0 commit comments

Comments
 (0)