Replace editor checkboxes with Fluent switches

Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
Loren Eteval
2026-08-17 21:13:56 +08:00
parent bcefbe6b86
commit c9f0c3ed3c
10 changed files with 61 additions and 51 deletions
+2 -2
View File
@@ -29,7 +29,7 @@ from Furious.Qt import (
GuiEditorItemBasicRemark,
GuiEditorItemProxyHttp,
GuiEditorItemProxySocks,
GuiEditorItemTextCheckBox,
GuiEditorItemTextSwitch,
GuiEditorItemTextSpinBox,
GuiEditorWidgetQDialog,
GuiEditorWidgetQGroupBox,
@@ -290,7 +290,7 @@ class ExternalCoreShutdownTimeoutInput(GuiEditorItemTextSpinBox):
self.setValue(value if isinstance(value, int) else 5)
class ExternalCoreApplicationTun2socksInput(GuiEditorItemTextCheckBox):
class ExternalCoreApplicationTun2socksInput(GuiEditorItemTextSwitch):
"""Bind this profile's host-managed tun2socks participation flag."""
def __init__(self):
+1 -1
View File
@@ -188,7 +188,7 @@ class GuiHy1ItemSpeedDownMbps(GuiEditorItemTextSpinBox):
self.setValue(96)
class GuiHy1ItemTLSInsecure(GuiEditorItemTextCheckBox):
class GuiHy1ItemTLSInsecure(GuiEditorItemTextSwitch):
"""Represent GUI hy1 item TLS insecure."""
def __init__(self, *args, **kwargs):
+1 -1
View File
@@ -607,7 +607,7 @@ class GuiHy2ItemTLSTextInput(GuiEditorItemTextInput):
self.setText(value)
class GuiHy2ItemTLSInsecure(GuiEditorItemTextCheckBox):
class GuiHy2ItemTLSInsecure(GuiEditorItemTextSwitch):
"""Represent GUI hy2 item TLS insecure."""
def __init__(self, *args, **kwargs):
+1 -1
View File
@@ -374,7 +374,7 @@ class GuiVTLSItemTLSXXXTextInput(GuiEditorItemTextInput):
self.setText('')
class GuiVTLSItemTLSAllowInsecure(GuiEditorItemTextCheckBox):
class GuiVTLSItemTLSAllowInsecure(GuiEditorItemTextSwitch):
"""Represent GUI vtls item TLS allow insecure."""
def __init__(self, *args, **kwargs):
+10 -6
View File
@@ -34,7 +34,7 @@ __all__ = [
'GuiEditorItemTextInput',
'GuiEditorItemTextSpinBox',
'GuiEditorItemTextComboBox',
'GuiEditorItemTextCheckBox',
'GuiEditorItemTextSwitch',
'GuiEditorItemBasicRemark',
'GuiEditorItemProxyHttp',
'GuiEditorItemProxySocks',
@@ -146,18 +146,22 @@ class GuiEditorItemTextComboBox(EditorWidgetBinding):
return self._title, self._input
class GuiEditorItemTextCheckBox(EditorWidgetBinding):
"""Represent GUI editor item text check box."""
class GuiEditorItemTextSwitch(EditorWidgetBinding):
"""Represent one labeled, compact Fluent switch in an editor form."""
def __init__(self, *args, **kwargs):
"""Initialize the GuiEditorItemTextCheckBox."""
"""Initialize the editor label and its form-sized switch."""
title = kwargs.pop('title', '')
translatable = kwargs.pop('translatable', True)
parent = kwargs.pop('parent', None)
super().__init__(*args, **kwargs)
self._input = AppQCheckBox(_(title), translatable=translatable, parent=parent)
if translatable:
title = _(title)
self._title = AppQLabel(title, translatable=translatable, parent=parent)
self._input = AppQSwitch(parent=parent, compact=True)
def isChecked(self) -> bool:
"""Return whether checked."""
@@ -169,7 +173,7 @@ class GuiEditorItemTextCheckBox(EditorWidgetBinding):
def widgets(self):
"""Return the widgets owned by this editor item."""
return (self._input,)
return self._title, self._input
class GuiEditorItemBasicRemark(GuiEditorItemTextInput):
+3 -15
View File
@@ -36,7 +36,6 @@ import functools
__all__ = [
'moveToCenter',
'AppQCheckBox',
'AppQSwitch',
'AppQComboBox',
'AppQComboBoxSeparatorDelegate',
@@ -88,31 +87,20 @@ def moveToCenter(widget, parent=None):
widget.move(geometry.topLeft())
class AppQCheckBox(Mixins.QTranslatable, QCheckBox):
"""Represent app q check box."""
def __init__(self, *args, **kwargs):
"""Initialize the AppQCheckBox."""
super().__init__(*args, **kwargs)
def retranslate(self):
"""Refresh translated text for the app q check box."""
self.setText(_(self.text()))
class AppQSwitch(Mixins.ThemeAware, QCheckBox):
"""Paint one compact, animated Fluent-style binary switch."""
ControlSize = QtCore.QSize(38, 22)
CompactControlSize = QtCore.QSize(34, 20)
AnimationDuration = 160
def __init__(self, parent=None):
def __init__(self, parent=None, *, compact=False):
"""Initialize the reusable switch and its owned thumb animation."""
super().__init__(parent)
self.setObjectName('SettingsToggle')
self.setCursor(QtCore.Qt.CursorShape.PointingHandCursor)
self.setFixedSize(self.ControlSize)
self.setFixedSize(self.CompactControlSize if compact else self.ControlSize)
self._thumbPosition = 0.0
+2 -4
View File
@@ -31,7 +31,7 @@ from .EditorWidgets import (
GuiEditorItemBasicRemark,
GuiEditorItemProxyHttp,
GuiEditorItemProxySocks,
GuiEditorItemTextCheckBox,
GuiEditorItemTextSwitch,
GuiEditorItemTextComboBox,
GuiEditorItemTextInput,
GuiEditorItemTextSpinBox,
@@ -51,7 +51,6 @@ from .QtGui import (
)
from .QtNetwork import AppQNetworkAccessManager
from .QtWidgets import (
AppQCheckBox,
AppQComboBox,
AppQComboBoxSeparatorDelegate,
AppQDialog,
@@ -104,7 +103,6 @@ __all__ = [
'AppHue',
'AppQAction',
'AppQActionGroup',
'AppQCheckBox',
'AppQComboBox',
'AppQComboBoxSeparatorDelegate',
'AppQDialog',
@@ -142,7 +140,7 @@ __all__ = [
'GuiEditorItemBasicRemark',
'GuiEditorItemProxyHttp',
'GuiEditorItemProxySocks',
'GuiEditorItemTextCheckBox',
'GuiEditorItemTextSwitch',
'GuiEditorItemTextComboBox',
'GuiEditorItemTextInput',
'GuiEditorItemTextSpinBox',
+23 -15
View File
@@ -65,8 +65,8 @@ class _SubscriptionEditorDialog(AppQTransientDialog):
self.remarkEdit = QLineEdit(subscription.get('remark', ''))
self.urlEdit = QLineEdit(subscription.get('webURL', ''))
self.enabledCheckBox = QCheckBox()
self.enabledCheckBox.setChecked(subscription.get('enabled', True))
self.enabledSwitch = AppQSwitch()
self.enabledSwitch.syncChecked(subscription.get('enabled', True))
self.autoUpdateComboBox = AppQComboBox()
self.proxyComboBox = AppQComboBox()
self.userAgentEdit = QLineEdit(subscription.get('userAgent', ''))
@@ -113,26 +113,34 @@ class _SubscriptionEditorDialog(AppQTransientDialog):
form.setColumnStretch(1, 1)
form.setColumnStretch(3, 1)
self.remarkLabel = AppQLabel(_('Remark'))
self.urlLabel = AppQLabel(_('URL'))
self.enabledLabel = AppQLabel(_('Enabled'))
self.autoUpdateLabel = AppQLabel(_('Auto Update'))
self.proxyLabel = AppQLabel(_('Auto Update Use Proxy'))
self.userAgentLabel = AppQLabel(_('User Agent'))
self.filterLabel = AppQLabel(_('Profile Filter (Regex)'))
(
self.remarkLabel,
self.urlLabel,
self.enabledLabel,
self.autoUpdateLabel,
self.proxyLabel,
self.userAgentLabel,
self.filterLabel,
) = (
AppQLabel(_('Remark')),
AppQLabel(_('URL')),
AppQLabel(_('Enabled')),
AppQLabel(_('Auto Update')),
AppQLabel(_('Auto Update Use Proxy')),
AppQLabel(_('User Agent')),
AppQLabel(_('Profile Filter (Regex)')),
)
enabledControl = QWidget()
enabledLayout = QHBoxLayout(enabledControl)
enabledLayout = QHBoxLayout()
enabledLayout.setContentsMargins(0, 0, 0, 0)
enabledLayout.setSpacing(10)
enabledLayout.addWidget(self.enabledLabel)
enabledLayout.addWidget(self.enabledCheckBox)
enabledLayout.addWidget(self.enabledSwitch)
enabledLayout.addStretch(1)
form.addWidget(self.remarkLabel, 0, 0)
form.addWidget(self.remarkEdit, 0, 1)
form.addWidget(enabledControl, 0, 2, 1, 2)
form.addLayout(enabledLayout, 0, 2, 1, 2)
form.addWidget(self.urlLabel, 1, 0)
form.addWidget(self.urlEdit, 1, 1, 1, 3)
form.addWidget(self.autoUpdateLabel, 2, 0)
@@ -194,7 +202,7 @@ class _SubscriptionEditorDialog(AppQTransientDialog):
return {
'remark': self.remarkEdit.text().strip(),
'webURL': self.urlEdit.text().strip(),
'enabled': self.enabledCheckBox.isChecked(),
'enabled': self.enabledSwitch.isChecked(),
'autoupdate': self.autoUpdateComboBox.currentData() or '',
'proxy': self.proxyComboBox.currentData() or '',
'userAgent': self.userAgentEdit.text().strip(),
+5 -5
View File
@@ -106,11 +106,11 @@ class GuiTUNSettingsItemSpinBoxBufferSizeXXX(GuiEditorItemTextSpinBox):
self.setValue(oldValue)
class GuiTUNSettingsItemCheckBoxXXX(GuiEditorItemTextCheckBox):
"""Represent GUI TUN settings item check box xxx."""
class GuiTUNSettingsItemSwitchXXX(GuiEditorItemTextSwitch):
"""Represent GUI TUN settings item switch xxx."""
def __init__(self, *args, **kwargs):
"""Initialize the GuiTUNSettingsItemCheckBoxXXX."""
"""Initialize the GuiTUNSettingsItemSwitchXXX."""
self.key = kwargs.pop('key', '')
self.default = kwargs.pop('default', 'False')
@@ -245,7 +245,7 @@ class GuiTUNSettingsGroupBoxBasic(GuiEditorWidgetQGroupBox):
title=_('Bypass Tun2socks Adapter Interface IP (separated by commas)'),
key='bypassTUNAdapterInterfaceIP',
),
GuiTUNSettingsItemCheckBoxXXX(
GuiTUNSettingsItemSwitchXXX(
title=_(
'Disable Primary Adapter Interface DNS (Mitigating DNS leaks on Windows)'
),
@@ -280,7 +280,7 @@ class GuiTUNSettingsGroupBoxMemory(GuiEditorWidgetQGroupBox):
start=1,
end=4,
),
GuiTUNSettingsItemCheckBoxXXX(
GuiTUNSettingsItemSwitchXXX(
title=_('TCP Receive Buffer Auto-tuning'),
key='tcpAutoTuning',
default='False',
+13 -1
View File
@@ -36,7 +36,7 @@ from Furious.Controllers.SettingsController import (
)
from Furious.Frozenlib import AppSettings
from Furious.Models import ProfileMetadata, ServerProfile
from Furious.Qt import AppQMessageBox
from Furious.Qt import AppQMessageBox, AppQSwitch
from Furious.Service import (
APPLICATION_LOG_CATEGORY,
CORE_LOG_CATEGORY,
@@ -108,6 +108,12 @@ class EditorMappingTest(unittest.TestCase):
{'TOKEN': 'one=two', 'UNICODE': '测试'},
)
self.assertTrue(editor._applicationTun2socksInput.isChecked())
tunLabel, tunSwitch = editor._applicationTun2socksInput.widgets()
self.assertTrue(tunLabel.text())
self.assertIsInstance(tunSwitch, AppQSwitch)
self.assertEqual(tunSwitch.size(), AppQSwitch.CompactControlSize)
self.assertTrue(editor._tunRemoteAddressInput.widgets()[1].isEnabled())
self.assertEqual(editor._tunRemoteAddressInput.text(), '2001:db8::42')
self.assertEqual(len(editor.groupBoxSequence()), 1)
@@ -152,6 +158,12 @@ class EditorMappingTest(unittest.TestCase):
values = dialog.subscription()
self.assertIsInstance(dialog.enabledSwitch, AppQSwitch)
self.assertEqual(dialog.enabledSwitch.size(), AppQSwitch.ControlSize)
self.assertEqual(
dialog.enabledSwitch.parentWidget().objectName(),
'SubscriptionEditorForm',
)
self.assertEqual(values['remark'], 'Fixture subscription')
self.assertEqual(values['webURL'], 'https://example.test/subscription')
self.assertFalse(values['enabled'])