fix: convert Empty Latent Batch swap button from canvas to DOM widget
- Switch from unreliable canvas-based drawing to DOM widget approach - Eliminate coordinate calculation complexity and mouse position issues - Use same proven pattern as Seed History node buttons - Add proper hover/click animations and visual feedback - Fix callback bug in heightWidget.callback assignment - Remove all canvas drawing, mouse handling, and coordinate code - Button now works consistently without coordinate system problems Resolves swap button not working issue by using reliable DOM elements instead of manual canvas coordinate calculations.
This commit is contained in:
@@ -202,8 +202,25 @@ def format_parameter_text(param: Dict, mode: str = "full") -> str:
|
||||
|
||||
# Optional LoRA line
|
||||
if "lora" in param and param["lora"]:
|
||||
lora_name = param["lora"][:32] if len(param["lora"]) > 32 else param["lora"]
|
||||
lora_line = f"LoRA: {lora_name}, str: {param.get('lora_strength', 'N/A')}"
|
||||
lora_path = param["lora"]
|
||||
# Extract just the filename and immediate parent directory for better readability
|
||||
import os
|
||||
|
||||
path_parts = lora_path.replace("\\", "/").split("/")
|
||||
if len(path_parts) > 2:
|
||||
# Show parent directory and filename
|
||||
lora_display = f"{path_parts[-2]}/{path_parts[-1]}"
|
||||
else:
|
||||
# Use full path if it's short
|
||||
lora_display = lora_path
|
||||
|
||||
# Remove file extension for cleaner display
|
||||
if lora_display.endswith(".safetensors"):
|
||||
lora_display = lora_display[:-12]
|
||||
|
||||
lora_line = (
|
||||
f"LoRA: {lora_display}, str: {param.get('lora_strength', 'N/A')}"
|
||||
)
|
||||
# Add batch info if available
|
||||
if "lora_batch" in param:
|
||||
lora_line += f" [{param['lora_batch']}]"
|
||||
|
||||
+58
-184
@@ -9,9 +9,6 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
if (onNodeCreated) onNodeCreated.apply(this, []);
|
||||
|
||||
// Track button click state for visual feedback
|
||||
this.swapButtonPressed = false;
|
||||
|
||||
// Helper function to extract resolution from formatted preset string
|
||||
this.extractResolutionFromPreset = function (presetValue) {
|
||||
if (presetValue === "custom") return null;
|
||||
@@ -26,6 +23,9 @@ app.registerExtension({
|
||||
return presetValue;
|
||||
};
|
||||
|
||||
// Create swap button as DOM widget
|
||||
this.createSwapButton();
|
||||
|
||||
// Override preset callback to update width/height widgets when preset changes
|
||||
const presetWidget = this.widgets.find((w) => w.name === "preset");
|
||||
if (presetWidget) {
|
||||
@@ -194,200 +194,74 @@ app.registerExtension({
|
||||
this.graph?.setDirtyCanvas(true, true);
|
||||
}
|
||||
};
|
||||
|
||||
// Override onResize to refresh button position
|
||||
const originalOnResize = this.onResize;
|
||||
this.onResize = function (size) {
|
||||
if (originalOnResize) {
|
||||
originalOnResize.call(this, size);
|
||||
}
|
||||
// Force redraw to update button position
|
||||
this.setDirtyCanvas(true, true);
|
||||
// Also mark the graph as dirty
|
||||
if (this.graph) {
|
||||
this.graph.setDirtyCanvas(true, true);
|
||||
}
|
||||
};
|
||||
|
||||
// Override onBounding to ensure proper updates
|
||||
const originalOnBounding = this.onBounding;
|
||||
this.onBounding = function (out) {
|
||||
if (originalOnBounding) {
|
||||
originalOnBounding.call(this, out);
|
||||
}
|
||||
// Force redraw when bounds change
|
||||
this.setDirtyCanvas(true, true);
|
||||
};
|
||||
};
|
||||
|
||||
const onDrawForeground = nodeType.prototype.onDrawForeground;
|
||||
nodeType.prototype.onDrawForeground = function (ctx) {
|
||||
if (onDrawForeground) {
|
||||
onDrawForeground.apply(this, arguments);
|
||||
}
|
||||
// Create swap button as DOM widget
|
||||
nodeType.prototype.createSwapButton = function () {
|
||||
// Create button container
|
||||
const buttonContainer = document.createElement("div");
|
||||
buttonContainer.style.cssText = `
|
||||
padding: 4px;
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
if (this.flags.collapsed) return;
|
||||
// Create swap button
|
||||
const swapButton = document.createElement("button");
|
||||
swapButton.innerHTML = "↔️ Swap W×H";
|
||||
swapButton.style.cssText = `
|
||||
background: #4A90E2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
|
||||
`;
|
||||
|
||||
// Draw swap button with consistent spacing from widgets
|
||||
const swapButtonSize = 24;
|
||||
const margin = 6;
|
||||
const swapButtonX = this.size[0] - swapButtonSize - margin;
|
||||
// Button hover effects
|
||||
swapButton.addEventListener("mouseenter", () => {
|
||||
swapButton.style.background = "#5BA0F2";
|
||||
swapButton.style.transform = "translateY(-1px)";
|
||||
swapButton.style.boxShadow = "0 3px 6px rgba(0,0,0,0.3)";
|
||||
});
|
||||
|
||||
// Calculate button position based on widget spacing rather than bottom margin
|
||||
// Estimate widget area height and add consistent spacing
|
||||
const estimatedWidgetHeight = 90; // Approximate height for 3 widgets
|
||||
const topMargin = 35; // Space from top to first widget
|
||||
const buttonSpacing = 40; // Space between last widget and button (moved down 5)
|
||||
const swapButtonY = topMargin + estimatedWidgetHeight + buttonSpacing;
|
||||
swapButton.addEventListener("mouseleave", () => {
|
||||
swapButton.style.background = "#4A90E2";
|
||||
swapButton.style.transform = "translateY(0)";
|
||||
swapButton.style.boxShadow = "0 2px 4px rgba(0,0,0,0.2)";
|
||||
});
|
||||
|
||||
// Button background - change color based on pressed state
|
||||
if (this.swapButtonPressed) {
|
||||
// Darker when pressed
|
||||
ctx.fillStyle = "rgba(30, 120, 200, 0.9)"; // Darker blue when clicked
|
||||
} else {
|
||||
// Normal state
|
||||
ctx.fillStyle = "rgba(66, 165, 245, 0.8)"; // Material blue
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.roundRect(
|
||||
swapButtonX,
|
||||
swapButtonY,
|
||||
swapButtonSize,
|
||||
swapButtonSize,
|
||||
4,
|
||||
);
|
||||
ctx.fill();
|
||||
// Button click effect and functionality
|
||||
swapButton.addEventListener("mousedown", () => {
|
||||
swapButton.style.background = "#3A80D2";
|
||||
swapButton.style.transform = "translateY(1px)";
|
||||
swapButton.style.boxShadow = "0 1px 2px rgba(0,0,0,0.2)";
|
||||
});
|
||||
|
||||
// Button border with subtle highlight
|
||||
ctx.strokeStyle = this.swapButtonPressed
|
||||
? "rgba(20, 100, 180, 1.0)"
|
||||
: "rgba(33, 150, 243, 0.9)";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.stroke();
|
||||
swapButton.addEventListener("mouseup", () => {
|
||||
swapButton.style.background = "#5BA0F2";
|
||||
swapButton.style.transform = "translateY(-1px)";
|
||||
swapButton.style.boxShadow = "0 3px 6px rgba(0,0,0,0.3)";
|
||||
});
|
||||
|
||||
// Draw swap icon - modern double arrow design
|
||||
ctx.strokeStyle = "rgba(255, 255, 255, 0.95)";
|
||||
ctx.lineWidth = 2;
|
||||
ctx.lineCap = "round";
|
||||
|
||||
const centerX = swapButtonX + 12;
|
||||
const centerY = swapButtonY + 12;
|
||||
|
||||
// Top arrow (pointing right) - width to height
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX - 7, centerY - 3);
|
||||
ctx.lineTo(centerX + 5, centerY - 3);
|
||||
ctx.stroke();
|
||||
|
||||
// Top arrow head
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX + 5, centerY - 3);
|
||||
ctx.lineTo(centerX + 2, centerY - 5);
|
||||
ctx.moveTo(centerX + 5, centerY - 3);
|
||||
ctx.lineTo(centerX + 2, centerY - 1);
|
||||
ctx.stroke();
|
||||
|
||||
// Bottom arrow (pointing left) - height to width
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX + 5, centerY + 3);
|
||||
ctx.lineTo(centerX - 7, centerY + 3);
|
||||
ctx.stroke();
|
||||
|
||||
// Bottom arrow head
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(centerX - 7, centerY + 3);
|
||||
ctx.lineTo(centerX - 4, centerY + 1);
|
||||
ctx.moveTo(centerX - 7, centerY + 3);
|
||||
ctx.lineTo(centerX - 4, centerY + 5);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
const onMouseDown = nodeType.prototype.onMouseDown;
|
||||
nodeType.prototype.onMouseDown = function (e) {
|
||||
// Check if click is on swap button
|
||||
const swapButtonSize = 24;
|
||||
const margin = 6;
|
||||
const swapButtonX =
|
||||
this.pos[0] + this.size[0] - swapButtonSize - margin;
|
||||
|
||||
// Use same positioning logic as drawing
|
||||
const estimatedWidgetHeight = 90;
|
||||
const topMargin = 35;
|
||||
const buttonSpacing = 40;
|
||||
const swapButtonY =
|
||||
this.pos[1] + topMargin + estimatedWidgetHeight + buttonSpacing;
|
||||
|
||||
if (
|
||||
e.canvasX >= swapButtonX &&
|
||||
e.canvasX <= swapButtonX + swapButtonSize &&
|
||||
e.canvasY >= swapButtonY &&
|
||||
e.canvasY <= swapButtonY + swapButtonSize
|
||||
) {
|
||||
// Visual feedback - set button as pressed
|
||||
this.swapButtonPressed = true;
|
||||
this.setDirtyCanvas(true, true);
|
||||
|
||||
// Execute swap
|
||||
// Main click functionality
|
||||
swapButton.addEventListener("click", () => {
|
||||
this.swapDimensions();
|
||||
});
|
||||
|
||||
// Reset button state after a short delay for visual feedback
|
||||
setTimeout(() => {
|
||||
this.swapButtonPressed = false;
|
||||
this.setDirtyCanvas(true, true);
|
||||
}, 150);
|
||||
buttonContainer.appendChild(swapButton);
|
||||
|
||||
return true; // Consume the event
|
||||
}
|
||||
|
||||
// Call original onMouseDown if not clicking swap button
|
||||
if (onMouseDown) {
|
||||
return onMouseDown.apply(this, arguments);
|
||||
}
|
||||
// Add as DOM widget
|
||||
this.swapButtonWidget = this.addDOMWidget(
|
||||
"swap_button",
|
||||
"div",
|
||||
buttonContainer
|
||||
);
|
||||
};
|
||||
|
||||
// Optional: Add hover effect for better user feedback
|
||||
const onMouseMove = nodeType.prototype.onMouseMove;
|
||||
nodeType.prototype.onMouseMove = function (e) {
|
||||
// Check if hovering over swap button
|
||||
const swapButtonSize = 24;
|
||||
const margin = 6;
|
||||
const swapButtonX =
|
||||
this.pos[0] + this.size[0] - swapButtonSize - margin;
|
||||
|
||||
// Use same positioning logic as drawing
|
||||
const estimatedWidgetHeight = 90;
|
||||
const topMargin = 35;
|
||||
const buttonSpacing = 40;
|
||||
const swapButtonY =
|
||||
this.pos[1] + topMargin + estimatedWidgetHeight + buttonSpacing;
|
||||
|
||||
const isHovering =
|
||||
e.canvasX >= swapButtonX &&
|
||||
e.canvasX <= swapButtonX + swapButtonSize &&
|
||||
e.canvasY >= swapButtonY &&
|
||||
e.canvasY <= swapButtonY + swapButtonSize;
|
||||
|
||||
// Update cursor style for better UX (safely)
|
||||
if (
|
||||
isHovering &&
|
||||
this.graph &&
|
||||
this.graph.canvas &&
|
||||
this.graph.canvas.canvas
|
||||
) {
|
||||
this.graph.canvas.canvas.style.cursor = "pointer";
|
||||
} else if (
|
||||
this.graph &&
|
||||
this.graph.canvas &&
|
||||
this.graph.canvas.canvas
|
||||
) {
|
||||
this.graph.canvas.canvas.style.cursor = "default";
|
||||
}
|
||||
|
||||
// Call original onMouseMove
|
||||
if (onMouseMove) {
|
||||
return onMouseMove.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user