From 6ce8010d2f3e025a702a37a52e76e695923c33fe Mon Sep 17 00:00:00 2001 From: tritant Date: Sun, 21 Sep 2025 13:45:03 +0200 Subject: [PATCH] code cleanup --- js/layer_system_final.js | 17 +++++---------- js/magic_wand_manager.js | 45 ++++++++++++---------------------------- js/toolbar.js | 39 +++++----------------------------- 3 files changed, 23 insertions(+), 78 deletions(-) diff --git a/js/layer_system_final.js b/js/layer_system_final.js index 7d16434..a7e862c 100644 --- a/js/layer_system_final.js +++ b/js/layer_system_final.js @@ -126,10 +126,10 @@ app.registerExtension({ if (this.toolbar?.maskManager?.contextualToolbar) { this.toolbar.maskManager.contextualToolbar.remove(); } - if (this.toolbar.selectionSubMenu) { // Le sous-menu de choix d'outil + if (this.toolbar.selectionSubMenu) { this.toolbar.selectionSubMenu.remove(); } - if (this.toolbar.magicWandManager?.settingsToolbar) { // La barre de réglages de la baguette + if (this.toolbar.magicWandManager?.settingsToolbar) { this.toolbar.magicWandManager.settingsToolbar.remove(); } }; @@ -189,11 +189,11 @@ this.widgets.push(topSpacer); this.previewCtx = canvas.getContext("2d"); const overlayCanvas = document.createElement("canvas"); - overlayCanvas.id = "ls-overlay-canvas"; // On lui donne un ID + overlayCanvas.id = "ls-overlay-canvas"; overlayCanvas.style.position = "absolute"; overlayCanvas.style.top = "0"; overlayCanvas.style.left = "0"; - overlayCanvas.style.pointerEvents = "none"; // Très important: pour que les clics passent à travers + overlayCanvas.style.pointerEvents = "none"; container.appendChild(overlayCanvas); this.overlayCanvas = overlayCanvas; @@ -207,14 +207,11 @@ this.previewCanvas.addEventListener('mousedown', (e) => { const mouseX = e.offsetX; const mouseY = e.offsetY; - // PRIORITÉ N°1 : Le clic est-il sur une icône de la barre d'outils principale ? - // C'est la priorité absolue pour pouvoir changer d'outil à tout moment. if (this.toolbar.isClickOnToolbar(mouseX, mouseY)) { this.toolbar.handleClick(e, mouseX, mouseY); return; } - // PRIORITÉ N°2 : Sommes-nous dans un mode d'action spécial (comme le déplacement de texte) ? if (this.textActionMode === 'moving' && this.activeTextObject) { const targetText = this.findTextElementAtPos(mouseX, mouseY); if (targetText && targetText.id === this.activeTextObject.id) { @@ -229,18 +226,15 @@ this.previewCanvas.addEventListener('mousedown', (e) => { return; } - // PRIORITÉ N°3 : Un outil est-il actif pour un clic sur la zone de l'image ? - // Ici, on met la baguette magique, puis les autres outils. if (this.toolbar.activeTool === 'magic_wand' && this.toolbar.magicWandManager) { this.toolbar.magicWandManager.handleCanvasClick(e); return; } - if (this.toolbar.activeTool) { // Gère les autres outils comme le texte + if (this.toolbar.activeTool) { this.toolbar.handleCanvasClick(e); return; } - // PRIORITÉ N°4 : Si rien de tout ça, est-ce une action sur un calque (déplacement, etc.) ? if (!this.movingLayer) return; const props = this.layer_properties[this.movingLayer]; if (!props) return; @@ -1184,7 +1178,6 @@ canvas.dataset.layerName = layerName; ctx.scale(moveIconSize / 24, moveIconSize / 24); ctx.strokeStyle = isMoving ? "#F44" : (isMoveDisabled ? "#555" : LiteGraph.WIDGET_TEXT_COLOR); - //ctx.strokeStyle = isMoving ? "#F44" : (props.resize_mode !== 'crop' || (this.toolbar && this.toolbar.activeTool) ? "#555" : LiteGraph.WIDGET_TEXT_COLOR); ctx.lineWidth = 2; ctx.stroke(moveIconPath); ctx.restore(); diff --git a/js/magic_wand_manager.js b/js/magic_wand_manager.js index 4b71d89..f89e5d7 100644 --- a/js/magic_wand_manager.js +++ b/js/magic_wand_manager.js @@ -3,8 +3,6 @@ export class MagicWandManager { this.node = node; this.contextualToolbar = null; this.activeMaskPreview = null; - //this.lineDashOffset = 0; - // On stocke les réglages de l'outil ici this.settings = { tolerance: 32, contiguous: true, @@ -109,7 +107,6 @@ showSelectionPreview(maskImage, props, layerImage) { overlay.height = preview.height; ctx.clearRect(0, 0, overlay.width, overlay.height); - // --- Étape 1 : On crée l'image du surlignage, pixel par pixel --- const tempCanvas = document.createElement('canvas'); const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true }); tempCanvas.width = maskImage.naturalWidth; @@ -119,17 +116,15 @@ showSelectionPreview(maskImage, props, layerImage) { const highlightData = tempCtx.createImageData(tempCanvas.width, tempCanvas.height); for (let i = 0; i < maskData.data.length; i += 4) { - if (maskData.data[i] > 128) { // Si le pixel du masque est blanc... - highlightData.data[i] = 255; // R - highlightData.data[i + 1] = 0; // G - highlightData.data[i + 2] = 0; // B - highlightData.data[i + 3] = 102; // Alpha (opacité de 40%) + if (maskData.data[i] > 128) { + highlightData.data[i] = 255; + highlightData.data[i + 1] = 0; + highlightData.data[i + 2] = 0; + highlightData.data[i + 3] = 102; } } - // On met notre surlignage rouge sur le canvas temporaire - tempCtx.putImageData(highlightData, 0, 0); + tempCtx.putImageData(highlightData, 0, 0); - // --- Étape 2 : On dessine ce surlignage avec les bonnes transformations --- const previewCanvasScale = this.node.previewCanvasScale || 1.0; const imageAreaCenterX = this.node.toolbar.width + (preview.width - this.node.toolbar.width) / 2; const imageAreaCenterY = preview.height / 2; @@ -143,8 +138,7 @@ showSelectionPreview(maskImage, props, layerImage) { ctx.save(); ctx.translate(centerX, centerY); ctx.rotate(angleRad); - - // On dessine notre canvas temporaire (qui contient le surlignage) avec les transformations + ctx.drawImage(tempCanvas, -transformedWidth / 2, -transformedHeight / 2, transformedWidth, transformedHeight); ctx.restore(); @@ -175,14 +169,13 @@ hideSelectionPreview() { Object.assign(this.contextualToolbar.style, { position: 'fixed', display: 'none', - top: '20px', // Position temporaire - left: '20px', // Position temporaire + top: '20px', + left: '20px', zIndex: '10001', backgroundColor: 'rgba(40, 40, 40, 0.9)', border: '1px solid #555', borderRadius: '8px', padding: '10px', - //display: 'flex', alignItems: 'center', gap: '6px', fontFamily: 'sans-serif', @@ -190,7 +183,6 @@ hideSelectionPreview() { color: 'white', }); - // --- Slider de Tolérance --- const toleranceLabel = document.createElement("label"); toleranceLabel.innerText = "Tolerance :"; const toleranceInput = document.createElement("input"); @@ -207,7 +199,6 @@ hideSelectionPreview() { toleranceValue.innerText = this.settings.tolerance; toleranceValue.style.minWidth = "25px"; - // --- Case à cocher "Contigu" --- const contiguousLabel = document.createElement("label"); contiguousLabel.innerText = "Contigu :"; const contiguousInput = document.createElement("input"); @@ -234,7 +225,6 @@ hideSelectionPreview() { this.settings.fusionMode = e.target.value; }; - // --- Bouton "Appliquer" --- const applyButton = document.createElement("button"); applyButton.innerText = "Apply mask"; applyButton.onclick = async () => { @@ -249,7 +239,6 @@ hideSelectionPreview() { applyButton.disabled = true; try { - // 1. On transforme notre image de preview en un fichier temporaire sur le serveur const tempCanvas = document.createElement('canvas'); tempCanvas.width = this.activeMaskPreview.naturalWidth; tempCanvas.height = this.activeMaskPreview.naturalHeight; @@ -258,11 +247,9 @@ hideSelectionPreview() { const tempFile = new File([blob], `temp_selection_mask.png`, { type: "image/png" }); const newMaskDetails = await this._uploadFile(tempFile, false); - // 2. On récupère le nom du masque déjà existant sur le calque const props = this.node.layer_properties[activeLayer.name]; const existingMaskFilename = props.internal_mask_filename || null; - // 3. On appelle l'API Python pour fusionner les deux masques const response = await fetch("/layersystem/apply_mask", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ @@ -276,15 +263,13 @@ hideSelectionPreview() { if (result.success && result.render_mask_details && result.preview_mask_details && result.editor_mask_details) { - // On met à jour les propriétés du calque avec les 3 nouveaux masques - props.internal_mask_filename = result.render_mask_details.name; // Le rendu final utilise le _render_ + props.internal_mask_filename = result.render_mask_details.name; props.internal_mask_details = result.render_mask_details; props.internal_preview_mask_details = result.preview_mask_details; - props.internal_editor_mask_details = result.editor_mask_details; // On pourrait stocker celui-ci aussi + props.internal_editor_mask_details = result.editor_mask_details; props.mask_last_update = Date.now(); this.node.updatePropertiesJSON(); - // 5. On met à jour les previews dans l'interface const finalMaskUrl = new URL('/view', window.location.origin); finalMaskUrl.searchParams.set("filename", result.preview_mask_details.name); finalMaskUrl.searchParams.set("type", "input"); @@ -309,31 +294,27 @@ hideSelectionPreview() { } }; - // On assemble le tout this.contextualToolbar.append( toleranceLabel, toleranceInput, toleranceValue, contiguousLabel, contiguousInput, - modeLabel, modeSelect, // <-- On l'ajoute ici + modeLabel, modeSelect, applyButton ); document.body.appendChild(this.contextualToolbar); } - // Affiche et positionne la barre d'outils show() { if (!this.contextualToolbar) return; this.contextualToolbar.style.display = 'flex'; this.positionToolbar(); } - // Cache la barre d'outils hide() { if (!this.contextualToolbar) return; this.contextualToolbar.style.display = 'none'; - this.hideSelectionPreview(); // On efface le surlignage quand on change d'outil + this.hideSelectionPreview(); } - // Calcule la position idéale de la barre d'outils (en haut de la preview) positionToolbar() { if (!this.node.previewCanvas) return; const canvasRect = this.node.previewCanvas.getBoundingClientRect(); diff --git a/js/toolbar.js b/js/toolbar.js index 907575b..b3c04d4 100644 --- a/js/toolbar.js +++ b/js/toolbar.js @@ -93,7 +93,7 @@ export class Toolbar { this.setupColorPicker(); this.createContextualToolbar(); - this.selectionSubMenu = null; // Notre futur menu de sélection + this.selectionSubMenu = null; this.createSelectionSubMenu(); } createContextualToolbar() { @@ -215,7 +215,7 @@ createContextualToolbar() { (tool.name === 'magic_wand' && this.selectionSubMenu?.style.display === 'flex'); if (isActive) { - ctx.fillStyle = "rgba(255, 221, 255, 0.6)"; // Couleur de surbrillance + ctx.fillStyle = "rgba(255, 221, 255, 0.6)"; const padding = 2; ctx.fillRect(iconX_start - padding, iconY_start - padding, iconSize + (padding * 2), iconSize + (padding * 2)); } @@ -231,13 +231,6 @@ createContextualToolbar() { ctx.font = `${iconSize}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; - - /*if (this.activeTool === tool.name) { - ctx.fillStyle = "rgba(255, 221, 255, 0.6)"; - const padding = 2; - ctx.fillRect(iconX_start - padding, iconY_start - padding, iconSize + (padding*2), iconSize + (padding*2)); - }*/ - ctx.fillText(tool.icon, iconX_start + iconSize / 2, iconY_start + iconSize / 2); } @@ -285,44 +278,32 @@ handleClick(e, mouseX, mouseY) { if (clickedTool) { const toolName = clickedTool.name; - //const isDeactivating = this.activeTool === toolName; const isDeactivating = this.activeTool === toolName || (toolName === 'magic_wand' && this.selectionSubMenu?.style.display === 'flex'); - // --- Étape 1 : On nettoie systématiquement l'état précédent --- this.activeTool = null; this.maskManager.hide(); this.magicWandManager.hide(); if (this.selectionSubMenu) { this.selectionSubMenu.style.display = 'none'; } - // --- Étape 2 : On active le nouvel outil, SEULEMENT si ce n'était pas un clic pour désactiver --- + if (!isDeactivating) { if (toolName !== 'magic_wand') { this.activeTool = toolName; } - - // On affiche l'interface correspondante switch (toolName) { case 'mask': this.maskManager.show(); break; case 'magic_wand': - // Pour l'icône 🪄, "activer" signifie montrer le sous-menu if (this.selectionSubMenu) { this.selectionSubMenu.style.display = 'flex'; - // On positionne le sous-menu this.positionSelectionSubMenu(); - //const bounds = this.toolBounds[toolName]; - //const canvasRect = this.node.previewCanvas.getBoundingClientRect(); - //this.selectionSubMenu.style.left = `${canvasRect.left + bounds.x + bounds.size}px`; - //this.selectionSubMenu.style.top = `${canvasRect.top + bounds.y}px`; } - break; case 'text': - // L'outil texte est juste actif, il n'a pas d'interface à afficher via cette fonction break; } } @@ -330,7 +311,6 @@ if (clickedTool || this.activeTool) { this.node.movingLayer = null; } - //this.node.graph.setDirtyCanvas(true, true); this.node.refreshUI(); } } @@ -516,12 +496,10 @@ createSelectionSubMenu() { border: '1px solid #555', borderRadius: '8px', padding: '4px', - //display: 'flex', flexDirection: 'column', gap: '4px', }); - // Petite fonction pour créer nos boutons-emojis const createEmojiButton = (emoji, title) => { const button = document.createElement("button"); button.innerText = emoji; @@ -535,26 +513,22 @@ createSelectionSubMenu() { cursor: 'pointer', width: '26px', height: '26px', - fontSize: '16px', // Taille de l'emoji + fontSize: '16px', textAlign: 'center', - lineHeight: '26px', // Centre l'emoji verticalement + lineHeight: '26px', }); button.onmouseover = () => button.style.backgroundColor = '#5E5E5E'; button.onmouseout = () => button.style.backgroundColor = '#444'; return button; }; - // Bouton pour la Baguette Magique const wandButton = createEmojiButton("🪄", "Baguette Magique"); wandButton.onclick = () => { this.activeTool = 'magic_wand'; this.magicWandManager.show(); this.selectionSubMenu.style.display = 'none'; - //if (this.node.movingLayer) this.node.movingLayer = null; - //this.node.refreshUI(); }; - // Bouton pour la Sélection Rectangle const rectButton = createEmojiButton("🔲", "Sélection Rectangle"); rectButton.onclick = () => { console.log("Outil Rectangle sélectionné ! (non implémenté)"); @@ -568,18 +542,15 @@ createSelectionSubMenu() { } positionSelectionSubMenu() { - // On s'assure que le menu existe et est visible avant de faire quoi que ce soit if (!this.selectionSubMenu || this.selectionSubMenu.style.display === 'none') { return; } - // On récupère la position de l'icône 🪄 sur laquelle on a cliqué const bounds = this.toolBounds['magic_wand']; if (!bounds || !this.node.previewCanvas) return; const canvasRect = this.node.previewCanvas.getBoundingClientRect(); - // On positionne le sous-menu à côté de l'icône this.selectionSubMenu.style.left = `${canvasRect.left + bounds.x + bounds.size}px`; this.selectionSubMenu.style.top = `${canvasRect.top + bounds.y - 40}px`; }