mirror of
https://github.com/LorenEteval/Furious.git
synced 2026-09-29 02:18:02 +03:00
Extract reusable settings controller
Signed-off-by: Loren Eteval <loren.eteval@proton.me>
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""Implement tray actions for edit configuration."""
|
||||
"""Provide the tray action that foregrounds the Home page."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -23,20 +23,41 @@ from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
|
||||
__all__ = ['EditConfigurationAction']
|
||||
__all__ = ['ShowHomePageAction']
|
||||
|
||||
|
||||
class EditConfigurationAction(AppQAction):
|
||||
"""Handle the edit configuration action."""
|
||||
class ShowHomePageAction(AppQAction):
|
||||
"""Foreground the main window and navigate to server management."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize the EditConfigurationAction."""
|
||||
"""Initialize the Home-page navigation action."""
|
||||
super().__init__(
|
||||
_('Edit Configuration...'),
|
||||
icon=bootstrapIcon('pencil-square.svg'),
|
||||
_('Show Home Page...'),
|
||||
icon=bootstrapIcon('house-door.svg'),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.setToolTip(_('Show the server management Home page'))
|
||||
self.setStatusTip(_('Show the server management Home page'))
|
||||
|
||||
def triggeredCallback(self, checked):
|
||||
"""Handle activation of the action."""
|
||||
APP().mainWindow.show()
|
||||
mainWindow = APP().mainWindow
|
||||
|
||||
if mainWindow.isMinimized():
|
||||
mainWindow.showNormal()
|
||||
else:
|
||||
mainWindow.show()
|
||||
|
||||
mainWindow.showPage('home')
|
||||
mainWindow.raise_()
|
||||
mainWindow.activateWindow()
|
||||
|
||||
def retranslate(self):
|
||||
"""Refresh the action text and explanatory hover text."""
|
||||
super().retranslate()
|
||||
|
||||
description = _('Show the server management Home page')
|
||||
|
||||
self.setToolTip(description)
|
||||
self.setStatusTip(description)
|
||||
|
||||
@@ -122,10 +122,11 @@ class ConnectAction(AppQAction):
|
||||
APP().systemTray.RoutingAction.setDisabled(value)
|
||||
APP().systemTray.SystemProxyAction.setDisabled(value)
|
||||
|
||||
# TODO: Need this?
|
||||
if PLATFORM == 'Linux' or SystemRuntime.isAdmin():
|
||||
TUNModeAction = APP().systemTray.SettingsAction.getTUNModeAction()
|
||||
TUNModeAction.setDisabled(value)
|
||||
try:
|
||||
APP().mainWindow.settingsPage.setTUNModeControlEnabled(not value)
|
||||
except (AttributeError, RuntimeError):
|
||||
pass
|
||||
|
||||
def isConnected(self) -> bool:
|
||||
"""Return whether connected."""
|
||||
|
||||
+3
-210
@@ -15,177 +15,16 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""Implement tray actions for settings."""
|
||||
"""Provide the compatibility action used by the Settings-page TUN control."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Controllers import SettingsController
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt import *
|
||||
from Furious.Qt import gettext as _
|
||||
from Furious.Service.TrafficStatsManager import (
|
||||
CLEAR_TRAFFIC_USAGE_ON_RECONNECT_SETTING,
|
||||
)
|
||||
|
||||
__all__ = ['SettingsAction', 'SettingsController', 'TUNModeAction']
|
||||
|
||||
registerAppSettings('VPNMode', isBinary=True)
|
||||
registerAppSettings('DarkMode', isBinary=True)
|
||||
registerAppSettings('UseMonochromeTrayIcon', isBinary=True)
|
||||
|
||||
if PLATFORM == 'Darwin':
|
||||
registerAppSettings('HideDockIcon', isBinary=True)
|
||||
|
||||
registerAppSettings('StartupOnBoot', isBinary=True, default=AppBinarySettings.ON_)
|
||||
registerAppSettings('PowerSaveMode', isBinary=True, default=AppBinarySettings.ON_)
|
||||
registerAppSettings(
|
||||
'ForceToLocalhostWhenSettingLocalProxy',
|
||||
isBinary=True,
|
||||
default=AppBinarySettings.OFF,
|
||||
)
|
||||
registerAppSettings(
|
||||
'AutoUpdateAssetFiles', isBinary=True, default=AppBinarySettings.ON_
|
||||
)
|
||||
registerAppSettings(
|
||||
'ShowProgressBarWhenConnecting', isBinary=True, default=AppBinarySettings.ON_
|
||||
)
|
||||
registerAppSettings('ShowTabAndSpacesInEditor', isBinary=True)
|
||||
|
||||
|
||||
class SettingsController:
|
||||
"""Apply application settings independently from their presentation."""
|
||||
|
||||
@staticmethod
|
||||
def setTUNMode(enabled: bool):
|
||||
"""Persist the global TUN mode and notify active workflows."""
|
||||
if PLATFORM != 'Linux':
|
||||
assert SystemRuntime.isAdmin()
|
||||
|
||||
if enabled:
|
||||
AppSettings.turnON_('VPNMode')
|
||||
else:
|
||||
AppSettings.turnOFF('VPNMode')
|
||||
|
||||
showMBoxNewChangesNextTime()
|
||||
|
||||
@staticmethod
|
||||
def setDarkMode(enabled: bool):
|
||||
"""Switch between the explicit dark theme and automatic mode."""
|
||||
if enabled:
|
||||
AppSettings.turnON_('DarkMode')
|
||||
|
||||
try:
|
||||
APP().switchToDarkMode()
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
pass
|
||||
else:
|
||||
AppSettings.turnOFF('DarkMode')
|
||||
|
||||
try:
|
||||
APP().switchToAutoMode()
|
||||
except Exception:
|
||||
# Any non-exit exceptions
|
||||
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def setLanguage(language: str):
|
||||
"""Persist a supported UI language and refresh translated objects."""
|
||||
if language not in SUPPORTED_LANGUAGE:
|
||||
return
|
||||
|
||||
if AppSettings.get('Language') != language:
|
||||
AppSettings.set('Language', language)
|
||||
Mixins.QTranslatable.retranslateAll()
|
||||
|
||||
@staticmethod
|
||||
def setMonochromeTrayIcon(enabled: bool):
|
||||
"""Persist and immediately refresh the tray-icon presentation."""
|
||||
if enabled:
|
||||
AppSettings.turnON_('UseMonochromeTrayIcon')
|
||||
APP().systemTray.setMonochromeIcon()
|
||||
else:
|
||||
AppSettings.turnOFF('UseMonochromeTrayIcon')
|
||||
|
||||
if APP().isSystemTrayConnected():
|
||||
APP().systemTray.setConnectedIcon()
|
||||
else:
|
||||
APP().systemTray.setDisconnectedIcon()
|
||||
|
||||
@staticmethod
|
||||
def setDockIconHidden(enabled: bool):
|
||||
"""Apply the macOS dock-icon visibility preference."""
|
||||
if enabled:
|
||||
APP().installDockIconVisibilityFeature()
|
||||
AppSettings.turnON_('HideDockIcon')
|
||||
else:
|
||||
APP().installDockIconVisibilityFeature(remove=True)
|
||||
AppSettings.turnOFF('HideDockIcon')
|
||||
|
||||
@staticmethod
|
||||
def setStartupOnBoot(enabled: bool):
|
||||
"""Apply the platform startup registration preference."""
|
||||
if enabled:
|
||||
StartupOnBoot.on_()
|
||||
AppSettings.turnON_('StartupOnBoot')
|
||||
else:
|
||||
StartupOnBoot.off()
|
||||
AppSettings.turnOFF('StartupOnBoot')
|
||||
|
||||
@staticmethod
|
||||
def setPowerSaveMode(enabled: bool):
|
||||
"""Persist power-saving behavior for the next connection."""
|
||||
if enabled:
|
||||
AppSettings.turnON_('PowerSaveMode')
|
||||
else:
|
||||
AppSettings.turnOFF('PowerSaveMode')
|
||||
|
||||
showMBoxNewChangesNextTime()
|
||||
|
||||
@staticmethod
|
||||
def setForceLocalProxy(enabled: bool):
|
||||
"""Persist local system-proxy address normalization."""
|
||||
if enabled:
|
||||
AppSettings.turnON_('ForceToLocalhostWhenSettingLocalProxy')
|
||||
else:
|
||||
AppSettings.turnOFF('ForceToLocalhostWhenSettingLocalProxy')
|
||||
|
||||
showMBoxNewChangesNextTime()
|
||||
|
||||
@staticmethod
|
||||
def setAutoUpdateAssets(enabled: bool):
|
||||
"""Persist automatic core-asset updates."""
|
||||
if enabled:
|
||||
AppSettings.turnON_('AutoUpdateAssetFiles')
|
||||
else:
|
||||
AppSettings.turnOFF('AutoUpdateAssetFiles')
|
||||
|
||||
@staticmethod
|
||||
def setConnectionProgressVisible(enabled: bool):
|
||||
"""Persist connection-progress visibility."""
|
||||
if enabled:
|
||||
AppSettings.turnON_('ShowProgressBarWhenConnecting')
|
||||
else:
|
||||
AppSettings.turnOFF('ShowProgressBarWhenConnecting')
|
||||
|
||||
@staticmethod
|
||||
def setClearTrafficUsageOnReconnect(enabled: bool):
|
||||
"""Persist whether reconnecting starts a fresh usage session."""
|
||||
if enabled:
|
||||
AppSettings.turnON_(CLEAR_TRAFFIC_USAGE_ON_RECONNECT_SETTING)
|
||||
else:
|
||||
AppSettings.turnOFF(CLEAR_TRAFFIC_USAGE_ON_RECONNECT_SETTING)
|
||||
|
||||
@staticmethod
|
||||
def setEditorWhitespaceVisible(enabled: bool):
|
||||
"""Apply and persist editor whitespace visibility."""
|
||||
if enabled:
|
||||
APP().mainWindow.showTabAndSpaces()
|
||||
AppSettings.turnON_('ShowTabAndSpacesInEditor')
|
||||
else:
|
||||
APP().mainWindow.hideTabAndSpaces()
|
||||
AppSettings.turnOFF('ShowTabAndSpacesInEditor')
|
||||
__all__ = ['TUNModeAction']
|
||||
|
||||
|
||||
class TUNModeAction(AppQAction):
|
||||
@@ -211,49 +50,3 @@ class TUNModeAction(AppQAction):
|
||||
def triggeredCallback(self, checked):
|
||||
"""Handle activation of the action."""
|
||||
SettingsController.setTUNMode(checked)
|
||||
|
||||
|
||||
class SettingsChildAction(AppQAction):
|
||||
"""Handle the settings child action."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialize the SettingsChildAction."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def triggeredCallback(self, checked):
|
||||
"""Handle activation of the action."""
|
||||
if self.textCompare('Dark Mode'):
|
||||
SettingsController.setDarkMode(checked)
|
||||
elif self.textCompare('Use Monochrome Tray Icon'):
|
||||
SettingsController.setMonochromeTrayIcon(checked)
|
||||
elif self.textCompare('Hide Dock Icon'):
|
||||
SettingsController.setDockIconHidden(checked)
|
||||
elif self.textCompare('Startup On Boot'):
|
||||
SettingsController.setStartupOnBoot(checked)
|
||||
elif self.textCompare('Power Save Mode'):
|
||||
SettingsController.setPowerSaveMode(checked)
|
||||
elif self.textCompare('Force To 127.0.0.1 When Setting Local Proxy'):
|
||||
SettingsController.setForceLocalProxy(checked)
|
||||
elif self.textCompare('Automatically Update Asset Files'):
|
||||
SettingsController.setAutoUpdateAssets(checked)
|
||||
elif self.textCompare('Show Progress Bar When Connecting'):
|
||||
SettingsController.setConnectionProgressVisible(checked)
|
||||
elif self.textCompare('Show Tab And Spaces In Editor'):
|
||||
SettingsController.setEditorWhitespaceVisible(checked)
|
||||
|
||||
|
||||
class SettingsAction(AppQAction):
|
||||
"""Navigate from the system tray to the page-based settings UI."""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize the SettingsAction."""
|
||||
super().__init__(
|
||||
_('Settings'),
|
||||
icon=bootstrapIcon('gear-wide-connected.svg'),
|
||||
callback=lambda: APP().mainWindow.showSettingsPage(),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def getTUNModeAction(self) -> AppQAction:
|
||||
"""Return the Settings page's compatibility TUN action."""
|
||||
return APP().mainWindow.settingsPage.tunModeAction
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from .Configuration import EditConfigurationAction
|
||||
from .Configuration import ShowHomePageAction
|
||||
from .Connection import ConnectAction
|
||||
from .Exit import ExitAction
|
||||
from .Import import (
|
||||
@@ -31,12 +31,11 @@ from .Import import (
|
||||
)
|
||||
from .Language import LanguageAction
|
||||
from .Routing import RoutingAction
|
||||
from .Settings import SettingsAction
|
||||
from .Settings import TUNModeAction
|
||||
from .SystemProxy import SystemProxyAction
|
||||
|
||||
__all__ = [
|
||||
'ConnectAction',
|
||||
'EditConfigurationAction',
|
||||
'ExitAction',
|
||||
'ImportAction',
|
||||
'ImportFromFileAction',
|
||||
@@ -45,6 +44,7 @@ __all__ = [
|
||||
'ImportURIFromClipboardAction',
|
||||
'LanguageAction',
|
||||
'RoutingAction',
|
||||
'SettingsAction',
|
||||
'ShowHomePageAction',
|
||||
'TUNModeAction',
|
||||
'SystemProxyAction',
|
||||
]
|
||||
|
||||
@@ -59,9 +59,7 @@ class TrayIcon(
|
||||
SystemProxyAction(isTrayAction=True),
|
||||
AppQSeperator(),
|
||||
ImportAction(isTrayAction=True),
|
||||
EditConfigurationAction(isTrayAction=True),
|
||||
AppQSeperator(),
|
||||
SettingsAction(isTrayAction=True),
|
||||
ShowHomePageAction(isTrayAction=True),
|
||||
AppQSeperator(),
|
||||
ExitAction(isTrayAction=True),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
# Copyright (C) 2024–present Loren Eteval & contributors <loren.eteval@proton.me>
|
||||
#
|
||||
# This file is part of Furious.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
"""Apply persistent application preferences independently from their UI."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Furious.Frozenlib import *
|
||||
from Furious.Qt.DynamicTranslate import SUPPORTED_LANGUAGE
|
||||
from Furious.Qt.QtWidgets import showMBoxNewChangesNextTime
|
||||
from Furious.Service.TrafficStatsManager import (
|
||||
CLEAR_TRAFFIC_USAGE_ON_RECONNECT_SETTING,
|
||||
METRICS_COLLECTION_SETTING,
|
||||
)
|
||||
|
||||
__all__ = ['SettingsController']
|
||||
|
||||
registerAppSettings('VPNMode', isBinary=True)
|
||||
registerAppSettings('DarkMode', isBinary=True)
|
||||
registerAppSettings('UseMonochromeTrayIcon', isBinary=True)
|
||||
|
||||
if PLATFORM == 'Darwin':
|
||||
registerAppSettings('HideDockIcon', isBinary=True)
|
||||
|
||||
registerAppSettings('StartupOnBoot', isBinary=True, default=AppBinarySettings.ON_)
|
||||
registerAppSettings('PowerSaveMode', isBinary=True, default=AppBinarySettings.ON_)
|
||||
registerAppSettings(
|
||||
'ForceToLocalhostWhenSettingLocalProxy',
|
||||
isBinary=True,
|
||||
default=AppBinarySettings.OFF,
|
||||
)
|
||||
registerAppSettings(
|
||||
'AutoUpdateAssetFiles', isBinary=True, default=AppBinarySettings.ON_
|
||||
)
|
||||
registerAppSettings(
|
||||
'ShowProgressBarWhenConnecting', isBinary=True, default=AppBinarySettings.ON_
|
||||
)
|
||||
registerAppSettings('ShowTabAndSpacesInEditor', isBinary=True)
|
||||
|
||||
|
||||
class SettingsController:
|
||||
"""Apply application settings independently from their presentation."""
|
||||
|
||||
@staticmethod
|
||||
def _setBinary(settingName: str, enabled: bool):
|
||||
"""Persist one registered binary setting."""
|
||||
if enabled:
|
||||
AppSettings.turnON_(settingName)
|
||||
else:
|
||||
AppSettings.turnOFF(settingName)
|
||||
|
||||
@classmethod
|
||||
def setTUNMode(cls, enabled: bool):
|
||||
"""Persist the global TUN mode and notify active workflows."""
|
||||
if PLATFORM != 'Linux':
|
||||
assert SystemRuntime.isAdmin()
|
||||
|
||||
cls._setBinary('VPNMode', enabled)
|
||||
showMBoxNewChangesNextTime()
|
||||
|
||||
@classmethod
|
||||
def setDarkMode(cls, enabled: bool):
|
||||
"""Switch between the explicit dark theme and automatic mode."""
|
||||
cls._setBinary('DarkMode', enabled)
|
||||
|
||||
try:
|
||||
if enabled:
|
||||
APP().switchToDarkMode()
|
||||
else:
|
||||
APP().switchToAutoMode()
|
||||
except Exception:
|
||||
# The controller can be exercised before the full desktop UI exists.
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def setLanguage(language: str):
|
||||
"""Persist a supported UI language and refresh translated objects."""
|
||||
if language not in SUPPORTED_LANGUAGE:
|
||||
return
|
||||
|
||||
if AppSettings.get('Language') != language:
|
||||
AppSettings.set('Language', language)
|
||||
Mixins.QTranslatable.retranslateAll()
|
||||
|
||||
@classmethod
|
||||
def setMonochromeTrayIcon(cls, enabled: bool):
|
||||
"""Persist and immediately refresh the tray-icon presentation."""
|
||||
cls._setBinary('UseMonochromeTrayIcon', enabled)
|
||||
|
||||
try:
|
||||
if enabled:
|
||||
APP().systemTray.setMonochromeIcon()
|
||||
elif APP().isSystemTrayConnected():
|
||||
APP().systemTray.setConnectedIcon()
|
||||
else:
|
||||
APP().systemTray.setDisconnectedIcon()
|
||||
except (AttributeError, RuntimeError):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def setDockIconHidden(cls, enabled: bool):
|
||||
"""Apply the macOS dock-icon visibility preference."""
|
||||
if enabled:
|
||||
APP().installDockIconVisibilityFeature()
|
||||
else:
|
||||
APP().installDockIconVisibilityFeature(remove=True)
|
||||
|
||||
cls._setBinary('HideDockIcon', enabled)
|
||||
|
||||
@classmethod
|
||||
def setStartupOnBoot(cls, enabled: bool):
|
||||
"""Apply the platform startup registration preference."""
|
||||
if enabled:
|
||||
StartupOnBoot.on_()
|
||||
else:
|
||||
StartupOnBoot.off()
|
||||
|
||||
cls._setBinary('StartupOnBoot', enabled)
|
||||
|
||||
@classmethod
|
||||
def setPowerSaveMode(cls, enabled: bool):
|
||||
"""Persist power-saving behavior for the next connection."""
|
||||
cls._setBinary('PowerSaveMode', enabled)
|
||||
showMBoxNewChangesNextTime()
|
||||
|
||||
@classmethod
|
||||
def setForceLocalProxy(cls, enabled: bool):
|
||||
"""Persist local system-proxy address normalization."""
|
||||
cls._setBinary('ForceToLocalhostWhenSettingLocalProxy', enabled)
|
||||
showMBoxNewChangesNextTime()
|
||||
|
||||
@classmethod
|
||||
def setAutoUpdateAssets(cls, enabled: bool):
|
||||
"""Persist automatic core-asset updates."""
|
||||
cls._setBinary('AutoUpdateAssetFiles', enabled)
|
||||
|
||||
@classmethod
|
||||
def setConnectionProgressVisible(cls, enabled: bool):
|
||||
"""Persist connection-progress visibility."""
|
||||
cls._setBinary('ShowProgressBarWhenConnecting', enabled)
|
||||
|
||||
@classmethod
|
||||
def setClearTrafficUsageOnReconnect(cls, enabled: bool):
|
||||
"""Persist whether reconnecting starts a fresh usage session."""
|
||||
cls._setBinary(CLEAR_TRAFFIC_USAGE_ON_RECONNECT_SETTING, enabled)
|
||||
|
||||
@classmethod
|
||||
def setMetricsCollectionEnabled(cls, enabled: bool):
|
||||
"""Persist and immediately apply network metrics collection."""
|
||||
cls._setBinary(METRICS_COLLECTION_SETTING, enabled)
|
||||
|
||||
try:
|
||||
APP().mainWindow.trafficStatsManager.setCollectionEnabled(enabled)
|
||||
except (AttributeError, RuntimeError):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def setEditorWhitespaceVisible(cls, enabled: bool):
|
||||
"""Apply and persist editor whitespace visibility."""
|
||||
cls._setBinary('ShowTabAndSpacesInEditor', enabled)
|
||||
|
||||
try:
|
||||
if enabled:
|
||||
APP().mainWindow.showTabAndSpaces()
|
||||
else:
|
||||
APP().mainWindow.hideTabAndSpaces()
|
||||
except (AttributeError, RuntimeError):
|
||||
pass
|
||||
@@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2024–present Loren Eteval & contributors <loren.eteval@proton.me>
|
||||
#
|
||||
# This file is part of Furious.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
"""Expose presentation-independent application controllers."""
|
||||
|
||||
from .SettingsController import SettingsController
|
||||
|
||||
__all__ = ['SettingsController']
|
||||
Reference in New Issue
Block a user