85 lines
4.1 KiB
Python
85 lines
4.1 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
|
|
from ..element_types.instantiate_template_tree import instantiate_template_tree
|
|
|
|
|
|
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 ET que cet élément de jeu a un écran-modèle
|
|
(le cas normal désormais — voir create_element_type), tout son arbre est
|
|
COPIÉ en profondeur ici plutôt que de créer un seul élément plat : c'est
|
|
ce qui permet à un élément de jeu d'être un mini-conteneur réutilisable
|
|
avec plusieurs éléments imbriqués à l'intérieur, pas juste un widget
|
|
unique avec un contenu par défaut."""
|
|
if element_type_id:
|
|
types = {t["id"]: t for t in list_element_types(slug)}
|
|
et = types.get(int(element_type_id))
|
|
if et and et.get("template_screen_id"):
|
|
created = instantiate_template_tree(slug, et["template_screen_id"], screen_id, parent_id)
|
|
if created:
|
|
conn = db.connect(slug)
|
|
conn.execute(
|
|
"UPDATE _screen_elements SET element_type_id = ? WHERE id = ?", (et["id"], created[0])
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
return created[0]
|
|
# Modèle vide (aucun élément dedans pour l'instant) : on retombe
|
|
# sur l'ancien comportement plat ci-dessous, pour ne jamais
|
|
# échouer silencieusement à poser quelque chose.
|
|
|
|
widget_key = widget_key if widget_key in WIDGETS else "conteneur"
|
|
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
|