diff --git a/README.md b/README.md index 09a21a4..982ddc0 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ A collection of custom nodes for ComfyUI providing video manipulation, LoRA loading, path animation, and visual drawing tools for WanVideo workflows. ### Available Nodes -![Node List](git_assets/list.png) +![Node List](git_assets/img/list.png) -### Spline Editor & Features -![Spline Editor](git_assets/spline.png) +### Spline Editor & Other nodes +![Spline Editor](git_assets/img/spline.png) ## Features @@ -27,7 +27,7 @@ cd ComfyUI/custom_nodes/ 2. Clone this repository: ```bash -git clone https://github.com/siraxe/ComfyUI-WanVideoWrapper_QQ +git clone https://github.com/siraxe/ComfyUI-WanVideoWrapper_QQ.git ``` 3. Restart ComfyUI diff --git a/git_assets/docs/power_spline_editor.md b/git_assets/docs/power_spline_editor.md new file mode 100644 index 0000000..cfd8f89 --- /dev/null +++ b/git_assets/docs/power_spline_editor.md @@ -0,0 +1,5 @@ +# Power Spline Editor +![Spline Editor](../img/s1.webp) + - Click alt + left/right/middle to rotate/scale/move curves from their base point + +#TODO diff --git a/git_assets/list.png b/git_assets/img/list.png similarity index 100% rename from git_assets/list.png rename to git_assets/img/list.png diff --git a/git_assets/img/s1.webp b/git_assets/img/s1.webp new file mode 100644 index 0000000..a42e596 Binary files /dev/null and b/git_assets/img/s1.webp differ diff --git a/git_assets/spline.png b/git_assets/img/spline.png similarity index 100% rename from git_assets/spline.png rename to git_assets/img/spline.png diff --git a/web/power_spline_editor/power_spline_editor.js b/web/power_spline_editor/power_spline_editor.js index f588b60..2fd8aab 100644 --- a/web/power_spline_editor/power_spline_editor.js +++ b/web/power_spline_editor/power_spline_editor.js @@ -1,5 +1,5 @@ import { app } from '../../../scripts/app.js'; -import { makeUUID, loadScript, create_documentation_stylesheet, chainCallback, hideWidgetForGood, RgthreeBaseWidget, DimensionsWidget, PowerSplineWidget, PowerSplineHeaderWidget, NodeSizeManager } from './spline_utils.js'; +import { makeUUID, loadScript, create_documentation_stylesheet, chainCallback, hideWidgetForGood, RgthreeBaseWidget, DimensionsWidget, PowerSplineWidget, PowerSplineHeaderWidget, NodeSizeManager, drawWidgetButton } from './spline_utils.js'; import SplineEditor2 from './spline_canvas.js'; import { getSlotInPosition, getSlotMenuOptions, showCustomDrivenToggleMenu } from './context_menu.js'; @@ -465,46 +465,94 @@ app.registerExtension({ // Add header widget this.addCustomWidget(new PowerSplineHeaderWidget("spline_header")); - // Create a container for the buttons - const buttonContainer = document.createElement("div"); - buttonContainer.style.display = "flex"; - buttonContainer.style.alignItems = "center"; - buttonContainer.style.width = "100%"; - buttonContainer.style.padding = "2px 5px"; - buttonContainer.style.boxSizing = "border-box"; + // Add button bar widget using custom button drawing (similar to Add Lora button) + const buttonBarWidget = new RgthreeBaseWidget("button_bar"); + buttonBarWidget.type = "custom"; + buttonBarWidget.serialize = false; - // Create Add Spline button - const addSplineButton = document.createElement("button"); - addSplineButton.textContent = "➕ Add Spline"; - addSplineButton.style.width = "70%"; - addSplineButton.style.padding = "2px"; - addSplineButton.style.marginRight = "5px"; // Space between buttons - addSplineButton.onclick = () => { - this.layerManager.addNewSpline(); - }; + // Track mouse state for button hover effects + buttonBarWidget.addSplineMouseDown = false; + buttonBarWidget.duplicateMouseDown = false; - // Create Duplicate button - const duplicateButton = document.createElement("button"); - duplicateButton.textContent = "Duplicate"; - duplicateButton.style.width = "30%"; - duplicateButton.style.padding = "2px"; - duplicateButton.onclick = () => { - const activeWidget = this.layerManager.getActiveWidget(); - if (activeWidget) { - this.layerManager.duplicateSpline(activeWidget); - } - }; - - buttonContainer.appendChild(addSplineButton); - buttonContainer.appendChild(duplicateButton); - - const buttonBarWidget = this.addDOMWidget("button_bar", "BUTTON_BAR", buttonContainer, { - serialize: false, - hideOnZoom: false, - }); buttonBarWidget.computeSize = function(width) { - return [width, 26]; - } + return [width, LiteGraph.NODE_WIDGET_HEIGHT]; + }; + + buttonBarWidget.draw = function(ctx, node, width, posY, height) { + const margin = 15; + const gap = 5; + const addSplineWidth = (width - margin * 2 - gap) * 0.70; + const duplicateWidth = (width - margin * 2 - gap) * 0.30; + + // Draw Add Spline button (70% width) + drawWidgetButton( + ctx, + { size: [addSplineWidth, height], pos: [margin, posY] }, + "➕ Add Spline", + this.addSplineMouseDown + ); + + // Draw Duplicate button (30% width) + drawWidgetButton( + ctx, + { size: [duplicateWidth, height], pos: [margin + addSplineWidth + gap, posY] }, + "Duplicate", + this.duplicateMouseDown + ); + }; + + buttonBarWidget.mouse = function(event, pos, node) { + if (event.type === "pointerdown" || event.type === "mousedown") { + const margin = 15; + const gap = 5; + const width = node.size[0]; + const addSplineWidth = (width - margin * 2 - gap) * 0.70; + const duplicateWidth = (width - margin * 2 - gap) * 0.30; + + // Check if click is on Add Spline button + if (pos[0] >= margin && pos[0] <= margin + addSplineWidth) { + this.addSplineMouseDown = true; + node.setDirtyCanvas(true, false); + return true; + } + // Check if click is on Duplicate button + else if (pos[0] >= margin + addSplineWidth + gap && + pos[0] <= margin + addSplineWidth + gap + duplicateWidth) { + this.duplicateMouseDown = true; + node.setDirtyCanvas(true, false); + return true; + } + } + else if (event.type === "pointerup" || event.type === "mouseup") { + const margin = 15; + const gap = 5; + const width = node.size[0]; + const addSplineWidth = (width - margin * 2 - gap) * 0.70; + const duplicateWidth = (width - margin * 2 - gap) * 0.30; + + // Handle Add Spline button click + if (this.addSplineMouseDown && pos[0] >= margin && pos[0] <= margin + addSplineWidth) { + node.layerManager.addNewSpline(); + } + // Handle Duplicate button click + else if (this.duplicateMouseDown && + pos[0] >= margin + addSplineWidth + gap && + pos[0] <= margin + addSplineWidth + gap + duplicateWidth) { + const activeWidget = node.layerManager.getActiveWidget(); + if (activeWidget) { + node.layerManager.duplicateSpline(activeWidget); + } + } + + this.addSplineMouseDown = false; + this.duplicateMouseDown = false; + node.setDirtyCanvas(true, false); + return true; + } + return false; + }; + + this.addCustomWidget(buttonBarWidget); // Set initial node size this.updateNodeHeight(); diff --git a/web/power_spline_editor/spline_utils.js b/web/power_spline_editor/spline_utils.js index aa37c8f..014bbf5 100644 --- a/web/power_spline_editor/spline_utils.js +++ b/web/power_spline_editor/spline_utils.js @@ -129,6 +129,62 @@ export function drawRoundedRectangle(ctx, options) { ctx.restore(); } +export function drawWidgetButton(ctx, options, text = null, isMouseDownedAndOver = false) { + var _a; + const borderRadius = isLowQuality() ? 0 : ((_a = options.borderRadius) !== null && _a !== void 0 ? _a : 4); + ctx.save(); + if (!isLowQuality() && !isMouseDownedAndOver) { + drawRoundedRectangle(ctx, { + size: [options.size[0] - 2, options.size[1]], + pos: [options.pos[0] + 1, options.pos[1] + 1], + borderRadius, + colorBackground: "#000000aa", + colorStroke: "#000000aa", + }); + } + drawRoundedRectangle(ctx, { + size: options.size, + pos: [options.pos[0], options.pos[1] + (isMouseDownedAndOver ? 1 : 0)], + borderRadius, + colorBackground: isMouseDownedAndOver ? "#444" : LiteGraph.WIDGET_BGCOLOR, + colorStroke: "transparent", + }); + if (isLowQuality()) { + ctx.restore(); + return; + } + if (!isMouseDownedAndOver) { + drawRoundedRectangle(ctx, { + size: [options.size[0] - 0.75, options.size[1] - 0.75], + pos: options.pos, + borderRadius: borderRadius - 0.5, + colorBackground: "transparent", + colorStroke: "#00000044", + }); + drawRoundedRectangle(ctx, { + size: [options.size[0] - 0.75, options.size[1] - 0.75], + pos: [options.pos[0] + 0.75, options.pos[1] + 0.75], + borderRadius: borderRadius - 0.5, + colorBackground: "transparent", + colorStroke: "#ffffff11", + }); + } + drawRoundedRectangle(ctx, { + size: [options.size[0] - 1.5, options.size[1] - 1.5], + pos: [options.pos[0] + 0.75, options.pos[1] + (isMouseDownedAndOver ? 1.75 : 0.75)], + borderRadius: borderRadius - 1, + colorBackground: "transparent", + colorStroke: isMouseDownedAndOver ? "#00000088" : "#ffffff22", + }); + if (text) { + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + ctx.fillStyle = LiteGraph.WIDGET_TEXT_COLOR; + ctx.fillText(text, options.pos[0] + options.size[0] / 2, options.pos[1] + options.size[1] / 2 + (isMouseDownedAndOver ? 1 : 0)); + } + ctx.restore(); +} + export function drawTogglePart(ctx, options) { const lowQuality = isLowQuality(); ctx.save();