Retire le voile plein écran et le forçage de visibilité de l'éditeur (retour utilisateur)

Deux retours après le dernier correctif (78a373a, qui forçait
"display:flex" dans l'éditeur pour que la boîte de dialogue reste
visible) :
- "je souhaite avoir la main sur la visibilité de la modale sinon elle
  s'affiche toujours sur la scène, court-circuite ma logique" — le
  forçage empêchait de vraiment utiliser "Visibilité" pendant l'édition.
- "quand j'édite la modale ou quand je la mets dans une scène je
  souhaite que rien ne soit assombri, l'assombrissement ne se fait que
  quand la scène est jouée" — le voile plein écran (position:fixed +
  fond assombri) restait aussi actif dans l'éditeur.

Correctif (render_overlay.py) : le voile plein écran ET le forçage de
visibilité sont retirés de l'ÉDITEUR — ce widget s'y comporte maintenant
comme un CONTENEUR NORMAL (position/taille selon x/y/width/height, aucun
voile, réglage "Visibilité" respecté normalement, comme n'importe quel
autre widget masqué). Le comportement plein écran/voile/masquage par
défaut n'est conservé qu'en mode JOUABLE (ctx["_forge_play_mode"]).

En creusant pourquoi "Visible" ne suffisait pas à faire réapparaître la
boîte dans l'éditeur (menant l'utilisateur à essayer "Invisible" à la
place, visible dans ses captures), trouvé un vrai bug latent dans
save_element_controls.py : l'option "Visible" du réglage "Visibilité" ne
touche volontairement jamais "display" (pour ne pas écraser le
"display:flex" d'un conteneur en disposition ligne/colonne — voir
visibility_control.py) — ça fonctionne seulement parce que, pour un
widget AVEC un réglage "Disposition interne", celui-ci réaffirme lui-même
un display non-"none" au même enregistrement. La "superposition" n'a PAS
ce réglage : "display:none" (posé à la création ou par un "Masqué"
précédent) restait donc bloqué pour toujours, quel que soit le nombre de
fois où "Visible" était ensuite choisi. Corrigé : "Visible" efface aussi
"display" pour tout widget SANS réglage "Disposition interne" (safe : les
widgets qui EN ont un ne sont pas concernés, donc aucune régression sur
leur comportement existant).

Tests mis à jour (l'ancien test attendait le forçage, désormais retiré) +
nouveau test qui couvre le cycle complet (masqué par défaut -> "Visible"
choisi -> apparaît sans voile dans l'éditeur -> voile plein écran
retrouvé en mode jouable). 140 tests au vert au total.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-08-28 07:14:51 +02:00
co-authored by Claude Sonnet 5
parent 6e46949cf8
commit 7c237d6f1c
3 changed files with 103 additions and 65 deletions
+35 -18
View File
@@ -203,33 +203,50 @@ def test_overlay_box_default_text_color_survives_the_bulma_box_class(client, gam
assert "color:#e8eaf0" in box_tag
def test_overlay_stays_visible_in_the_editor_despite_starting_masked(client, game):
"""Régression : une "superposition" démarre MASQUÉE par défaut à sa
création (display:none — voir default_style_for_widget.py, pour ne
pas couvrir tout l'écran dès qu'on la pose). Ce display:none est écrit
tel quel dans le HTML, aussi bien en mode jouable QUE dans l'éditeur —
sans forçage, la boîte de dialogue restait donc invisible dans le
CANEVAS DE L'ÉDITEUR dès l'instant où elle était créée, rendant
impossible d'y voir/positionner visuellement titres/textes/boutons
posés à l'intérieur ("j'ai mis un texte dedans mais il ne se voit pas"
— le texte était là, mais toute la boîte qui le contient était
display:none). Seul le mode JOUABLE doit respecter ce réglage."""
def test_overlay_behaves_like_a_normal_container_in_the_editor(client, game):
"""Régression (deux retours utilisateur successifs) :
1. Un essai précédent forçait "display:flex" dans l'éditeur, quel que
soit le réglage "Visibilité" -> "je veux avoir la main sur la
visibilité de la modale, sinon elle s'affiche toujours sur la
scène [éditeur] et court-circuite ma logique". L'éditeur doit donc
respecter "Visibilité" normalement, EXACTEMENT comme n'importe quel
autre widget (masqué = display:none aussi dans l'éditeur).
2. La boîte gardait son voile plein écran (position:fixed + fond
assombri) même dans l'éditeur -> "je souhaite que rien ne soit
assombri, l'assombrissement ne se fait que quand la scène est
jouée". Le voile plein écran ne doit donc apparaître qu'en mode
JOUABLE — dans l'éditeur, ce widget se comporte comme un conteneur
normal (position/taille selon x/y/width/height, pas de voile)."""
screen_id = _create_screen(client, game)
overlay_id = _add_element(client, game, screen_id, "superposition")
client.post(f"/game/{game}/elements/{overlay_id}/children/add", data={"widget": "texte"})
# Par défaut ("Masqué" à la création) : invisible dans l'éditeur aussi,
# comme n'importe quel autre widget masqué (pas de forçage).
edit_html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
idx = edit_html.find(f'id="elt-{overlay_id}"')
assert idx != -1
wrapper_style = re.search(r'style="([^"]*)"', edit_html[edit_html.rfind("<div", 0, idx):idx]).group(1)
# display:none PUIS display:flex (la dernière déclaration CSS gagne).
assert wrapper_style.rstrip().endswith("display:flex;")
tag = edit_html[edit_html.rfind("<div", 0, idx):idx + 30]
assert "display:none" in tag
assert "position:fixed" not in tag
assert "rgba(0,0,0,0.6)" not in tag
# Repassé "Visible" à la main (garder la main sur la visibilité) :
# apparaît dans l'éditeur, mais toujours SANS voile plein écran.
client.post(f"/game/{game}/elements/{overlay_id}/save", data={"ctrl_visibilite": "visible"})
edit_html2 = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
idx2 = edit_html2.find(f'id="elt-{overlay_id}"')
tag2 = edit_html2[edit_html2.rfind("<div", 0, idx2):idx2 + 30]
assert "display:none" not in tag2
assert "position:fixed" not in tag2
assert "rgba(0,0,0,0.6)" not in tag2
# En mode JOUABLE, le comportement plein écran + voile reste inchangé.
play_html = client.get(f"/game/{game}/play").data.decode()
idx2 = play_html.find(f'id="elt-{overlay_id}"')
wrapper_style2 = re.search(r'style="([^"]*)"', play_html[play_html.rfind("<div", 0, idx2):idx2]).group(1)
# En mode jouable, pas de forçage : le display:none par défaut reste actif.
assert wrapper_style2.rstrip().endswith("display:none;")
idx3 = play_html.find(f'id="elt-{overlay_id}"')
tag3 = play_html[play_html.rfind("<div", 0, idx3):idx3 + 250]
assert "position:fixed" in tag3
assert "rgba(0,0,0,0.6)" in tag3
def test_overlay_element_type_instance_has_no_visible_wrapper_box(client, game):