mirror of
https://github.com/Azornes/Comfyui-LayerForge.git
synced 2026-03-21 20:52:12 -03:00
Add layer visibility toggle and icon support
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.
This commit is contained in:
106
js/CanvasView.js
106
js/CanvasView.js
@@ -7,6 +7,7 @@ import { Canvas } from "./Canvas.js";
|
||||
import { clearAllCanvasStates } from "./db.js";
|
||||
import { ImageCache } from "./ImageCache.js";
|
||||
import { createModuleLogger } from "./utils/LoggerUtils.js";
|
||||
import { iconLoader, LAYERFORGE_TOOLS } from "./utils/IconLoader.js";
|
||||
import { setupSAMDetectorHook } from "./SAMDetectorIntegration.js";
|
||||
const log = createModuleLogger('Canvas_view');
|
||||
async function createCanvasWidget(node, widget, app) {
|
||||
@@ -371,19 +372,32 @@ async function createCanvasWidget(node, widget, app) {
|
||||
$el("div.painter-button-group", { id: "mask-controls" }, [
|
||||
$el("button.painter-button.primary", {
|
||||
id: `toggle-mask-btn-${node.id}`,
|
||||
textContent: "Show Mask",
|
||||
textContent: "M", // Fallback text until icon loads
|
||||
title: "Toggle mask overlay visibility",
|
||||
style: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minWidth: '32px',
|
||||
maxWidth: '32px',
|
||||
padding: '4px',
|
||||
fontSize: '12px',
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
onclick: (e) => {
|
||||
const button = e.target;
|
||||
const button = e.currentTarget;
|
||||
canvas.maskTool.toggleOverlayVisibility();
|
||||
canvas.render();
|
||||
if (canvas.maskTool.isOverlayVisible) {
|
||||
button.classList.add('primary');
|
||||
button.textContent = "Show Mask";
|
||||
}
|
||||
else {
|
||||
button.classList.remove('primary');
|
||||
button.textContent = "Hide Mask";
|
||||
const iconContainer = button.querySelector('.mask-icon-container');
|
||||
if (iconContainer) {
|
||||
if (canvas.maskTool.isOverlayVisible) {
|
||||
button.classList.add('primary');
|
||||
iconContainer.style.opacity = '1';
|
||||
}
|
||||
else {
|
||||
button.classList.remove('primary');
|
||||
iconContainer.style.opacity = '0.5';
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
@@ -503,6 +517,47 @@ async function createCanvasWidget(node, widget, app) {
|
||||
]),
|
||||
$el("div.painter-separator")
|
||||
]);
|
||||
// Function to create mask icon
|
||||
const createMaskIcon = () => {
|
||||
const iconContainer = document.createElement('div');
|
||||
iconContainer.className = 'mask-icon-container';
|
||||
iconContainer.style.cssText = `
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
const icon = iconLoader.getIcon(LAYERFORGE_TOOLS.MASK);
|
||||
if (icon) {
|
||||
if (icon instanceof HTMLImageElement) {
|
||||
const img = icon.cloneNode();
|
||||
img.style.cssText = `
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
filter: brightness(0) invert(1);
|
||||
`;
|
||||
iconContainer.appendChild(img);
|
||||
}
|
||||
else if (icon instanceof HTMLCanvasElement) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 16;
|
||||
canvas.height = 16;
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (ctx) {
|
||||
ctx.drawImage(icon, 0, 0, 16, 16);
|
||||
}
|
||||
iconContainer.appendChild(canvas);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Fallback text
|
||||
iconContainer.textContent = 'M';
|
||||
iconContainer.style.fontSize = '12px';
|
||||
iconContainer.style.color = '#ffffff';
|
||||
}
|
||||
return iconContainer;
|
||||
};
|
||||
const updateButtonStates = () => {
|
||||
const selectionCount = canvas.canvasSelection.selectedLayers.length;
|
||||
const hasSelection = selectionCount > 0;
|
||||
@@ -531,10 +586,39 @@ async function createCanvasWidget(node, widget, app) {
|
||||
};
|
||||
updateButtonStates();
|
||||
canvas.updateHistoryButtons();
|
||||
// Add mask icon to toggle mask button after icons are loaded
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await iconLoader.preloadToolIcons();
|
||||
const toggleMaskBtn = controlPanel.querySelector(`#toggle-mask-btn-${node.id}`);
|
||||
if (toggleMaskBtn && !toggleMaskBtn.querySelector('.mask-icon-container')) {
|
||||
// Clear fallback text
|
||||
toggleMaskBtn.textContent = '';
|
||||
const maskIcon = createMaskIcon();
|
||||
toggleMaskBtn.appendChild(maskIcon);
|
||||
// Set initial state based on mask visibility
|
||||
if (canvas.maskTool.isOverlayVisible) {
|
||||
toggleMaskBtn.classList.add('primary');
|
||||
maskIcon.style.opacity = '1';
|
||||
}
|
||||
else {
|
||||
toggleMaskBtn.classList.remove('primary');
|
||||
maskIcon.style.opacity = '0.5';
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
log.warn('Failed to load mask icon:', error);
|
||||
}
|
||||
}, 200);
|
||||
// Debounce timer for updateOutput to prevent excessive updates
|
||||
let updateOutputTimer = null;
|
||||
const updateOutput = async (node, canvas) => {
|
||||
// Check if preview is disabled - if so, skip updateOutput entirely
|
||||
const triggerWidget = node.widgets.find((w) => w.name === "trigger");
|
||||
if (triggerWidget) {
|
||||
triggerWidget.value = (triggerWidget.value + 1) % 99999999;
|
||||
}
|
||||
const showPreviewWidget = node.widgets.find((w) => w.name === "show_preview");
|
||||
if (showPreviewWidget && !showPreviewWidget.value) {
|
||||
log.debug("Preview disabled, skipping updateOutput");
|
||||
@@ -544,10 +628,6 @@ async function createCanvasWidget(node, widget, app) {
|
||||
node.imgs = [placeholder];
|
||||
return;
|
||||
}
|
||||
const triggerWidget = node.widgets.find((w) => w.name === "trigger");
|
||||
if (triggerWidget) {
|
||||
triggerWidget.value = (triggerWidget.value + 1) % 99999999;
|
||||
}
|
||||
// Clear previous timer
|
||||
if (updateOutputTimer) {
|
||||
clearTimeout(updateOutputTimer);
|
||||
|
||||
Reference in New Issue
Block a user