mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Introduces a 'visible' property to layers and updates all relevant logic to support toggling layer visibility. Adds visibility toggle icons to the layers panel using a new IconLoader utility, with SVG and fallback canvas icons. Updates rendering, selection, and batch preview logic to respect layer visibility. Also improves blend mode menu UI and ensures new/pasted layers are always added on top with correct z-index.
172 lines
6.4 KiB
TypeScript
172 lines
6.4 KiB
TypeScript
import { createModuleLogger } from "./utils/LoggerUtils.js";
|
|
|
|
const log = createModuleLogger('CanvasSelection');
|
|
|
|
export class CanvasSelection {
|
|
canvas: any;
|
|
onSelectionChange: any;
|
|
selectedLayer: any;
|
|
selectedLayers: any;
|
|
constructor(canvas: any) {
|
|
this.canvas = canvas;
|
|
this.selectedLayers = [];
|
|
this.selectedLayer = null;
|
|
this.onSelectionChange = null;
|
|
}
|
|
|
|
/**
|
|
* Duplikuje zaznaczone warstwy (w pamięci, bez zapisu stanu)
|
|
*/
|
|
duplicateSelectedLayers() {
|
|
if (this.selectedLayers.length === 0) return [];
|
|
|
|
const newLayers: any = [];
|
|
const sortedLayers = [...this.selectedLayers].sort((a,b) => a.zIndex - b.zIndex);
|
|
|
|
sortedLayers.forEach(layer => {
|
|
const newLayer = {
|
|
...layer,
|
|
id: `layer_${+new Date()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
zIndex: this.canvas.layers.length, // Nowa warstwa zawsze na wierzchu
|
|
};
|
|
this.canvas.layers.push(newLayer);
|
|
newLayers.push(newLayer);
|
|
});
|
|
|
|
// Aktualizuj zaznaczenie, co powiadomi panel (ale nie renderuje go całego)
|
|
this.updateSelection(newLayers);
|
|
|
|
// Powiadom panel o zmianie struktury, aby się przerysował
|
|
if (this.canvas.canvasLayersPanel) {
|
|
this.canvas.canvasLayersPanel.onLayersChanged();
|
|
}
|
|
|
|
log.info(`Duplicated ${newLayers.length} layers (in-memory).`);
|
|
return newLayers;
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje zaznaczenie warstw i powiadamia wszystkie komponenty.
|
|
* To jest "jedyne źródło prawdy" o zmianie zaznaczenia.
|
|
* @param {Array} newSelection - Nowa lista zaznaczonych warstw
|
|
*/
|
|
updateSelection(newSelection: any) {
|
|
const previousSelection = this.selectedLayers.length;
|
|
// Filter out invisible layers from selection
|
|
this.selectedLayers = (newSelection || []).filter((layer: any) => layer.visible !== false);
|
|
this.selectedLayer = this.selectedLayers.length > 0 ? this.selectedLayers[this.selectedLayers.length - 1] : null;
|
|
|
|
// Sprawdź, czy zaznaczenie faktycznie się zmieniło, aby uniknąć pętli
|
|
const hasChanged = previousSelection !== this.selectedLayers.length ||
|
|
this.selectedLayers.some((layer: any, i: any) => this.selectedLayers[i] !== (newSelection || [])[i]);
|
|
|
|
if (!hasChanged && previousSelection > 0) {
|
|
// return; // Zablokowane na razie, może powodować problemy
|
|
}
|
|
|
|
log.debug('Selection updated', {
|
|
previousCount: previousSelection,
|
|
newCount: this.selectedLayers.length,
|
|
selectedLayerIds: this.selectedLayers.map((l: any) => l.id || 'unknown')
|
|
});
|
|
|
|
// 1. Zrenderuj ponownie canvas, aby pokazać nowe kontrolki transformacji
|
|
this.canvas.render();
|
|
|
|
// 2. Powiadom inne części aplikacji (jeśli są)
|
|
if (this.onSelectionChange) {
|
|
this.onSelectionChange();
|
|
}
|
|
|
|
// 3. Powiadom panel warstw, aby zaktualizował swój wygląd
|
|
if (this.canvas.canvasLayersPanel) {
|
|
this.canvas.canvasLayersPanel.onSelectionChanged();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Logika aktualizacji zaznaczenia, wywoływana przez panel warstw.
|
|
*/
|
|
updateSelectionLogic(layer: any, isCtrlPressed: any, isShiftPressed: any, index: any) {
|
|
let newSelection = [...this.selectedLayers];
|
|
let selectionChanged = false;
|
|
|
|
if (isShiftPressed && this.canvas.canvasLayersPanel.lastSelectedIndex !== -1) {
|
|
const sortedLayers = [...this.canvas.layers].sort((a, b) => b.zIndex - a.zIndex);
|
|
const startIndex = Math.min(this.canvas.canvasLayersPanel.lastSelectedIndex, index);
|
|
const endIndex = Math.max(this.canvas.canvasLayersPanel.lastSelectedIndex, index);
|
|
|
|
newSelection = [];
|
|
for (let i = startIndex; i <= endIndex; i++) {
|
|
if (sortedLayers[i]) {
|
|
newSelection.push(sortedLayers[i]);
|
|
}
|
|
}
|
|
selectionChanged = true;
|
|
} else if (isCtrlPressed) {
|
|
const layerIndex = newSelection.indexOf(layer);
|
|
if (layerIndex === -1) {
|
|
newSelection.push(layer);
|
|
} else {
|
|
newSelection.splice(layerIndex, 1);
|
|
}
|
|
this.canvas.canvasLayersPanel.lastSelectedIndex = index;
|
|
selectionChanged = true;
|
|
} else {
|
|
// Jeśli kliknięta warstwa nie jest częścią obecnego zaznaczenia,
|
|
// wyczyść zaznaczenie i zaznacz tylko ją.
|
|
if (!this.selectedLayers.includes(layer)) {
|
|
newSelection = [layer];
|
|
selectionChanged = true;
|
|
}
|
|
// Jeśli kliknięta warstwa JEST już zaznaczona (potencjalnie z innymi),
|
|
// NIE rób nic, aby umożliwić przeciąganie całej grupy.
|
|
this.canvas.canvasLayersPanel.lastSelectedIndex = index;
|
|
}
|
|
|
|
// Aktualizuj zaznaczenie tylko jeśli faktycznie się zmieniło
|
|
if (selectionChanged) {
|
|
this.updateSelection(newSelection);
|
|
}
|
|
}
|
|
|
|
removeSelectedLayers() {
|
|
if (this.selectedLayers.length > 0) {
|
|
log.info('Removing selected layers', {
|
|
layersToRemove: this.selectedLayers.length,
|
|
totalLayers: this.canvas.layers.length
|
|
});
|
|
|
|
this.canvas.saveState();
|
|
this.canvas.layers = this.canvas.layers.filter((l: any) => !this.selectedLayers.includes(l));
|
|
|
|
this.updateSelection([]);
|
|
|
|
this.canvas.render();
|
|
this.canvas.saveState();
|
|
|
|
if (this.canvas.canvasLayersPanel) {
|
|
this.canvas.canvasLayersPanel.onLayersChanged();
|
|
}
|
|
|
|
log.debug('Layers removed successfully, remaining layers:', this.canvas.layers.length);
|
|
} else {
|
|
log.debug('No layers selected for removal');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Aktualizuje zaznaczenie po operacji historii
|
|
*/
|
|
updateSelectionAfterHistory() {
|
|
const newSelectedLayers: any = [];
|
|
if (this.selectedLayers) {
|
|
this.selectedLayers.forEach((sl: any) => {
|
|
const found = this.canvas.layers.find((l: any) => l.id === sl.id);
|
|
if (found) newSelectedLayers.push(found);
|
|
});
|
|
}
|
|
this.updateSelection(newSelectedLayers);
|
|
}
|
|
}
|