From e5060fd8c3e67fe1d4b9174c1c49ae8f99eeedd6 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Thu, 3 Jul 2025 03:55:04 +0200 Subject: [PATCH] Support multiple batch preview menus on canvas Refactored batch preview management to allow multiple BatchPreviewManager instances per canvas. Updated positioning logic to use an initial spawn position, adjusted UI updates, and ensured batch preview menus move correctly with canvas panning. Removed single-instance references and updated related event handling. --- js/BatchPreviewManager.js | 34 +++++++++++++++++++++------------- js/Canvas.js | 25 ++++++++++++++++++++++--- js/CanvasInteractions.js | 8 ++++++++ js/CanvasRenderer.js | 8 +++++--- 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/js/BatchPreviewManager.js b/js/BatchPreviewManager.js index 4a54999..3cdbf72 100644 --- a/js/BatchPreviewManager.js +++ b/js/BatchPreviewManager.js @@ -3,7 +3,7 @@ import {createModuleLogger} from "./utils/LoggerUtils.js"; const log = createModuleLogger('BatchPreviewManager'); export class BatchPreviewManager { - constructor(canvas) { + constructor(canvas, initialPosition = { x: 0, y: 0 }) { this.canvas = canvas; this.active = false; this.layers = []; @@ -13,8 +13,8 @@ export class BatchPreviewManager { this.maskWasVisible = false; // Position in canvas world coordinates - this.worldX = 0; - this.worldY = 0; + this.worldX = initialPosition.x; + this.worldY = initialPosition.y; this.isDragging = false; } @@ -143,13 +143,6 @@ export class BatchPreviewManager { this._createUI(); - // Set initial position to be centered horizontally and just below the output area - const menuWidthInWorld = this.element.offsetWidth / this.canvas.viewport.zoom; - const paddingInWorld = 20 / this.canvas.viewport.zoom; // 20px padding in screen space - - this.worldX = (this.canvas.width / 2) - (menuWidthInWorld / 2); - this.worldY = this.canvas.height + paddingInWorld; - // Auto-hide mask logic this.maskWasVisible = this.canvas.maskTool.isOverlayVisible; if (this.maskWasVisible) { @@ -165,17 +158,32 @@ export class BatchPreviewManager { log.info(`Showing batch preview for ${layers.length} layers.`); this.layers = layers; this.currentIndex = 0; + + // Make the element visible BEFORE calculating its size this.element.style.display = 'flex'; this.active = true; + + // Now that it's visible, we can get its dimensions and adjust the position. + const menuWidthInWorld = this.element.offsetWidth / this.canvas.viewport.zoom; + const paddingInWorld = 20 / this.canvas.viewport.zoom; + + this.worldX -= menuWidthInWorld / 2; // Center horizontally + this.worldY += paddingInWorld; // Add padding below the output area + this._update(); } hide() { log.info('Hiding batch preview.'); - this.element.style.display = 'none'; + if (this.element) { + this.element.remove(); + } this.active = false; - this.layers = []; - this.currentIndex = 0; + + const index = this.canvas.batchPreviewManagers.indexOf(this); + if (index > -1) { + this.canvas.batchPreviewManagers.splice(index, 1); + } // Restore mask visibility if it was hidden by this manager if (this.maskWasVisible && !this.canvas.maskTool.isOverlayVisible) { diff --git a/js/Canvas.js b/js/Canvas.js index 20bd719..e0c39c1 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -167,7 +167,8 @@ export class Canvas { this.canvasRenderer = new CanvasRenderer(this); this.canvasIO = new CanvasIO(this); this.imageReferenceManager = new ImageReferenceManager(this); - this.batchPreviewManager = new BatchPreviewManager(this); + this.batchPreviewManagers = []; + this.pendingBatchSpawnPosition = null; log.debug('Canvas modules initialized successfully'); } @@ -485,7 +486,12 @@ export class Canvas { const handleExecutionStart = () => { lastExecutionStartTime = Date.now(); - log.debug(`Execution started, timestamp set to: ${lastExecutionStartTime}`); + // Store the spawn position for the next batch menu, relative to the output area + this.pendingBatchSpawnPosition = { + x: this.width / 2, // Horizontally centered on the output area + y: this.height // At the bottom of the output area + }; + log.debug(`Execution started, pending spawn position set relative to output area at:`, this.pendingBatchSpawnPosition); }; const handleExecutionSuccess = async () => { @@ -494,7 +500,20 @@ export class Canvas { const newLayers = await this.canvasIO.importLatestImages(lastExecutionStartTime); if (newLayers && newLayers.length > 1) { - this.batchPreviewManager.show(newLayers); + if (!this.pendingBatchSpawnPosition) { + // Fallback in case execution_start didn't fire + this.pendingBatchSpawnPosition = { + x: this.width / 2, + y: this.height + }; + log.warn("execution_start did not fire, using fallback spawn position."); + } + + const newManager = new BatchPreviewManager(this, this.pendingBatchSpawnPosition); + this.batchPreviewManagers.push(newManager); + newManager.show(newLayers); + + this.pendingBatchSpawnPosition = null; // Consume the position } } }; diff --git a/js/CanvasInteractions.js b/js/CanvasInteractions.js index 15885cf..cf19f7e 100644 --- a/js/CanvasInteractions.js +++ b/js/CanvasInteractions.js @@ -532,6 +532,14 @@ export class CanvasInteractions { this.canvas.maskTool.updatePosition(-finalX, -finalY); + // Also move any active batch preview menus + if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) { + this.canvas.batchPreviewManagers.forEach(manager => { + manager.worldX -= finalX; + manager.worldY -= finalY; + }); + } + this.canvas.viewport.x -= finalX; this.canvas.viewport.y -= finalY; } diff --git a/js/CanvasRenderer.js b/js/CanvasRenderer.js index 237a25b..e60bdcd 100644 --- a/js/CanvasRenderer.js +++ b/js/CanvasRenderer.js @@ -113,9 +113,11 @@ export class CanvasRenderer { } this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0); - // Update Batch Preview UI position - if (this.canvas.batchPreviewManager) { - this.canvas.batchPreviewManager.updateScreenPosition(this.canvas.viewport); + // Update Batch Preview UI positions + if (this.canvas.batchPreviewManagers && this.canvas.batchPreviewManagers.length > 0) { + this.canvas.batchPreviewManagers.forEach(manager => { + manager.updateScreenPosition(this.canvas.viewport); + }); } }