Ajoute la fonctionnalite quiz autonome/plein ecran a la boite a quiz
Introduit la double categorie de modeles (boite de dialogue / page de quiz plein ecran) avec plein ecran, minuteur, score integre et ecran de resultat pour les modeles page ; ajoute les modeles "Manga" (boite et page) et "Classique" (page), pilotables aussi par l'assistant IA Ruby. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
559331f9cf
commit
7db4803b93
+14
-1
@@ -42,6 +42,18 @@
|
||||
#playFrame.sceneMode{ width:100vw; height:100vh; }
|
||||
.playScreen{ position:absolute; inset:0; display:none; }
|
||||
.playScreen.playScene{ inset:auto; top:50%; left:50%; transform:translate(-50%, -50%); background:#1a1d24; }
|
||||
/* "La notion de caméra disparaît" quand un quiz plein écran existe sur
|
||||
cet écran (demande explicite, voir screens/payload/full_game_payload.py
|
||||
::any_quiz_box_fullscreen) — le réglage "Caméra (px)" (scene_width/
|
||||
scene_height, qui donne à .playScene ci-dessus sa taille ET son
|
||||
`transform` de centrage) ne s'applique alors plus DU TOUT : cet écran
|
||||
devient lui-même plein viewport, sans transform (qui casserait
|
||||
position:fixed pour tout descendant, voir dialogue-box-controller.js
|
||||
::forgeEscapeCameraForFullscreen — plus nécessaire ici puisque
|
||||
.playScene n'a elle-même plus de transform dans ce cas). */
|
||||
.playScreen.playScene.playScene--quizFullscreen{
|
||||
position:fixed; inset:0; top:0; left:0; width:100vw; height:100vh; transform:none;
|
||||
}
|
||||
.playElement{
|
||||
position:absolute; box-sizing:border-box; font-family:system-ui,sans-serif;
|
||||
}
|
||||
@@ -89,7 +101,7 @@
|
||||
<!-- Un seul moteur d'écran désormais (jeu_2d) : objets déjà entièrement
|
||||
positionnés en pixels absolus par render_scene_object.py (voir
|
||||
screens/scenes/) — juste le HTML tel quel. -->
|
||||
<div class="playScreen playScene" data-screen-id="{{ s.id }}" data-kind="jeu_2d" style="width:{{ s.scene_width }}px; height:{{ s.scene_height }}px;">
|
||||
<div class="playScreen playScene {{ 'playScene--quizFullscreen' if s.any_quiz_box_fullscreen else '' }}" data-screen-id="{{ s.id }}" data-kind="jeu_2d" style="{{ '' if s.any_quiz_box_fullscreen else 'width:' + s.scene_width|string + 'px; height:' + s.scene_height|string + 'px;' }}">
|
||||
<!-- .sceneWorld : le "monde" (voir static/style.css et
|
||||
static/js/play/personnage-controller.js) — taille par défaut 100%
|
||||
(identique au viewport ci-dessus), agrandie/décalée en JS
|
||||
@@ -180,6 +192,7 @@
|
||||
<script src="{{ asset_url('js/play/actions.js') }}"></script>
|
||||
<script src="{{ asset_url('js/play/triggers.js') }}"></script>
|
||||
<script src="{{ asset_url('js/play/personnage-controller.js') }}"></script>
|
||||
<script src="{{ asset_url('js/play/quiz-box-templates.js') }}"></script>
|
||||
<script src="{{ asset_url('js/play/dialogue-box-controller.js') }}"></script>
|
||||
<script src="{{ asset_url('js/play/video-overlay.js') }}"></script>
|
||||
<script src="{{ asset_url('js/play/collision-rules-controller.js') }}"></script>
|
||||
|
||||
+110
-4
@@ -274,9 +274,20 @@
|
||||
|
||||
<div class="builderTabPanel active" data-tab-panel="screen">
|
||||
<div class="canvasFrame">
|
||||
<div class="canvas" id="canvas" style="width:{{ world_width }}px; height:{{ world_height }}px; aspect-ratio:auto; margin:0 auto; position:relative; overflow:hidden; background:#1a1d24;"
|
||||
<!-- Un quiz plein écran fait disparaître la notion de caméra
|
||||
(demande explicite) — pas seulement le cadre "🎥 Champ de la
|
||||
caméra" (déjà masqué plus bas), mais le CANEVAS lui-même :
|
||||
sans ce cas particulier, il restait dimensionné en pixels
|
||||
fixes (world_width/world_height, une notion de caméra/scène
|
||||
nominale) — .canvasElement.isQuizFullscreenPreview a beau
|
||||
remplir 100% de CE canevas, le canevas restait lui-même de la
|
||||
taille de la caméra, laissant une bande vide visible autour.
|
||||
width/height:100% (voir la règle .canvas déjà prévue pour ça
|
||||
dans static/style.css) fait remplir tout .canvasFrame à la
|
||||
place, indépendamment de toute taille de scène/caméra. -->
|
||||
<div class="canvas" id="canvas" style="{{ 'width:100%; height:100%;' if any_quiz_box_fullscreen else 'width:' + world_width|string + 'px; height:' + world_height|string + 'px;' }} aspect-ratio:auto; margin:0 auto; position:relative; overflow:hidden; background:#1a1d24;"
|
||||
ondragover="onCanvasDragOver(event)" ondrop="onCanvasDrop(event)">
|
||||
{% if world_width > screen.scene_width or world_height > screen.scene_height %}
|
||||
{% if not any_quiz_box_fullscreen and (world_width > screen.scene_width or world_height > screen.scene_height) %}
|
||||
<!-- "🎥 Champ de la caméra" : le monde (ce canevas, voir
|
||||
world_width/world_height ci-dessus, routes/scenes/
|
||||
scene_edit_view.py) peut être plus grand que la scène
|
||||
@@ -286,11 +297,14 @@
|
||||
controller.js::forgeUpdateSceneCamera), pour que le monde
|
||||
agrandi reste lisible plutôt qu'une simple étendue grise
|
||||
sans repère. Purement visuel (pointer-events:none) : ne
|
||||
gêne jamais la sélection/le glisser d'un objet dessous. -->
|
||||
gêne jamais la sélection/le glisser d'un objet dessous.
|
||||
Masqué dès qu'un quiz PLEIN ÉCRAN existe sur cet écran
|
||||
(demande explicite : "la notion de caméra disparaît") —
|
||||
il n'a plus aucun sens une fois que ce widget couvre tout. -->
|
||||
<div class="sceneCameraFrame" style="width:{{ screen.scene_width }}px; height:{{ screen.scene_height }}px;" title="Champ de la caméra en jeu ({{ screen.scene_width }}×{{ screen.scene_height }} px)"></div>
|
||||
{% endif %}
|
||||
{% for o in objects %}
|
||||
<div class="canvasElement {{ 'selected' if o.id == selected_id else '' }}" id="el-{{ o.id }}"
|
||||
<div class="canvasElement {{ 'selected' if o.id == selected_id else '' }} {{ 'isQuizFullscreenPreview' if o.quiz_fullscreen else '' }}" id="el-{{ o.id }}"
|
||||
style="position:absolute; left:{{ o.x }}px; top:{{ o.y }}px; width:{{ o.width }}px; height:{{ o.height }}px;"
|
||||
onmousedown="onSceneObjectMouseDown(event, {{ o.id }})">
|
||||
<div class="canvasElementInner">{{ o.rendered_html|safe }}</div>
|
||||
@@ -562,6 +576,98 @@
|
||||
</details>
|
||||
{% endif %}
|
||||
|
||||
{% if obj.kind == 'quiz_box' %}
|
||||
<!-- "⚙️ Options du quiz" (demande explicite : "étendre les
|
||||
fonctionnalités de la boîte à quiz pour les RH qui veulent
|
||||
juste faire un quiz") — voir screens/rendering/quiz_box_config.py,
|
||||
routes/scenes/scene_object_quiz_config.py. Plein écran/minuteur,
|
||||
DISTINCT de "🎨 Style" ci-dessus (couleurs/police, partagé avec
|
||||
dialogue_box/score_widget). -->
|
||||
<details class="propGroup" open>
|
||||
<summary>Options du quiz</summary>
|
||||
<div class="propGroupBody">
|
||||
<label class="checkbox is-small">
|
||||
<input type="checkbox" id="quiz-fullscreen" data-was-checked="{{ '1' if quiz_box_config.fullscreen else '0' }}" {{ 'checked' if quiz_box_config.fullscreen else '' }} onchange="saveQuizBoxConfig({{ obj.id }})">
|
||||
Plein écran
|
||||
</label>
|
||||
<div class="field mt-2">
|
||||
<label class="label is-small">Minuteur</label>
|
||||
<div class="control">
|
||||
<select class="input is-small" id="quiz-timer-mode" onchange="saveQuizBoxConfig({{ obj.id }})">
|
||||
{% for mode in quiz_box_timer_modes %}
|
||||
<option value="{{ mode }}" {{ 'selected' if quiz_box_config.timer_mode == mode else '' }}>
|
||||
{{ {'aucun': 'Aucun', 'question': 'Par question', 'quiz': 'Pour tout le quiz'}[mode] }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label is-small">Durée (secondes)</label>
|
||||
<div class="control">
|
||||
<input class="input is-small" type="number" id="quiz-timer-seconds" min="3" max="600" value="{{ quiz_box_config.timer_seconds }}" onchange="saveQuizBoxConfig({{ obj.id }})">
|
||||
</div>
|
||||
</div>
|
||||
<p class="help">Le plein écran et le minuteur ne s'appliquent qu'au jeu (jamais dans cet éditeur).</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<!-- "🖼️ Modèles" — DEUX catégories bien distinctes (demande
|
||||
explicite), une SEULE des deux visible/modifiable à la fois
|
||||
selon "Plein écran" ci-dessus (les deux valeurs restent
|
||||
conservées indépendamment, voir screens/rendering/
|
||||
quiz_box_config.py) :
|
||||
- "boîte de dialogue" (hors plein écran) : variantes de forme/
|
||||
couleur sur une structure proche de "Classique" ;
|
||||
- "page de quiz" (plein écran) : thème complet et autonome,
|
||||
structure HTML propre à CHAQUE modèle (voir
|
||||
screens/rendering/quiz_box_templates.py,
|
||||
render_quiz_box_page_manga pour le premier).
|
||||
Ce <div> caché retient la valeur de CELUI des deux réglages
|
||||
qui n'a pas de galerie affichée ici, pour que saveQuizBoxConfig
|
||||
(scene-editor.js) ne l'écrase jamais par erreur en l'absence de
|
||||
sa grille. -->
|
||||
<div id="quizBoxTemplateMeta" data-dialog-template="{{ quiz_box_config.dialog_template }}" data-page-template="{{ quiz_box_config.page_template }}" style="display:none;"></div>
|
||||
|
||||
{% if quiz_box_config.fullscreen %}
|
||||
<details class="propGroup" open>
|
||||
<summary>Modèles — page de quiz</summary>
|
||||
<div class="propGroupBody">
|
||||
<p class="help mb-2">Visibles seulement en plein écran — un thème complet et autonome par modèle (police, formes, couleurs et structure propres à chacun).</p>
|
||||
<div class="quizTemplateGrid" id="quizPageTemplateGrid" data-object-id="{{ obj.id }}">
|
||||
{% for key in quiz_box_page_templates %}
|
||||
<button type="button" class="quizTemplateTile {{ 'is-selected' if quiz_box_config.page_template == key else '' }}"
|
||||
data-template="{{ key }}" onclick="selectQuizBoxPageTemplate({{ obj.id }}, '{{ key }}')">
|
||||
<span class="quizTemplatePreviewImg">
|
||||
<img src="{{ url_for('static', filename='quiz-templates/' + key + '.png') }}" alt="Aperçu du modèle « {{ quiz_box_page_template_labels[key] }} »" loading="lazy">
|
||||
</span>
|
||||
<span class="quizTemplateName">{{ quiz_box_page_template_labels[key] }}</span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% else %}
|
||||
<details class="propGroup" open>
|
||||
<summary>Modèles — boîte de dialogue</summary>
|
||||
<div class="propGroupBody">
|
||||
<p class="help mb-2">Visibles seulement hors plein écran — un thème complet et autonome par modèle (police, formes, couleurs et structure propres à chacun).</p>
|
||||
<div class="quizTemplateGrid" id="quizDialogTemplateGrid" data-object-id="{{ obj.id }}">
|
||||
{% for key in quiz_box_dialog_templates %}
|
||||
<button type="button" class="quizTemplateTile {{ 'is-selected' if quiz_box_config.dialog_template == key else '' }}"
|
||||
data-template="{{ key }}" onclick="selectQuizBoxDialogTemplate({{ obj.id }}, '{{ key }}')">
|
||||
<span class="quizTemplatePreviewImg">
|
||||
<img src="{{ url_for('static', filename='quiz-templates/' + key + '.png') }}" alt="Aperçu du modèle « {{ quiz_box_dialog_template_labels[key] }} »" loading="lazy">
|
||||
</span>
|
||||
<span class="quizTemplateName">{{ quiz_box_dialog_template_labels[key] }}</span>
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if obj.kind not in ('fond', 'dialogue_box', 'quiz_box', 'score_widget') %}
|
||||
<!-- Boîte de collision (voir screens/rendering/collision_settings.py,
|
||||
static/js/play/conditions.js::elementsOverlap) : posée AUTOMATIQUEMENT
|
||||
|
||||
Reference in New Issue
Block a user