From 2737936739a870c5792262e6e8d3c255f82026dd Mon Sep 17 00:00:00 2001 From: kWAY <55437151+kWAYTV@users.noreply.github.com> Date: Wed, 27 Aug 2025 17:19:32 +0200 Subject: [PATCH] Implement dark mode theme (#744) Co-authored-by: DevilXD <4180725+DevilXD@users.noreply.github.com> --- .github/workflows/ci.yml | 14 ++- README.md | 1 + gui.py | 240 +++++++++++++++++++++++++++++++++++++-- lang/Dansk.json | 1 + lang/Deutsch.json | 1 + lang/Español.json | 1 + lang/Français.json | 1 + lang/Indonesian.json | 1 + lang/Italiano.json | 1 + lang/Nederlandse.json | 1 + lang/Polski.json | 1 + lang/Português.json | 2 + lang/Română.json | 1 + lang/Türkçe.json | 1 + lang/Čeština.json | 1 + lang/Русский.json | 1 + lang/Українська.json | 1 + lang/العربية.json | 1 + lang/日本語.json | 1 + lang/简体中文.json | 1 + lang/繁體中文.json | 3 +- settings.py | 3 + translate.py | 2 + 23 files changed, 265 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86d166a..c134148 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,12 @@ name: Build on: push: branches: - - 'master' + - "master" pull_request: workflow_dispatch: env: - PYTHON_VERSION: '3.10' + PYTHON_VERSION: "3.10" jobs: validate: @@ -267,8 +267,14 @@ jobs: with: path: artifacts - - name: Delete the existing pre-release - run: gh release delete dev-build --cleanup-tag --yes --repo $GITHUB_REPOSITORY + - name: Delete the existing pre-release (if present) + run: | + TAG=dev-build + if gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then + gh release delete "$TAG" --cleanup-tag --yes --repo "$GITHUB_REPOSITORY" + else + echo "No $TAG release; skipping delete" + fi env: GITHUB_TOKEN: ${{github.token}} diff --git a/README.md b/README.md index 58c6ec7..5b03b97 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ placed past the period character at the end. --> @guihkx - For the CI script, CI maintenance, and everything related to Linux builds. +@kWAYTV - For the implementation of the dark mode theme. @Bamboozul - For the entirety of the Arabic (العربية) translation. @Suz1e - For the entirety of the Chinese (简体中文) translation and revisions. diff --git a/gui.py b/gui.py index 95b2e4c..8d68edb 100644 --- a/gui.py +++ b/gui.py @@ -264,6 +264,10 @@ class PaddedListbox(tk.Listbox): # because 'config = configure' makes mypy complain self.configure(*args, **kwargs) + def configure_theme(self, *, bg: str, fg: str, sel_bg: str, sel_fg: str): + # Apply basic colors for dark/light mode + super().config(bg=bg, fg=fg, selectbackground=sel_bg, selectforeground=sel_fg) + class MouseOverLabel(ttk.Label): def __init__(self, *args, alt_text: str = '', reverse: bool = False, **kwargs) -> None: @@ -819,6 +823,16 @@ class ConsoleOutput: self._text.see("end") # scroll to the newly added line self._text.config(state="disabled") + def configure_theme(self, *, bg: str, fg: str, sel_bg: str, sel_fg: str): + # Apply colors to the Tk Text widget used for console output + self._text.config( + bg=bg, + fg=fg, + insertbackground=fg, + selectbackground=sel_bg, + selectforeground=sel_fg, + ) + class _Buttons(TypedDict): frame: ttk.Frame @@ -1291,6 +1305,10 @@ class InventoryOverview: self._campaigns: dict[DropsCampaign, CampaignDisplay] = {} self._drops: dict[str, ttk.Label] = {} + def configure_theme(self, *, bg: str): + # Canvas background needs manual control + self._canvas.configure(bg=bg) + def _update_visibility(self, campaign: DropsCampaign): # True if the campaign is supposed to show, False makes it hidden. frame = self._campaigns[campaign]["frame"] @@ -1528,6 +1546,7 @@ class _SettingsVars(TypedDict): tray: IntVar proxy: StringVar autostart: IntVar + dark_mode: IntVar language: StringVar priority_mode: StringVar tray_notifications: IntVar @@ -1550,7 +1569,7 @@ class SettingsPanel: } def __init__(self, manager: GUIManager, master: ttk.Widget): - self._twitch = manager._twitch + self._manager = manager self._settings: Settings = manager._twitch.settings priority_mode = self._settings.priority_mode if priority_mode not in self.PRIORITY_MODES: @@ -1561,6 +1580,7 @@ class SettingsPanel: "language": StringVar(master, _.current), "proxy": StringVar(master, str(self._settings.proxy)), "tray": IntVar(master, self._settings.autostart_tray), + "dark_mode": IntVar(master, int(self._settings.dark_mode)), "priority_mode": StringVar(master, self.PRIORITY_MODES[priority_mode]), "tray_notifications": IntVar(master, self._settings.tray_notifications), } @@ -1616,6 +1636,14 @@ class SettingsPanel: variable=self._vars["tray_notifications"], command=self.update_notifications, ).grid(column=1, row=irow, sticky="w") + ttk.Label( + checkboxes_frame, text=_("gui", "settings", "general", "dark_mode") + ).grid(column=0, row=(irow := irow + 1), sticky="e") + ttk.Checkbutton( + checkboxes_frame, + variable=self._vars["dark_mode"], + command=self.update_dark_mode, + ).grid(column=1, row=irow, sticky="w") ttk.Label( checkboxes_frame, text=_("gui", "settings", "general", "priority_mode") ).grid(column=0, row=(irow := irow + 1), sticky="e") @@ -1719,7 +1747,7 @@ class SettingsPanel: ttk.Button( reload_frame, text=_("gui", "settings", "reload"), - command=self._twitch.state_change(State.INVENTORY_FETCH), + command=self._manager._twitch.state_change(State.INVENTORY_FETCH), ).grid(column=1, row=0) self._vars["autostart"].set(self._query_autostart()) @@ -1728,6 +1756,11 @@ class SettingsPanel: self._priority_list.selection_clear(0, "end") self._exclude_list.selection_clear(0, "end") + def update_dark_mode(self) -> None: + self._settings.dark_mode = bool(self._vars["dark_mode"].get()) + self._settings.alter() + self._manager.apply_theme(self._settings.dark_mode) + def update_notifications(self) -> None: self._settings.tray_notifications = bool(self._vars["tray_notifications"].get()) @@ -2025,7 +2058,6 @@ class GUIManager: # style adjustements self._style = style = ttk.Style(root) - default_font = nametofont("TkDefaultFont") # theme theme = '' # theme = style.theme_names()[6] @@ -2057,17 +2089,23 @@ class GUIManager: style.configure("green.TLabel", foreground="green") style.configure("yellow.TLabel", foreground="goldenrod") style.configure("red.TLabel", foreground="red") + # fonts + default_font = nametofont("TkDefaultFont") + self._fonts: dict[str, Font] = { + "default": default_font, + "large": default_font.copy(), + "monospaced": default_font.copy(), + "underlined": default_font.copy(), + } + self._fonts["large"].config(size=10) + self._fonts["underlined"].config(underline=True) + self._fonts["monospaced"].config(family="Courier New", size=10) # label style with a monospace font - monospaced_font = Font(root, family="Courier New", size=10) - style.configure("MS.TLabel", font=monospaced_font) + style.configure("MS.TLabel", font=self._fonts["monospaced"]) # button style with a larger font - large_font = default_font.copy() - large_font.config(size=10) - style.configure("Large.TButton", font=large_font) + style.configure("Large.TButton", font=self._fonts["large"]) # label style that mimics links - link_font = default_font.copy() - link_font.config(underline=True) - style.configure("Link.TLabel", font=link_font, foreground="blue") + style.configure("Link.TLabel", font=self._fonts["underlined"], foreground="blue") # end of style changes root_frame = ttk.Frame(root, padding=8) @@ -2133,6 +2171,12 @@ class GUIManager: # use old-style window closing protocol for non-windows platforms root.protocol("WM_DELETE_WINDOW", self.close) root.protocol("WM_DESTROY_WINDOW", self.close) + # Save current theme and apply palette after widgets are created + try: + self._orig_theme_name = self._style.theme_use() + except Exception: + self._orig_theme_name = '' + self.apply_theme(self._twitch.settings.dark_mode) # stay hidden in tray if needed, otherwise show the window when everything's ready if self._twitch.settings.tray: # NOTE: this starts the tray icon thread @@ -2277,6 +2321,179 @@ class GUIManager: # print to our custom output self.output.print(message) + def apply_theme(self, dark: bool) -> None: + """ + Apply dark/light palette to ttk styles and Tk widgets in a minimal, non-invasive way. + """ + # Palette + if dark: + # Switch to a configurable ttk theme for better color control + if self._style.theme_use() != "clam": + self._style.theme_use("clam") + bg = "#1e1e1e" + fg = "#e6e6e6" + sel_bg = "#094771" + sel_fg = "#ffffff" + link = "#4ea3ff" + surface = "#252525" + header = "#2a2a2a" + fieldbg = "#2b2b2b" + border = "#3c3c3c" + muted = "#b3b3b3" + accent = "#0d99ff" + else: + # Restore original theme if we changed it + if getattr(self, "_orig_theme_name", '') and self._style.theme_use() == "clam": + self._style.theme_use(self._orig_theme_name) + # Use platform defaults but ensure toggling back is readable + bg = "#f0f0f0" + fg = "#000000" + sel_bg = "#cce5ff" + sel_fg = "#000000" + link = "blue" + surface = "#ffffff" + header = "#eeeeee" + fieldbg = "#ffffff" + border = "#cccccc" + muted = "#404040" + accent = "#0a84ff" + + s = self._style + # Base containers and labels + s.configure("TFrame", background=bg, foreground=fg) + s.configure("TLabel", background=bg, foreground=fg) + s.configure("TLabelframe", background=bg, foreground=fg) + s.configure("TLabelframe.Label", background=bg, foreground=fg) + s.configure("MS.TLabel", background=bg, foreground=fg) + s.configure("green.TLabel", background=bg) + s.configure("yellow.TLabel", background=bg) + s.configure("red.TLabel", background=bg) + s.configure("Link.TLabel", font=self._fonts["underlined"], background=bg, foreground=link) + # Buttons and checks + s.configure("TButton", background=surface, foreground=fg, bordercolor=border) + s.configure("Large.TButton", background=surface, foreground=fg, bordercolor=border) + s.map( + "TButton", + background=[("active", header), ("pressed", border)], + foreground=[("disabled", muted)], + ) + s.configure( + "TCheckbutton", + background=bg, + foreground=fg, + focuscolor=bg, + bordercolor=border, + ) + s.map( + "TCheckbutton", + # Remove hover visuals by mapping active/pressed to the base background + background=[ + ("active", bg), + ("pressed", bg), + ], + foreground=[("disabled", muted)], + indicatorcolor=[ + ("selected", accent if dark else fg), + ("!selected", border), + ], + ) + # Notebook + s.configure("TNotebook", background=bg, bordercolor=border) + s.configure("TNotebook.Tab", background=surface, foreground=fg, bordercolor=border) + s.map( + "TNotebook.Tab", + background=[("selected", header), ("active", header)], + foreground=[("disabled", muted)], + ) + # Entries/Combos + s.configure( + "TEntry", fieldbackground=fieldbg, background=fieldbg, foreground=fg, insertcolor=fg + ) + s.configure( + "TCombobox", fieldbackground=fieldbg, background=fieldbg, foreground=fg, arrowcolor=fg + ) + # Ensure readability for readonly comboboxes (Language, Priority mode) + s.map( + "TCombobox", + foreground=[("readonly", fg), ("disabled", muted)], + fieldbackground=[("readonly", fieldbg)], + background=[("readonly", fieldbg)], + arrowcolor=[("readonly", fg)], + ) + s.map("TEntry", foreground=[("disabled", muted)]) + # Treeview + s.configure( + "Treeview", + background=surface, + fieldbackground=surface, + foreground=fg, + bordercolor=border, + ) + s.map( + "Treeview", + background=[("selected", sel_bg)], + foreground=[("selected", sel_fg)], + ) + s.configure("Treeview.Heading", background=header, foreground=fg, bordercolor=border) + # Progressbar + s.configure("TProgressbar", background=accent, troughcolor=surface) + # Scrollbars + s.configure( + "Vertical.TScrollbar", + background=surface, + troughcolor=bg, + arrowcolor=fg, + bordercolor=border, + ) + s.configure( + "Horizontal.TScrollbar", + background=surface, + troughcolor=bg, + arrowcolor=fg, + bordercolor=border, + ) + + # Pure Tk widgets + # Console text + self.output.configure_theme(bg=surface, fg=fg, sel_bg=sel_bg, sel_fg=sel_fg) + # Listboxes + self.settings._priority_list.configure_theme( + bg=surface, fg=fg, sel_bg=sel_bg, sel_fg=sel_fg + ) + self.settings._exclude_list.configure_theme( + bg=surface, fg=fg, sel_bg=sel_bg, sel_fg=sel_fg + ) + # Inventory canvas + self.inv.configure_theme(bg=bg) + + # Tk option database for selection/popup list readability (affects Tk-backed widgets) + # Global selection colors and listbox defaults (covers Combobox dropdown) + self._root.option_add("*selectBackground", sel_bg) + self._root.option_add("*selectForeground", sel_fg) + # Combobox dropdown list (Tk Listbox) + for key in ( + "*TCombobox*Listbox.background", + "*TCombobox*Listbox.Background", + "*Listbox.background", + ): + self._root.option_add(key, surface) + for key in ( + "*TCombobox*Listbox.foreground", + "*TCombobox*Listbox.Foreground", + "*Listbox.foreground", + ): + self._root.option_add(key, fg) + for key in ( + "*TCombobox*Listbox.selectBackground", + "*Listbox.selectBackground", + ): + self._root.option_add(key, sel_bg) + for key in ( + "*TCombobox*Listbox.selectForeground", + "*Listbox.selectForeground", + ): + self._root.option_add(key, sel_fg) + ################### # GUI MANAGER END # @@ -2403,6 +2620,7 @@ if __name__ == "__main__": tray=False, priority=[], proxy=URL(), + dark_mode=False, alter=lambda: None, language="English", autostart_tray=False, diff --git a/lang/Dansk.json b/lang/Dansk.json index cab0cc3..2c46383 100644 --- a/lang/Dansk.json +++ b/lang/Dansk.json @@ -135,6 +135,7 @@ "tray": "Automatisk start i systembakken: ", "tray_notifications": "Bakkemeddelelser: ", "priority_only": "Kun prioritet: ", + "dark_mode": "Mørk tilstand: ", "proxy": "Proxy (kræver genstart):" }, "game_name": "Spilnavn", diff --git a/lang/Deutsch.json b/lang/Deutsch.json index 3b09b06..08fd982 100644 --- a/lang/Deutsch.json +++ b/lang/Deutsch.json @@ -134,6 +134,7 @@ "autostart": "Autostart: ", "tray": "Autostart ins System Tray: ", "priority_only": "Nur Priorität: ", + "dark_mode": "Dunkler Modus: ", "proxy": "Proxy (Erfordert Neustart):" }, "game_name": "Spiel", diff --git a/lang/Español.json b/lang/Español.json index 55bccec..df5b560 100644 --- a/lang/Español.json +++ b/lang/Español.json @@ -134,6 +134,7 @@ "autostart": "Ejecutar al iniciar el sistema: ", "tray": "Ejecutar en la bandeja del sistema: ", "tray_notifications": "Mostrar notificaciones: ", + "dark_mode": "Modo oscuro: ", "priority_mode": "Modo de prioridad: ", "proxy": "Proxy (requiere reinicio):" }, diff --git a/lang/Français.json b/lang/Français.json index 7471e22..bb04f2d 100644 --- a/lang/Français.json +++ b/lang/Français.json @@ -134,6 +134,7 @@ "autostart": "Démarrage automatique :", "tray": "Démarrage automatique dans la barre des tâches :", "tray_notifications": "Notifications dans la barre des tâches :", + "dark_mode": "Mode sombre : ", "priority_mode": "Mode de priorité :", "proxy": "Proxy (nécessite un redémarrage) :" }, diff --git a/lang/Indonesian.json b/lang/Indonesian.json index 8d7dda0..40fc7d4 100644 --- a/lang/Indonesian.json +++ b/lang/Indonesian.json @@ -134,6 +134,7 @@ "autostart": "Mulai secara otomatis :", "tray": "Mulai otomatis di bilah tugas :", "tray_notifications": "Pemberitahuan bilah tugas :", + "dark_mode": "Mode gelap: ", "priority_only": "Prioritas saja :", "proxy": "Proksi (harus dimulai ulang) :" }, diff --git a/lang/Italiano.json b/lang/Italiano.json index ec670fd..28b4eaa 100644 --- a/lang/Italiano.json +++ b/lang/Italiano.json @@ -134,6 +134,7 @@ "autostart": "Avvio automatico: ", "tray": "Avvio automatico nella barra delle applicazioni: ", "tray_notifications": "Notifiche: ", + "dark_mode": "Modalità scura: ", "priority_only": "Solo priorità: ", "proxy": "Proxy (richiede il riavvio):" }, diff --git a/lang/Nederlandse.json b/lang/Nederlandse.json index 695e6b3..3a62793 100644 --- a/lang/Nederlandse.json +++ b/lang/Nederlandse.json @@ -134,6 +134,7 @@ "autostart": "Autostart: ", "tray": "Automatisch starten in het taakbalk: ", "priority_only": "Alleen prioriteit: ", + "dark_mode": "Donkere modus: ", "proxy": "Proxy (Vereist opnieuw opstarten):" }, "game_name": "Spel", diff --git a/lang/Polski.json b/lang/Polski.json index b36095f..fca5c4f 100644 --- a/lang/Polski.json +++ b/lang/Polski.json @@ -134,6 +134,7 @@ "autostart": "Autostart: ", "tray": "Autostart z zasobnika: ", "tray_notifications": "Powiadomienia z zasobnika: ", + "dark_mode": "Tryb ciemny: ", "priority_mode": "Tryb priorytetu: ", "proxy": "Proxy (wymaga restartu):" }, diff --git a/lang/Português.json b/lang/Português.json index 0c0a9d3..2a9b1a2 100644 --- a/lang/Português.json +++ b/lang/Português.json @@ -133,6 +133,8 @@ "name": "Geral", "autostart": "Auto-início: ", "tray": "Auto-iniciar na área de notificação: ", + "tray_notifications": "Notificações: ", + "dark_mode": "Modo escuro: ", "priority_only": "Apenas com prioridade: ", "proxy": "Proxy (requer o reinício do app):" }, diff --git a/lang/Română.json b/lang/Română.json index 2d23096..db964cf 100644 --- a/lang/Română.json +++ b/lang/Română.json @@ -134,6 +134,7 @@ "autostart": "Pornire automată :", "tray": "Pornire automată în bara de activități :", "tray_notifications": "Notificări în bara de activități :", + "dark_mode": "Mod întunecat :", "priority_only": "Doar cu prioritate :", "proxy": "Proxy (necesită repornire) :" }, diff --git a/lang/Türkçe.json b/lang/Türkçe.json index b426a9d..94dc578 100644 --- a/lang/Türkçe.json +++ b/lang/Türkçe.json @@ -134,6 +134,7 @@ "autostart": "Otomatik başlatma: ", "tray": "Sistem tepsisine otomatik başlatma: ", "priority_only": "Yalnızca öncelik: ", + "dark_mode": "Karanlık mod: ", "proxy": "Proxy (Yeniden başlatma gerektirir):" }, "game_name": "Oyun", diff --git a/lang/Čeština.json b/lang/Čeština.json index 7b39c24..d5de4a0 100644 --- a/lang/Čeština.json +++ b/lang/Čeština.json @@ -127,6 +127,7 @@ "autostart": "Automatické spuštění: ", "tray": "Automaticky spusti minimalizovaně: ", "priority_only": "Pouze prioritní: ", + "dark_mode": "Tmavý režim: ", "proxy": "Proxy:" }, "game_name": "Název Hry", diff --git a/lang/Русский.json b/lang/Русский.json index f06c6b2..b8c242b 100644 --- a/lang/Русский.json +++ b/lang/Русский.json @@ -134,6 +134,7 @@ "autostart": "Автозапуск: ", "tray": "Автозапуск свёрнутым: ", "tray_notifications": "Всплывающие уведомления: ", + "dark_mode": "Тёмный режим: ", "priority_mode": "Режим приоритета: ", "proxy": "Прокси (Требуется перезапуск):" }, diff --git a/lang/Українська.json b/lang/Українська.json index fe9c7c5..4774a06 100644 --- a/lang/Українська.json +++ b/lang/Українська.json @@ -134,6 +134,7 @@ "autostart": "Автозапуск: ", "tray": "Автозапуск у треї: ", "tray_notifications": "Сповіщення: ", + "dark_mode": "Темний режим: ", "priority_mode": "Режим пріоритету: ", "proxy": "Проксі (потребує перезапуску):" }, diff --git a/lang/العربية.json b/lang/العربية.json index efa0fe5..762b9d8 100644 --- a/lang/العربية.json +++ b/lang/العربية.json @@ -134,6 +134,7 @@ "autostart": "تشغيل تلقائي ", "tray": "تشغيل تلقائي في الدرج ", "tray_notifications": "إشعارات الدرج ", + "dark_mode": "الوضع الداكن: ", "priority_only": "الأولوية فقط ", "proxy": "بروكسي (يتطلب إعادة التشغيل) " }, diff --git a/lang/日本語.json b/lang/日本語.json index 378833d..a1c18bf 100644 --- a/lang/日本語.json +++ b/lang/日本語.json @@ -134,6 +134,7 @@ "autostart": "自動起動:", "tray": "トレイに自動起動:", "tray_notifications": "トレイ通知:", + "dark_mode": "ダークモード:", "priority_only": "優先のみ:", "proxy": "プロキシ(再起動が必要):" }, diff --git a/lang/简体中文.json b/lang/简体中文.json index 448d387..cd78d75 100644 --- a/lang/简体中文.json +++ b/lang/简体中文.json @@ -134,6 +134,7 @@ "autostart": "开机自启: ", "tray": "开机自启并最小化: ", "tray_notifications": "启用托盘通知: ", + "dark_mode": "深色模式: ", "priority_mode": "掉宝模式: ", "proxy": "代理:" }, diff --git a/lang/繁體中文.json b/lang/繁體中文.json index 6358f4a..83a2e8d 100644 --- a/lang/繁體中文.json +++ b/lang/繁體中文.json @@ -134,6 +134,7 @@ "autostart": "開機時自動啟動:", "tray": "開機時以最小化自動啟動:", "tray_notifications": "啟用系統列通知:", + "dark_mode": "深色模式:", "priority_mode": "掉寶模式:", "proxy": "代理伺服器(需要重新啟動):" }, @@ -158,6 +159,6 @@ "how_it_works_text": "每隔一段時間,應用程式就會向 Twitch 請求當前觀看頻道的原始串流數據的 URL。然後提取此串流的元數據——這足以推進掉寶進度。請注意,這完全繞過了下載實際的串流視頻和音頻的需求。為了使頻道的狀態(上線或離線)的即時更新,應用程式建立了一個 WebSocket 連接,該連接會接收有關串流上下線的事件或當前觀眾數量的更新。", "getting_started": "事前準備", "getting_started_text": "1. 透過應用程式登入。\n2. 確保您的 Twitch 帳號已經和您所有感興趣的遊戲帳號已經連接。\n 3. 如果您想要獲取所有感興趣的掉寶,請不要勾選「僅參與優先掉寶遊戲」,然後按「重新載入」。\n4. 如果您想優先獲取特定遊戲,請使用「優先掉寶遊戲」列表設置您想獲取的遊戲的優先順序。應用程式將嘗試先獲取列表頂端的遊戲,然後依序獲取之後的遊戲。\n5. 勾選「僅參與優先掉寶遊戲」以避免獲取不在優先列表中的遊戲。當然,你也可以選擇不勾選——這取決於您。\n6. 使用「排除掉寶遊戲」列表設置永不獲取的遊戲。\n7. 更改任一列表的內容,或更改「僅參與優先掉寶遊戲」勾選狀態,需要手動按「重新載入」才能使更改生效。" - } + } } } diff --git a/settings.py b/settings.py index 6e9492f..910666d 100644 --- a/settings.py +++ b/settings.py @@ -14,6 +14,7 @@ if TYPE_CHECKING: class SettingsFile(TypedDict): proxy: URL language: str + dark_mode: bool exclude: set[str] priority: list[str] autostart_tray: bool @@ -26,6 +27,7 @@ default_settings: SettingsFile = { "proxy": URL(), "priority": [], "exclude": set(), + "dark_mode": False, "autostart_tray": False, "connection_quality": 1, "language": DEFAULT_LANG, @@ -46,6 +48,7 @@ class Settings: # from settings file proxy: URL language: str + dark_mode: bool exclude: set[str] priority: list[str] autostart_tray: bool diff --git a/translate.py b/translate.py index 7cc6122..33a1b7d 100644 --- a/translate.py +++ b/translate.py @@ -161,6 +161,7 @@ class GUISettingsGeneral(TypedDict): autostart: str tray: str tray_notifications: str + dark_mode: str priority_mode: str proxy: str @@ -361,6 +362,7 @@ default_translation: Translation = { "autostart": "Autostart: ", "tray": "Autostart into tray: ", "tray_notifications": "Tray notifications: ", + "dark_mode": "Dark mode: ", "priority_mode": "Priority mode: ", "proxy": "Proxy (requires restart):", },