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