Polish sing-tun settings presentation

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-10-07 12:25:04 +08:00
parent 1cf79beeb8
commit eca3b8e939
4 changed files with 438 additions and 116 deletions
+2
View File
@@ -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
+130 -82
View File
@@ -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"
}
}
+109 -30
View File
@@ -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
View File
@@ -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()