diff --git a/js/Canvas.js b/js/Canvas.js index 683446e..470a46b 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -119,9 +119,11 @@ export class Canvas { updateHistoryButtons() { if (this.onHistoryChange) { + // Pobierz informacje o historii odpowiednią dla aktualnego trybu + const historyInfo = this.canvasState.getHistoryInfo(); this.onHistoryChange({ - canUndo: this.canvasState.undoStack.length > 1, - canRedo: this.canvasState.redoStack.length > 0 + canUndo: historyInfo.canUndo, + canRedo: historyInfo.canRedo }); } } diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index 03e09dc..9c554a5 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -156,7 +156,7 @@ export class CanvasInteractions { return; } this.canvas.maskTool.handleMouseUp(); - this.canvas.saveState(); + // Nie wywołujemy saveState - to już jest obsługiwane w MaskTool this.canvas.render(); return; } @@ -266,7 +266,11 @@ export class CanvasInteractions { this.canvas.viewport.y = worldCoords.y - (mouseBufferY / this.canvas.viewport.zoom); } this.canvas.render(); - this.canvas.saveState(true); + + // Nie zapisujemy stanu podczas scrollowania w trybie maski + if (!this.canvas.maskTool.isActive) { + this.canvas.saveState(true); + } } handleKeyDown(e) { diff --git a/js/CanvasRenderer.js b/js/CanvasRenderer.js index 947c64c..bc61fef 100644 --- a/js/CanvasRenderer.js +++ b/js/CanvasRenderer.js @@ -90,12 +90,14 @@ export class CanvasRenderer { // W trybie maski pokazuj maskę z przezroczystością 0.5 ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 0.5; + // Rysuj maskę w pozycji (0,0) - będzie dopasowana do obszaru canvasu ctx.drawImage(maskImage, 0, 0); ctx.globalAlpha = 1.0; } else if (maskImage) { // W trybie warstw pokazuj maskę jako widoczną, ale nieedytowalną ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 1.0; + // Rysuj maskę w pozycji (0,0) - będzie dopasowana do obszaru canvasu ctx.drawImage(maskImage, 0, 0); ctx.globalAlpha = 1.0; } diff --git a/js/CanvasState.js b/js/CanvasState.js index 76dabc0..4f539f1 100644 --- a/js/CanvasState.js +++ b/js/CanvasState.js @@ -9,8 +9,11 @@ const log = createModuleLogger('CanvasState'); export class CanvasState { constructor(canvas) { this.canvas = canvas; - this.undoStack = []; - this.redoStack = []; + // Osobne stosy dla trybu warstw i trybu maski + this.layersUndoStack = []; + this.layersRedoStack = []; + this.maskUndoStack = []; + this.maskRedoStack = []; this.historyLimit = 100; this.saveTimeout = null; this.lastSavedStateSignature = null; @@ -253,25 +256,34 @@ export class CanvasState { } saveState(replaceLast = false) { - if (replaceLast && this.undoStack.length > 0) { - this.undoStack.pop(); + // Sprawdź czy jesteśmy w trybie maski + if (this.canvas.maskTool && this.canvas.maskTool.isActive) { + this.saveMaskState(replaceLast); + } else { + this.saveLayersState(replaceLast); + } + } + + saveLayersState(replaceLast = false) { + if (replaceLast && this.layersUndoStack.length > 0) { + this.layersUndoStack.pop(); } const currentState = cloneLayers(this.canvas.layers); - if (this.undoStack.length > 0) { - const lastState = this.undoStack[this.undoStack.length - 1]; + if (this.layersUndoStack.length > 0) { + const lastState = this.layersUndoStack[this.layersUndoStack.length - 1]; if (getStateSignature(currentState) === getStateSignature(lastState)) { return; } } - this.undoStack.push(currentState); + this.layersUndoStack.push(currentState); - if (this.undoStack.length > this.historyLimit) { - this.undoStack.shift(); + if (this.layersUndoStack.length > this.historyLimit) { + this.layersUndoStack.shift(); } - this.redoStack = []; + this.layersRedoStack = []; this.canvas.updateHistoryButtons(); // Użyj debounce dla częstych zapisów @@ -279,33 +291,124 @@ export class CanvasState { this._debouncedSave(); } + saveMaskState(replaceLast = false) { + if (!this.canvas.maskTool) return; + + if (replaceLast && this.maskUndoStack.length > 0) { + this.maskUndoStack.pop(); + } + + // Klonuj aktualny stan maski + const maskCanvas = this.canvas.maskTool.getMask(); + const clonedCanvas = document.createElement('canvas'); + clonedCanvas.width = maskCanvas.width; + clonedCanvas.height = maskCanvas.height; + const clonedCtx = clonedCanvas.getContext('2d'); + clonedCtx.drawImage(maskCanvas, 0, 0); + + this.maskUndoStack.push(clonedCanvas); + + if (this.maskUndoStack.length > this.historyLimit) { + this.maskUndoStack.shift(); + } + this.maskRedoStack = []; + this.canvas.updateHistoryButtons(); + } + undo() { - if (this.undoStack.length <= 1) return; - const currentState = this.undoStack.pop(); - this.redoStack.push(currentState); - const prevState = this.undoStack[this.undoStack.length - 1]; + // Sprawdź czy jesteśmy w trybie maski + if (this.canvas.maskTool && this.canvas.maskTool.isActive) { + this.undoMaskState(); + } else { + this.undoLayersState(); + } + } + + redo() { + // Sprawdź czy jesteśmy w trybie maski + if (this.canvas.maskTool && this.canvas.maskTool.isActive) { + this.redoMaskState(); + } else { + this.redoLayersState(); + } + } + + undoLayersState() { + if (this.layersUndoStack.length <= 1) return; + + const currentState = this.layersUndoStack.pop(); + this.layersRedoStack.push(currentState); + const prevState = this.layersUndoStack[this.layersUndoStack.length - 1]; this.canvas.layers = cloneLayers(prevState); this.canvas.updateSelectionAfterHistory(); this.canvas.render(); this.canvas.updateHistoryButtons(); } - redo() { - if (this.redoStack.length === 0) return; - const nextState = this.redoStack.pop(); - this.undoStack.push(nextState); + redoLayersState() { + if (this.layersRedoStack.length === 0) return; + + const nextState = this.layersRedoStack.pop(); + this.layersUndoStack.push(nextState); this.canvas.layers = cloneLayers(nextState); this.canvas.updateSelectionAfterHistory(); this.canvas.render(); this.canvas.updateHistoryButtons(); } + undoMaskState() { + if (!this.canvas.maskTool || this.maskUndoStack.length <= 1) return; + + const currentState = this.maskUndoStack.pop(); + this.maskRedoStack.push(currentState); + + if (this.maskUndoStack.length > 0) { + const prevState = this.maskUndoStack[this.maskUndoStack.length - 1]; + // Przywróć poprzedni stan maski + const maskCanvas = this.canvas.maskTool.getMask(); + const maskCtx = maskCanvas.getContext('2d'); + + // Wyczyść obecną maskę + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + // Przywróć poprzedni stan + maskCtx.drawImage(prevState, 0, 0); + + this.canvas.render(); + } + + this.canvas.updateHistoryButtons(); + } + + redoMaskState() { + if (!this.canvas.maskTool || this.maskRedoStack.length === 0) return; + + const nextState = this.maskRedoStack.pop(); + this.maskUndoStack.push(nextState); + + // Przywróć następny stan maski + const maskCanvas = this.canvas.maskTool.getMask(); + const maskCtx = maskCanvas.getContext('2d'); + + // Wyczyść obecną maskę + maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height); + // Przywróć następny stan + maskCtx.drawImage(nextState, 0, 0); + + this.canvas.render(); + this.canvas.updateHistoryButtons(); + } + /** * Czyści historię undo/redo */ clearHistory() { - this.undoStack = []; - this.redoStack = []; + if (this.canvas.maskTool && this.canvas.maskTool.isActive) { + this.maskUndoStack = []; + this.maskRedoStack = []; + } else { + this.layersUndoStack = []; + this.layersRedoStack = []; + } this.canvas.updateHistoryButtons(); log.info("History cleared"); } @@ -315,12 +418,23 @@ export class CanvasState { * @returns {Object} Informacje o historii */ getHistoryInfo() { - return { - undoCount: this.undoStack.length, - redoCount: this.redoStack.length, - canUndo: this.undoStack.length > 1, - canRedo: this.redoStack.length > 0, - historyLimit: this.historyLimit - }; + // Zwraca dane historii w zależności od aktywnego trybu + if (this.canvas.maskTool && this.canvas.maskTool.isActive) { + return { + undoCount: this.maskUndoStack.length, + redoCount: this.maskRedoStack.length, + canUndo: this.maskUndoStack.length > 1, + canRedo: this.maskRedoStack.length > 0, + historyLimit: this.historyLimit + }; + } else { + return { + undoCount: this.layersUndoStack.length, + redoCount: this.layersRedoStack.length, + canUndo: this.layersUndoStack.length > 1, + canRedo: this.layersRedoStack.length > 0, + historyLimit: this.historyLimit + }; + } } } diff --git a/js/Mask_tool.js b/js/Mask_tool.js index 4ef7bf9..87ed1e8 100644 --- a/js/Mask_tool.js +++ b/js/Mask_tool.js @@ -27,18 +27,32 @@ export class MaskTool { initMaskCanvas() { this.maskCanvas.width = this.mainCanvas.width; this.maskCanvas.height = this.mainCanvas.height; - this.clear(); + // Wyczyść canvas bez zapisywania do historii + this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height); } activate() { this.isActive = true; this.canvasInstance.interaction.mode = 'drawingMask'; + + // Zapisz początkowy stan maski tylko jeśli historia jest pusta + if (this.canvasInstance.canvasState && this.canvasInstance.canvasState.maskUndoStack.length === 0) { + this.canvasInstance.canvasState.saveMaskState(); + } + + // Aktualizuj przyciski historii po przełączeniu na tryb maski + this.canvasInstance.updateHistoryButtons(); + log.info("Mask tool activated"); } deactivate() { this.isActive = false; this.canvasInstance.interaction.mode = 'none'; + + // Aktualizuj przyciski historii po przełączeniu z trybu maski + this.canvasInstance.updateHistoryButtons(); + log.info("Mask tool deactivated"); } @@ -65,8 +79,18 @@ export class MaskTool { handleMouseUp() { if (!this.isActive) return; - this.isDrawing = false; - this.lastPosition = null; + + // Jeśli narzędzie rysowało, zapisz stan maski + if (this.isDrawing) { + // Zakończ rysowanie + this.isDrawing = false; + this.lastPosition = null; + + // Zapisz stan maski do historii + if (this.canvasInstance.canvasState) { + this.canvasInstance.canvasState.saveMaskState(); + } + } } draw(worldCoords) { @@ -99,6 +123,11 @@ export class MaskTool { clear() { this.maskCtx.clearRect(0, 0, this.maskCanvas.width, this.maskCanvas.height); + + // Zapisz stan po wyczyszczeniu maski tylko jeśli narzędzie jest aktywne + if (this.isActive && this.canvasInstance.canvasState) { + this.canvasInstance.canvasState.saveMaskState(); + } } getMask() { @@ -142,6 +171,12 @@ export class MaskTool { this.maskCanvas.width = width; this.maskCanvas.height = height; this.maskCtx = this.maskCanvas.getContext('2d'); - this.maskCtx.drawImage(oldMask, 0, 0); + + // Zachowaj zawartość starej maski + if (oldMask.width > 0 && oldMask.height > 0) { + this.maskCtx.drawImage(oldMask, 0, 0); + } + + log.info(`Mask canvas resized to ${width}x${height}`); } }