"""Liste de termes éditable et scrollable (pas de pastilles). Utilisée dans l'éditeur de profils pour « mots à masquer / à conserver / à ignorer ». Reste lisible avec 50+ termes (zone scrollable + ajout/suppression). """ from __future__ import annotations import customtkinter as ctk from gui_v6 import ui_kit class EditableTermList(ctk.CTkFrame): def __init__(self, master, palette: dict, *, title: str, initial=None, height: int = 150, **kwargs): super().__init__(master, fg_color="transparent", **kwargs) self._palette = palette self._title = title self._terms: list[str] = [str(t) for t in (initial or [])] self._editable = True self._build(height) self._render() # -- API testable -------------------------------------------------------- def terms(self) -> list[str]: return list(self._terms) def set_terms(self, terms) -> None: self._terms = [str(t) for t in (terms or [])] self._render() def set_editable(self, editable: bool) -> None: self._editable = bool(editable) state = "normal" if self._editable else "disabled" self._entry.configure(state=state) self._add_btn.configure(state=state) self._render() def add_term(self, term: str) -> bool: term = str(term).strip() if not term or term in self._terms: return False self._terms.append(term) self._render() return True def remove_term(self, term: str) -> None: if term in self._terms: self._terms.remove(term) self._render() # -- UI ------------------------------------------------------------------ def _build(self, height: int) -> None: p = self._palette ctk.CTkLabel(self, text=self._title, text_color=p["text"], font=ui_kit.font(12, "bold"), anchor="w").pack( fill="x", pady=(0, 2) ) row = ctk.CTkFrame(self, fg_color="transparent") row.pack(fill="x", pady=(0, 4)) self._entry = ctk.CTkEntry( row, placeholder_text="Ajouter un terme…", fg_color=p["btn_sec_bg"], border_color=p["btn_sec_border"], text_color=p["text"], height=28, ) self._entry.pack(side="left", fill="x", expand=True, padx=(0, 6)) self._entry.bind("", lambda _e: self._on_add()) self._add_btn = ui_kit.secondary_button(row, p, "+ Ajouter", command=self._on_add) self._add_btn.pack(side="right") self._list = ctk.CTkScrollableFrame(self, fg_color=p["divider"], height=height) self._list.pack(fill="both", expand=True) self._count = ctk.CTkLabel(self, text="", text_color=p["text_muted"], font=ui_kit.font(10), anchor="w") self._count.pack(fill="x", pady=(2, 0)) def _on_add(self) -> None: if not self._editable: return if self.add_term(self._entry.get()): self._entry.delete(0, "end") def _render(self) -> None: p = self._palette for child in self._list.winfo_children(): child.destroy() if not self._terms: ctk.CTkLabel(self._list, text="Aucun terme.", text_color=p["text_muted"], font=ui_kit.font(11)).pack( anchor="w", padx=8, pady=6 ) for term in self._terms: line = ctk.CTkFrame(self._list, fg_color="transparent") line.pack(fill="x", pady=1) ctk.CTkLabel(line, text=term, text_color=p["text"], font=ui_kit.font(12), anchor="w").pack( side="left", fill="x", expand=True, padx=(6, 4) ) btn = ctk.CTkButton( line, text="×", width=26, height=24, corner_radius=6, fg_color=p["btn_sec_bg"], hover_color=p["card_border"], text_color=p["text"], command=lambda t=term: self.remove_term(t), ) if not self._editable: btn.configure(state="disabled") btn.pack(side="right", padx=(0, 4)) self._count.configure(text=f"{len(self._terms)} terme(s)")