diff --git a/Furious/Actions/Connection.py b/Furious/Actions/Connection.py index 8381a6e..a40ad5e 100644 --- a/Furious/Actions/Connection.py +++ b/Furious/Actions/Connection.py @@ -20,11 +20,10 @@ from __future__ import annotations from Furious.Controllers.ConnectionController import ( - ConnectionController, ConnectionError, ConnectionState, ) -from Furious.Frozenlib import APP, AppSettings +from Furious.Frozenlib import APP, AppConnectionController, AppSettings from Furious.Qt import AppQAction, AppQMessageBox, bootstrapIcon from Furious.Qt import gettext as _ from Furious.Widget.ConnectionProgressWidget import ConnectionProgressWidget @@ -44,9 +43,9 @@ _TRANSLATABLE_CONNECTION_STATES = ( class ConnectAction(AppQAction): """Adapt connection state and operations to a tray QAction.""" - def __init__(self, controller: ConnectionController, **kwargs): + def __init__(self, **kwargs): """Bind tray presentation to the shared connection controller.""" - self.controller = controller + self.controller = AppConnectionController() super().__init__( _('Connect'), diff --git a/Furious/Actions/Routing.py b/Furious/Actions/Routing.py index adabb59..2f5f23f 100644 --- a/Furious/Actions/Routing.py +++ b/Furious/Actions/Routing.py @@ -19,7 +19,7 @@ from __future__ import annotations -from Furious.Controllers import RoutingController +from Furious.Frozenlib import AppRoutingController from Furious.Qt import * from Furious.Qt import gettext as _ @@ -38,30 +38,23 @@ _TRANSLATABLE_BUILTIN_ROUTING = [ class RoutingChildAction(AppQAction): """Forward one tray route choice to the shared controller.""" - def __init__( - self, - *args, - controller: RoutingController, - routingValue: str, - **kwargs, - ): + def __init__(self, *args, routingValue: str, **kwargs): """Initialize the RoutingChildAction.""" - self.controller = controller self.routingValue = routingValue super().__init__(*args, **kwargs) def triggeredCallback(self, checked): """Handle activation of the action.""" - self.controller.selectRouting(self.routingValue) + AppRoutingController().selectRouting(self.routingValue) class RoutingAction(AppQAction): """Render application routing options as a synchronized tray menu.""" - def __init__(self, controller: RoutingController, **kwargs): + def __init__(self, **kwargs): """Initialize the RoutingAction.""" - self.controller = controller + self.controller = AppRoutingController() super().__init__( _('Routing'), @@ -90,7 +83,6 @@ class RoutingAction(AppQAction): if option.translatable else option.displayName ), - controller=self.controller, routingValue=option.id, checkable=True, checked=routing == option.id, diff --git a/Furious/Application/DesktopApplication.py b/Furious/Application/DesktopApplication.py index 1dcb936..4d146cf 100644 --- a/Furious/Application/DesktopApplication.py +++ b/Furious/Application/DesktopApplication.py @@ -23,7 +23,11 @@ from Furious.Frozenlib import * from Furious.Interface import * from Furious.Core import Tun2socks from Furious.Backends import OFFICIAL_PLUGIN_TYPES -from Furious.Controllers import ConnectionController, RoutingController +from Furious.Controllers import ( + ConnectionController, + RoutingController, + SettingsController, +) from Furious.Extensions import BUNDLED_EXTENSION_TYPES from Furious.Plugins import getPluginRegistry, initializePluginRegistry from Furious.Qt import AppStyleSheet @@ -182,6 +186,7 @@ class DesktopApplication(ApplicationRunner, SingletonApplication): self.systemTray = None self.connectionController = None self.routingController = None + self.settingsController = None # Unified logging service and presentation self.logManager = None @@ -369,7 +374,7 @@ class DesktopApplication(ApplicationRunner, SingletonApplication): @QtCore.Slot() def cleanup(): """Release resources owned by the application.""" - controller = getattr(APP(), 'connectionController', None) + controller = AppConnectionController() if controller is not None: controller.shutdown() @@ -452,13 +457,23 @@ class DesktopApplication(ApplicationRunner, SingletonApplication): self.addEnviron() self.addStorage() - # Controllers exist before presentation services so even early - # widgets and plugin UI can observe a stable disconnected state. - self.connectionController = ConnectionController(parent=self) - self.routingController = RoutingController(parent=self) + + # The application owns these lifetime-scoped services; Frozenlib + # exposes them through App*Controller accessors. + ( + self.connectionController, + self.routingController, + self.settingsController, + ) = ( + ConnectionController(parent=self), + RoutingController(parent=self), + SettingsController(), + ) + self.connectionController.interactionEnabledChanged.connect( self.routingController.setInteractionEnabled ) + self.addCustomFont() # self.configureApplicationFont() self.configureLogging() @@ -566,7 +581,7 @@ class DesktopApplication(ApplicationRunner, SingletonApplication): if state == QtCore.Qt.ApplicationState.ApplicationActive: if ( not self.mainWindow.isVisible() - and not self.connectionController.isConnecting() + and not AppConnectionController().isConnecting() ): self.mainWindow.show() @@ -581,7 +596,8 @@ class DesktopApplication(ApplicationRunner, SingletonApplication): self.systemTray.show() self.systemTray.setCustomToolTip() self.systemTray.bootstrap() - self.connectionController.restoreStartupState() + + AppConnectionController().restoreStartupState() return self.exec() except SystemTrayUnavailable: diff --git a/Furious/Application/TrayIcon.py b/Furious/Application/TrayIcon.py index b54cec8..dc4bbdb 100644 --- a/Furious/Application/TrayIcon.py +++ b/Furious/Application/TrayIcon.py @@ -54,8 +54,8 @@ class TrayIcon( super().__init__(*args, **kwargs) actions = [ - ConnectAction(APP().connectionController, isTrayAction=True), - RoutingAction(APP().routingController, isTrayAction=True), + ConnectAction(isTrayAction=True), + RoutingAction(isTrayAction=True), ImportAction(isTrayAction=True), AppQSeperator(), ShowHomePageAction(isTrayAction=True), @@ -173,7 +173,7 @@ class TrayIcon( def setMonochromeIcon(self): """Set monochrome icon.""" - if APP().connectionController.isConnected(): + if AppConnectionController().isConnected(): self.setConnectedMonochromeIcon() else: self.setDisconnectedMonochromeIcon() @@ -265,7 +265,7 @@ class TrayIcon( return - if APP().connectionController.isConnected(): + if AppConnectionController().isConnected(): self.setConnectedIcon() else: self.setDisconnectedIcon() diff --git a/Furious/Controllers/RoutingController.py b/Furious/Controllers/RoutingController.py index 5222280..e1d731a 100644 --- a/Furious/Controllers/RoutingController.py +++ b/Furious/Controllers/RoutingController.py @@ -20,7 +20,7 @@ from __future__ import annotations from Furious.Frozenlib import ( - APP, + AppConnectionController, AppBuiltinRouting, AppSettings, registerAppSettings, @@ -134,10 +134,8 @@ class RoutingController(QtCore.QObject): self.refresh(force=True) - app = APP() - - if app is not None and app.connectionController.isConnected(): - app.connectionController.startReconnection() + if AppConnectionController().isConnected(): + AppConnectionController().startReconnection() return True diff --git a/Furious/Controllers/SettingsController.py b/Furious/Controllers/SettingsController.py index e64e607..59ab5b6 100644 --- a/Furious/Controllers/SettingsController.py +++ b/Furious/Controllers/SettingsController.py @@ -112,7 +112,7 @@ class SettingsController: try: if enabled: APP().systemTray.setMonochromeIcon() - elif APP().connectionController.isConnected(): + elif AppConnectionController().isConnected(): APP().systemTray.setConnectedIcon() else: APP().systemTray.setDisconnectedIcon() diff --git a/Furious/Frozenlib/Globals.py b/Furious/Frozenlib/Globals.py index d2c29f0..664b5d0 100644 --- a/Furious/Frozenlib/Globals.py +++ b/Furious/Frozenlib/Globals.py @@ -25,9 +25,12 @@ import functools __all__ = [ 'APP', + 'AppConnectionController', 'AppFontName', 'AppLogManager', 'AppLogPage', + 'AppRoutingController', + 'AppSettingsController', 'AppThreadPool', ] @@ -40,13 +43,19 @@ def getAppAttributes(name: str): ( + AppConnectionController, AppFontName, AppLogManager, AppLogPage, + AppRoutingController, + AppSettingsController, AppThreadPool, ) = ( + functools.partial(getAppAttributes, 'connectionController'), functools.partial(getAppAttributes, 'customFontName'), functools.partial(getAppAttributes, 'logManager'), functools.partial(getAppAttributes, 'logPage'), + functools.partial(getAppAttributes, 'routingController'), + functools.partial(getAppAttributes, 'settingsController'), functools.partial(getAppAttributes, 'threadPool'), ) diff --git a/Furious/Frozenlib/__init__.py b/Furious/Frozenlib/__init__.py index 72928dd..713cca6 100644 --- a/Furious/Frozenlib/__init__.py +++ b/Furious/Frozenlib/__init__.py @@ -67,7 +67,16 @@ from .Constants import ( XRAY_ASSET_PATH_GEOSITE, ) from .Enum import AppBuiltinCommand, AppBuiltinProxyMode, AppBuiltinRouting -from .Globals import APP, AppFontName, AppLogManager, AppLogPage, AppThreadPool +from .Globals import ( + APP, + AppConnectionController, + AppFontName, + AppLogManager, + AppLogPage, + AppRoutingController, + AppSettingsController, + AppThreadPool, +) from .Mixins import Mixins from .PySide6Legacy import PySide6Legacy from .StartupOnBoot import StartupOnBoot @@ -110,11 +119,14 @@ __all__ = [ 'AppBuiltinCommand', 'AppBuiltinProxyMode', 'AppBuiltinRouting', + 'AppConnectionController', 'AppFontName', 'AppLogManager', 'AppLogPage', 'AppResources', 'AppSettings', + 'AppRoutingController', + 'AppSettingsController', 'AppThreadPool', 'CORE_CHECK_ALIVE_INTERVAL', 'CRASH_LOG_DIR', diff --git a/Furious/Qt/DynamicTheme.py b/Furious/Qt/DynamicTheme.py index 7aba51c..75108ae 100644 --- a/Furious/Qt/DynamicTheme.py +++ b/Furious/Qt/DynamicTheme.py @@ -64,7 +64,7 @@ class AppHue: @staticmethod def currentColor() -> str: """Return the current color value.""" - if APP().connectionController.isConnected(): + if AppConnectionController().isConnected(): return AppHue.connectedColor() else: return AppHue.disconnectedColor() @@ -72,7 +72,7 @@ class AppHue: @staticmethod def currentWindowIcon() -> AppQIcon: """Return the current window icon value.""" - if APP().connectionController.isConnected(): + if AppConnectionController().isConnected(): return AppHue.connectedWindowIcon() else: return AppHue.disconnectedWindowIcon() diff --git a/Furious/Qt/QtWidgets.py b/Furious/Qt/QtWidgets.py index be82686..0dcf3bd 100644 --- a/Furious/Qt/QtWidgets.py +++ b/Furious/Qt/QtWidgets.py @@ -1066,7 +1066,7 @@ def showMBoxNewChangesNextTime(**kwargs): def handleResultCode(code): """Handle result code.""" if code == PySide6Legacy.enumValueWrapper(AppQMessageBox.StandardButton.Yes): - APP().connectionController.startReconnection() + AppConnectionController().startReconnection() else: # Do nothing pass @@ -1074,7 +1074,7 @@ def showMBoxNewChangesNextTime(**kwargs): try: method = kwargs.pop('method', 'open') - if APP().connectionController.isConnected(): + if AppConnectionController().isConnected(): mbox = MBoxNewChangesNextTime(**kwargs) if isinstance(mbox.parent(), QMainWindow): @@ -1135,11 +1135,9 @@ def showMBoxDirectRulesNotAllowed(**kwargs): def handleResultCode(code): """Handle result code.""" if code == PySide6Legacy.enumValueWrapper(AppQMessageBox.StandardButton.Yes): - app = APP() - controller, wasConnected = ( - app.routingController, - app.connectionController.isConnected(), + AppRoutingController(), + AppConnectionController().isConnected(), ) changed = controller.selectRouting(AppBuiltinRouting.Global.value) @@ -1148,7 +1146,7 @@ def showMBoxDirectRulesNotAllowed(**kwargs): # Selecting a route reconnects an established connection itself. # During an initial connection attempt, resume that attempt here. if not wasConnected: - app.connectionController.startConnection() + AppConnectionController().startConnection() else: # Do nothing pass diff --git a/Furious/Repository/Storage.py b/Furious/Repository/Storage.py index 78e6b33..32ff118 100644 --- a/Furious/Repository/Storage.py +++ b/Furious/Repository/Storage.py @@ -127,7 +127,8 @@ class Storage: def UserHttpProxy() -> Union[str, None]: """Return the user HTTP proxy value.""" try: - controller = APP().connectionController + controller = AppConnectionController() + configuration = controller.activeConfiguration if controller.isConnected() and isinstance( @@ -145,7 +146,8 @@ class Storage: def UserServerRemark() -> Union[str, None]: """Return the user server remark value.""" try: - controller = APP().connectionController + controller = AppConnectionController() + configuration = controller.activeConfiguration if not controller.isConnected() or not isinstance( diff --git a/Furious/Service/PluginUIManager.py b/Furious/Service/PluginUIManager.py index df93dba..d958e0e 100644 --- a/Furious/Service/PluginUIManager.py +++ b/Furious/Service/PluginUIManager.py @@ -19,7 +19,7 @@ from __future__ import annotations -from Furious.Frozenlib import APP +from Furious.Frozenlib import AppConnectionController from Furious.Plugins import ( CapabilityKind, NavigationPageDescriptor, @@ -38,7 +38,7 @@ logger = logging.getLogger(__name__) def isCoreActive(coreType) -> bool: """Return whether the active connection owns a core of ``coreType``.""" try: - controller = APP().connectionController + controller = AppConnectionController() return controller.isConnected() and any( isinstance(process, coreType) for process in controller.processes diff --git a/Furious/Service/TrafficStatsManager.py b/Furious/Service/TrafficStatsManager.py index 9bdf2f8..80e312e 100644 --- a/Furious/Service/TrafficStatsManager.py +++ b/Furious/Service/TrafficStatsManager.py @@ -21,6 +21,7 @@ from __future__ import annotations from Furious.Frozenlib import ( APP, + AppConnectionController, AppBinarySettings, AppSettings, Mixins, @@ -239,7 +240,7 @@ class TrafficStatsManager( def _activeProcesses(): """Return the processes owned by the active connection controller.""" try: - return APP().connectionController.processes + return AppConnectionController().processes except (AttributeError, RuntimeError): return tuple() diff --git a/Furious/Widget/ConnectionButton.py b/Furious/Widget/ConnectionButton.py index f21bf91..72be9ef 100644 --- a/Furious/Widget/ConnectionButton.py +++ b/Furious/Widget/ConnectionButton.py @@ -19,8 +19,8 @@ from __future__ import annotations -from Furious.Controllers import ConnectionController, ConnectionState -from Furious.Frozenlib import Mixins +from Furious.Controllers import ConnectionState +from Furious.Frozenlib import AppConnectionController, Mixins from Furious.Qt import bootstrapIcon, AppQPushButton from Furious.Qt import gettext as _ @@ -37,12 +37,11 @@ class ConnectionButton(AppQPushButton): def __init__( self, - controller: ConnectionController, activateSelected: Callable[[], bool], parent=None, ): """Bind Home presentation and selection policy to the controller.""" - self.controller = controller + self.controller = AppConnectionController() self.activateSelected = activateSelected self._selectionCount = 0 diff --git a/Furious/Widget/RoutingSelector.py b/Furious/Widget/RoutingSelector.py index 964145f..92e5b01 100644 --- a/Furious/Widget/RoutingSelector.py +++ b/Furious/Widget/RoutingSelector.py @@ -19,8 +19,7 @@ from __future__ import annotations -from Furious.Controllers import RoutingController -from Furious.Frozenlib import Mixins +from Furious.Frozenlib import AppRoutingController, Mixins from Furious.Qt import AppQComboBox from Furious.Qt import gettext as _ @@ -33,9 +32,9 @@ __all__ = ['RoutingSelector'] class RoutingSelector(AppQComboBox): """Select routes through the application-wide routing controller.""" - def __init__(self, controller: RoutingController, parent=None): + def __init__(self, parent=None): """Bind the selector to one shared routing controller.""" - self.controller = controller + self.controller = AppRoutingController() # This widget restores its enabled state from the shared controller in # retranslate(), so the generic translation disable/enable wrapper is diff --git a/Furious/Widget/ServerTableView.py b/Furious/Widget/ServerTableView.py index 0c71870..49cf173 100644 --- a/Furious/Widget/ServerTableView.py +++ b/Furious/Widget/ServerTableView.py @@ -1043,7 +1043,7 @@ class DeleteServersProgressDialog(AppQDialog): self.table.activeServerChanged.emit() - controller = APP().connectionController + controller = AppConnectionController() if controller.isConnected(): controller.startDisconnection( @@ -1817,7 +1817,7 @@ class ServerTableView( # Same item activated. Do nothing return - if APP().connectionController.isConnecting(): + if AppConnectionController().isConnecting(): mbox = AppQMessageBox(icon=AppQMessageBox.Icon.Information) mbox.setWindowTitle(_('Connecting')) mbox.setText(_('Connecting. Please wait...')) @@ -1837,8 +1837,8 @@ class ServerTableView( self.activateItemByIndex(newIndex, True) - if APP().connectionController.isConnected(): - APP().connectionController.startReconnection() + if AppConnectionController().isConnected(): + AppConnectionController().startReconnection() @functools.lru_cache(None) def getGuiEditorByProtocol(self, protocol, **kwargs): @@ -2263,7 +2263,7 @@ class ServerTableView( self.activeServerChanged.emit() - controller = APP().connectionController + controller = AppConnectionController() if controller.isConnected(): controller.startDisconnection( @@ -2565,7 +2565,7 @@ class ServerTableView( active.itemSubscription == unique and active.itemSubscriptionManaged ) - wasConnected = APP().connectionController.isConnected() + wasConnected = AppConnectionController().isConnected() self.sourceModel.beginResetModel() @@ -2592,9 +2592,9 @@ class ServerTableView( if wasConnected and activeProfileId: if newActivatedIndex < 0 and activeWasManagedByGroup: - APP().connectionController.startDisconnection() + AppConnectionController().startDisconnection() elif activeProfileId in result.changedProfileIds: - APP().connectionController.startReconnection() + AppConnectionController().startReconnection() return result @@ -2620,7 +2620,7 @@ class ServerTableView( self.setCurrentIndex(self.proxyIndexFromSourceRow(0)) # Try to be user-friendly in some extreme cases - if not APP().connectionController.isConnected(): + if not AppConnectionController().isConnected(): # Activate automatically self.activateItemByIndex(0, True) diff --git a/Furious/Window/HomePage.py b/Furious/Window/HomePage.py index 929ddb9..2194a92 100644 --- a/Furious/Window/HomePage.py +++ b/Furious/Window/HomePage.py @@ -90,7 +90,7 @@ class AppConnectivityManager(ConnectivityManager): def startSingleTest(self): """Start single test.""" - if not APP().connectionController.isConnected(): + if not AppConnectionController().isConnected(): parent = self.parent() if isinstance(parent, HomePage): @@ -512,12 +512,10 @@ class HomePage(Mixins.QTranslatable, QMainWindow): self.pageTitleLabel.setObjectName('HomePageTitle') self.connectButton = ConnectionButton( - APP().connectionController, self.activateSelectedServerForConnection, parent=self, ) - self.routingController = APP().routingController - self.routingSelector = RoutingSelector(self.routingController, parent=self) + self.routingSelector = RoutingSelector(parent=self) self.searchLineEdit = AppQLineEdit() self.searchLineEdit.setPlaceholderText( @@ -582,7 +580,7 @@ class HomePage(Mixins.QTranslatable, QMainWindow): self.handleServerSelectionChanged ) self.userServersQTableWidget.activeServerChanged.connect( - self.routingController.refresh + AppRoutingController().refresh ) self.refreshSubscriptionFilter() @@ -614,7 +612,8 @@ class HomePage(Mixins.QTranslatable, QMainWindow): return False self.userServersQTableWidget.activateSelectedServer() - self.routingController.refresh() + + AppRoutingController().refresh() return Storage.UserActivatedItemIndex() == indexes[0] diff --git a/Furious/Window/MainWindow.py b/Furious/Window/MainWindow.py index 0de9c3d..d677859 100644 --- a/Furious/Window/MainWindow.py +++ b/Furious/Window/MainWindow.py @@ -101,12 +101,12 @@ class MainWindow(AppQMainWindow): parent=self.navigationView, ) - APP().connectionController.interactionEnabledChanged.connect( + AppConnectionController().interactionEnabledChanged.connect( self.settingsPage.setConnectionControlsEnabled ) self.settingsPage.setConnectionControlsEnabled( - APP().connectionController.interactionEnabled + AppConnectionController().interactionEnabled ) if not isinstance(self.logPage, LogPage): diff --git a/Furious/Window/SettingsPage.py b/Furious/Window/SettingsPage.py index d42ee1e..5520cf0 100644 --- a/Furious/Window/SettingsPage.py +++ b/Furious/Window/SettingsPage.py @@ -19,7 +19,6 @@ from __future__ import annotations -from Furious.Controllers import SettingsController from Furious.Frozenlib import * from Furious.Plugins import ( CapabilityKind, @@ -499,7 +498,7 @@ class _LanguageSettingsCard(_SettingsCard): language = self.comboBox.currentData() if isinstance(language, str): - SettingsController.setLanguage(language) + AppSettingsController().setLanguage(language) class _SystemProxySettingsCard(_SettingsCard): @@ -550,7 +549,7 @@ class _SystemProxySettingsCard(_SettingsCard): mode = self.comboBox.currentData() if isinstance(mode, str): - SettingsController.setSystemProxyMode(mode) + AppSettingsController().setSystemProxyMode(mode) class _SettingsSection(QWidget): @@ -645,7 +644,7 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): self.tunModeCard = _ToggleSettingsCard( 'shield-check.svg', 'VPNMode', - SettingsController.setTUNMode, + AppSettingsController().setTUNMode, _tunModeTitle(), _('Route system traffic through the active proxy connection.'), ) @@ -661,7 +660,7 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): _ToggleSettingsCard( 'moon-stars.svg', 'DarkMode', - SettingsController.setDarkMode, + AppSettingsController().setDarkMode, _('Dark Mode'), _('Use the application dark theme instead of automatic appearance.'), ), @@ -672,21 +671,21 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): _ToggleSettingsCard( 'circle-half.svg', 'UseMonochromeTrayIcon', - SettingsController.setMonochromeTrayIcon, + AppSettingsController().setMonochromeTrayIcon, _('Use Monochrome Tray Icon'), _('Use a theme-aware single-color system tray icon.'), ), _ToggleSettingsCard( 'power.svg', 'StartupOnBoot', - SettingsController.setStartupOnBoot, + AppSettingsController().setStartupOnBoot, _('Startup On Boot'), _('Start the application automatically after signing in.'), ), _ToggleSettingsCard( 'battery-half.svg', 'PowerSaveMode', - SettingsController.setPowerSaveMode, + AppSettingsController().setPowerSaveMode, _('Power Save Mode'), _('Reduce background activity when the application is idle.'), ), @@ -701,7 +700,7 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): self.hideDockCard = _ToggleSettingsCard( 'window.svg', 'HideDockIcon', - SettingsController.setDockIconHidden, + AppSettingsController().setDockIconHidden, _('Hide Dock Icon'), _('Keep the application available from the menu bar only.'), ) @@ -753,28 +752,28 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): _ToggleSettingsCard( 'pc-display-horizontal.svg', 'ForceToLocalhostWhenSettingLocalProxy', - SettingsController.setForceLocalProxy, + AppSettingsController().setForceLocalProxy, _('Force To 127.0.0.1 When Setting Local Proxy'), _('Use the IPv4 loopback address when configuring the system proxy.'), ), _ToggleSettingsCard( 'hourglass-split.svg', 'ShowProgressBarWhenConnecting', - SettingsController.setConnectionProgressVisible, + AppSettingsController().setConnectionProgressVisible, _('Show Progress Bar When Connecting'), _('Show connection progress while proxy services are starting.'), ), _ToggleSettingsCard( 'speedometer2.svg', METRICS_COLLECTION_SETTING, - SettingsController.setMetricsCollectionEnabled, + AppSettingsController().setMetricsCollectionEnabled, _('Enable Metrics Collection'), _('Collect network speed and traffic history while connected.'), ), _ToggleSettingsCard( 'arrow-repeat.svg', CLEAR_TRAFFIC_USAGE_ON_RECONNECT_SETTING, - SettingsController.setClearTrafficUsageOnReconnect, + AppSettingsController().setClearTrafficUsageOnReconnect, _('Clear Traffic Usage Statistics On Reconnect'), _( 'Start accumulated upload and download usage from zero after reconnecting.' @@ -783,7 +782,7 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): _ToggleSettingsCard( 'text-paragraph.svg', 'ShowTabAndSpacesInEditor', - SettingsController.setEditorWhitespaceVisible, + AppSettingsController().setEditorWhitespaceVisible, _('Show Tab And Spaces In Editor'), _('Display whitespace markers in configuration editors.'), ), @@ -805,7 +804,7 @@ class SettingsPage(Mixins.QTranslatable, QMainWindow): self.autoAssetsCard = _ToggleSettingsCard( 'cloud-arrow-down.svg', 'AutoUpdateAssetFiles', - SettingsController.setAutoUpdateAssets, + AppSettingsController().setAutoUpdateAssets, _('Automatically Update Asset Files'), _('Keep supported proxy-core data files up to date.'), )