Skip to content

Commit c294e59

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 a7e1b25 commit c294e59

File tree

3 files changed

+97
-10
lines changed

3 files changed

+97
-10
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: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,16 @@ ColumnLayout {
4444
header: qsTr("IP and Port")
4545
errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.")
4646
state: !defaultProxyEnable.loadedItem.checked ? "DISABLED" : "FILLED"
47-
actionItem: ValueInput {
47+
showErrorText: !defaultProxy.loadedItem.validInput
48+
actionItem: IPAddressValueInput {
4849
parentState: defaultProxy.state
4950
description: "127.0.0.1:9050"
50-
onEditingFinished: {
51-
defaultProxy.forceActiveFocus()
52-
}
51+
activeFocusOnTab: true
52+
}
53+
onClicked: {
54+
loadedItem.filled = true
55+
loadedItem.forceActiveFocus()
5356
}
54-
onClicked: loadedItem.forceActiveFocus()
5557
}
5658
Separator { Layout.fillWidth: true }
5759
Header {
@@ -90,14 +92,16 @@ ColumnLayout {
9092
header: qsTr("IP and Port")
9193
errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.")
9294
state: !torProxyEnable.loadedItem.checked ? "DISABLED" : "FILLED"
93-
actionItem: ValueInput {
95+
showErrorText: !defaultProxy.loadedItem.validInput
96+
actionItem: IPAddressValueInput {
9497
parentState: torProxy.state
9598
description: "127.0.0.1:9050"
96-
onEditingFinished: {
97-
torProxy.forceActiveFocus()
98-
}
99+
activeFocusOnTab: true
100+
}
101+
onClicked: {
102+
loadedItem.filled = true
103+
loadedItem.forceActiveFocus()
99104
}
100-
onClicked: loadedItem.forceActiveFocus()
101105
}
102106
Separator { Layout.fillWidth: true }
103107
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
// Expose a property to indicate validity, initial value will be true (no error message displayed)
16+
property bool validInput: true
17+
enabled: true
18+
state: root.parentState
19+
validator: RegExpValidator { regExp: /[0-9.:]*/ } // Allow only digits, dots, and colons
20+
21+
maximumLength: 21
22+
23+
states: [
24+
State {
25+
name: "ACTIVE"
26+
PropertyChanges { target: root; textColor: Theme.color.orange }
27+
},
28+
State {
29+
name: "HOVER"
30+
PropertyChanges {
31+
target: root
32+
textColor: root.filled ? Theme.color.orangeLight1 : Theme.color.neutral5
33+
}
34+
},
35+
State {
36+
name: "DISABLED"
37+
PropertyChanges {
38+
target: root
39+
enabled: false
40+
textColor: Theme.color.neutral4
41+
}
42+
}
43+
]
44+
45+
font.family: "Inter"
46+
font.styleName: "Regular"
47+
font.pixelSize: root.descriptionSize
48+
color: root.textColor
49+
text: root.description
50+
horizontalAlignment: Text.AlignRight
51+
wrapMode: Text.WordWrap
52+
53+
Behavior on color {
54+
ColorAnimation { duration: 150 }
55+
}
56+
57+
function isValidIPPort(input)
58+
{
59+
var parts = input.split(":");
60+
if (parts.length !== 2) return false;
61+
if (parts[1].length === 0) return false; // port part is empty
62+
var ipAddress = parts[0];
63+
var ipAddressParts = ipAddress.split(".");
64+
if (ipAddressParts.length !== 4) return false;
65+
for (var i = 0; (i < ipAddressParts.length); i++) {
66+
if (ipAddressParts[i].length === 0) return false; // ip group number part is empty
67+
if (parseInt(ipAddressParts[i]) > 255) return false;
68+
}
69+
var port = parseInt(parts[1]);
70+
if (port < 1 || port > 65535) return false;
71+
return true;
72+
}
73+
74+
// Connections element to ensure validation on editing finished
75+
Connections {
76+
target: root
77+
onEditingFinished: {
78+
// Validate the input whenever editing is finished
79+
validInput = isValidIPPort(root.text);
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)