Checkpoint before more window sizing work
This commit is contained in:
101
tooltips.py
Normal file
101
tooltips.py
Normal file
@@ -0,0 +1,101 @@
|
||||
"""Tooltip catalog and widget helper utilities."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tkinter as tk
|
||||
|
||||
|
||||
_TOOLTIP_FILE = Path(__file__).with_name("tooltip.json")
|
||||
|
||||
|
||||
def load_tooltip_catalog() -> dict:
|
||||
"""Load the tooltip catalog from JSON, returning a safe default on errors."""
|
||||
|
||||
try:
|
||||
return json.loads(_TOOLTIP_FILE.read_text(encoding="utf-8"))
|
||||
except Exception:
|
||||
return {"default_language": "IT", "IT": {}, "ENG": {}}
|
||||
|
||||
|
||||
def tooltip_text(key: str, *, language: str | None = None, catalog: dict | None = None) -> str:
|
||||
"""Return the localized tooltip text for ``key``."""
|
||||
|
||||
data = catalog or load_tooltip_catalog()
|
||||
lang = str(language or data.get("default_language") or "IT").upper()
|
||||
texts = data.get(lang, {}) or {}
|
||||
if key in texts:
|
||||
return str(texts[key])
|
||||
fallback = data.get("IT", {}) or {}
|
||||
return str(fallback.get(key, ""))
|
||||
|
||||
|
||||
class WidgetToolTip:
|
||||
"""Simple delayed tooltip for Tk/customtkinter widgets."""
|
||||
|
||||
def __init__(self, widget: tk.Misc, text: str, *, delay_ms: int = 400, wraplength: int = 320):
|
||||
self.widget = widget
|
||||
self.text = text.strip()
|
||||
self.delay_ms = int(delay_ms)
|
||||
self.wraplength = int(wraplength)
|
||||
self._after_id: str | None = None
|
||||
self._tip: tk.Toplevel | None = None
|
||||
if self.text:
|
||||
self.widget.bind("<Enter>", self._schedule_show, add="+")
|
||||
self.widget.bind("<Leave>", self._hide, add="+")
|
||||
self.widget.bind("<ButtonPress>", self._hide, add="+")
|
||||
|
||||
def _schedule_show(self, _event=None):
|
||||
self._cancel_schedule()
|
||||
self._after_id = self.widget.after(self.delay_ms, self._show)
|
||||
|
||||
def _cancel_schedule(self):
|
||||
if self._after_id is not None:
|
||||
try:
|
||||
self.widget.after_cancel(self._after_id)
|
||||
except Exception:
|
||||
pass
|
||||
self._after_id = None
|
||||
|
||||
def _show(self):
|
||||
self._after_id = None
|
||||
if self._tip is not None or not self.text:
|
||||
return
|
||||
try:
|
||||
x = self.widget.winfo_rootx() + 18
|
||||
y = self.widget.winfo_rooty() + self.widget.winfo_height() + 8
|
||||
tip = tk.Toplevel(self.widget)
|
||||
tip.withdraw()
|
||||
tip.overrideredirect(True)
|
||||
tip.attributes("-topmost", True)
|
||||
frame = tk.Frame(tip, bg="#fff7c7", bd=1, relief="solid")
|
||||
frame.pack(fill="both", expand=True)
|
||||
label = tk.Label(
|
||||
frame,
|
||||
text=self.text,
|
||||
justify="left",
|
||||
anchor="w",
|
||||
wraplength=self.wraplength,
|
||||
bg="#fff7c7",
|
||||
fg="#1f1f1f",
|
||||
font=("Segoe UI", 9),
|
||||
padx=8,
|
||||
pady=6,
|
||||
)
|
||||
label.pack(fill="both", expand=True)
|
||||
tip.geometry(f"+{x}+{y}")
|
||||
tip.deiconify()
|
||||
self._tip = tip
|
||||
except Exception:
|
||||
self._tip = None
|
||||
|
||||
def _hide(self, _event=None):
|
||||
self._cancel_schedule()
|
||||
tip = self._tip
|
||||
self._tip = None
|
||||
if tip is not None:
|
||||
try:
|
||||
tip.destroy()
|
||||
except Exception:
|
||||
pass
|
||||
Reference in New Issue
Block a user