This commit is contained in:
comfyanonymous 2023-06-10 03:23:25 -04:00
commit b18946c53b
2 changed files with 128 additions and 113 deletions

View File

@ -2,51 +2,50 @@ import { app } from "/scripts/app.js";
// Adds filtering to combo context menus // Adds filtering to combo context menus
const id = "Comfy.ContextMenuFilter"; const ext = {
app.registerExtension({ name: "Comfy.ContextMenuFilter",
name: id,
init() { init() {
const ctxMenu = LiteGraph.ContextMenu; const ctxMenu = LiteGraph.ContextMenu;
LiteGraph.ContextMenu = function (values, options) { LiteGraph.ContextMenu = function (values, options) {
const ctx = ctxMenu.call(this, 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 we are a dark menu (only used for combo boxes) then add a filter input
if (options?.className === "dark" && values?.length > 10) { if (options?.className === "dark" && values?.length > 10) {
const filter = document.createElement("input"); const filter = document.createElement("input");
Object.assign(filter.style, { filter.classList.add("comfy-context-menu-filter");
width: "calc(100% - 10px)",
border: "0",
boxSizing: "border-box",
background: "#333",
border: "1px solid #999",
margin: "0 0 5px 5px",
color: "#fff",
});
filter.placeholder = "Filter list"; filter.placeholder = "Filter list";
this.root.prepend(filter); this.root.prepend(filter);
let selectedIndex = 0; const items = Array.from(this.root.querySelectorAll(".litemenu-entry"));
let items = this.root.querySelectorAll(".litemenu-entry"); let displayedItems = [...items];
let itemCount = items.length; let itemCount = displayedItems.length;
let selectedItem;
// 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 = values.findIndex(v => v === clickedComboValue);
let selectedItem = displayedItems?.[selectedIndex];
updateSelected();
// Apply highlighting to the selected item // Apply highlighting to the selected item
function updateSelected() { function updateSelected() {
if (selectedItem) { selectedItem?.style.setProperty("background-color", "");
selectedItem.style.setProperty("background-color", ""); selectedItem?.style.setProperty("color", "");
selectedItem.style.setProperty("color", ""); selectedItem = displayedItems[selectedIndex];
} selectedItem?.style.setProperty("background-color", "#ccc", "important");
selectedItem = items[selectedIndex]; selectedItem?.style.setProperty("color", "#000", "important");
if (selectedItem) {
selectedItem.style.setProperty("background-color", "#ccc", "important");
selectedItem.style.setProperty("color", "#000", "important");
}
} }
const positionList = () => { const positionList = () => {
const rect = this.root.getBoundingClientRect(); const rect = this.root.getBoundingClientRect();
// If the top is off screen then shift the element with scaling applied // If the top is off-screen then shift the element with scaling applied
if (rect.top < 0) { if (rect.top < 0) {
const scale = 1 - this.root.getBoundingClientRect().height / this.root.clientHeight; const scale = 1 - this.root.getBoundingClientRect().height / this.root.clientHeight;
const shift = (this.root.clientHeight * scale) / 2; const shift = (this.root.clientHeight * scale) / 2;
@ -54,56 +53,62 @@ app.registerExtension({
} }
} }
updateSelected();
// Arrow up/down to select items // Arrow up/down to select items
filter.addEventListener("keydown", (e) => { filter.addEventListener("keydown", (event) => {
if (e.key === "ArrowUp") { switch (event.key) {
case "ArrowUp":
event.preventDefault();
if (selectedIndex === 0) { if (selectedIndex === 0) {
selectedIndex = itemCount - 1; selectedIndex = itemCount - 1;
} else { } else {
selectedIndex--; selectedIndex--;
} }
updateSelected(); updateSelected();
e.preventDefault(); break;
} else if (e.key === "ArrowDown") { case "ArrowRight":
event.preventDefault();
selectedIndex = itemCount - 1;
updateSelected();
break;
case "ArrowDown":
event.preventDefault();
if (selectedIndex === itemCount - 1) { if (selectedIndex === itemCount - 1) {
selectedIndex = 0; selectedIndex = 0;
} else { } else {
selectedIndex++; selectedIndex++;
} }
updateSelected(); updateSelected();
e.preventDefault(); break;
} else if ((selectedItem && e.key === "Enter") || e.keyCode === 13 || e.keyCode === 10) { case "ArrowLeft":
selectedItem.click(); event.preventDefault();
} else if(e.key === "Escape") { selectedIndex = 0;
updateSelected();
break;
case "Enter":
selectedItem?.click();
break;
case "Escape":
this.close(); this.close();
break;
} }
}); });
filter.addEventListener("input", () => { filter.addEventListener("input", () => {
// Hide all items that dont match our filter // Hide all items that don't match our filter
const term = filter.value.toLocaleLowerCase(); const term = filter.value.toLocaleLowerCase();
items = this.root.querySelectorAll(".litemenu-entry"); // When filtering, recompute which items are visible for arrow up/down and maintain selection.
// When filtering recompute which items are visible for arrow up/down displayedItems = items.filter(item => {
// Try and maintain selection const isVisible = !term || item.textContent.toLocaleLowerCase().includes(term);
let visibleItems = []; item.style.display = isVisible ? "block" : "none";
for (const item of items) { return isVisible;
const visible = !term || item.textContent.toLocaleLowerCase().includes(term); });
if (visible) {
item.style.display = "block";
if (item === selectedItem) {
selectedIndex = visibleItems.length;
}
visibleItems.push(item);
} else {
item.style.display = "none";
if (item === selectedItem) {
selectedIndex = 0; selectedIndex = 0;
if (displayedItems.includes(selectedItem)) {
selectedIndex = displayedItems.findIndex(d => d === selectedItem);
} }
} itemCount = displayedItems.length;
}
items = visibleItems;
updateSelected(); updateSelected();
// If we have an event then we can try and position the list under the source // If we have an event then we can try and position the list under the source
@ -127,6 +132,7 @@ app.registerExtension({
positionList(); positionList();
}); });
})
} }
return ctx; return ctx;
@ -134,4 +140,6 @@ app.registerExtension({
LiteGraph.ContextMenu.prototype = ctxMenu.prototype; LiteGraph.ContextMenu.prototype = ctxMenu.prototype;
}, },
}); }
app.registerExtension(ext);

View File

@ -50,7 +50,7 @@ body {
padding: 30px 30px 10px 30px; padding: 30px 30px 10px 30px;
background-color: var(--comfy-menu-bg); /* Modal background */ background-color: var(--comfy-menu-bg); /* Modal background */
color: var(--error-text); color: var(--error-text);
box-shadow: 0px 0px 20px #888888; box-shadow: 0 0 20px #888888;
border-radius: 10px; border-radius: 10px;
top: 50%; top: 50%;
left: 50%; left: 50%;
@ -84,7 +84,7 @@ body {
font-size: 15px; font-size: 15px;
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 0%; right: 0;
text-align: center; text-align: center;
z-index: 100; z-index: 100;
width: 170px; width: 170px;
@ -252,7 +252,7 @@ button.comfy-queue-btn {
bottom: 0 !important; bottom: 0 !important;
left: auto !important; left: auto !important;
right: 0 !important; right: 0 !important;
border-radius: 0px; border-radius: 0;
} }
.comfy-menu span.drag-handle { .comfy-menu span.drag-handle {
visibility:hidden visibility:hidden
@ -291,7 +291,7 @@ button.comfy-queue-btn {
.litegraph .dialog { .litegraph .dialog {
z-index: 1; z-index: 1;
font-family: Arial; font-family: Arial, sans-serif;
} }
.litegraph .litemenu-entry.has_submenu { .litegraph .litemenu-entry.has_submenu {
@ -330,6 +330,13 @@ button.comfy-queue-btn {
color: var(--input-text) !important; color: var(--input-text) !important;
} }
.comfy-context-menu-filter {
box-sizing: border-box;
border: 1px solid #999;
margin: 0 0 5px 5px;
width: calc(100% - 10px);
}
/* Search box */ /* Search box */
.litegraph.litesearchbox { .litegraph.litesearchbox {