Files
Forge-Engine/screens/rendering/render_overlay.py
T
williamandClaude Sonnet 5 81c31a9c49 Corrige la boîte de dialogue (superposition) : un élément posé après elle s'affichait par-dessus
Bug visible sur le jeu de démo : le bouton "Clique-moi !" restait visible
ET cliquable AU-DESSUS du dialogue de bienvenue censé couvrir tout l'écran,
et la boîte de dialogue elle-même s'étirait bord à bord au lieu de rester
une boîte centrée lisible.

Cause (stacking context CSS) : le widget "superposition" ignore x/y/
width/height et pose lui-même position:fixed; inset:0; z-index:9999 sur
SA PROPRE balise (render_overlay.py) — mais le cadre .playElement/
.canvasElement qui l'entoure, PARTAGÉ PAR TOUS LES WIDGETS (filters/
element_style_filter.py), continuait quand même à poser
"position:absolute; z-index:<sa place dans le canevas>" (souvent petit,
ex. 1). 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 ajouté APRÈS l'overlay sur le canevas — qui
s'affichait donc par-dessus le dialogue.

Correctif : _element_style ne pose plus aucune position/z-index pour ce
widget (position:static — sa place dans le flux est de toute façon
invisible, son contenu réel étant en position:fixed). Plus de contexte
d'empilement local créé à ce niveau : le z-index:9999 se compare
directement à tous les autres éléments de l'écran, et gagne toujours.

Profité de l'occasion pour donner à la boîte une largeur par défaut plus
raisonnable (render_overlay.py : max-width:min(560px, 90%) au lieu de
90% seul) — sur un écran de jeu large, "90%" donnait une boîte étirée
bord à bord peu lisible comme dialogue ; 560px reste confortable, et 90%
prend toujours le relais sur un écran étroit (mobile/portrait).

Nouveau test de régression (test_overlay_wrapper_does_not_trap_its_own_z_index) :
confirmé en échec sur l'ancien code (git stash), au vert avec le
correctif. 129 tests au vert au total.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 18:43:29 +02:00

60 lines
3.0 KiB
Python

import html as html_lib
from .visible_attrs import _visible_attrs
from .style_string import _style_string
from .render_children import _render_children
def _render_overlay(el, meta, slug, children_map=None, ctx=None, parent_flex_direction=None):
"""3.4 (Confort) — overlay/modale réutilisable : une boîte de dialogue
prête à l'emploi, par-dessus TOUT le reste de l'écran, fermeture
manuelle uniquement (aucun clic-en-dehors-pour-fermer volontairement,
conformément au manque documenté). Contrairement aux autres widgets, sa
position ne dépend PAS de x/y/width/height (glissé-déposé sur le
canevas) : `position:fixed; inset:0` la fait toujours couvrir tout
l'écran, quel que soit l'endroit où elle a été posée dans l'éditeur —
seul un voile semi-transparent + une boîte centrée, contenant les
éléments posés à l'intérieur (comme un conteneur normal).
Ouverture/fermeture : PAS de mécanisme dédié — elle réutilise l'action
existante "Modifier un élément → Visibilité" (masquer/rendre visible),
exactement comme n'importe quel autre élément. C'est pour ça qu'on
fusionne le style de positionnement fixe ci-dessous AVEC le style normal
de l'élément (_style_string) : si l'élément est réglé "Masqué" dans ses
propriétés, ou si une action "Modifier un élément" le cache plus tard,
ce masquage continue de fonctionner normalement."""
attrs_raw = el.get("attributes") or {}
box_color = attrs_raw.get("_couleur_boite") or "#1f2430"
radius = attrs_raw.get("_arrondi") or "12"
attrs = _visible_attrs(el, meta, ctx)
base_overlay_style = (
"position:fixed; inset:0; z-index:9999; display:flex; "
"align-items:center; justify-content:center; background:rgba(0,0,0,0.6);"
)
style = base_overlay_style + " " + _style_string(el, parent_flex_direction=parent_flex_direction)
attr_parts = [f'style="{html_lib.escape(style)}"']
for k, v in attrs.items():
if v in (None, "", False):
continue
attr_parts.append(
html_lib.escape(str(k)) if v is True else f'{html_lib.escape(str(k))}="{html_lib.escape(str(v))}"'
)
box_style = (
f"background:{html_lib.escape(str(box_color))}; border-radius:{html_lib.escape(str(radius))}px; "
# max-width en min(...) plutôt qu'un simple pourcentage : sur un
# écran de jeu large (desktop), "90%" seul donnait une boîte étirée
# bord à bord peu lisible comme dialogue — 560px reste une largeur
# de boîte de dialogue confortable, tout en retombant sur 90% sur un
# écran de jeu étroit (mobile/portrait) pour ne jamais déborder.
"padding:24px; width:100%; max-width:min(560px, 90%); max-height:90%; overflow:auto; box-sizing:border-box;"
)
child_html = _render_children(el, slug, children_map, ctx)
return (
f'<div {" ".join(attr_parts)}>'
f'<div class="forgeOverlayBox" style="{html_lib.escape(box_style)}">{child_html}</div>'
f"</div>"
)