Instantly share code, notes, and snippets.
Created
September 3, 2023 18:07
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save sliceofbytes/b494550cf15f0753aaf39723a26d6713 to your computer and use it in GitHub Desktop.
Comfy.ContextMenuFilter Fix contextMenuFilter.js (Fix filter not working)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {app} from "../../scripts/app.js"; | |
// Adds filtering to combo context menus | |
const ext = { | |
name: "Comfy.ContextMenuFilter", | |
init() { | |
const ctxMenu = LiteGraph.ContextMenu; | |
LiteGraph.ContextMenu = function (values, options) { | |
const ctx = ctxMenu.call(this, values, options); | |
// If we are a dark menu (only used for combo boxes) then add a filter input | |
if (options?.className === "dark" && values?.length > 10) { | |
const filter = document.createElement("input"); | |
filter.classList.add("comfy-context-menu-filter"); | |
filter.placeholder = "Filter list"; | |
this.root.prepend(filter); | |
const items = Array.from(this.root.querySelectorAll(".litemenu-entry")); | |
let displayedItems = [...items]; | |
let itemCount = displayedItems.length; | |
// We must request an animation frame for the current node of the active canvas to update. | |
requestAnimationFrame(() => { | |
const currentNode = LGraphCanvas.active_canvas.current_node; | |
const clickedComboValue = currentNode.widgets?.filter(w => w.type === "combo" && w.options.values.length === values.length) | |
?.find(w => w.options.values.every((v, i) => v === values[i])) | |
?.value; | |
let selectedIndex = clickedComboValue ? values.findIndex(v => v === clickedComboValue) : 0; | |
if (selectedIndex < 0) { | |
selectedIndex = 0; | |
} | |
let selectedItem = displayedItems[selectedIndex]; | |
updateSelected(); | |
// Apply highlighting to the selected item | |
function updateSelected() { | |
selectedItem?.style.setProperty("background-color", ""); | |
selectedItem?.style.setProperty("color", ""); | |
selectedItem = displayedItems[selectedIndex]; | |
selectedItem?.style.setProperty("background-color", "#ccc", "important"); | |
selectedItem?.style.setProperty("color", "#000", "important"); | |
} | |
const positionList = () => { | |
const rect = this.root.getBoundingClientRect(); | |
// If the top is off-screen then shift the element with scaling applied | |
if (rect.top < 0) { | |
const scale = 1 - this.root.getBoundingClientRect().height / this.root.clientHeight; | |
const shift = (this.root.clientHeight * scale) / 2; | |
this.root.style.top = -shift + "px"; | |
} | |
} | |
// Arrow up/down to select items | |
filter.addEventListener("keydown", (event) => { | |
switch (event.key) { | |
case "ArrowUp": | |
event.preventDefault(); | |
if (selectedIndex === 0) { | |
selectedIndex = itemCount - 1; | |
} else { | |
selectedIndex--; | |
} | |
updateSelected(); | |
break; | |
case "ArrowRight": | |
event.preventDefault(); | |
selectedIndex = itemCount - 1; | |
updateSelected(); | |
break; | |
case "ArrowDown": | |
event.preventDefault(); | |
if (selectedIndex === itemCount - 1) { | |
selectedIndex = 0; | |
} else { | |
selectedIndex++; | |
} | |
updateSelected(); | |
break; | |
case "ArrowLeft": | |
event.preventDefault(); | |
selectedIndex = 0; | |
updateSelected(); | |
break; | |
case "Enter": | |
selectedItem?.click(); | |
break; | |
case "Escape": | |
this.close(); | |
break; | |
} | |
}); | |
filter.addEventListener("input", () => { | |
// Hide all items that don't match our filter | |
const term = filter.value.toLocaleLowerCase(); | |
// When filtering, recompute which items are visible for arrow up/down and maintain selection. | |
displayedItems = items.filter(item => { | |
const isVisible = !term || item.textContent.toLocaleLowerCase().includes(term); | |
item.style.display = isVisible ? "block" : "none"; | |
return isVisible; | |
}); | |
selectedIndex = 0; | |
if (displayedItems.includes(selectedItem)) { | |
selectedIndex = displayedItems.findIndex(d => d === selectedItem); | |
} | |
itemCount = displayedItems.length; | |
updateSelected(); | |
// If we have an event then we can try and position the list under the source | |
if (options.event) { | |
let top = options.event.clientY - 10; | |
const bodyRect = document.body.getBoundingClientRect(); | |
const rootRect = this.root.getBoundingClientRect(); | |
if (bodyRect.height && top > bodyRect.height - rootRect.height - 10) { | |
top = Math.max(0, bodyRect.height - rootRect.height - 10); | |
} | |
this.root.style.top = top + "px"; | |
positionList(); | |
} | |
}); | |
requestAnimationFrame(() => { | |
// Focus the filter box when opening | |
filter.focus(); | |
positionList(); | |
}); | |
}) | |
} | |
return ctx; | |
}; | |
LiteGraph.ContextMenu.prototype = ctxMenu.prototype; | |
}, | |
} | |
app.registerExtension(ext); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment