Files
Forge-Engine/screens/__init__.py
T
williamandClaude Sonnet 5 b094342097 Ajoute "Ligne cliquée (Répéteur)" comme cible pour une Condition/action de la Logique de la scène
Problème remonté : un déclencheur "Au clic" posé sur un Répéteur exécute
le MÊME graphe pour n'importe quelle ligne cliquée - or une Condition
("Si is_opened est égal à Non") ou une action "Modifier une donnée" ne
pouvaient viser qu'une ligne FIXE, choisie à la création du nœud dans
l'éditeur. Impossible donc de dire "modifie le champ DE LA LIGNE QUE JE
VIENS DE CLIQUER", puisque cette ligne n'est justement jamais connue à
l'avance.

Nouvelle valeur sentinelle CLICKED_ROW_ID (-1, screens/flow/constants.py,
ne collisionne jamais avec un vrai id de ligne) proposée en tête de TOUTE
liste déroulante "Ligne concernée" (clause principale et clauses
supplémentaires d'un nœud Condition, cible d'une action "Modifier une
donnée") : "🖱️ Ligne cliquée (Répéteur)".

Résolution au moment de l'exécution, pas à la création du nœud :
- Condition (évaluée côté client) : readFieldValue() (play.html) résout
  -1 en window.lastClickedRowId, déjà capturé par bindClicks() au clic sur
  une ligne de Répéteur (déjà utilisé par "Ouvrir la ligne cliquée").
- Action "Modifier une donnée" (exécutée côté serveur) : le client envoie
  clicked_row_id dans le corps de la requête POST ; flow_node_run_data.py
  ne s'en sert que si le nœud vise justement CLICKED_ROW_ID, sinon la
  ligne fixe stockée sur le nœud reste utilisée normalement.

Ajoute tests/test_flow_clicked_row.py (ligne cliquée seule modifiée,
absence de clic = no-op plutôt que plantage, non-régression d'une cible
fixe, présence de l'option dans l'éditeur).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 17:19:41 +02:00

122 lines
6.2 KiB
Python

"""
screens.py — écrans de jeu : éléments posés en glisser-déposer sur un
écran, sans jamais toucher à un fichier HTML/CSS, enchaînement des écrans,
et actions déclenchées par clic ou soumission de formulaire.
Version 3 — inspirée d'Elementor / Bricks Builder : un catalogue fermé de
WIDGETS (Texte, Titre, Image, Bouton, Case à cocher, Répéteur de
données...), chacun avec ses propres réglages ("controls") : un réglage =
un champ, une case à cocher, un bouton radio ou un curseur — jamais de nom
de balise ni de nom de propriété CSS à taper. En coulisses, chaque widget
correspond à une vraie balise HTML avec de vrais attributs et un vrai
style CSS ; l'utilisateur ne les manipule jamais directement.
Tout est stocké dans la même base SQLite que le reste du jeu
(projects/<slug>/game.db) :
- _screens : un écran du jeu (accueil, formulaire, scène...)
- _element_types : le "catalogue" des éléments de jeu réutilisables
(Personnage, Outil, IA...) que l'utilisateur crée
- _screen_elements : chaque élément posé sur un écran (widget, position
et taille en POURCENTAGE de l'écran, contenu,
attributs et style — dérivés des réglages du
widget, jamais saisis en texte libre)
- _actions : ce qui se passe au clic / à la soumission d'un
élément (écran suivant, écran précédent, aller à
un écran précis, ne rien faire)
Un élément peut aussi être un "Répéteur de données" : au lieu d'un contenu
fixe, il est relié à un objet défini dans ce jeu (voir db.py) et affiche
automatiquement une ligne par donnée existante — par exemple, tous les
niveaux d'un parcours — à partir d'un modèle de texte avec des
{{nom_de_champ}}.
Ce fichier est désormais un package (voir les sous-dossiers) organisé
selon "un fichier = une fonction, un dossier = une responsabilité" ; ce
module se contente de ré-exporter l'intégralité de l'API publique
utilisée par app.py et les templates, afin que `import screens` continue
de fonctionner à l'identique.
"""
from .widgets.registry import WIDGETS
from .widgets.container_widgets import CONTAINER_WIDGETS
from .widgets.layout_capable_widgets import LAYOUT_CAPABLE_WIDGETS
from .widgets.font_options import GOOGLE_FONTS_LINK
from .widgets.fontawesome_icons import FONTAWESOME_ICONS
from .widgets.widget_meta import widget_meta
from .widgets.controls_with_values import controls_with_values
from .widgets.data_definition_options import data_definition_options
from .labels.trigger_labels import TRIGGER_LABELS
from .labels.action_type_labels import ACTION_TYPE_LABELS
from .labels.element_action_properties import (
ELEMENT_ACTION_PROPERTIES, ELEMENT_ACTION_PROPERTY_LABELS,
ELEMENT_SURBRILLANCE_VALUES, ELEMENT_SURBRILLANCE_LABELS,
ELEMENT_DESACTIVE_VALUES, ELEMENT_DESACTIVE_LABELS,
)
from .labels.element_visibility import ELEMENT_VISIBILITY_VALUES, ELEMENT_VISIBILITY_LABELS
from .labels.data_operations import DATA_OPERATIONS, DATA_OPERATION_LABELS
from .flow.constants import TRIGGER_EVENTS, CONDITION_OPERATORS, CONDITION_OPERATOR_LABELS, CLICKED_ROW_ID
from .rendering.render_element_html import render_element_html
from .rendering.trigger_for import _trigger_for
from .screens_repo.ensure_schema import ensure_schema
from .screens_repo.list_screens import list_screens
from .screens_repo.get_screen import get_screen
from .screens_repo.create_screen import create_screen
from .screens_repo.rename_screen import rename_screen
from .screens_repo.delete_screen import delete_screen
from .screens_repo.move_screen import move_screen
from .screens_repo.screens_referencing import screens_referencing
from .screens_repo.set_screen_aspect import set_screen_aspect
from .elements.list_elements import list_elements
from .elements.get_element import get_element
from .elements.add_element import add_element
from .elements.add_tab import add_tab
from .elements.update_element_geometry import update_element_geometry
from .elements.reorder_element import reorder_element
from .elements.move_element_to_container import move_element_to_container
from .elements.save_element_controls import save_element_controls
from .elements.delete_element import delete_element
from .elements.flow_nodes_referencing_element import flow_nodes_referencing_element
from .elements.duplicate_element import duplicate_element
from .elements.rename_element import rename_element
from .elements.element_label import element_label
from .element_types.list_element_types import list_element_types
from .element_types.get_element_type_by_template_screen import get_element_type_by_template_screen
from .element_types.create_element_type import create_element_type
from .element_types.update_element_type import update_element_type
from .element_types.element_type_usage_count import element_type_usage_count
from .element_types.delete_element_type import delete_element_type
from .legacy_actions.list_actions import list_actions
from .legacy_actions.add_action import add_action
from .legacy_actions.get_action import get_action
from .legacy_actions.delete_action import delete_action
from .flow.ensure_flow_schema import ensure_flow_schema
from .flow.list_flow_nodes import list_flow_nodes
from .flow.list_flow_edges import list_flow_edges
from .flow.get_flow_node import get_flow_node
from .flow.add_flow_node import add_flow_node
from .flow.move_flow_node import move_flow_node
from .flow.delete_flow_node import delete_flow_node
from .flow.add_flow_edge import add_flow_edge
from .flow.delete_flow_edge import delete_flow_edge
from .data_actions.apply_data_action import apply_data_action
from .data_actions.apply_variable_action import apply_variable_action
from .animations.ensure_animation_schema import ensure_animation_schema
from .animations.list_animation_clips import list_animation_clips
from .animations.get_animation_clip import get_animation_clip
from .animations.add_animation_clip import add_animation_clip
from .animations.update_animation_clip import update_animation_clip
from .animations.delete_animation_clip import delete_animation_clip
from .animations.animate_css_catalog import ANIMATE_CSS_CATALOG, ANIMATE_CSS_LABELS
from .payload.full_game_payload import full_game_payload