"""Fenêtre « Tableau des termes » d'un profil (lisible même avec 50+ termes). Table scrollable avec recherche/filtre — colonnes Type / Terme / Source (profil). Lecture seule pour l'instant : ajouter/supprimer/éditer sont désactivés et marqués « (à venir) » (l'écriture par profil n'est pas encore câblée). """ from __future__ import annotations import tkinter as tk from typing import Optional import customtkinter as ctk from gui_v6 import ui_kit from gui_v6.profile_view import filter_term_rows _TYPE_COLORS = { "À conserver": "success", "À masquer": "primary", "À ignorer": "text_muted", } class TermsTableWindow(ctk.CTkToplevel): def __init__( self, master, palette: dict, rows, *, title: str = "Tableau des termes", profile_label: str = "", ) -> None: super().__init__(master) self._palette = palette self._rows = list(rows) self._profile_label = profile_label self._visible = list(self._rows) self.title(title) self.geometry("740x560") self.minsize(520, 360) try: self.configure(fg_color=palette["bg"]) except Exception: pass self._query = tk.StringVar() self._count_text = tk.StringVar(value="") self._build() self._refresh() try: self.transient(master) self.after(120, lambda: (self.lift(), self.focus_force())) except Exception: pass # -- coutures testables -------------------------------------------------- def set_query(self, query: str) -> None: self._query.set(query) self._refresh() def visible_count(self) -> int: return len(self._visible) def add_is_disabled(self) -> bool: return str(self._add_btn.cget("state")) == "disabled" # -- UI ------------------------------------------------------------------ def _build(self) -> None: p = self._palette head = ctk.CTkFrame(self, fg_color="transparent") head.pack(fill="x", padx=12, pady=(12, 4)) title = "Termes du profil" if self._profile_label: title += f" « {self._profile_label} »" ctk.CTkLabel(head, text=title, text_color=p["text"], font=ui_kit.font(15, "bold")).pack(side="left") bar = ctk.CTkFrame(self, fg_color="transparent") bar.pack(fill="x", padx=12, pady=(0, 6)) ctk.CTkLabel(bar, text="🔎 Rechercher :", text_color=p["text_dim"], font=ui_kit.font(12)).pack(side="left") entry = ctk.CTkEntry(bar, textvariable=self._query, width=240) entry.pack(side="left", padx=6) self._query.trace_add("write", lambda *_: self._refresh()) self._add_btn = ui_kit.secondary_button(bar, p, "+ Ajouter (à venir)", command=lambda: None) self._add_btn.configure(state="disabled") self._add_btn.pack(side="right") header = ctk.CTkFrame(self, fg_color=p["card"], corner_radius=6) header.pack(fill="x", padx=12) for text, width in [("TYPE", 130), ("TERME", 360), ("SOURCE (PROFIL)", 180)]: ctk.CTkLabel(header, text=text, width=width, anchor="w", text_color=p["text_muted"], font=ui_kit.font(10, "bold")).pack(side="left", padx=8, pady=4) self._table = ctk.CTkScrollableFrame(self, fg_color=p["bg"]) self._table.pack(fill="both", expand=True, padx=12, pady=(2, 4)) ctk.CTkLabel(self, textvariable=self._count_text, text_color=p["text_muted"], font=ui_kit.font(11), anchor="w").pack(fill="x", padx=14, pady=(0, 10)) def _refresh(self) -> None: p = self._palette self._visible = filter_term_rows(self._rows, self._query.get()) for child in self._table.winfo_children(): child.destroy() if not self._visible: ctk.CTkLabel(self._table, text="Aucun terme.", text_color=p["text_muted"], font=ui_kit.font(12)).pack(anchor="w", padx=8, pady=8) for type_label, term, source in self._visible: row = ctk.CTkFrame(self._table, fg_color="transparent") row.pack(fill="x", pady=1) color = p[_TYPE_COLORS.get(type_label, "text")] ctk.CTkLabel(row, text=type_label, width=130, anchor="w", text_color=color, font=ui_kit.font(11, "bold")).pack(side="left", padx=8) ctk.CTkLabel(row, text=term, width=360, anchor="w", text_color=p["text"], font=ui_kit.font(12)).pack(side="left", padx=8) ctk.CTkLabel(row, text=source, width=180, anchor="w", text_color=p["text_muted"], font=ui_kit.font(11)).pack(side="left", padx=8) self._count_text.set(f"{len(self._visible)} terme(s) affiché(s) sur {len(self._rows)}.")