Files
Forge-Engine/screens/elements/duplicate_element.py
T
williamandClaude Sonnet 5 bb79f2f93d Redesign the screen editor's element tree and add duplication
The "Éléments de cet écran" tree now renders every level of nesting
(previously stopped after one level of children) as a compact single-line
list, and right-clicking a row opens a context menu to duplicate the
element (and its full subtree) in place, in its current container.

Also: all property panels start collapsed instead of some being open by
default, the redundant nested element list inside "Ajouter DANS ce
conteneur" is removed (it only needs the widget picker), and the
now-unneeded "a container is selected" warning banner is gone.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-23 19:53:38 +02:00

61 lines
2.5 KiB
Python

import db
def duplicate_element(slug, element_id):
"""Duplique un élément et tout son sous-arbre DANS SON CONTENEUR ACTUEL
(même écran, même parent_id que l'original) — le clic droit "Dupliquer"
de l'arborescence n'a pas à faire choisir où poser la copie, elle
apparaît juste à côté de l'original. Renvoie l'id de la copie (celui à
sélectionner ensuite), ou None si l'élément n'existe pas."""
conn = db.connect(slug)
root_row = conn.execute("SELECT * FROM _screen_elements WHERE id = ?", (element_id,)).fetchone()
if not root_row:
conn.close()
return None
root = dict(root_row)
source = [
dict(r) for r in conn.execute(
"SELECT * FROM _screen_elements WHERE screen_id = ?", (root["screen_id"],)
).fetchall()
]
conn.close()
def copy_one(src, new_parent_id, is_root):
conn = db.connect(slug)
max_z = conn.execute(
"SELECT MAX(z_index) AS m FROM _screen_elements WHERE screen_id = ?", (src["screen_id"],)
).fetchone()["m"]
x, y = src["x"], src["y"]
if is_root and not new_parent_id:
# Petit décalage pour que la copie ne recouvre pas exactement
# l'original (qui serait alors invisible en dessous).
x = min(100, (x or 0) + 4)
y = min(100, (y or 0) + 4)
name = src.get("name")
if is_root and name:
name = f"{name} (copie)"
conn.execute(
"""INSERT INTO _screen_elements
(screen_id, parent_id, widget, element_type_id, name, content, attributes,
x, y, width, height, z_index, style)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
src["screen_id"], new_parent_id, src["widget"], src.get("element_type_id"), name,
src["content"], src["attributes"], x, y, src["width"], src["height"],
(max_z or 0) + 1, src["style"],
),
)
new_id = conn.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]
conn.commit()
conn.close()
return new_id
def walk(src_parent_id, new_parent_id):
for child in [s for s in source if s.get("parent_id") == src_parent_id]:
new_child_id = copy_one(child, new_parent_id, is_root=False)
walk(child["id"], new_child_id)
new_root_id = copy_one(root, root.get("parent_id"), is_root=True)
walk(root["id"], new_root_id)
return new_root_id