Dev #4

Merged
w-vandal merged 16 commits from dev into main 2026-08-29 04:02:11 +00:00
2 changed files with 61 additions and 27 deletions
Showing only changes of commit 7c0f1ed427 - Show all commits
+27 -16
View File
@@ -20,25 +20,36 @@ def _element_style(el):
Bug corrigé : le widget "superposition" (render_overlay.py) ignore déjà
x/y/width/height et pose lui-même position:fixed; inset:0; z-index:9999
sur SA PROPRE balise — mais tant que CE cadre-ci gardait quand même
"position:absolute; z-index:{z_index}" (le z-index de sa place dans le
canevas, souvent petit), il devenait un élément positionné avec z-index
explicite, donc un NOUVEAU contexte d'empilement CSS — le z-index:9999
posé plus profond ne se comparait alors plus qu'AU SEIN de ce contexte,
et perdait face au z-index (plus grand) d'un frère ajouté après lui sur
le canevas, qui se retrouvait affiché PAR-DESSUS le dialogue censé tout
couvrir. En ne posant ICI aucune position/z-index pour ce widget (sa
place dans le flux normal, invisible puisque son contenu est en
position:fixed de toute façon), plus aucun contexte d'empilement n'est
créé à ce niveau : le z-index:9999 se compare alors directement aux
autres éléments de l'écran, et gagne toujours."""
if el.get("widget") == "superposition":
return "position:static;"
return "; ".join([
"z-index:{z_index}" (le z-index de sa place dans le canevas, souvent
petit), un élément positionné avec un z-index EXPLICITE (même sur ce
cadre, pas sur son contenu) devient un NOUVEAU contexte d'empilement
CSS — le z-index:9999 posé plus profond ne se comparait alors plus
qu'AU SEIN de ce contexte, et perdait face au z-index (plus grand) d'un
frère ajouté après lui sur le canevas, qui se retrouvait affiché
PAR-DESSUS le dialogue censé tout couvrir.
Première correction tentée (retirer aussi position/left/top/width/
height ici) : régression dans l'ÉDITEUR — ce même cadre (.canvasElement)
sert aussi de prise pour glisser-déposer/redimensionner CE widget dans
le canevas (voir screen_edit.html), dont le calcul se base sur ses
dimensions réelles ; sans position:absolute + left/top/width/height, ce
cadre s'effondre à 0×0 (son contenu réel est en position:fixed, donc
hors flux), le calcul division par zéro produit NaN, et
JSON.stringify(NaN) donne "null" — element_geometry.py plantait alors
sur float(None). left/top/width/height restent donc posés comme pour
n'importe quel widget (l'éditeur continue de fonctionner normalement) :
SEUL le z-index est omis pour ce widget précis. position:absolute SANS
z-index explicite (donc z-index:auto) ne crée PAS de nouveau contexte
d'empilement — le z-index:9999 posé plus profond se compare alors
directement aux autres éléments de l'écran, et gagne toujours."""
parts = [
"position:absolute",
f"left:{el['x']}%", f"top:{el['y']}%",
f"width:{el['width']}%", f"height:{el['height']}%",
f"z-index:{el['z_index']}",
]) + ";"
]
if el.get("widget") != "superposition":
parts.append(f"z-index:{el['z_index']}")
return "; ".join(parts) + ";"
def _element_transform_style(el):
+34 -11
View File
@@ -184,15 +184,19 @@ def test_overlay_widget_renders_fullscreen_fixed_box(client, game):
def test_overlay_wrapper_does_not_trap_its_own_z_index(client, game):
"""Régression : le cadre .playElement/.canvasElement partagé par TOUS
les widgets (voir filters/element_style_filter.py) posait quand même
"position:absolute; z-index:<sa place dans le canevas>" sur la
superposition, MÊME SI son propre contenu (render_overlay.py) ignore
x/y/width/height et pose déjà position:fixed + z-index:9999 lui-même.
Un élément positionné avec un z-index explicite crée un NOUVEAU contexte
d'empilement CSS : le 9999 posé plus profond ne se comparait alors plus
qu'AU SEIN de ce contexte, et perdait face au z-index (plus grand) d'un
élément normal ajouté APRÈS l'overlay sur le canevas — qui s'affichait
donc PAR-DESSUS le dialogue censé tout couvrir. Le cadre ne doit donc
plus poser aucune position/z-index pour ce widget."""
"z-index:<sa place dans le canevas>" sur la superposition, MÊME SI son
propre contenu (render_overlay.py) ignore x/y/width/height et pose déjà
position:fixed + z-index:9999 lui-même. Un élément positionné avec un
z-index EXPLICITE crée un NOUVEAU contexte d'empilement CSS : le 9999
posé plus profond ne se comparait alors plus qu'AU SEIN de ce contexte,
et perdait face au z-index (plus grand) d'un élément normal ajouté
APRÈS l'overlay sur le canevas — qui s'affichait donc PAR-DESSUS le
dialogue censé tout couvrir. Le cadre garde position/left/top/width/
height comme tout widget (l'éditeur en a besoin pour glisser-déposer/
redimensionner ce cadre — les retirer a fait planter element_geometry
en régression), mais n'écrit plus DU TOUT de z-index pour ce widget :
position:absolute avec z-index:auto (omis) ne crée pas de contexte
d'empilement, donc le 9999 se compare directement aux autres éléments."""
screen_id = _create_screen(client, game)
overlay_id = _add_element(client, game, screen_id, "superposition")
# Ajouté APRÈS l'overlay -> z_index plus grand que le sien.
@@ -204,8 +208,27 @@ def test_overlay_wrapper_does_not_trap_its_own_z_index(client, game):
assert idx != -1
tag_start = html.rfind("<div", 0, idx)
wrapper_tag = html[tag_start:idx + 200]
assert "position:static" in wrapper_tag
assert "position:absolute" not in wrapper_tag
assert "position:absolute" in wrapper_tag
assert "z-index" not in wrapper_tag
def test_overlay_geometry_can_still_be_saved_from_the_editor(client, game):
"""Régression : la première version du correctif ci-dessus retirait
AUSSI position/left/top/width/height du cadre d'une superposition, ce
qui effondrait ce cadre à 0×0 dans l'ÉDITEUR (son contenu réel est en
position:fixed, hors flux) — le calcul de glisser-déposer/
redimensionnement (screen_edit.html) divise alors par une dimension
nulle, produit NaN, et JSON.stringify(NaN) envoie "null" : le serveur
plantait sur float(None) dans element_geometry.py. Le cadre doit donc
continuer à porter une position/taille en % normale pour ce widget."""
screen_id = _create_screen(client, game)
overlay_id = _add_element(client, game, screen_id, "superposition")
resp = client.post(
f"/game/{game}/elements/{overlay_id}/geometry",
data=json.dumps({"x": 12.5, "y": 20, "width": 55, "height": 45}),
content_type="application/json",
)
assert resp.status_code == 200
def test_overlay_starts_hidden_by_default(client, game):