from __future__ import annotations from typing import Any import tkinter as tk import customtkinter as ctk from ui_theme import theme_color, theme_font, theme_padding, theme_value class InlineBusyOverlay: """Busy overlay rendered inside the same window, avoiding extra toplevels.""" def __init__(self, parent: tk.Misc, theme_cfg: dict[str, Any] | None = None): self.parent = parent self.theme_cfg = theme_cfg or {} self._cover: ctk.CTkFrame | None = None self._label: ctk.CTkLabel | None = None self._bar: ctk.CTkProgressBar | None = None def show(self, message: str = "Attendere..."): if self._cover and self._cover.winfo_exists(): if self._label: self._label.configure(text=message) try: self._cover.lift() except Exception: pass return cover = ctk.CTkFrame( self.parent, corner_radius=0, fg_color=theme_color(self.theme_cfg, "overlay_cover_fg_color", ("#d9d9d9", "#4a4a4a")), ) cover.place(relx=0, rely=0, relwidth=1, relheight=1) try: cover.lift() except Exception: pass wrap = ctk.CTkFrame( cover, corner_radius=int(theme_value(self.theme_cfg, "overlay_panel_corner_radius", 10)), fg_color=theme_color(self.theme_cfg, "overlay_panel_fg_color", ("#f2f2f2", "#353535")), ) wrap.place(relx=0.5, rely=0.5, anchor="center") label = ctk.CTkLabel( wrap, text=message, font=theme_font(self.theme_cfg, "overlay_label_font", ("Segoe UI", 11, "bold")), ) label_pad = theme_padding(self.theme_cfg, "overlay_label_padding", (18, 14, 18, 8)) label.pack(padx=(label_pad[0], label_pad[2]), pady=(label_pad[1], label_pad[3])) bar = ctk.CTkProgressBar( wrap, mode="indeterminate", width=int(theme_value(self.theme_cfg, "overlay_progress_width", 220)), ) bar_pad = theme_padding(self.theme_cfg, "overlay_progress_padding", (18, 0, 18, 14)) bar.pack(padx=(bar_pad[0], bar_pad[2]), pady=(bar_pad[1], bar_pad[3])) try: bar.start() except Exception: pass self._cover = cover self._label = label self._bar = bar def hide(self): if self._bar: try: self._bar.stop() except Exception: pass self._bar = None self._label = None if self._cover and self._cover.winfo_exists(): try: self._cover.destroy() except Exception: pass self._cover = None