diff --git a/linux.py b/linux.py index 860bf62..3fa8bc8 100644 --- a/linux.py +++ b/linux.py @@ -138,7 +138,7 @@ def _on_exit(icon=None, item=None) -> None: def _edit_config_dialog() -> None: - if not ensure_ctk_thread(ctk): + if not ensure_ctk_thread(ctk, _config.get("appearance", "auto")): _show_error("customtkinter не установлен.") return @@ -193,7 +193,7 @@ def _show_first_run() -> None: ensure_dirs() if FIRST_RUN_MARKER.exists(): return - if not ensure_ctk_thread(ctk): + if not ensure_ctk_thread(ctk, _config.get("appearance", "auto")): FIRST_RUN_MARKER.touch() return diff --git a/ui/ctk_theme.py b/ui/ctk_theme.py index e1f23c3..41bb786 100644 --- a/ui/ctk_theme.py +++ b/ui/ctk_theme.py @@ -51,8 +51,11 @@ def ctk_theme_for_platform() -> CtkTheme: return CtkTheme() -def apply_ctk_appearance(ctk: Any) -> None: - ctk.set_appearance_mode("auto") +_APPEARANCE_MODE_MAP = {"auto": "system", "light": "Light", "dark": "Dark"} + + +def apply_ctk_appearance(ctk: Any, mode: str = "auto") -> None: + ctk.set_appearance_mode(_APPEARANCE_MODE_MAP.get(mode, "system")) ctk.set_default_color_theme("blue") def center_ctk_geometry(root: Any, width: int, height: int) -> None: diff --git a/ui/ctk_tray_ui.py b/ui/ctk_tray_ui.py index 8bcdaeb..6d5538e 100644 --- a/ui/ctk_tray_ui.py +++ b/ui/ctk_tray_ui.py @@ -146,6 +146,11 @@ def _cfproxy_show_test_results(domain: str, results: dict) -> None: _INNER_W = 396 +_APPEARANCE_OPTIONS = ["Авто", "Светлая", "Тёмная"] +_APPEARANCE_FROM_CFG = {"auto": "Авто", "light": "Светлая", "dark": "Тёмная"} +_APPEARANCE_TO_CFG = {"Авто": "auto", "Светлая": "light", "Тёмная": "dark"} +_APPEARANCE_TO_CTK = {"auto": "system", "light": "Light", "dark": "Dark"} + def _entry(ctk, parent, theme, *, var=None, width=0, height=36, radius=10, **kw): opts = dict( @@ -249,6 +254,7 @@ class TrayConfigFormWidgets: cfproxy_var: Optional[Any] = None cfproxy_priority_var: Optional[Any] = None cfproxy_domain_var: Optional[Any] = None + appearance_var: Optional[Any] = None def install_tray_config_form( @@ -272,6 +278,33 @@ def install_tray_config_form( header, text=f"v{__version__}", font=(theme.ui_font_family, 12), text_color=theme.text_secondary, anchor="e", + ).pack(side="right", padx=(4, 0)) + appearance_var = ctk.StringVar( + value=_APPEARANCE_FROM_CFG.get(cfg.get("appearance", "auto"), "Авто") + ) + + def _on_appearance_change(choice: str) -> None: + cfg_val = _APPEARANCE_TO_CFG.get(choice, "auto") + ctk.set_appearance_mode(_APPEARANCE_TO_CTK[cfg_val]) + + ctk.CTkComboBox( + header, + values=_APPEARANCE_OPTIONS, + variable=appearance_var, + width=102, + height=28, + font=(theme.ui_font_family, 12), + text_color=theme.text_secondary, + fg_color=theme.field_bg, + border_color=theme.field_border, + button_color=theme.field_border, + button_hover_color=theme.text_secondary, + dropdown_fg_color=theme.field_bg, + dropdown_text_color=theme.text_primary, + dropdown_hover_color=theme.field_border, + corner_radius=8, + state="readonly", + command=_on_appearance_change, ).pack(side="right") conn = _config_section(ctk, frame, theme, "Подключение MTProto") @@ -488,6 +521,7 @@ def install_tray_config_form( cfproxy_var=cfproxy_var, cfproxy_priority_var=cfproxy_priority_var, cfproxy_domain_var=cfproxy_domain_var, + appearance_var=appearance_var, ) @@ -572,6 +606,8 @@ def validate_config_form( domain = widgets.cfproxy_domain_var.get().strip() if domain: new_cfg["cfproxy_domain"] = domain + if widgets.appearance_var is not None: + new_cfg["appearance"] = _APPEARANCE_TO_CFG.get(widgets.appearance_var.get(), "auto") return new_cfg diff --git a/utils/tray_common.py b/utils/tray_common.py index f9a60a2..2019c72 100644 --- a/utils/tray_common.py +++ b/utils/tray_common.py @@ -401,7 +401,7 @@ _ctk_root: Any = None _ctk_root_ready = threading.Event() -def ensure_ctk_thread(ctk: Any) -> bool: +def ensure_ctk_thread(ctk: Any, mode: str = "auto") -> bool: global _ctk_root if ctk is None: return False @@ -413,7 +413,7 @@ def ensure_ctk_thread(ctk: Any) -> bool: from ui.ctk_theme import apply_ctk_appearance, install_tkinter_variable_del_guard install_tkinter_variable_del_guard() - apply_ctk_appearance(ctk) + apply_ctk_appearance(ctk, mode) _ctk_root = ctk.CTk() _ctk_root.withdraw() _ctk_root_ready.set() diff --git a/windows.py b/windows.py index a047ec5..70a93d3 100644 --- a/windows.py +++ b/windows.py @@ -200,7 +200,7 @@ def _on_exit(icon=None, item=None) -> None: # settings dialog def _edit_config_dialog() -> None: - if not ensure_ctk_thread(ctk): + if not ensure_ctk_thread(ctk, _config.get("appearance", "auto")): _show_error("customtkinter не установлен.") return @@ -266,7 +266,7 @@ def _show_first_run() -> None: ensure_dirs() if FIRST_RUN_MARKER.exists(): return - if not ensure_ctk_thread(ctk): + if not ensure_ctk_thread(ctk, _config.get("appearance", "auto")): FIRST_RUN_MARKER.touch() return