mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-10-09 07:09:51 +03:00
Polish sing-tun settings presentation
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Vendored
+2
@@ -27,6 +27,8 @@ structure and human-reviewed translations.
|
||||
retranslation under explicit locales rather than treating collision diagnostics as cosmetic. A collision-free
|
||||
extraction report does not prove reverse lookup is unambiguous across every language; inspect the runtime index
|
||||
and language-switch behavior when two source keys share translated text.
|
||||
In Chinese UI text, do not insert spaces between Chinese characters and Latin abbreviations or numerals.
|
||||
Preserve spaces within English names or phrases.
|
||||
|
||||
## Extractable source text
|
||||
|
||||
|
||||
Vendored
+130
-82
@@ -3368,8 +3368,8 @@ TRANSLATION = {
|
||||
"source": [
|
||||
"Furious.Window.SettingsPage"
|
||||
],
|
||||
"RU": "Настроить sing-tun...",
|
||||
"ZH": "自定义 sing-tun 设置...",
|
||||
"RU": "Настроить параметры sing-tun...",
|
||||
"ZH": "自定义sing-tun设置...",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Customize sing-tun Settings": {
|
||||
@@ -3377,7 +3377,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Настройка sing-tun",
|
||||
"ZH": "自定义 sing-tun 设置",
|
||||
"ZH": "自定义sing-tun设置",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"SOCKS Transit": {
|
||||
@@ -3385,7 +3385,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Передача через SOCKS",
|
||||
"ZH": "SOCKS 转发",
|
||||
"ZH": "SOCKS转发",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Stack": {
|
||||
@@ -3404,14 +3404,6 @@ TRANSLATION = {
|
||||
"ZH": "日志级别",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"SOCKS Interface Binding (empty for automatic)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Интерфейс SOCKS (пусто — автоматически)",
|
||||
"ZH": "SOCKS 绑定接口(留空自动选择)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Maximum Sessions": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
@@ -3428,62 +3420,6 @@ TRANSLATION = {
|
||||
"ZH": "接口与协议栈",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Device Name (empty for automatic)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Имя устройства (пусто — автоматически)",
|
||||
"ZH": "设备名称(留空自动选择)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"MTU (bytes)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "MTU (байты)",
|
||||
"ZH": "MTU(字节)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"IPv4 Address Prefixes (comma separated)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Префиксы IPv4 (через запятую)",
|
||||
"ZH": "IPv4 地址前缀(以逗号分隔)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"IPv6 Address Prefixes (comma separated)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Префиксы IPv6 (через запятую)",
|
||||
"ZH": "IPv6 地址前缀(以逗号分隔)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Native UDP Timeout (Go duration, e.g. 1m)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Тайм-аут UDP стека (длительность Go, например 1m)",
|
||||
"ZH": "原生 UDP 超时(Go 时长,例如 1m)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Native ICMP Timeout (Go duration, 0 for default)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Тайм-аут ICMP стека (длительность Go, 0 — по умолчанию)",
|
||||
"ZH": "原生 ICMP 超时(Go 时长,0 使用默认值)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Native UDP NAT Limit (0 for default)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Лимит UDP NAT стека (0 — по умолчанию)",
|
||||
"ZH": "原生 UDP NAT 上限(0 使用默认值)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Advanced Options": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
@@ -3497,7 +3433,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Используйте точные имена полей JSON. Поддерживаются списки маршрутов и исключений, StrictRoute, InterfaceScope, ForwarderBindInterface и IncludeAllNetworks. AutoRoute, режим внешней конфигурации и настройки DNS управляются приложением. Неподдерживаемые поля отклоняются.",
|
||||
"ZH": "请使用准确的原生 JSON 字段名。支持路由地址及排除列表、StrictRoute、InterfaceScope、ForwarderBindInterface 和 IncludeAllNetworks。AutoRoute、外部配置模式和 DNS 配置均由应用统一管理。不支持的字段会被拒绝。",
|
||||
"ZH": "请使用准确的原生JSON字段名。支持路由地址及排除列表、StrictRoute、InterfaceScope、ForwarderBindInterface和IncludeAllNetworks。AutoRoute、外部配置模式和DNS配置均由应用统一管理。不支持的字段会被拒绝。",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Connect Timeout (seconds)": {
|
||||
@@ -3513,7 +3449,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Тайм-аут UDP SOCKS (секунды)",
|
||||
"ZH": "SOCKS UDP 超时(秒)",
|
||||
"ZH": "SOCKS UDP超时(秒)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"TCP Idle Timeout (seconds)": {
|
||||
@@ -3521,7 +3457,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Тайм-аут простоя TCP (секунды)",
|
||||
"ZH": "TCP 空闲超时(秒)",
|
||||
"ZH": "TCP空闲超时(秒)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"UDP Mapping": {
|
||||
@@ -3529,7 +3465,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Сопоставление UDP",
|
||||
"ZH": "UDP 映射方式",
|
||||
"ZH": "UDP映射方式",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"UDP Filtering": {
|
||||
@@ -3537,7 +3473,7 @@ TRANSLATION = {
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Фильтрация UDP",
|
||||
"ZH": "UDP 过滤方式",
|
||||
"ZH": "UDP过滤方式",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Automatic": {
|
||||
@@ -3592,7 +3528,7 @@ TRANSLATION = {
|
||||
"source": [
|
||||
"Furious.Window.SettingsPage"
|
||||
],
|
||||
"RU": "Настройте интерфейс, стек, SOCKS-передачу и параметры хоста sing-tun.",
|
||||
"RU": "Настройте интерфейс, стек, передачу через SOCKS и параметры хоста sing-tun.",
|
||||
"ZH": "配置 sing-tun 的接口、协议栈、SOCKS 转发和主机设置。",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
@@ -3604,14 +3540,6 @@ TRANSLATION = {
|
||||
"ZH": "主机设置",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"TCP Congestion Control (Go stack)": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Управление перегрузкой TCP (стек Go)",
|
||||
"ZH": "TCP 拥塞控制(Go 协议栈)",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"TUN Adapter Interface DNS": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog",
|
||||
@@ -3709,5 +3637,125 @@ TRANSLATION = {
|
||||
"RU": "Показывать только избранные профили",
|
||||
"ZH": "仅显示已收藏的配置",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"SOCKS Interface Binding": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Интерфейс SOCKS",
|
||||
"ZH": "SOCKS绑定接口",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Device Name": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Имя устройства",
|
||||
"ZH": "设备名称",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"IPv4 Address Prefixes": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Префиксы IPv4",
|
||||
"ZH": "IPv4地址前缀",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"IPv6 Address Prefixes": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Префиксы IPv6",
|
||||
"ZH": "IPv6地址前缀",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Native UDP Timeout": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Тайм-аут UDP стека",
|
||||
"ZH": "原生UDP超时",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Native ICMP Timeout": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Тайм-аут ICMP стека",
|
||||
"ZH": "原生ICMP超时",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Native UDP NAT Limit": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Лимит UDP NAT стека",
|
||||
"ZH": "原生UDP NAT上限",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"empty for automatic": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "пусто — автоматически",
|
||||
"ZH": "留空自动配置",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"separated by commas": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "через запятую",
|
||||
"ZH": "以逗号分隔",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"duration with units, e.g. 1m": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "длительность с единицей измерения, например 1m",
|
||||
"ZH": "带单位的时长,例如1m",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"duration with units, or 0 for default": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "длительность с единицей измерения; 0 — по умолчанию",
|
||||
"ZH": "带单位的时长,或填0使用默认值",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"0 for default": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "0 — по умолчанию",
|
||||
"ZH": "0使用默认值",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"MTU": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "MTU",
|
||||
"ZH": "MTU",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"TCP Congestion Control": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Управление перегрузкой TCP",
|
||||
"ZH": "TCP拥塞控制",
|
||||
"isReviewed": "True"
|
||||
},
|
||||
"Available when Stack is set to go or Automatic.": {
|
||||
"source": [
|
||||
"Furious.Window.SingTUNSettingsDialog"
|
||||
],
|
||||
"RU": "Доступно, если для «Стек» выбрано go или «Автоматически».",
|
||||
"ZH": "协议栈设为go或自动时可用。",
|
||||
"isReviewed": "True"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ from Furious.Qt import (
|
||||
AppQDialogButtonBox,
|
||||
DraculaJSONTextEditor,
|
||||
)
|
||||
from Furious.Qt.Signals import connectWeakly
|
||||
from Furious.Qt.Signals import connectWeakly, singleShotWeakly
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
from PySide6 import QtCore
|
||||
@@ -47,6 +47,8 @@ from PySide6.QtWidgets import (
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
QScrollArea,
|
||||
QStyle,
|
||||
QStyleOptionFrame,
|
||||
)
|
||||
|
||||
import copy
|
||||
@@ -113,8 +115,10 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
self._text(
|
||||
bridge,
|
||||
'network_interface',
|
||||
_('SOCKS Interface Binding (empty for automatic)'),
|
||||
_('SOCKS Interface Binding'),
|
||||
common.get('network_interface'),
|
||||
placeholder=_('empty for automatic'),
|
||||
maximumWidth=360,
|
||||
)
|
||||
self._number(
|
||||
bridge,
|
||||
@@ -125,12 +129,27 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
16384,
|
||||
)
|
||||
|
||||
for key, label in (
|
||||
('connect_timeout', _('Connect Timeout (seconds)')),
|
||||
('udp_timeout', _('SOCKS UDP Timeout (seconds)')),
|
||||
('tcp_idle_timeout', _('TCP Idle Timeout (seconds)')),
|
||||
):
|
||||
self._text(bridge, key, label, common.get(key))
|
||||
self._text(
|
||||
bridge,
|
||||
'connect_timeout',
|
||||
_('Connect Timeout (seconds)'),
|
||||
common.get('connect_timeout'),
|
||||
maximumWidth=160,
|
||||
)
|
||||
self._text(
|
||||
bridge,
|
||||
'udp_timeout',
|
||||
_('SOCKS UDP Timeout (seconds)'),
|
||||
common.get('udp_timeout'),
|
||||
maximumWidth=160,
|
||||
)
|
||||
self._text(
|
||||
bridge,
|
||||
'tcp_idle_timeout',
|
||||
_('TCP Idle Timeout (seconds)'),
|
||||
common.get('tcp_idle_timeout'),
|
||||
maximumWidth=160,
|
||||
)
|
||||
|
||||
native = self._page(_('Interface and Stack'))
|
||||
tun = (
|
||||
@@ -140,28 +159,37 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
)
|
||||
|
||||
self._text(
|
||||
native, 'Name', _('Device Name (empty for automatic)'), tun.get('Name', '')
|
||||
native,
|
||||
'Name',
|
||||
_('Device Name'),
|
||||
tun.get('Name', ''),
|
||||
placeholder=_('empty for automatic'),
|
||||
maximumWidth=360,
|
||||
)
|
||||
self._number(native, 'MTU', _('MTU (bytes)'), tun.get('MTU', 1500), 68, 65535)
|
||||
self._number(native, 'MTU', _('MTU'), tun.get('MTU', 1500), 68, 65535)
|
||||
self._text(
|
||||
native,
|
||||
'Inet4Address',
|
||||
_('IPv4 Address Prefixes (comma separated)'),
|
||||
_('IPv4 Address Prefixes'),
|
||||
(
|
||||
', '.join(str(value) for value in tun.get('Inet4Address', []))
|
||||
if isinstance(tun.get('Inet4Address'), list)
|
||||
else ''
|
||||
),
|
||||
placeholder=_('separated by commas'),
|
||||
maximumWidth=600,
|
||||
)
|
||||
self._text(
|
||||
native,
|
||||
'Inet6Address',
|
||||
_('IPv6 Address Prefixes (comma separated)'),
|
||||
_('IPv6 Address Prefixes'),
|
||||
(
|
||||
', '.join(str(value) for value in tun.get('Inet6Address', []))
|
||||
if isinstance(tun.get('Inet6Address'), list)
|
||||
else ''
|
||||
),
|
||||
placeholder=_('separated by commas'),
|
||||
maximumWidth=600,
|
||||
)
|
||||
|
||||
stack = (
|
||||
@@ -173,7 +201,7 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
self._choice(
|
||||
native,
|
||||
'TCPCongestionControl',
|
||||
_('TCP Congestion Control (Go stack)'),
|
||||
_('TCP Congestion Control'),
|
||||
[
|
||||
(_('Automatic'), ''),
|
||||
('cubic', 'cubic'),
|
||||
@@ -182,23 +210,33 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
],
|
||||
stack.get('TCPCongestionControl', ''),
|
||||
)
|
||||
self.fields['TCPCongestionControl'].setToolTip(
|
||||
_('Available when Stack is set to go or Automatic.')
|
||||
)
|
||||
|
||||
self._text(
|
||||
native,
|
||||
'UDPNATMax',
|
||||
_('Native UDP NAT Limit'),
|
||||
stack.get('UDPNATMax', 0),
|
||||
placeholder=_('0 for default'),
|
||||
maximumWidth=200,
|
||||
)
|
||||
self._text(
|
||||
native,
|
||||
'UDPTimeout',
|
||||
_('Native UDP Timeout (Go duration, e.g. 1m)'),
|
||||
_('Native UDP Timeout'),
|
||||
stack.get('UDPTimeout', '1m'),
|
||||
placeholder=_('duration with units, e.g. 1m'),
|
||||
maximumWidth=180,
|
||||
)
|
||||
self._text(
|
||||
native,
|
||||
'ICMPTimeout',
|
||||
_('Native ICMP Timeout (Go duration, 0 for default)'),
|
||||
_('Native ICMP Timeout'),
|
||||
stack.get('ICMPTimeout', '0'),
|
||||
)
|
||||
self._text(
|
||||
native,
|
||||
'UDPNATMax',
|
||||
_('Native UDP NAT Limit (0 for default)'),
|
||||
stack.get('UDPNATMax', 0),
|
||||
placeholder=_('duration with units, or 0 for default'),
|
||||
maximumWidth=180,
|
||||
)
|
||||
|
||||
for key, label in (
|
||||
@@ -224,16 +262,17 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
)
|
||||
hostForm = self._page(_('Host Settings'))
|
||||
|
||||
for key, label in (
|
||||
('primaryAdapterInterfaceName', _('Primary Adapter Interface Name')),
|
||||
('primaryAdapterInterfaceIP', _('Primary Adapter Interface IP')),
|
||||
('tunAdapterInterfaceDNS', _('TUN Adapter Interface DNS')),
|
||||
for key, label, width in (
|
||||
('primaryAdapterInterfaceName', _('Primary Adapter Interface Name'), 360),
|
||||
('primaryAdapterInterfaceIP', _('Primary Adapter Interface IP'), 360),
|
||||
('tunAdapterInterfaceDNS', _('TUN Adapter Interface DNS'), 360),
|
||||
(
|
||||
'bypassTUNAdapterInterfaceIP',
|
||||
_('Bypass TUN Adapter Interface IP (separated by commas)'),
|
||||
600,
|
||||
),
|
||||
):
|
||||
self._text(hostForm, key, label, host.get(key, ''))
|
||||
self._text(hostForm, key, label, host.get(key, ''), maximumWidth=width)
|
||||
|
||||
self.disableDNS = AppQSwitch()
|
||||
self.disableDNS.setChecked(
|
||||
@@ -313,6 +352,38 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
|
||||
layout.addWidget(buttons)
|
||||
|
||||
def _fitDurationInputs(self):
|
||||
for key in ('UDPTimeout', 'ICMPTimeout'):
|
||||
widget = self.fields[key]
|
||||
widget.ensurePolished()
|
||||
|
||||
option = QStyleOptionFrame()
|
||||
widget.initStyleOption(option)
|
||||
margins = widget.textMargins()
|
||||
textWidth = widget.fontMetrics().horizontalAdvance(widget.placeholderText())
|
||||
size = widget.style().sizeFromContents(
|
||||
QStyle.ContentsType.CT_LineEdit,
|
||||
option,
|
||||
QtCore.QSize(
|
||||
textWidth + margins.left() + margins.right() + 4,
|
||||
widget.sizeHint().height(),
|
||||
),
|
||||
widget,
|
||||
)
|
||||
width = max(180, size.width() + 80)
|
||||
widget.setMinimumWidth(width)
|
||||
widget.setMaximumWidth(width)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
|
||||
self._fitDurationInputs()
|
||||
|
||||
def retranslate(self):
|
||||
super().retranslate()
|
||||
|
||||
singleShotWeakly(0, self, '_fitDurationInputs')
|
||||
|
||||
def _page(self, title):
|
||||
page = QWidget()
|
||||
|
||||
@@ -327,22 +398,30 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
|
||||
return form
|
||||
|
||||
def _text(self, form, key, label, value):
|
||||
def _addField(self, form, label, widget):
|
||||
form.addRow(AppQLabel(label), widget)
|
||||
|
||||
def _text(self, form, key, label, value, *, placeholder='', maximumWidth=None):
|
||||
widget = AppQLineEdit()
|
||||
widget.setText(str(value) if value is not None else '')
|
||||
widget.setPlaceholderText(placeholder)
|
||||
|
||||
if maximumWidth is not None:
|
||||
widget.setMaximumWidth(maximumWidth)
|
||||
|
||||
self.fields[key] = widget
|
||||
|
||||
form.addRow(AppQLabel(label), widget)
|
||||
self._addField(form, label, widget)
|
||||
|
||||
def _number(self, form, key, label, value, minimum, maximum):
|
||||
widget = AppQSpinBox()
|
||||
widget.setRange(minimum, maximum)
|
||||
widget.setValue(value if type(value) is int else minimum)
|
||||
widget.setMaximumWidth(135)
|
||||
|
||||
self.fields[key] = widget
|
||||
|
||||
form.addRow(AppQLabel(label), widget)
|
||||
self._addField(form, label, widget)
|
||||
|
||||
def _choice(self, form, key, label, choices, value):
|
||||
widget = AppQComboBox()
|
||||
@@ -354,7 +433,7 @@ class SingTUNSettingsDialog(AppQTransientDialog):
|
||||
|
||||
self.fields[key] = widget
|
||||
|
||||
form.addRow(AppQLabel(label), widget)
|
||||
self._addField(form, label, widget)
|
||||
|
||||
def document(self):
|
||||
"""Prepare sing-tun's complete candidate before its live collection changes."""
|
||||
|
||||
+197
-4
@@ -25,7 +25,7 @@ from Furious.Core.MultiprocessingRuntime import (
|
||||
MultiprocessingRuntime,
|
||||
)
|
||||
from Furious.Interface import CoreRuntime, RuntimeState, RuntimeExitReason
|
||||
from Furious.Frozenlib import AppSettings
|
||||
from Furious.Frozenlib import AppSettings, Mixins
|
||||
from Furious.Models.SingTUN import prepareSingTUNSettings, addSingTUNExclusions
|
||||
from Furious.Frozenlib.SocksProxy import socksURL
|
||||
from Furious.Repository import Storage
|
||||
@@ -56,7 +56,8 @@ from Furious.Service.RuntimeLease import RuntimeEventRouter, RuntimeLeaseState
|
||||
from Furious.Service.LogManager import LogManager, TUN_LOG_CATEGORY
|
||||
|
||||
from PySide6 import QtCore
|
||||
from PySide6.QtWidgets import QLabel, QWidget
|
||||
from PySide6.QtTest import QTest
|
||||
from PySide6.QtWidgets import QLabel, QStyle, QStyleOptionFrame, QWidget
|
||||
from shiboken6 import isValid
|
||||
|
||||
from tests.support import application, isolatedSettings, processQtEvents, waitFor
|
||||
@@ -564,8 +565,12 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
|
||||
|
||||
for language in ('ZH', 'RU'):
|
||||
self.assertNotEqual(
|
||||
_('TCP Congestion Control (Go stack)', language),
|
||||
'TCP Congestion Control (Go stack)',
|
||||
_('TCP Congestion Control', language),
|
||||
'TCP Congestion Control',
|
||||
)
|
||||
self.assertNotEqual(
|
||||
_('Available when Stack is set to go or Automatic.', language),
|
||||
'Available when Stack is set to go or Automatic.',
|
||||
)
|
||||
|
||||
dialog.reject()
|
||||
@@ -589,6 +594,153 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
|
||||
processQtEvents()
|
||||
self.assertFalse(isValid(dialog))
|
||||
|
||||
def testSelectiveRowsKeepInputsBoundedAndPreserveKeyboardOrderAndValues(self):
|
||||
pages = (
|
||||
(
|
||||
0,
|
||||
(),
|
||||
(
|
||||
'stack',
|
||||
'log_level',
|
||||
'network_interface',
|
||||
'max_sessions',
|
||||
'connect_timeout',
|
||||
'udp_timeout',
|
||||
'tcp_idle_timeout',
|
||||
),
|
||||
),
|
||||
(
|
||||
1,
|
||||
(),
|
||||
(
|
||||
'Name',
|
||||
'MTU',
|
||||
'Inet4Address',
|
||||
'Inet6Address',
|
||||
'TCPCongestionControl',
|
||||
'UDPNATMax',
|
||||
'UDPTimeout',
|
||||
'ICMPTimeout',
|
||||
'UDPMapping',
|
||||
'UDPFiltering',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
for language in ('EN', 'RU', 'ZH'):
|
||||
with self.subTest(language=language), isolatedSettings():
|
||||
AppSettings.set('Language', language)
|
||||
dialog = self._dialog({})
|
||||
original = dialog.document()
|
||||
dialog.open()
|
||||
dialog.activateWindow()
|
||||
processQtEvents()
|
||||
|
||||
try:
|
||||
for tab, pairs, keys in pages:
|
||||
dialog.tabs.setCurrentIndex(tab)
|
||||
processQtEvents()
|
||||
|
||||
if tab == 0:
|
||||
self.assertLess(
|
||||
dialog.fields['stack'].geometry().bottom(),
|
||||
dialog.fields['log_level'].geometry().top(),
|
||||
)
|
||||
self.assertLess(
|
||||
dialog.fields['connect_timeout'].geometry().bottom(),
|
||||
dialog.fields['udp_timeout'].geometry().top(),
|
||||
)
|
||||
|
||||
for left, right in pairs:
|
||||
first, second = dialog.fields[left], dialog.fields[right]
|
||||
self.assertLessEqual(
|
||||
abs(
|
||||
first.geometry().center().y()
|
||||
- second.geometry().center().y()
|
||||
),
|
||||
2,
|
||||
)
|
||||
self.assertLess(
|
||||
first.geometry().right(), second.geometry().left()
|
||||
)
|
||||
|
||||
ordered = [dialog.fields[key] for key in keys]
|
||||
ordered[0].setFocus()
|
||||
processQtEvents()
|
||||
|
||||
for current, following in zip(ordered, ordered[1:]):
|
||||
QTest.keyClick(current, QtCore.Qt.Key_Tab)
|
||||
self.assertTrue(following.hasFocus())
|
||||
|
||||
for first, second in (
|
||||
('Name', 'MTU'),
|
||||
('Inet4Address', 'Inet6Address'),
|
||||
('TCPCongestionControl', 'UDPNATMax'),
|
||||
('UDPTimeout', 'ICMPTimeout'),
|
||||
('UDPMapping', 'UDPFiltering'),
|
||||
):
|
||||
self.assertLess(
|
||||
dialog.fields[first].geometry().bottom(),
|
||||
dialog.fields[second].geometry().top(),
|
||||
)
|
||||
|
||||
for key, width in (
|
||||
('Name', 360),
|
||||
('network_interface', 360),
|
||||
('UDPNATMax', 200),
|
||||
('connect_timeout', 160),
|
||||
('udp_timeout', 160),
|
||||
('tcp_idle_timeout', 160),
|
||||
):
|
||||
self.assertLessEqual(dialog.fields[key].width(), width)
|
||||
|
||||
for key in ('Inet4Address', 'Inet6Address'):
|
||||
self.assertLessEqual(dialog.fields[key].width(), 600)
|
||||
self.assertEqual(dialog.fields[key].maximumWidth(), 600)
|
||||
|
||||
self.assertEqual(dialog.document(), original)
|
||||
finally:
|
||||
dialog.reject()
|
||||
processQtEvents()
|
||||
|
||||
def testDurationInputsFitTranslatedPlaceholdersWithoutChangingValues(self):
|
||||
with isolatedSettings():
|
||||
AppSettings.set('Language', 'EN')
|
||||
dialog = self._dialog({})
|
||||
original = dialog.document()
|
||||
dialog.tabs.setCurrentIndex(1)
|
||||
dialog.open()
|
||||
processQtEvents()
|
||||
|
||||
try:
|
||||
for language in ('RU', 'ZH', 'EN'):
|
||||
AppSettings.set('Language', language)
|
||||
Mixins.QTranslatable.retranslateAll()
|
||||
processQtEvents()
|
||||
|
||||
for key in ('UDPTimeout', 'ICMPTimeout'):
|
||||
field = dialog.fields[key]
|
||||
option = QStyleOptionFrame()
|
||||
field.initStyleOption(option)
|
||||
content = field.style().subElementRect(
|
||||
QStyle.SubElement.SE_LineEditContents, option, field
|
||||
)
|
||||
margins = field.textMargins()
|
||||
available = content.width() - margins.left() - margins.right()
|
||||
self.assertGreaterEqual(
|
||||
available,
|
||||
field.fontMetrics().horizontalAdvance(
|
||||
field.placeholderText()
|
||||
)
|
||||
+ 80,
|
||||
)
|
||||
self.assertLess(field.width(), dialog.width())
|
||||
|
||||
self.assertEqual(dialog.document(), original)
|
||||
finally:
|
||||
dialog.reject()
|
||||
processQtEvents()
|
||||
|
||||
def testDialogPreparesNativeAndHostOptionsInOneIndependentDocument(self):
|
||||
sing = {'host_options': {'tunAdapterInterfaceDNS': '9.9.9.9'}}
|
||||
dialog = self._dialog(sing)
|
||||
@@ -689,6 +841,10 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
|
||||
|
||||
form = dialog.disableDNS.parentWidget().layout()
|
||||
legacyLabels = {label.text() for label in legacy.findChildren(QLabel)}
|
||||
singLabels = {
|
||||
label.text()
|
||||
for label in dialog.disableDNS.parentWidget().findChildren(QLabel)
|
||||
}
|
||||
|
||||
for key, source in (
|
||||
('primaryAdapterInterfaceName', 'Primary Adapter Interface Name'),
|
||||
@@ -700,6 +856,7 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
|
||||
),
|
||||
):
|
||||
expected = _(source)
|
||||
self.assertIn(expected, singLabels)
|
||||
self.assertEqual(
|
||||
form.labelForField(dialog.fields[key]).text(), expected
|
||||
)
|
||||
@@ -722,6 +879,42 @@ class SingTUNUIAndStorageTest(unittest.TestCase):
|
||||
self.assertIn(dnsLabel, legacyLabels)
|
||||
self.assertEqual(form.rowCount(), 5)
|
||||
|
||||
dialog.tabs.setCurrentIndex(2)
|
||||
dialog.open()
|
||||
processQtEvents()
|
||||
name = dialog.fields['primaryAdapterInterfaceName']
|
||||
address = dialog.fields['primaryAdapterInterfaceIP']
|
||||
self.assertLess(name.geometry().bottom(), address.geometry().top())
|
||||
for key, width in (
|
||||
('primaryAdapterInterfaceName', 360),
|
||||
('primaryAdapterInterfaceIP', 360),
|
||||
('tunAdapterInterfaceDNS', 360),
|
||||
('bypassTUNAdapterInterfaceIP', 600),
|
||||
):
|
||||
self.assertLessEqual(dialog.fields[key].width(), width)
|
||||
|
||||
ordered = [
|
||||
dialog.fields[key]
|
||||
for key in (
|
||||
'primaryAdapterInterfaceName',
|
||||
'primaryAdapterInterfaceIP',
|
||||
'tunAdapterInterfaceDNS',
|
||||
'bypassTUNAdapterInterfaceIP',
|
||||
)
|
||||
]
|
||||
ordered[0].setFocus()
|
||||
for current, following in zip(ordered, ordered[1:]):
|
||||
QTest.keyClick(current, QtCore.Qt.Key_Tab)
|
||||
self.assertTrue(following.hasFocus())
|
||||
|
||||
if language == 'ZH':
|
||||
self.assertEqual(
|
||||
_('Customize sing-tun Settings'), '自定义sing-tun设置'
|
||||
)
|
||||
self.assertEqual(
|
||||
_('Customize sing-tun Settings...'), '自定义sing-tun设置...'
|
||||
)
|
||||
|
||||
legacy.reject()
|
||||
legacy.deleteLater()
|
||||
dialog.reject()
|
||||
|
||||
Reference in New Issue
Block a user