feat(ui): add hover tooltips to all controls
Add ToolTip class (modules/ui_tooltip.py) and wire descriptive hover tooltips onto every button, switch, slider, and dropdown in the main window. Tooltips appear after a 500ms hover delay and are clamped to screen bounds. This requires no new dependencies — ToolTip uses only customtkinter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
"""Lightweight hover tooltip for CustomTkinter widgets."""
|
||||
|
||||
import customtkinter as ctk
|
||||
|
||||
|
||||
class ToolTip:
|
||||
"""Show a floating tooltip popup when the user hovers over a widget.
|
||||
|
||||
Usage:
|
||||
ToolTip(my_button, "Helpful description text")
|
||||
"""
|
||||
|
||||
def __init__(self, widget: ctk.CTkBaseClass, text: str, delay: int = 500):
|
||||
self._widget = widget
|
||||
self._text = text
|
||||
self._delay = delay
|
||||
self._tooltip_window = None
|
||||
self._after_id = None
|
||||
|
||||
widget.bind("<Enter>", self._schedule_show, add="+")
|
||||
widget.bind("<Leave>", self._hide, add="+")
|
||||
|
||||
def _schedule_show(self, event=None):
|
||||
self._cancel()
|
||||
self._after_id = self._widget.after(self._delay, self._show)
|
||||
|
||||
def _show(self):
|
||||
if self._tooltip_window is not None:
|
||||
return
|
||||
|
||||
x = self._widget.winfo_rootx() + 20
|
||||
y = self._widget.winfo_rooty() + self._widget.winfo_height() + 5
|
||||
|
||||
self._tooltip_window = tw = ctk.CTkToplevel(self._widget)
|
||||
tw.withdraw()
|
||||
tw.overrideredirect(True)
|
||||
|
||||
label = ctk.CTkLabel(
|
||||
tw,
|
||||
text=self._text,
|
||||
fg_color="#333333",
|
||||
text_color="#EEEEEE",
|
||||
corner_radius=6,
|
||||
padx=8,
|
||||
pady=4,
|
||||
)
|
||||
label.pack()
|
||||
|
||||
tw.update_idletasks()
|
||||
|
||||
# Clamp to screen bounds
|
||||
screen_w = tw.winfo_screenwidth()
|
||||
screen_h = tw.winfo_screenheight()
|
||||
tip_w = tw.winfo_reqwidth()
|
||||
tip_h = tw.winfo_reqheight()
|
||||
|
||||
if x + tip_w > screen_w:
|
||||
x = screen_w - tip_w - 5
|
||||
if y + tip_h > screen_h:
|
||||
y = self._widget.winfo_rooty() - tip_h - 5
|
||||
|
||||
tw.geometry(f"+{x}+{y}")
|
||||
tw.deiconify()
|
||||
|
||||
def _hide(self, event=None):
|
||||
self._cancel()
|
||||
if self._tooltip_window is not None:
|
||||
self._tooltip_window.destroy()
|
||||
self._tooltip_window = None
|
||||
|
||||
def _cancel(self):
|
||||
if self._after_id is not None:
|
||||
self._widget.after_cancel(self._after_id)
|
||||
self._after_id = None
|
||||
Reference in New Issue
Block a user