"""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("", self._schedule_show, add="+") self.widget.bind("", self._hide, add="+") self.widget.bind("", 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