|
1 | 1 | from __future__ import annotations |
| 2 | + |
2 | 3 | from pathlib import Path |
3 | | -from typing import Optional, Callable, Dict |
4 | | -import platform, subprocess, sys |
| 4 | +from typing import Callable |
| 5 | + |
5 | 6 | import tkinter as tk |
6 | | -from tkinter import ttk, messagebox, simpledialog |
7 | | -from tkinter.scrolledtext import ScrolledText |
| 7 | +from tkinter import ttk, messagebox |
8 | 8 |
|
9 | | -# Always use the project's full Help/About implementation |
10 | 9 | from help_window import open_help_window, show_about_dialog, set_app_meta |
11 | | -from tooltips import set_tooltip_delay, set_tooltip_wrap, get_tooltip_settings |
12 | 10 |
|
13 | | -def _t(parent: tk.Misc, key: str, default: str, get_text: Optional[Callable[[str, str], str]]) -> str: |
14 | | - try: |
15 | | - if get_text: |
16 | | - return get_text(key, default) |
17 | | - if hasattr(parent, "lang") and hasattr(parent.lang, "get"): |
18 | | - v = parent.lang.get(key) |
19 | | - if isinstance(v, str) and v: |
20 | | - return v |
21 | | - except Exception: |
22 | | - pass |
23 | | - return default |
| 11 | +# Re-exported tooltip settings; if not available, provide fallbacks. |
| 12 | +try: |
| 13 | + from tooltips import set_tooltip_delay, set_tooltip_wrap, get_tooltip_settings # type: ignore |
| 14 | +except Exception: # pragma: no cover |
| 15 | + def set_tooltip_delay(_ms: int) -> None: # noqa: D401 - trivial shim |
| 16 | + """No-op when tooltips module not present.""" |
| 17 | + return |
24 | 18 |
|
25 | | -def _copy_diagnostics(parent: tk.Misc) -> None: |
26 | | - lines = [] |
27 | | - # Try to pull app name/version if the caller set it in About |
28 | | - app = getattr(parent, "title", lambda: "Application")() |
29 | | - try: |
30 | | - # Pull VERSION attr from parent/App if present |
31 | | - ver = getattr(parent, "VERSION", "") |
32 | | - if not ver and hasattr(parent, "title"): |
33 | | - ver = "" |
34 | | - except Exception: |
35 | | - ver = "" |
36 | | - if ver: |
37 | | - lines.append(f"{app} {ver}") |
38 | | - else: |
39 | | - lines.append(app) |
40 | | - |
41 | | - lines.append(f"Python: {sys.version.splitlines()[0]}") |
42 | | - lines.append(f"OS: {platform.system()} {platform.release()}") |
43 | | - |
44 | | - for tool, args in [("yt-dlp", ["--version"]), ("ffmpeg", ["-version"]), ("ffprobe", ["-version"]), ("mp3gain", ["-v"])]: |
45 | | - try: |
46 | | - p = subprocess.run([tool, *args], capture_output=True, text=True, timeout=3) |
47 | | - first = p.stdout.splitlines()[0] if p.stdout.strip() else (p.stderr.splitlines()[0] if p.stderr.strip() else "(no output)") |
48 | | - lines.append(f"{tool}: {first}") |
49 | | - except Exception as e: |
50 | | - lines.append(f"{tool}: error: {e}") |
| 19 | + def set_tooltip_wrap(_chars: int) -> None: # noqa: D401 - trivial shim |
| 20 | + """No-op when tooltips module not present.""" |
| 21 | + return |
51 | 22 |
|
| 23 | + def get_tooltip_settings() -> tuple[int, int]: |
| 24 | + return (600, 60) |
| 25 | + |
| 26 | + |
| 27 | +def _t(parent: tk.Misc, key: str, default: str, get_text: Callable[[str, str], str] | None) -> str: |
| 28 | + if get_text is None: |
| 29 | + return default |
52 | 30 | try: |
53 | | - parent.clipboard_clear(); parent.clipboard_append("\n".join(lines)) |
54 | | - messagebox.showinfo("Diagnostics copied", "Diagnostic info copied to clipboard.") |
| 31 | + return get_text(key, default) |
55 | 32 | except Exception: |
56 | | - pass |
| 33 | + return default |
| 34 | + |
57 | 35 |
|
58 | 36 | def add_help_right_aligned_menu( |
59 | 37 | parent: tk.Misc, |
60 | 38 | app_name: str, |
61 | 39 | version: str, |
62 | | - help_md_path: Optional[Path] = None, |
63 | | - get_text: Optional[Callable[[str, str], str]] = None, |
64 | | - locales: Optional[Dict[str, Path]] = None, |
65 | | - on_switch_language: Optional[Callable[[str], None]] = None |
| 40 | + help_md_path: Path | None = None, |
| 41 | + get_text: Callable[[str, str], str] | None = None, |
| 42 | + locales: dict[str, Path] | None = None, |
| 43 | + on_switch_language: Callable[[str], None] | None = None, |
66 | 44 | ) -> tk.Frame: |
67 | 45 | """ |
68 | | - Create a simple right-aligned strip with a Help menubutton. |
69 | | - Always opens the project's full Help/About windows. |
| 46 | + Adds a right-aligned toolbar-style frame with Help/About and optional Language menu. |
| 47 | + Returns the frame so callers can pack/grid/place it as needed. |
70 | 48 | """ |
71 | | - # Make a thin bar at the top |
72 | | - bar = ttk.Frame(parent) |
73 | | - bar.pack(side="top", fill="x", padx=0, pady=(0, 6)) |
| 49 | + set_app_meta(app_name, version) |
74 | 50 |
|
75 | | - # Right-aligned Help menu |
76 | | - btn = ttk.Menubutton(bar, text=_t(parent, "menu.help", "Help", get_text)) |
77 | | - btn.pack(side="right", padx=(0, 6), pady=(6, 0)) |
78 | | - menu = tk.Menu(btn, tearoff=0) |
79 | | - |
80 | | - # Open Help |
81 | | - menu.add_command( |
82 | | - label=_t(parent, "menu.open_help", "Open Help", get_text), |
83 | | - command=lambda: open_help_window(parent, help_md_path, get_text=get_text), |
84 | | - ) |
| 51 | + frame = ttk.Frame(parent) |
| 52 | + menubtn = ttk.Menubutton(frame, text=_t(parent, "menu.help", "Help", get_text)) |
| 53 | + menu = tk.Menu(menubtn, tearoff=False) |
85 | 54 |
|
86 | | - # Copy diagnostics |
| 55 | + if help_md_path is not None: |
| 56 | + menu.add_command( |
| 57 | + label=_t(parent, "menu.help.view", "View Help", get_text), |
| 58 | + command=lambda: open_help_window(parent, help_md_path, get_text or (lambda _k, d: d)), |
| 59 | + ) |
87 | 60 | menu.add_command( |
88 | | - label=_t(parent, "menu.copy_diagnostics", "Copy diagnostic info", get_text), |
89 | | - command=lambda: _copy_diagnostics(parent), |
| 61 | + label=_t(parent, "menu.help.about", "About", get_text), |
| 62 | + command=lambda: show_about_dialog(parent, help_md_path or Path("HELP.md"), get_text or (lambda _k, d: d)), |
90 | 63 | ) |
91 | | - # Tooltips submenu |
92 | | - tips_menu = tk.Menu(menu, tearoff=0) |
93 | | - def _set_delay(): |
94 | | - cur_delay, _cur_wrap = get_tooltip_settings() |
95 | | - val = simpledialog.askinteger( |
96 | | - title=_t(parent, "menu.tooltips.delay", "Tooltip delay", get_text), |
97 | | - prompt=_t(parent, "menu.tooltips.delay", "Tooltip delay (ms):", get_text), |
98 | | - initialvalue=int(cur_delay), |
99 | | - minvalue=0 |
100 | | - ) |
101 | | - if val is not None: |
102 | | - set_tooltip_delay(val) |
103 | | - try: |
104 | | - getattr(parent, '_save_config')() |
105 | | - except Exception: |
106 | | - pass |
107 | | - |
108 | | - def _set_wrap(): |
109 | | - _cur_delay, cur_wrap = get_tooltip_settings() |
110 | | - val = simpledialog.askinteger( |
111 | | - title=_t(parent, "menu.tooltips.wrap", "Tooltip wrap", get_text), |
112 | | - prompt=_t(parent, "menu.tooltips.wrap", "Wrap length (px):", get_text), |
113 | | - initialvalue=int(cur_wrap), |
114 | | - minvalue=0 |
115 | | - ) |
116 | | - if val is not None: |
117 | | - set_tooltip_wrap(val) |
118 | | - try: |
119 | | - getattr(parent, '_save_config')() |
120 | | - except Exception: |
121 | | - pass |
122 | 64 |
|
123 | | - tips_menu.add_command(label=_t(parent, "menu.tooltips.delay", "Tooltip delay…", get_text), command=_set_delay) |
124 | | - tips_menu.add_command(label=_t(parent, "menu.tooltips.wrap", "Tooltip wrap…", get_text), command=_set_wrap) |
125 | | - menu.add_cascade(label=_t(parent, "menu.tooltips", "Tooltips", get_text), menu=tips_menu) |
126 | | - |
127 | | - |
128 | | - # Language submenu (optional) |
| 65 | + # Optional language submenu |
129 | 66 | if locales and on_switch_language: |
130 | | - lang_menu = tk.Menu(menu, tearoff=0) |
131 | | - current = tk.StringVar(value=getattr(parent, "current_language", "en")) |
132 | | - for code in sorted(locales.keys()): |
133 | | - lang_menu.add_radiobutton( |
| 67 | + lang_menu = tk.Menu(menu, tearoff=False) |
| 68 | + for code in sorted(locales): |
| 69 | + lang_menu.add_command( |
134 | 70 | label=code, |
135 | | - variable=current, |
136 | | - value=code, |
137 | 71 | command=lambda c=code: on_switch_language(c), |
138 | 72 | ) |
139 | | - menu.add_cascade(label=_t(parent, "menu.language", "Language", get_text), menu=lang_menu) |
140 | | - |
141 | | - # About: set metadata then show |
142 | | - def _about(): |
143 | | - try: |
144 | | - set_app_meta(app_name, version) |
145 | | - except Exception: |
146 | | - pass |
147 | | - show_about_dialog(parent, help_md_path, get_text=get_text) |
148 | | - |
149 | | - menu.add_separator() |
150 | | - menu.add_command(label=_t(parent, "menu.about", "About", get_text), command=_about) |
151 | | - |
152 | | - btn.configure(menu=menu) |
| 73 | + menu.add_cascade(label=_t(parent, "menu.help.language", "Language", get_text), menu=lang_menu) |
| 74 | + |
| 75 | + # Optional tooltip submenu |
| 76 | + tip_menu = tk.Menu(menu, tearoff=False) |
| 77 | + delay, wrap = get_tooltip_settings() |
| 78 | + def _set_delay(ms: int) -> None: |
| 79 | + set_tooltip_delay(ms) |
| 80 | + messagebox.showinfo( |
| 81 | + _t(parent, "tooltips.title", "Tooltips", get_text), |
| 82 | + _t(parent, "tooltips.delay_set", f"Delay set to {ms} ms.", get_text), |
| 83 | + parent=parent, |
| 84 | + ) |
| 85 | + for ms in (0, 300, 600, 1000): |
| 86 | + tip_menu.add_command(label=f"Delay: {ms} ms", command=lambda x=ms: _set_delay(x)) |
| 87 | + |
| 88 | + def _set_wrap(ch: int) -> None: |
| 89 | + set_tooltip_wrap(ch) |
| 90 | + messagebox.showinfo( |
| 91 | + _t(parent, "tooltips.title", "Tooltips", get_text), |
| 92 | + _t(parent, "tooltips.wrap_set", f"Wrap set to {ch} chars.", get_text), |
| 93 | + parent=parent, |
| 94 | + ) |
| 95 | + for ch in (40, 60, 80): |
| 96 | + tip_menu.add_command(label=f"Wrap: {ch} chars", command=lambda x=ch: _set_wrap(x)) |
153 | 97 |
|
154 | | - # F1 opens full help |
155 | | - try: |
156 | | - parent.bind_all("<F1>", lambda e: open_help_window(parent, help_md_path, get_text=get_text)) |
157 | | - except Exception: |
158 | | - pass |
| 98 | + menu.add_cascade(label=_t(parent, "menu.help.tooltips", "Tooltips", get_text), menu=tip_menu) |
159 | 99 |
|
160 | | - return bar |
| 100 | + menubtn["menu"] = menu |
| 101 | + menubtn.pack(side="right") |
| 102 | + return frame |
0 commit comments