mirror of
https://github.com/amnezia-vpn/amnezia-client.git
synced 2026-05-08 14:33:23 +00:00
add UI PageProtocolXrayConfigsSettings, PageProtocolXrayFlowSettings, PageProtocolXraySecuritySettings
This commit is contained in:
@@ -82,9 +82,12 @@ namespace PageLoader
|
||||
|
||||
PageDevMenu,
|
||||
|
||||
PageProtocolXrayConfigsSettings,
|
||||
PageProtocolXrayTransportSettings,
|
||||
PageProtocolXrayXmuxSettings,
|
||||
PageProtocolXrayXPaddingSettings,
|
||||
PageProtocolXrayFlowSettings,
|
||||
PageProtocolXraySecuritySettings,
|
||||
};
|
||||
Q_ENUM_NS(PageEnum)
|
||||
|
||||
|
||||
217
client/ui/qml/Pages2/PageProtocolXrayConfigsSettings.qml
Normal file
217
client/ui/qml/Pages2/PageProtocolXrayConfigsSettings.qml
Normal file
@@ -0,0 +1,217 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
// Temporary model — will be replaced by real XrayConfigsModel
|
||||
ListModel {
|
||||
id: configsModel
|
||||
ListElement {
|
||||
configName: "XHTTP TLS Reality"; configDate: "24.02.2026 11:12"
|
||||
}
|
||||
ListElement {
|
||||
configName: "RAW (TCP) TLS Reality"; configDate: "24.02.2026 11:14"
|
||||
}
|
||||
ListElement {
|
||||
configName: "RAW (TCP) TLS Reality"; configDate: "24.02.2026 11:14"
|
||||
}
|
||||
ListElement {
|
||||
configName: "RAW (TCP) TLS Reality"; configDate: "24.02.2026 11:15"
|
||||
}
|
||||
}
|
||||
|
||||
// Currently selected config for the drawer
|
||||
property string selectedConfigName: ""
|
||||
property int selectedConfigIndex: -1
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + PageController.safeAreaTopMargin
|
||||
}
|
||||
|
||||
ListViewType {
|
||||
id: listView
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
header: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 0
|
||||
|
||||
// ── Header ────────────────────────────────────────────────
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 0
|
||||
Layout.bottomMargin: 24
|
||||
text: qsTr("XRay Configurations")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// ── Create config from current settings ───────────────────
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Create configuration based on current settings")
|
||||
textMaximumLineCount: 2
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () {
|
||||
// XrayConfigModel.createConfigFromCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ── Export ────────────────────────────────────────────────
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Export settings")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () {
|
||||
// XrayConfigModel.exportSettings()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ── Import ────────────────────────────────────────────────
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Import settings")
|
||||
descriptionText: qsTr("In JSON format")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () {
|
||||
// XrayConfigModel.importSettings()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ── Configurations section label ──────────────────────────
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 24
|
||||
Layout.bottomMargin: 8
|
||||
text: qsTr("Configurations")
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
}
|
||||
|
||||
model: configsModel
|
||||
|
||||
delegate: ColumnLayout {
|
||||
width: listView.width
|
||||
spacing: 0
|
||||
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
|
||||
text: configName
|
||||
descriptionText: configDate
|
||||
|
||||
rightImageSource: "qrc:/images/controls/more-vertical.svg"
|
||||
|
||||
clickedFunction: function () {
|
||||
root.selectedConfigName = configName
|
||||
root.selectedConfigIndex = index
|
||||
configActionsDrawer.openTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Per-config actions drawer ─────────────────────────────────────
|
||||
DrawerType2 {
|
||||
id: configActionsDrawer
|
||||
|
||||
parent: root
|
||||
anchors.fill: parent
|
||||
expandedHeight: root.height * 0.4
|
||||
|
||||
expandedStateContent: ColumnLayout {
|
||||
id: drawerContent
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
spacing: 0
|
||||
|
||||
onImplicitHeightChanged: {
|
||||
configActionsDrawer.expandedHeight = drawerContent.implicitHeight + 32
|
||||
}
|
||||
|
||||
BackButtonType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
backButtonFunction: function () {
|
||||
configActionsDrawer.closeTriggered()
|
||||
}
|
||||
}
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 8
|
||||
Layout.bottomMargin: 16
|
||||
text: root.selectedConfigName
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// ── Apply config ──────────────────────────────────────────
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Apply configuration")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () {
|
||||
configActionsDrawer.closeTriggered()
|
||||
// XrayConfigModel.applyConfig(root.selectedConfigIndex)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ── Delete config ─────────────────────────────────────────
|
||||
LabelWithButtonType {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Delete configuration")
|
||||
textColor: AmneziaStyle.color.vibrantRed
|
||||
clickedFunction: function () {
|
||||
configActionsDrawer.closeTriggered()
|
||||
// XrayConfigModel.deleteConfig(root.selectedConfigIndex)
|
||||
}
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
client/ui/qml/Pages2/PageProtocolXrayFlowSettings.qml
Normal file
109
client/ui/qml/Pages2/PageProtocolXrayFlowSettings.qml
Normal file
@@ -0,0 +1,109 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
// Temporary local state — will be replaced by model role
|
||||
property int selectedFlow: 1 // 0=Empty, 1=xtls-rprx-vision, 2=xtls-rprx-vision-udp443
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + PageController.safeAreaTopMargin
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: flickable
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: saveButton.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
contentHeight: mainColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: mainColumn
|
||||
width: flickable.width
|
||||
spacing: 0
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 0
|
||||
Layout.bottomMargin: 24
|
||||
text: qsTr("Flow")
|
||||
}
|
||||
|
||||
VerticalRadioButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("Empty")
|
||||
checked: root.selectedFlow === 0
|
||||
onClicked: root.selectedFlow = 0
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
VerticalRadioButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("xtls-rprx-vision")
|
||||
checked: root.selectedFlow === 1
|
||||
onClicked: root.selectedFlow = 1
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
VerticalRadioButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("xtls-rprx-vision-udp443")
|
||||
checked: root.selectedFlow === 2
|
||||
onClicked: root.selectedFlow = 2
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 16 + PageController.safeAreaBottomMargin
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
text: qsTr("Save")
|
||||
onClicked: {
|
||||
forceActiveFocus()
|
||||
// XrayConfigModel.setFlow(...)
|
||||
}
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
}
|
||||
316
client/ui/qml/Pages2/PageProtocolXraySecuritySettings.qml
Normal file
316
client/ui/qml/Pages2/PageProtocolXraySecuritySettings.qml
Normal file
@@ -0,0 +1,316 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import PageEnum 1.0
|
||||
import Style 1.0
|
||||
|
||||
import "./"
|
||||
import "../Controls2"
|
||||
import "../Controls2/TextTypes"
|
||||
import "../Config"
|
||||
import "../Components"
|
||||
|
||||
PageType {
|
||||
id: root
|
||||
|
||||
// Temporary local state — will be replaced by model roles
|
||||
property int selectedSecurity: 2 // 0=None, 1=TLS, 2=Reality
|
||||
|
||||
// Shared TLS + Reality fields
|
||||
property string fingerprint: "Mozilla/5.0"
|
||||
property string serverName: "cdn.example.com"
|
||||
|
||||
// TLS-only fields
|
||||
property string alpn: "HTTP/2"
|
||||
|
||||
BackButtonType {
|
||||
id: backButton
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.topMargin: 20 + PageController.safeAreaTopMargin
|
||||
}
|
||||
|
||||
FlickableType {
|
||||
id: flickable
|
||||
anchors.top: backButton.bottom
|
||||
anchors.bottom: saveButton.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
contentHeight: mainColumn.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: mainColumn
|
||||
width: flickable.width
|
||||
spacing: 0
|
||||
|
||||
Header2TextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 0
|
||||
Layout.bottomMargin: 24
|
||||
text: qsTr("Security")
|
||||
}
|
||||
|
||||
// ── Radio: None ───────────────────────────────────────────
|
||||
VerticalRadioButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("None")
|
||||
checked: root.selectedSecurity === 0
|
||||
onClicked: root.selectedSecurity = 0
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ── Radio: TLS ────────────────────────────────────────────
|
||||
VerticalRadioButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("TLS")
|
||||
checked: root.selectedSecurity === 1
|
||||
onClicked: root.selectedSecurity = 1
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ── Radio: Reality ────────────────────────────────────────
|
||||
VerticalRadioButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: qsTr("Reality")
|
||||
checked: root.selectedSecurity === 2
|
||||
onClicked: root.selectedSecurity = 2
|
||||
}
|
||||
|
||||
DividerType {
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// TLS fields (ALPN + Fingerprint + SNI)
|
||||
// ══════════════════════════════════════════════════════════
|
||||
ColumnLayout {
|
||||
visible: root.selectedSecurity === 1
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
DropDownType {
|
||||
id: tlsAlpnDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: root.alpn
|
||||
descriptionText: qsTr("ALPN")
|
||||
headerText: qsTr("ALPN")
|
||||
drawerParent: root
|
||||
listView: ListViewWithRadioButtonType {
|
||||
rootWidth: root.width
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
name: "HTTP/2"
|
||||
}
|
||||
ListElement {
|
||||
name: "HTTP/1.1"
|
||||
}
|
||||
ListElement {
|
||||
name: "HTTP/2,HTTP/1.1"
|
||||
}
|
||||
}
|
||||
clickedFunction: function () {
|
||||
root.alpn = selectedText
|
||||
tlsAlpnDropDown.text = selectedText
|
||||
tlsAlpnDropDown.closeTriggered()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < model.count; i++) {
|
||||
if (model.get(i).name === root.alpn) {
|
||||
selectedIndex = i;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropDownType {
|
||||
id: tlsFingerprintDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: root.fingerprint
|
||||
descriptionText: qsTr("Fingerprint")
|
||||
headerText: qsTr("Fingerprint")
|
||||
drawerParent: root
|
||||
listView: ListViewWithRadioButtonType {
|
||||
rootWidth: root.width
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
name: "Mozilla/5.0"
|
||||
}
|
||||
ListElement {
|
||||
name: "Chrome"
|
||||
}
|
||||
ListElement {
|
||||
name: "Firefox"
|
||||
}
|
||||
ListElement {
|
||||
name: "Safari"
|
||||
}
|
||||
ListElement {
|
||||
name: "iOS"
|
||||
}
|
||||
ListElement {
|
||||
name: "Android"
|
||||
}
|
||||
ListElement {
|
||||
name: "Edge"
|
||||
}
|
||||
ListElement {
|
||||
name: "360"
|
||||
}
|
||||
ListElement {
|
||||
name: "QQ"
|
||||
}
|
||||
ListElement {
|
||||
name: "Random"
|
||||
}
|
||||
}
|
||||
clickedFunction: function () {
|
||||
root.fingerprint = selectedText
|
||||
tlsFingerprintDropDown.text = selectedText
|
||||
tlsFingerprintDropDown.closeTriggered()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < model.count; i++) {
|
||||
if (model.get(i).name === root.fingerprint) {
|
||||
selectedIndex = i;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 8
|
||||
headerText: qsTr("Server Name (SNI)")
|
||||
textField.text: root.serverName
|
||||
textField.onEditingFinished: root.serverName = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
// ══════════════════════════════════════════════════════════
|
||||
// Reality fields (Fingerprint + SNI)
|
||||
// ══════════════════════════════════════════════════════════
|
||||
ColumnLayout {
|
||||
visible: root.selectedSecurity === 2
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
DropDownType {
|
||||
id: realityFingerprintDropDown
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
text: root.fingerprint
|
||||
descriptionText: qsTr("Fingerprint")
|
||||
headerText: qsTr("Fingerprint")
|
||||
drawerParent: root
|
||||
listView: ListViewWithRadioButtonType {
|
||||
rootWidth: root.width
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
name: "Mozilla/5.0"
|
||||
}
|
||||
ListElement {
|
||||
name: "Chrome"
|
||||
}
|
||||
ListElement {
|
||||
name: "Firefox"
|
||||
}
|
||||
ListElement {
|
||||
name: "Safari"
|
||||
}
|
||||
ListElement {
|
||||
name: "iOS"
|
||||
}
|
||||
ListElement {
|
||||
name: "Android"
|
||||
}
|
||||
ListElement {
|
||||
name: "Edge"
|
||||
}
|
||||
ListElement {
|
||||
name: "360"
|
||||
}
|
||||
ListElement {
|
||||
name: "QQ"
|
||||
}
|
||||
ListElement {
|
||||
name: "Random"
|
||||
}
|
||||
}
|
||||
clickedFunction: function () {
|
||||
root.fingerprint = selectedText
|
||||
realityFingerprintDropDown.text = selectedText
|
||||
realityFingerprintDropDown.closeTriggered()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
for (var i = 0; i < model.count; i++) {
|
||||
if (model.get(i).name === root.fingerprint) {
|
||||
selectedIndex = i;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TextFieldWithHeaderType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 8
|
||||
headerText: qsTr("Server Name (SNI)")
|
||||
textField.text: root.serverName
|
||||
textField.onEditingFinished: root.serverName = textField.text
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BasicButtonType {
|
||||
id: saveButton
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 16 + PageController.safeAreaBottomMargin
|
||||
anchors.leftMargin: 16
|
||||
anchors.rightMargin: 16
|
||||
|
||||
text: qsTr("Save")
|
||||
onClicked: {
|
||||
forceActiveFocus()
|
||||
// XrayConfigModel.setSecurity(...)
|
||||
}
|
||||
Keys.onEnterPressed: clicked()
|
||||
Keys.onReturnPressed: clicked()
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,9 @@ PageType {
|
||||
implicitHeight: 40
|
||||
image: "qrc:/images/controls/more-vertical.svg"
|
||||
imageColor: AmneziaStyle.color.mutedGray
|
||||
onClicked: function () {
|
||||
PageController.goToPage(PageEnum.PageProtocolXrayConfigsSettings)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,12 @@ PageType {
|
||||
// Traffic Shaping
|
||||
property string uplinkChunkSize: "0"
|
||||
property string scMaxBufferedPosts: ""
|
||||
property string scMaxEachPostBytesMin: "1"
|
||||
property string scMaxEachPostBytesMax: "100"
|
||||
property string scMinPostsIntervalMsMin: "100"
|
||||
property string scMinPostsIntervalMsMax: "800"
|
||||
property string scStreamUpServerSecsMin: "1"
|
||||
property string scStreamUpServerSecsMax: "100"
|
||||
// mKCP fields
|
||||
property string mkcpTti: ""
|
||||
property string mkcpUplinkCapacity: ""
|
||||
@@ -603,41 +609,64 @@ PageType {
|
||||
textField.onEditingFinished: root.scMaxBufferedPosts = textField.text
|
||||
}
|
||||
|
||||
// scMaxEachPostBytes → nav row
|
||||
LabelWithButtonType {
|
||||
// scMaxEachPostBytes — min/max range
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 8
|
||||
text: qsTr("scMaxEachPostBytes")
|
||||
descriptionText: qsTr("1—100")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () { /* navigate */
|
||||
}
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
DividerType {
|
||||
MinMaxRowType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
minValue: root.scMaxEachPostBytesMin
|
||||
maxValue: root.scMaxEachPostBytesMax
|
||||
onMinChanged: root.scMaxEachPostBytesMin = val
|
||||
onMaxChanged: root.scMaxEachPostBytesMax = val
|
||||
}
|
||||
|
||||
// scMinPostsIntervalMs → nav row
|
||||
LabelWithButtonType {
|
||||
// scMinPostsIntervalMs — min/max range
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 8
|
||||
text: qsTr("scMinPostsIntervalMs")
|
||||
descriptionText: qsTr("100—600ms")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () { /* navigate */
|
||||
}
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
DividerType {
|
||||
MinMaxRowType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
minValue: root.scMinPostsIntervalMsMin
|
||||
maxValue: root.scMinPostsIntervalMsMax
|
||||
onMinChanged: root.scMinPostsIntervalMsMin = val
|
||||
onMaxChanged: root.scMinPostsIntervalMsMax = val
|
||||
}
|
||||
|
||||
// scStreamUpServerSecs → nav row
|
||||
LabelWithButtonType {
|
||||
// scStreamUpServerSecs — min/max range
|
||||
CaptionTextType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
Layout.topMargin: 16
|
||||
Layout.bottomMargin: 8
|
||||
text: qsTr("scStreamUpServerSecs")
|
||||
descriptionText: qsTr("1—100")
|
||||
rightImageSource: "qrc:/images/controls/chevron-right.svg"
|
||||
clickedFunction: function () { /* navigate */
|
||||
}
|
||||
color: AmneziaStyle.color.mutedGray
|
||||
}
|
||||
DividerType {
|
||||
MinMaxRowType {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 16
|
||||
Layout.rightMargin: 16
|
||||
minValue: root.scStreamUpServerSecsMin
|
||||
maxValue: root.scStreamUpServerSecsMax
|
||||
onMinChanged: root.scStreamUpServerSecsMin = val
|
||||
onMaxChanged: root.scStreamUpServerSecsMax = val
|
||||
}
|
||||
|
||||
// ── Padding and multiplexing ──────────────────────────
|
||||
|
||||
@@ -78,6 +78,9 @@
|
||||
<file>Pages2/PageProtocolWireGuardSettings.qml</file>
|
||||
<file>Pages2/PageProtocolXraySettings.qml</file>
|
||||
|
||||
<file>Pages2/PageProtocolXrayConfigsSettings.qml</file>
|
||||
<file>Pages2/PageProtocolXrayFlowSettings.qml</file>
|
||||
<file>Pages2/PageProtocolXraySecuritySettings.qml</file>
|
||||
<file>Pages2/PageProtocolXrayTransportSettings.qml</file>
|
||||
<file>Pages2/PageProtocolXrayXmuxSettings.qml</file>
|
||||
<file>Pages2/PageProtocolXrayXPaddingSettings.qml</file>
|
||||
|
||||
Reference in New Issue
Block a user