code cleanup

This commit is contained in:
tritant
2025-09-21 13:45:03 +02:00
committed by GitHub
parent cebe2bf3e5
commit 6ce8010d2f
3 changed files with 23 additions and 78 deletions
+5 -12
View File
@@ -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();
+13 -32
View File
@@ -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();
+5 -34
View File
@@ -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`;
}