102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
"""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
|