Files
Forge-Engine/screens/elements/add_element.py
T
williamandClaude Sonnet 5 e9a991ed13 Un exemplaire d'élément de jeu posé sur un écran reste maintenant lié à son modèle
Jusqu'ici, poser un élément de jeu depuis le catalogue ("Mes éléments de
jeu") copiait tout son arbre en base (instantiate_template_tree) : chaque
exemplaire devenait indépendant, y compris de son propre modèle - modifier
l'élément de jeu dans son éditeur n'avait plus aucun effet sur les
exemplaires déjà posés ailleurs.

Change ce comportement pour qu'un exemplaire reste TOUJOURS lié à son
modèle, sur le même principe déjà utilisé par un modèle de ligne de
Répéteur (jamais copié, rechargé en direct à chaque affichage - voir
_load_template_tree/_render_repeater) : add_element.py ne crée plus
qu'UNE SEULE ligne plate (avec sa position/taille propres à cet
exemplaire) au lieu de copier tout l'arbre, et render_element_html.py
recharge le contenu depuis l'écran-modèle à chaque rendu quand
element_type_id est réglé. Modifier l'élément de jeu dans son propre
éditeur met donc à jour tous ses exemplaires déjà posés, sur n'importe
quel écran (y compris ceux placés AVANT ce correctif, qui portaient déjà
element_type_id sur leur ligne de premier niveau), sans avoir à les
retoucher un par un.

Contrepartie assumée (discutée avec l'utilisateur avant ce changement) :
un exemplaire ne peut plus être personnalisé individuellement à
l'INTÉRIEUR (texte, couleur d'un enfant précis...) - seules sa position et
sa taille sur l'écran restent propres à chaque exemplaire. Pour changer le
contenu, il faut désormais passer par l'éditeur de l'élément de jeu
lui-même.

instantiate_template_tree.py, devenu inutilisé, est supprimé.

Ajoute tests/test_element_type_live_instances.py (mise à jour d'un
exemplaire déjà posé, propagation jusqu'à "Jouer", position toujours
indépendante par exemplaire) et met à jour un commentaire de test devenu
obsolète dans test_screens_and_elements.py.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 15:26:45 +02:00

78 lines
3.8 KiB
Python

import json
import db
from ..widgets.registry import WIDGETS
from ..widgets.widget_meta import widget_meta
from ..widgets.default_style_for_widget import default_style_and_attributes
from ..element_types.list_element_types import list_element_types
def add_element(slug, screen_id, widget_key, element_type_id=None, parent_id=None):
"""parent_id : si fourni, l'élément est posé PHYSIQUEMENT à l'intérieur
de cet élément parent (qui doit être un conteneur/répéteur/groupe de
champs) plutôt que directement sur l'écran — sa position/taille ne sert
alors plus (il suit le flux du parent), mais on garde des valeurs par
défaut pour rester cohérent si jamais il en sort un jour.
element_type_id : si fourni, un SEUL élément est créé ici (ligne plate,
comme n'importe quel widget), qui garde juste une référence vers cet
élément de jeu — son contenu (l'arbre de son écran-modèle) n'est PAS
copié en base : il est rechargé EN DIRECT à chaque affichage (voir
render_element_html.py), exactement comme un modèle de ligne de
Répéteur (voir _load_template_tree/_render_repeater). Modifier
l'élément de jeu dans son propre éditeur met donc à jour tous ses
exemplaires déjà posés, sur n'importe quel écran, sans avoir à les
retoucher un par un — au prix de ne plus pouvoir personnaliser
individuellement le CONTENU d'un exemplaire précis (sa position/taille
sur l'écran, elles, restent propres à chaque exemplaire, comme
d'habitude)."""
widget_key = widget_key if widget_key in WIDGETS else "conteneur"
if element_type_id:
types = {t["id"]: t for t in list_element_types(slug)}
et = types.get(int(element_type_id))
if et:
widget_key = et.get("default_widget") or widget_key
meta = widget_meta(widget_key)
# Réglages dont la valeur par défaut a un effet visuel voulu dès la
# création (ex: une image démarre avec "cover" pour se recadrer
# proprement) — hors couleurs, qui restent volontairement transparentes/
# sans bordure tant qu'on ne les choisit pas explicitement.
attributes, style = default_style_and_attributes(widget_key)
content = meta.get("default_content", "")
width, height = meta.get("w", 30), meta.get("h", 10)
conn = db.connect(slug)
max_z = conn.execute(
"SELECT MAX(z_index) AS m FROM _screen_elements WHERE screen_id = ?", (screen_id,)
).fetchone()["m"]
# Décalage en cascade : sans ça, chaque nouvel élément posé DIRECTEMENT
# SUR L'ÉCRAN atterrit exactement au même endroit (10%, 10%) que le
# précédent et le recouvre entièrement — ce qui peut donner l'impression
# trompeuse qu'un "élément indésirable" vient d'apparaître et d'en
# cacher un autre (le plus souvent après une confusion entre "Ajouter un
# élément sur l'écran" et "Ajouter DANS ce conteneur", qui posent tous
# les deux un nouvel élément mais pas au même endroit dans l'arbre).
x = y = 10
if not parent_id:
top_level_count = conn.execute(
"SELECT COUNT(*) AS c FROM _screen_elements WHERE screen_id = ? AND parent_id IS NULL",
(screen_id,),
).fetchone()["c"]
offset = (top_level_count % 8) * 4
x = y = 10 + offset
conn.execute(
"""INSERT INTO _screen_elements
(screen_id, parent_id, widget, element_type_id, content, attributes, x, y, width, height, z_index, style)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
screen_id, parent_id or None, widget_key, element_type_id or None, content, json.dumps(attributes),
x, y, width, height, (max_z or 0) + 1,
json.dumps(style),
),
)
element_id = conn.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]
conn.commit()
conn.close()
return element_id