Skip to content

Commit

Permalink
qml, control: new IPAddressValueInput
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
pablomartin4btc committed Mar 26, 2024
1 parent a7e1b25 commit 6b2621e
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/qml/bitcoin_qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<file>controls/Header.qml</file>
<file>controls/Icon.qml</file>
<file>controls/InformationPage.qml</file>
<file>controls/IPAddressValueInput.qml</file>
<file>controls/NavButton.qml</file>
<file>controls/PageIndicator.qml</file>
<file>controls/NavigationBar.qml</file>
Expand Down
24 changes: 14 additions & 10 deletions src/qml/components/ProxySettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ ColumnLayout {
header: qsTr("IP and Port")
errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.")
state: !defaultProxyEnable.loadedItem.checked ? "DISABLED" : "FILLED"
actionItem: ValueInput {
showErrorText: !defaultProxy.loadedItem.validInput && state==="FILLED"
actionItem: IPAddressValueInput {
parentState: defaultProxy.state
description: "127.0.0.1:9050"
onEditingFinished: {
defaultProxy.forceActiveFocus()
}
activeFocusOnTab: true
}
onClicked: {
loadedItem.filled = true
loadedItem.forceActiveFocus()
}
onClicked: loadedItem.forceActiveFocus()
}
Separator { Layout.fillWidth: true }
Header {
Expand Down Expand Up @@ -90,14 +92,16 @@ ColumnLayout {
header: qsTr("IP and Port")
errorText: qsTr("Invalid IP address or port format. Please use the format '255.255.255.255:65535'.")
state: !torProxyEnable.loadedItem.checked ? "DISABLED" : "FILLED"
actionItem: ValueInput {
showErrorText: !torProxy.loadedItem.validInput && state==="FILLED"
actionItem: IPAddressValueInput {
parentState: torProxy.state
description: "127.0.0.1:9050"
onEditingFinished: {
torProxy.forceActiveFocus()
}
activeFocusOnTab: true
}
onClicked: {
loadedItem.filled = true
loadedItem.forceActiveFocus()
}
onClicked: loadedItem.forceActiveFocus()
}
Separator { Layout.fillWidth: true }
}
82 changes: 82 additions & 0 deletions src/qml/controls/IPAddressValueInput.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

import QtQuick 2.15
import QtQuick.Controls 2.15

TextInput {
id: root
required property string parentState
property string description: ""
property bool filled: false
property int descriptionSize: 18
property color textColor: root.filled ? Theme.color.neutral9 : Theme.color.neutral5
// Expose a property to indicate validity, initial value will be true (no error message displayed)
property bool validInput: true
enabled: true
state: root.parentState
validator: RegExpValidator { regExp: /[0-9.:]*/ } // Allow only digits, dots, and colons

maximumLength: 21

states: [
State {
name: "ACTIVE"
PropertyChanges { target: root; textColor: Theme.color.orange }
},
State {
name: "HOVER"
PropertyChanges {
target: root
textColor: root.filled ? Theme.color.orangeLight1 : Theme.color.neutral5
}
},
State {
name: "DISABLED"
PropertyChanges {
target: root
enabled: false
textColor: Theme.color.neutral4
}
}
]

font.family: "Inter"
font.styleName: "Regular"
font.pixelSize: root.descriptionSize
color: root.textColor
text: root.description
horizontalAlignment: Text.AlignRight
wrapMode: Text.WordWrap

Behavior on color {
ColorAnimation { duration: 150 }
}

function isValidIPPort(input)
{
var parts = input.split(":");
if (parts.length !== 2) return false;
if (parts[1].length === 0) return false; // port part is empty
var ipAddress = parts[0];
var ipAddressParts = ipAddress.split(".");
if (ipAddressParts.length !== 4) return false;
for (var i = 0; (i < ipAddressParts.length); i++) {
if (ipAddressParts[i].length === 0) return false; // ip group number part is empty
if (parseInt(ipAddressParts[i]) > 255) return false;
}
var port = parseInt(parts[1]);
if (port < 1 || port > 65535) return false;
return true;
}

// Connections element to ensure validation on editing finished
Connections {
target: root
function onEditingFinished() {
// Validate the input whenever editing is finished
validInput = isValidIPPort(root.text);
}
}
}

0 comments on commit 6b2621e

Please sign in to comment.