Skip to content

Commit aefd53b

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 069db47 commit aefd53b

File tree

3 files changed

+94
-8
lines changed

3 files changed

+94
-8
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: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ ColumnLayout {
4747
actionItem: ValueInput {
4848
parentState: defaultProxy.state
4949
description: "127.0.0.1:9050"
50-
onEditingFinished: {
51-
defaultProxy.forceActiveFocus()
52-
}
50+
activeFocusOnTab: true
51+
}
52+
onClicked: {
53+
loadedItem.filled = true
54+
loadedItem.forceActiveFocus()
5355
}
54-
onClicked: loadedItem.forceActiveFocus()
5556
}
5657
Separator { Layout.fillWidth: true }
5758
Header {
@@ -94,11 +95,12 @@ ColumnLayout {
9495
actionItem: ValueInput {
9596
parentState: torProxy.state
9697
description: "127.0.0.1:9050"
97-
onEditingFinished: {
98-
torProxy.forceActiveFocus()
99-
}
98+
activeFocusOnTab: true
99+
}
100+
onClicked: {
101+
loadedItem.filled = true
102+
loadedItem.forceActiveFocus()
100103
}
101-
onClicked: loadedItem.forceActiveFocus()
102104
}
103105
Separator { Layout.fillWidth: true }
104106
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
// if both properties errortText and description are set
17+
property bool validInput: description!=="" && errorText!==""
18+
enabled: true
19+
state: root.parentState
20+
validator: RegExpValidator { regExp: /[0-9.:]*/ } // Allow only digits, dots, and colons
21+
22+
maximumLength: 21
23+
24+
states: [
25+
State {
26+
name: "ACTIVE"
27+
PropertyChanges { target: root; textColor: Theme.color.orange }
28+
},
29+
State {
30+
name: "HOVER"
31+
PropertyChanges {
32+
target: root
33+
textColor: root.filled ? Theme.color.orangeLight1 : Theme.color.neutral5
34+
}
35+
},
36+
State {
37+
name: "DISABLED"
38+
PropertyChanges {
39+
target: root
40+
enabled: false
41+
textColor: Theme.color.neutral4
42+
}
43+
}
44+
]
45+
46+
font.family: "Inter"
47+
font.styleName: "Regular"
48+
font.pixelSize: root.descriptionSize
49+
color: root.textColor
50+
text: root.description
51+
horizontalAlignment: Text.AlignRight
52+
wrapMode: Text.WordWrap
53+
54+
Behavior on color {
55+
ColorAnimation { duration: 150 }
56+
}
57+
58+
function isValidIPPort(input)
59+
{
60+
var parts = input.split(":");
61+
if (parts.length !== 2) return false;
62+
if (parts[1].length === 0) return false; // port part is empty
63+
var ipAddress = parts[0];
64+
var ipAddressParts = ipAddress.split(".");
65+
if (ipAddressParts.length !== 4) return false;
66+
for (var i = 0; (i < ipAddressParts.length); i++) {
67+
if (ipAddressParts[i].length === 0) return false; // ip group number part is empty
68+
if (parseInt(ipAddressParts[i]) > 255) return false;
69+
}
70+
var port = parseInt(parts[1]);
71+
if (port < 1 || port > 65535) return false;
72+
return true;
73+
}
74+
75+
// Connections element to ensure validation on editing finished
76+
Connections {
77+
target: root
78+
onEditingFinished: {
79+
// Validate the input whenever editing is finished
80+
validInput = isValidIPPort(root.text);
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)