Rewire hover as a flow trigger

Add "survol"/"fin_survol" trigger events (mouseenter/mouseleave,
same anti-doublon pattern as bindClicks()) so hovering an element can
run flow nodes, instead of the old static hover-text-only control
removed from the properties panel. Also add "Contenu" as a settable
"Modifier un élément" property so an action can display a defined
text on another element on hover — the concrete use case that
motivated this.

Verified live with Playwright: a "Au survol" trigger on one element
correctly updates another element's text via the action, and text
stays put with no "Fin du survol" wired (explicit, no implicit
revert, consistent with "Au clic").
This commit is contained in:
william
2026-08-23 17:16:48 +02:00
parent 4a593d07e4
commit 4eac20a464
6 changed files with 121 additions and 4 deletions
+61 -3
View File
@@ -22,9 +22,10 @@ def test_hover_text_control_removed_from_properties_panel(client, game):
"""Le réglage "Survol" a été retiré du panneau de propriétés (voir
universal_controls.py) : survoler un élément est conceptuellement un
déclencheur de la Logique de la scène, pas une propriété statique —
il sera réintroduit là-bas. Poster ctrl_survol_texte ne doit donc plus
avoir d'effet (le mécanisme data-hover-text/bindHoverTexts sous-jacent
reste en place pour un usage futur, voir les tests ci-dessous)."""
reconstruit là-bas avec les déclencheurs "survol"/"fin_survol" (voir
plus bas). Poster ctrl_survol_texte ne doit donc plus avoir d'effet (le
mécanisme data-hover-text/bindHoverTexts sous-jacent reste en place, il
n'est simplement plus réglable depuis ce panneau)."""
screen_id = _create_screen(client, game)
el_id = _add_element(client, game, screen_id, "titre")
client.post(f"/game/{game}/elements/{el_id}/save", data={
@@ -54,6 +55,63 @@ def test_play_page_exposes_hover_binding_runtime(client, game):
assert "bindHoverTexts" in html
def test_survol_trigger_node_persists(client, game):
screen_id = _create_screen(client, game)
el_id = _add_element(client, game, screen_id, "titre")
resp = client.post(
f"/game/{game}/screens/{screen_id}/flow/nodes/add",
data=json.dumps({"node_type": "trigger", "trigger_element_id": el_id, "trigger_event": "survol"}),
content_type="application/json",
)
assert resp.status_code == 200
node = resp.get_json()
assert node["trigger_event"] == "survol"
assert node["trigger_element_id"] == el_id
def test_play_page_exposes_hover_trigger_runtime(client, game):
"""bindHoverTriggers() (mouseenter/mouseleave -> runFlowFrom) doit être
exposé et appelé, exactement comme bindClicks() pour "Au clic"."""
resp = client.get(f"/game/{game}/play")
html = resp.data.decode()
assert "bindHoverTriggers" in html
assert "'survol'" in html
assert "'fin_survol'" in html
def test_modifier_element_contenu_action_persists(client, game):
""""Modifier un élément → Contenu" : c'est ce qui permet de reconstruire
l'ancien "texte affiché au survol" (et bien d'autres usages) à la main
dans la Logique de la scène, en le combinant avec un déclencheur
"Au survol" posé sur un AUTRE élément."""
screen_id = _create_screen(client, game)
source_id = _add_element(client, game, screen_id, "titre")
target_id = _add_element(client, game, screen_id, "texte")
resp = client.post(
f"/game/{game}/screens/{screen_id}/flow/nodes/add",
data=json.dumps({
"node_type": "action", "action_type": "modifier_element",
"target_element_id": target_id, "element_property": "contenu", "element_value": "Survol actif !",
}),
content_type="application/json",
)
assert resp.status_code == 200
node = resp.get_json()
assert node["element_property"] == "contenu"
assert node["element_value"] == "Survol actif !"
assert node["target_element_id"] == target_id
# source_id n'est utilisé que pour documenter le scénario (le
# déclencheur "Au survol" se poserait dessus) — non exercé ici, déjà
# couvert par test_survol_trigger_node_persists.
assert source_id != target_id
def test_play_page_exposes_contenu_property_runtime(client, game):
resp = client.get(f"/game/{game}/play")
html = resp.data.decode()
assert "'contenu'" in html
# ---------- 3.2 — Séquences temporisées ----------
def test_attendre_action_node_persists_delay(client, game):