diff --git a/static/style.css b/static/style.css index 7f772bd7..2a5ec86b 100644 --- a/static/style.css +++ b/static/style.css @@ -150,33 +150,37 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d toutes les trois occupent exactement la même hauteur (celle qui reste disponible), avec leur propre défilement interne si leur contenu dépasse. */ .builder3{ display:flex; gap:20px; align-items:stretch; margin-top:14px; flex:1 1 auto; min-height:0; } -.builderPanel{ flex:0 0 260px; min-width:0; height:100%; overflow-y:auto; } .builderCanvasArea{ flex:1 1 auto; min-width:0; height:100%; overflow-y:auto; display:flex; flex-direction:column; } -/* ---------- Panneau de propriétés flottant ---------- */ -/* Sorti du flux flex de .builder3 (position:fixed) : .builderCanvasArea - (flex:1 1 auto) récupère automatiquement l'espace libéré, sans réglage - supplémentaire — un panneau flottant repositionné par l'utilisateur - n'agrandit/ne rétrécit donc jamais le canevas de travers. Position et - état ouvert/fermé mémorisés par écran (voir restorePropsPanelState dans - screen_edit.html) pour survivre à un changement de sélection ou un - rechargement. */ -.builderPanelRight{ - position:fixed; top:90px; right:24px; width:340px; max-height:calc(100vh - 120px); +/* ---------- Panneaux gauche/droite flottants ---------- */ +/* Les deux panneaux (éléments de l'écran / propriétés) sont sortis du flux + flex de .builder3 (position:fixed) : .builderCanvasArea (flex:1 1 auto) + récupère automatiquement tout l'espace libéré, sans réglage + supplémentaire — les fermer donne donc une prévisualisation aussi large + que possible, aussi fidèle que possible à ce que "Jouer" affiche + réellement. Position et état ouvert/fermé mémorisés par écran (voir + floatPanel*() dans screen_edit.html) pour survivre à un changement de + sélection (qui remplace tout #builder3) ou un rechargement. */ +.floatPanel{ + position:fixed; top:90px; max-height:calc(100vh - 120px); min-width:0; display:flex; flex-direction:column; z-index:60; background:var(--panel); border:1px solid var(--border); border-radius:10px; box-shadow:0 12px 40px rgba(0,0,0,.55); } -.builderPanelRight.hidden{ display:none; } -.propsPanelHeader{ +.floatPanel.hidden{ display:none; } +.floatPanel--left{ left:24px; width:280px; } +.floatPanel--right{ right:24px; width:340px; } +.floatPanelHeader{ display:flex; align-items:center; justify-content:space-between; gap:8px; padding:8px 10px; border-bottom:1px solid var(--border); background:var(--panel2); border-radius:10px 10px 0 0; cursor:move; flex:0 0 auto; user-select:none; } -.propsPanelHeader .propsPanelTitle{ font-size:11px; color:var(--text-dim); text-transform:uppercase; letter-spacing:.05em; } -.propsPanelBody{ padding:12px; overflow-y:auto; min-height:0; flex:1 1 auto; } -#propsPanelReopenBtn{ position:fixed; top:90px; right:24px; z-index:60; display:none; } -#propsPanelReopenBtn.visible{ display:inline-flex; } +.floatPanelHeader .floatPanelTitle{ font-size:11px; color:var(--text-dim); text-transform:uppercase; letter-spacing:.05em; } +.floatPanelBody{ padding:12px; overflow-y:auto; min-height:0; flex:1 1 auto; } +.floatPanelReopenBtn{ position:fixed; top:90px; z-index:60; display:none; } +.floatPanelReopenBtn.visible{ display:inline-flex; } +#panelLeftReopenBtn{ left:24px; } +#propsPanelReopenBtn{ right:24px; } .widgetGrid{ display:grid; grid-template-columns:1fr 1fr; gap:8px; } .widgetBtnForm{ margin:0; } @@ -314,7 +318,7 @@ body.builderBody > main.content{ flex:1 1 auto; min-height:0; overflow:hidden; d .sliderValue{ min-width:44px; text-align:right; } input[type=color]{ width:52px; height:34px; padding:2px; } -.propsPanel .sectionDivider, .builderPanelRight .sectionDivider{ border:none; border-top:1px solid var(--border); margin:14px 0; } +.propsPanel .sectionDivider, .floatPanel .sectionDivider{ border:none; border-top:1px solid var(--border); margin:14px 0; } .actionAddForm{ margin-top:10px; } .uploadRow{ margin-top:6px; } @@ -330,13 +334,13 @@ code{ background:var(--panel2); border:1px solid var(--border); border-radius:4p body.builderBody{ height:auto; overflow:visible; } body.builderBody > main.content{ overflow:visible; } .builder3{ flex-direction:column; flex:none; } - .builderPanel, .builderCanvasArea{ flex:none; width:100%; height:auto; max-height:none; overflow:visible; } + .builderCanvasArea{ flex:none; width:100%; height:auto; max-height:none; overflow:visible; } /* Trop peu de largeur pour qu'un panneau flottant ait un sens (il - couvrirait tout l'écran) : il rejoint la pile normale comme les deux - autres colonnes, redimensionnement/déplacement désactivés (voir le - garde useFloatingPanel() dans screen_edit.html). */ - .builderPanelRight{ position:static; flex:none; width:100%; height:auto; max-height:none; box-shadow:none; top:auto; right:auto; } - #propsPanelReopenBtn{ display:none !important; } + couvrirait tout l'écran) : les deux rejoignent la pile normale et + restent toujours ouverts/visibles, quel que soit l'état mémorisé — + .hidden est ignoré grâce au "!important" sur "display" ci-dessous. */ + .floatPanel{ position:static; flex:none; width:100%; height:auto; max-height:none; box-shadow:none; top:auto; left:auto; right:auto; display:flex !important; } + .floatPanelReopenBtn{ display:none !important; } } /* ---------- Logique de la scène : éditeur de flow à nœuds ---------- */ diff --git a/templates/screen_edit.html b/templates/screen_edit.html index 733f3702..d1a4efcd 100644 --- a/templates/screen_edit.html +++ b/templates/screen_edit.html @@ -57,8 +57,16 @@
{% set el = elements|selectattr("id", "equalto", selected_id)|first if selected_id else none %} - -
+ + +
+
+ ⋮⋮ Éléments de cet écran + +
+

Éléments de cet écran

{% if top_level_elements %}
@@ -112,6 +120,7 @@ {% endif %}
+
@@ -144,14 +153,14 @@ - -
-
- ⋮⋮ Propriétés + canevas (voir floatPanel*() plus bas). --> + +
+
+ ⋮⋮ Propriétés
-
+
{% if not selected_id %}
Sélectionne un élément (sur l'écran ou dans la liste) pour voir et modifier ses propriétés ici.
{% else %} @@ -738,35 +747,38 @@ function restoreTreeCollapsedState() { }); } -// ---------- Panneau de propriétés : flottant, déplaçable, fermable ---------- +// ---------- Panneaux gauche/droite : flottants, déplaçables, fermables ---------- // -// Sorti du flux (position:fixed en CSS) pour ne jamais rétrécir le canevas -// selon où on le pose — le fermer libère toute cette largeur pour une +// Sortis du flux (position:fixed en CSS) pour ne jamais rétrécir le canevas +// selon où on les pose — les fermer libère cette largeur pour une // prévisualisation plus fidèle à ce que "Jouer" affiche réellement (voir -// bug remonté : un panneau trop large réduisait le canevas au point de -// casser la mise en page des onglets). Position et état ouvert/fermé -// mémorisés par écran pour survivre à un changement de sélection (qui -// remplace tout #builder3, voir refreshBuilderPanel) ou un rechargement. -function propsPanelStateKey() { - return 'forgePropsPanel:' + GAME_SLUG + ':' + SCREEN_ID; +// bug remonté : des panneaux trop larges réduisaient le canevas au point de +// casser la mise en page des onglets). Un seul jeu de fonctions génériques +// (key = identifiant court mémorisé dans localStorage, distinct par +// panneau) plutôt qu'une copie par panneau. Position et état ouvert/fermé +// survivent à un changement de sélection (qui remplace tout #builder3, voir +// refreshBuilderPanel) ou un rechargement — voir les appels à +// restoreFloatPanelState() dans initBuilderPanel(). +function floatPanelStateKey(key) { + return 'forgeFloatPanel:' + GAME_SLUG + ':' + SCREEN_ID + ':' + key; } -function savePropsPanelState(state) { +function saveFloatPanelState(key, state) { try { - var current = JSON.parse(localStorage.getItem(propsPanelStateKey()) || '{}'); - localStorage.setItem(propsPanelStateKey(), JSON.stringify(Object.assign(current, state))); + var current = JSON.parse(localStorage.getItem(floatPanelStateKey(key)) || '{}'); + localStorage.setItem(floatPanelStateKey(key), JSON.stringify(Object.assign(current, state))); } catch (e) {} } -function loadPropsPanelState() { - try { return JSON.parse(localStorage.getItem(propsPanelStateKey()) || '{}'); } catch (e) { return {}; } +function loadFloatPanelState(key) { + try { return JSON.parse(localStorage.getItem(floatPanelStateKey(key)) || '{}'); } catch (e) { return {}; } } -function restorePropsPanelState() { - var panel = document.getElementById('propsPanelFloat'); - var reopenBtn = document.getElementById('propsPanelReopenBtn'); +function restoreFloatPanelState(key, panelId, reopenBtnId) { + var panel = document.getElementById(panelId); + var reopenBtn = document.getElementById(reopenBtnId); if (!panel) return; - var state = loadPropsPanelState(); + var state = loadFloatPanelState(key); if (state.left != null && state.top != null) { panel.style.left = state.left + 'px'; panel.style.top = state.top + 'px'; @@ -777,27 +789,35 @@ function restorePropsPanelState() { if (reopenBtn) reopenBtn.classList.toggle('visible', !open); } -function closePropsPanel() { - var panel = document.getElementById('propsPanelFloat'); - var reopenBtn = document.getElementById('propsPanelReopenBtn'); +function closeFloatPanel(key, panelId, reopenBtnId) { + var panel = document.getElementById(panelId); + var reopenBtn = document.getElementById(reopenBtnId); if (panel) panel.classList.add('hidden'); if (reopenBtn) reopenBtn.classList.add('visible'); - savePropsPanelState({ open: false }); + saveFloatPanelState(key, { open: false }); } -function openPropsPanel() { - var panel = document.getElementById('propsPanelFloat'); - var reopenBtn = document.getElementById('propsPanelReopenBtn'); +function openFloatPanel(key, panelId, reopenBtnId) { + var panel = document.getElementById(panelId); + var reopenBtn = document.getElementById(reopenBtnId); if (panel) panel.classList.remove('hidden'); if (reopenBtn) reopenBtn.classList.remove('visible'); - savePropsPanelState({ open: true }); + saveFloatPanelState(key, { open: true }); } -if (!window.__forgePropsPanelDragBound) { - window.__forgePropsPanelDragBound = true; +// Raccourcis utilisés directement par les attributs onclick du gabarit. +function closePropsPanel() { closeFloatPanel('props', 'propsPanelFloat', 'propsPanelReopenBtn'); } +function openPropsPanel() { openFloatPanel('props', 'propsPanelFloat', 'propsPanelReopenBtn'); } +function closeLeftPanel() { closeFloatPanel('left', 'panelLeftFloat', 'panelLeftReopenBtn'); } +function openLeftPanel() { openFloatPanel('left', 'panelLeftFloat', 'panelLeftReopenBtn'); } + +if (!window.__forgeFloatPanelDragBound) { + window.__forgeFloatPanelDragBound = true; document.addEventListener('mousedown', function (e) { - if (!e.target.closest('#propsPanelDragHandle') || e.target.closest('button')) return; - var panel = document.getElementById('propsPanelFloat'); + var handle = e.target.closest('.floatPanelHeader'); + if (!handle || e.target.closest('button')) return; + var panel = document.getElementById(handle.dataset.dragPanel); + var key = handle.dataset.dragKey; if (!panel) return; var rect = panel.getBoundingClientRect(); var startX = e.clientX, startY = e.clientY; @@ -814,7 +834,7 @@ if (!window.__forgePropsPanelDragBound) { function onUp() { document.removeEventListener('mousemove', onMove); document.removeEventListener('mouseup', onUp); - savePropsPanelState({ left: parseFloat(panel.style.left), top: parseFloat(panel.style.top) }); + saveFloatPanelState(key, { left: parseFloat(panel.style.left), top: parseFloat(panel.style.top) }); } document.addEventListener('mousemove', onMove); document.addEventListener('mouseup', onUp); @@ -960,8 +980,8 @@ function refreshAfterReorder() { curCanvas.innerHTML = newCanvas.innerHTML; applySelectionHighlight(CURRENT_SELECTED_ID); } - var newTree = doc.querySelector('.builderPanel .elementTree'); - var curTree = document.querySelector('.builderPanel .elementTree'); + var newTree = doc.querySelector('#panelLeftFloat .elementTree'); + var curTree = document.querySelector('#panelLeftFloat .elementTree'); if (newTree && curTree) { curTree.innerHTML = newTree.innerHTML; } restoreTreeCollapsedState(); }); @@ -1016,8 +1036,8 @@ function refreshCanvasOnly() { // Aperçu du contenu dans l'arborescence de gauche (ex: texte tronqué) — // sans toucher au panneau de droite, pour ne jamais faire perdre le // focus/curseur pendant qu'on modifie un réglage. - var newTree = doc.querySelector('.builderPanel .elementTree'); - var curTree = document.querySelector('.builderPanel .elementTree'); + var newTree = doc.querySelector('#panelLeftFloat .elementTree'); + var curTree = document.querySelector('#panelLeftFloat .elementTree'); if (newTree && curTree) { curTree.innerHTML = newTree.innerHTML; restoreTreeCollapsedState(); } }); } @@ -1224,7 +1244,8 @@ function initBuilderPanel() { bindJaugeDefinitionSelect(); restoreTreeCollapsedState(); applySelectionHighlight(CURRENT_SELECTED_ID); - restorePropsPanelState(); + restoreFloatPanelState('props', 'propsPanelFloat', 'propsPanelReopenBtn'); + restoreFloatPanelState('left', 'panelLeftFloat', 'panelLeftReopenBtn'); } // Jauge : quand on change l'objet de données à suivre SANS quitter le