Files
Forge-Engine/screens/elements/list_elements.py
T
williamandClaude Sonnet 5 4dc42a7f3b Corrige les réglages fantômes d'un exemplaire d'élément de jeu déjà posé
Bug remonté : la taille d'un Titre réglée à 18px dans le modèle affichait
21px "sur l'écran". Cause : un exemplaire posé AVANT le passage en mode
"toujours lié au modèle" (tour précédent) avait été créé par l'ancien
mécanisme instantiate_template_tree (retiré), qui copiait tout l'arbre en
base - ces lignes copiées (l'ancien enfant "Titre", encore à 21px depuis
avant le changement) ne sont plus jamais RENDUES (le contenu vient
désormais toujours en direct du modèle, voir le tour précédent), mais
restaient toujours sélectionnables dans l'arborescence de l'éditeur, avec
leurs propres réglages jamais synchronisés. Cliquer dessus dans l'arbre
affichait donc ses vieux réglages (21px) dans le panneau de propriétés,
donnant l'impression trompeuse que le modèle (18px) n'était pas pris en
compte - alors que le rendu réel utilisait déjà correctement 18px.

Fix, dans list_elements.py : tout élément dont un ANCÊTRE a
element_type_id réglé est maintenant exclu de la liste renvoyée à
l'éditeur (arborescence, sélection, panneau de propriétés) - son contenu
n'a plus aucune existence propre, seul l'écran-modèle fait foi. Les
lignes elles-mêmes restent en base (pas de suppression, un simple filtre
en lecture) mais ne sont plus jamais atteignables depuis l'éditeur.

Ajoute un test de régression qui simule exactement ce scénario (ligne
orpheline avec un ancien réglage figé) et vérifie qu'elle n'apparaît plus
nulle part - ni dans le canevas, ni via une sélection directe par id.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 16:10:30 +02:00

72 lines
3.1 KiB
Python

import json
import db
from ..rendering.render_element_html import render_element_html
def list_elements(slug, screen_id, enforce_visibility=False):
"""Renvoie TOUS les éléments de l'écran, à plat (parent_id indique
l'imbrication) — pratique pour construire l'arbre côté appelant. Seuls
les éléments de premier niveau (parent_id NULL) reçoivent un
"rendered_html" : celui d'un conteneur/répéteur/groupe de champs inclut
déjà, en le générant récursivement, le HTML de tout ce qui est posé à
l'intérieur — les enfants n'ont donc pas besoin d'être dessinés une
deuxième fois séparément.
enforce_visibility : True uniquement pour le mode jouable (voir
full_game_payload.py) — applique la "condition de visibilité" de chaque
élément (voir visibility_condition.py). Toujours False pour l'éditeur,
qui doit continuer à montrer/sélectionner un élément même si sa
condition serait actuellement fausse."""
conn = db.connect(slug)
rows = conn.execute(
"SELECT * FROM _screen_elements WHERE screen_id = ? ORDER BY z_index, id", (screen_id,)
).fetchall()
conn.close()
result = []
for r in rows:
d = dict(r)
d["style"] = json.loads(d["style"] or "{}")
d["attributes"] = json.loads(d["attributes"] or "{}")
result.append(d)
# Descendant d'un exemplaire d'élément de jeu (element_type_id réglé sur
# un ancêtre) : un ancien enfant copié en base par l'ex-instantiate_
# template_tree (mécanisme retiré, voir add_element.py) — son contenu
# vient désormais TOUJOURS en direct de l'écran-modèle au moment du
# rendu (voir render_element_html.py/_render_element_type_children),
# jamais de ces lignes, qui peuvent encore traîner en base pour un
# exemplaire posé avant ce changement. Exclu ici pour ne plus jamais
# apparaître sélectionnable dans l'arborescence de l'éditeur — sinon
# ses réglages, plus jamais synchronisés avec le modèle, donnaient
# l'impression trompeuse que "les propriétés ne sont pas prises en
# compte" (une taille de texte modifiée dans le modèle ne se reflétant
# jamais sur cette ligne fantôme, toujours sélectionnable à l'écran).
by_id = {d["id"]: d for d in result}
def _has_linked_ancestor(d):
seen = set()
parent_id = d.get("parent_id")
while parent_id and parent_id not in seen:
seen.add(parent_id)
parent = by_id.get(parent_id)
if not parent:
return False
if parent.get("element_type_id"):
return True
parent_id = parent.get("parent_id")
return False
result = [d for d in result if not _has_linked_ancestor(d)]
children_map = {}
for d in result:
if d.get("parent_id"):
children_map.setdefault(d["parent_id"], []).append(d)
play_ctx = {"_forge_play_mode": True} if enforce_visibility else None
for d in result:
if not d.get("parent_id"):
d["rendered_html"] = render_element_html(d, slug, children_map, play_ctx)
return result