diff --git a/NUSGet.py b/NUSGet.py index 9fadf48..7a3371e 100644 --- a/NUSGet.py +++ b/NUSGet.py @@ -32,7 +32,7 @@ from qt.py.ui_MainMenu import Ui_MainWindow from modules.config import * from modules.core import * from modules.language import * -from modules.theme import is_dark_theme +from modules.theme import * from modules.tree import NUSGetTreeModel, TIDFilterProxyModel from modules.download_batch import run_nus_download_batch from modules.download_wii import run_nus_download_wii @@ -135,9 +135,38 @@ class MainWindow(QMainWindow, Ui_MainWindow): # languages at once makes no sense. language_group = QActionGroup(self) language_group.setExclusive(True) + current_language = "" + try: + current_language = config_data["language"] + except KeyError: + pass for action in self.ui.menu_options_language.actions(): language_group.addAction(action) - language_group.triggered.connect(lambda lang=action: set_language(config_data, lang.text())) + if current_language != "": + if LANGS[current_language] == action.text(): + action.setChecked(True) + # There is no language set, so check the system language option. + if current_language == "": + self.ui.action_language_system.setChecked(True) + language_group.triggered.connect(lambda lang=action: self.change_language(lang.text())) + # Another QActionGroup used for the theme selector. + theme_group = QActionGroup(self) + theme_group.setExclusive(True) + for action in self.ui.menu_options_theme.actions(): + theme_group.addAction(action) + self.ui.action_theme_system.triggered.connect(lambda: self.change_theme("system")) + self.ui.action_theme_light.triggered.connect(lambda: self.change_theme("light")) + self.ui.action_theme_dark.triggered.connect(lambda: self.change_theme("dark")) + try: + match config_data["theme"]: + case "light": + self.ui.action_theme_light.setChecked(True) + case "dark": + self.ui.action_theme_dark.setChecked(True) + case _: + self.ui.action_theme_system.setChecked(True) + except KeyError: + pass # --------- # Help Menu # --------- @@ -146,7 +175,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.ui.action_about.triggered.connect(self.about_nusget) self.ui.action_about_qt.triggered.connect(lambda: QMessageBox.aboutQt(self)) # Save some light/dark theme values for later, including the appropriately colored info icon. - if is_dark_theme(): + if is_dark_theme(config_data): bg_color = "#2b2b2b" icon = QIcon(os.path.join(os.path.dirname(__file__), "resources", "information_white.svg")) else: @@ -622,6 +651,26 @@ class MainWindow(QMainWindow, Ui_MainWindow): about_box = AboutNUSGet([nusget_version, version("libWiiPy"), version("libTWLPy")]) about_box.exec() + def change_language(self, new_lang): + set_language(config_data, new_lang) + msg_box = QMessageBox() + msg_box.setIcon(QMessageBox.Icon.Warning) + msg_box.setStandardButtons(QMessageBox.StandardButton.Ok) + msg_box.setDefaultButton(QMessageBox.StandardButton.Ok) + msg_box.setWindowTitle(app.translate("MainWindow", "Restart Required")) + msg_box.setText(app.translate("MainWindow", "NUSGet must be restarted for the new language to take effect.")) + msg_box.exec() + + def change_theme(self, new_theme): + set_theme(config_data, new_theme) + msg_box = QMessageBox() + msg_box.setIcon(QMessageBox.Icon.Warning) + msg_box.setStandardButtons(QMessageBox.StandardButton.Ok) + msg_box.setDefaultButton(QMessageBox.StandardButton.Ok) + msg_box.setWindowTitle(app.translate("MainWindow", "Restart Required")) + msg_box.setText(app.translate("MainWindow", "NUSGet must be restarted for the new theme to take effect.")) + msg_box.exec() + if __name__ == "__main__": app = QApplication(sys.argv) @@ -684,7 +733,7 @@ if __name__ == "__main__": # NUSGet look nice and pretty. app.setStyle("fusion") theme_sheet = "style_dark.qss" - if is_dark_theme(): + if is_dark_theme(config_data): theme_sheet = "style_dark.qss" else: theme_sheet = "style_light.qss" diff --git a/modules/language.py b/modules/language.py index c246b52..a9259c6 100644 --- a/modules/language.py +++ b/modules/language.py @@ -6,6 +6,18 @@ from modules.config import update_setting from PySide6.QtCore import QLocale, QTranslator +LANGS = { + "en": "English", + "es": "Español", + "de": "Deutsch", + "fr": "Français", + "it": "Italiano", + "no": "Norsk", + "ro": "Românǎ", + "ko": "한국어", +} + + def set_language(config_data: dict, lang: str) -> None: # Match the selected language. These names will NOT be translated since they represent each language in that # language, but the "System (Default)" option will, so that will match the default case. @@ -46,6 +58,9 @@ def get_language(translator: QTranslator, config_data: dict, path: str) -> QTran lang = "" # A specific language was set in the app's settings. if lang != "": + # If the target language is English, then return an empty translator because that's the default. + if lang == "en": + return translator if translator.load(QLocale(lang), 'nusget', '_', path): return translator else: diff --git a/modules/theme.py b/modules/theme.py index b210fac..68357aa 100644 --- a/modules/theme.py +++ b/modules/theme.py @@ -5,6 +5,9 @@ import os import platform import subprocess +from modules.config import update_setting + + def is_dark_theme_windows(): # This has to be here so that Python doesn't try to import it on non-Windows. import winreg @@ -17,6 +20,7 @@ def is_dark_theme_windows(): except Exception: return False + def is_dark_theme_macos(): # macOS is weird. If the dark theme is on, then `defaults read -g AppleInterfaceStyle` returns "Dark". If the light # theme is on, then trying to read this key fails and returns an error instead. @@ -29,6 +33,7 @@ def is_dark_theme_macos(): except Exception: return False + def is_dark_theme_linux(): try: import subprocess @@ -43,7 +48,14 @@ def is_dark_theme_linux(): except Exception: return False -def is_dark_theme(): + +def is_dark_theme(config_data: dict): + # Theming priority order: + # 1. `THEME` environment variable + # 2. Theme saved in config.json + # 3. The system theme + # 4. Light, if all else fails (though personally I'd prefer dark) + # # First, check for an environment variable overriding the theme, and use that if it exists. try: if os.environ["THEME"].lower() == "light": @@ -54,7 +66,18 @@ def is_dark_theme(): print(f"Unknown theme specified: \"{os.environ['THEME']}\"") except KeyError: pass - # If the theme wasn't overridden, then check the current system theme. + # If the theme wasn't overridden, read the user's preference in config.json. + try: + match config_data["theme"]: + case "light": + return False + case "dark": + return True + case _: + pass + except KeyError: + pass + # If a theme wasn't set (or the theme is "system"), then check for the system theme. system = platform.system() if system == "Windows": return is_dark_theme_windows() @@ -62,3 +85,16 @@ def is_dark_theme(): return is_dark_theme_macos() else: return is_dark_theme_linux() + + +def set_theme(config_data: dict, theme: str) -> None: + match theme: + case "light": + print("setting theme to light") + update_setting(config_data, "theme", "light") + case "dark": + print("setting theme to dark") + update_setting(config_data, "theme", "dark") + case _: + print("setting theme to system (default)") + update_setting(config_data, "theme", "") diff --git a/qt/py/ui_MainMenu.py b/qt/py/ui_MainMenu.py index f731a80..3f33184 100644 --- a/qt/py/ui_MainMenu.py +++ b/qt/py/ui_MainMenu.py @@ -68,6 +68,15 @@ class Ui_MainWindow(object): self.action_language_korean = QAction(MainWindow) self.action_language_korean.setObjectName(u"action_language_korean") self.action_language_korean.setCheckable(True) + self.action_theme_system = QAction(MainWindow) + self.action_theme_system.setObjectName(u"action_theme_system") + self.action_theme_system.setCheckable(True) + self.action_theme_light = QAction(MainWindow) + self.action_theme_light.setObjectName(u"action_theme_light") + self.action_theme_light.setCheckable(True) + self.action_theme_dark = QAction(MainWindow) + self.action_theme_dark.setObjectName(u"action_theme_dark") + self.action_theme_dark.setCheckable(True) self.centralwidget = QWidget(MainWindow) self.centralwidget.setObjectName(u"centralwidget") self.horizontalLayout_3 = QHBoxLayout(self.centralwidget) @@ -354,13 +363,15 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 1010, 30)) + self.menubar.setGeometry(QRect(0, 0, 1010, 21)) self.menu_help = QMenu(self.menubar) self.menu_help.setObjectName(u"menu_help") self.menu_options = QMenu(self.menubar) self.menu_options.setObjectName(u"menu_options") self.menu_options_language = QMenu(self.menu_options) self.menu_options_language.setObjectName(u"menu_options_language") + self.menu_options_theme = QMenu(self.menu_options) + self.menu_options_theme.setObjectName(u"menu_options_theme") MainWindow.setMenuBar(self.menubar) self.menubar.addAction(self.menu_options.menuAction()) @@ -369,6 +380,7 @@ class Ui_MainWindow(object): self.menu_help.addAction(self.action_about_qt) self.menu_help.addSeparator() self.menu_options.addAction(self.menu_options_language.menuAction()) + self.menu_options.addAction(self.menu_options_theme.menuAction()) self.menu_options_language.addAction(self.action_language_system) self.menu_options_language.addAction(self.action_language_english) self.menu_options_language.addAction(self.action_language_spanish) @@ -378,6 +390,9 @@ class Ui_MainWindow(object): self.menu_options_language.addAction(self.action_language_norwegian) self.menu_options_language.addAction(self.action_language_romanian) self.menu_options_language.addAction(self.action_language_korean) + self.menu_options_theme.addAction(self.action_theme_system) + self.menu_options_theme.addAction(self.action_theme_light) + self.menu_options_theme.addAction(self.action_theme_dark) self.retranslateUi(MainWindow) @@ -401,6 +416,9 @@ class Ui_MainWindow(object): self.action_language_norwegian.setText(QCoreApplication.translate("MainWindow", u"Norsk", None)) self.action_language_romanian.setText(QCoreApplication.translate("MainWindow", u"Rom\u00e2n\u0103", None)) self.action_language_korean.setText(QCoreApplication.translate("MainWindow", u"\ud55c\uad6d\uc5b4", None)) + self.action_theme_system.setText(QCoreApplication.translate("MainWindow", u"System (Default)", None)) + self.action_theme_light.setText(QCoreApplication.translate("MainWindow", u"Light", None)) + self.action_theme_dark.setText(QCoreApplication.translate("MainWindow", u"Dark", None)) self.tree_filter_input.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Search", None)) self.tree_filter_reset_btn.setText(QCoreApplication.translate("MainWindow", u"Clear", None)) self.platform_tabs.setTabText(self.platform_tabs.indexOf(self.wii_tab), QCoreApplication.translate("MainWindow", u"Wii", None)) @@ -427,10 +445,11 @@ class Ui_MainWindow(object): "hr { height: 1px; border-width: 0; }\n" "li.unchecked::marker { content: \"\\2610\"; }\n" "li.checked::marker { content: \"\\2612\"; }\n" -"\n" +"\n" "


", None)) self.menu_help.setTitle(QCoreApplication.translate("MainWindow", u"Help", None)) self.menu_options.setTitle(QCoreApplication.translate("MainWindow", u"Options", None)) self.menu_options_language.setTitle(QCoreApplication.translate("MainWindow", u"Language", None)) + self.menu_options_theme.setTitle(QCoreApplication.translate("MainWindow", u"Theme", None)) # retranslateUi diff --git a/qt/ui/MainMenu.ui b/qt/ui/MainMenu.ui index cd7c557..e8f38ce 100755 --- a/qt/ui/MainMenu.ui +++ b/qt/ui/MainMenu.ui @@ -435,7 +435,7 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html> @@ -469,7 +469,7 @@ li.checked::marker { content: "\2612"; } 0 0 1010 - 30 + 21 @@ -498,7 +498,16 @@ li.checked::marker { content: "\2612"; } + + + Theme + + + + + + @@ -600,6 +609,30 @@ li.checked::marker { content: "\2612"; } 한국어 + + + true + + + System (Default) + + + + + true + + + Light + + + + + true + + + Dark + +