Revert "Format all code with prettier (#197)" (#198)

This reverts commit 28382b7e45.
This commit is contained in:
filtered
2024-10-09 04:14:36 +11:00
committed by GitHub
parent 28382b7e45
commit 5d510cd674
25 changed files with 14522 additions and 16453 deletions

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
import type { IContextMenuOptions, IContextMenuValue } from "./interfaces";
import type { IContextMenuOptions, IContextMenuValue } from "./interfaces"
import { LiteGraph } from "./litegraph";
/* LiteGraph GUI elements used for canvas editing *************************************/
@@ -16,140 +16,133 @@ import { LiteGraph } from "./litegraph";
* - event: you can pass a MouseEvent, this way the ContextMenu appears in that position
*/
export class ContextMenu {
options?: IContextMenuOptions;
parentMenu?: ContextMenu;
root: HTMLDivElement;
current_submenu?: ContextMenu;
lock?: boolean;
options?: IContextMenuOptions
parentMenu?: ContextMenu
root: HTMLDivElement
current_submenu?: ContextMenu
lock?: boolean
constructor(
values: IContextMenuValue[] | string[],
options: IContextMenuOptions,
) {
options = options || {};
this.options = options;
var that = this;
constructor(values: IContextMenuValue[] | string[], options: IContextMenuOptions) {
options = options || {};
this.options = options;
var that = this;
//to link a menu with its parent
if (options.parentMenu) {
if (!(options.parentMenu instanceof ContextMenu)) {
console.error("parentMenu must be of class ContextMenu, ignoring it");
options.parentMenu = null;
} else {
this.parentMenu = options.parentMenu;
this.parentMenu.lock = true;
this.parentMenu.current_submenu = this;
}
if (options.parentMenu.options?.className === "dark") {
options.className = "dark";
}
}
var eventClass = null;
if (options.event)
//use strings because comparing classes between windows doesnt work
eventClass = options.event.constructor.name;
if (
eventClass !== "MouseEvent" &&
eventClass !== "CustomEvent" &&
eventClass !== "PointerEvent"
) {
console.error(
"Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. (" +
eventClass +
")",
);
options.event = null;
}
var root = document.createElement("div");
root.className = "litegraph litecontextmenu litemenubar-panel";
if (options.className) {
root.className += " " + options.className;
}
root.style.minWidth = 100;
root.style.minHeight = 100;
root.style.pointerEvents = "none";
setTimeout(function () {
root.style.pointerEvents = "auto";
}, 100); //delay so the mouse up event is not caught by this element
//this prevents the default context browser menu to open in case this menu was created when pressing right button
LiteGraph.pointerListenerAdd(
root,
"up",
function (e) {
//console.log("pointerevents: ContextMenu up root prevent");
e.preventDefault();
return true;
},
true,
);
root.addEventListener(
"contextmenu",
function (e) {
if (e.button != 2) {
//right button
return false;
//to link a menu with its parent
if (options.parentMenu) {
if (!(options.parentMenu instanceof ContextMenu)) {
console.error(
"parentMenu must be of class ContextMenu, ignoring it"
);
options.parentMenu = null;
} else {
this.parentMenu = options.parentMenu;
this.parentMenu.lock = true;
this.parentMenu.current_submenu = this;
}
if (options.parentMenu.options?.className === "dark") {
options.className = "dark";
}
}
e.preventDefault();
return false;
},
true,
);
LiteGraph.pointerListenerAdd(
root,
"down",
function (e) {
//console.log("pointerevents: ContextMenu down");
if (e.button == 2) {
that.close();
e.preventDefault();
return true;
var eventClass = null;
if (options.event) //use strings because comparing classes between windows doesnt work
eventClass = options.event.constructor.name;
if (eventClass !== "MouseEvent" &&
eventClass !== "CustomEvent" &&
eventClass !== "PointerEvent") {
console.error(
"Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. (" + eventClass + ")"
);
options.event = null;
}
},
true,
);
function on_mouse_wheel(e) {
var pos = parseInt(root.style.top);
root.style.top = (pos + e.deltaY * options.scroll_speed).toFixed() + "px";
e.preventDefault();
return true;
}
var root = document.createElement("div");
root.className = "litegraph litecontextmenu litemenubar-panel";
if (options.className) {
root.className += " " + options.className;
}
root.style.minWidth = 100;
root.style.minHeight = 100;
root.style.pointerEvents = "none";
setTimeout(function () {
root.style.pointerEvents = "auto";
}, 100); //delay so the mouse up event is not caught by this element
if (!options.scroll_speed) {
options.scroll_speed = 0.1;
}
root.addEventListener("wheel", on_mouse_wheel, true);
root.addEventListener("mousewheel", on_mouse_wheel, true);
this.root = root;
//this prevents the default context browser menu to open in case this menu was created when pressing right button
LiteGraph.pointerListenerAdd(root, "up",
function (e) {
//console.log("pointerevents: ContextMenu up root prevent");
e.preventDefault();
return true;
},
true
);
root.addEventListener(
"contextmenu",
function (e) {
if (e.button != 2) {
//right button
return false;
}
e.preventDefault();
return false;
},
true
);
//title
if (options.title) {
var element = document.createElement("div");
element.className = "litemenu-title";
element.innerHTML = options.title;
root.appendChild(element);
}
LiteGraph.pointerListenerAdd(root, "down",
function (e) {
//console.log("pointerevents: ContextMenu down");
if (e.button == 2) {
that.close();
e.preventDefault();
return true;
}
},
true
);
//entries
var num = 0;
for (var i = 0; i < values.length; i++) {
var name = values.constructor == Array ? values[i] : i;
if (name != null && name.constructor !== String) {
name = name.content === undefined ? String(name) : name.content;
}
var value = values[i];
this.addItem(name, value, options);
num++;
}
function on_mouse_wheel(e) {
var pos = parseInt(root.style.top);
root.style.top =
(pos + e.deltaY * options.scroll_speed).toFixed() + "px";
e.preventDefault();
return true;
}
//close on leave? touch enabled devices won't work TODO use a global device detector and condition on that
/*LiteGraph.pointerListenerAdd(root,"leave", function(e) {
if (!options.scroll_speed) {
options.scroll_speed = 0.1;
}
root.addEventListener("wheel", on_mouse_wheel, true);
root.addEventListener("mousewheel", on_mouse_wheel, true);
this.root = root;
//title
if (options.title) {
var element = document.createElement("div");
element.className = "litemenu-title";
element.innerHTML = options.title;
root.appendChild(element);
}
//entries
var num = 0;
for (var i = 0; i < values.length; i++) {
var name = values.constructor == Array ? values[i] : i;
if (name != null && name.constructor !== String) {
name = name.content === undefined ? String(name) : name.content;
}
var value = values[i];
this.addItem(name, value, options);
num++;
}
//close on leave? touch enabled devices won't work TODO use a global device detector and condition on that
/*LiteGraph.pointerListenerAdd(root,"leave", function(e) {
console.log("pointerevents: ContextMenu leave");
if (that.lock) {
return;
@@ -160,298 +153,275 @@ export class ContextMenu {
root.closing_timer = setTimeout(that.close.bind(that, e), 500);
//that.close(e);
});*/
LiteGraph.pointerListenerAdd(root, "enter", function (e) {
//console.log("pointerevents: ContextMenu enter");
if (root.closing_timer) {
clearTimeout(root.closing_timer);
}
});
LiteGraph.pointerListenerAdd(root, "enter", function (e) {
//console.log("pointerevents: ContextMenu enter");
if (root.closing_timer) {
clearTimeout(root.closing_timer);
}
});
//insert before checking position
var root_document = document;
if (options.event) {
root_document = options.event.target.ownerDocument;
}
if (!root_document) {
root_document = document;
}
if (root_document.fullscreenElement)
root_document.fullscreenElement.appendChild(root);
else root_document.body.appendChild(root);
//compute best position
var left = options.left || 0;
var top = options.top || 0;
if (options.event) {
left = options.event.clientX - 10;
top = options.event.clientY - 10;
if (options.title) {
top -= 20;
}
if (options.parentMenu) {
var rect = options.parentMenu.root.getBoundingClientRect();
left = rect.left + rect.width;
}
var body_rect = document.body.getBoundingClientRect();
var root_rect = root.getBoundingClientRect();
if (body_rect.height == 0)
console.error(
"document.body height is 0. That is dangerous, set html,body { height: 100%; }",
);
if (body_rect.width && left > body_rect.width - root_rect.width - 10) {
left = body_rect.width - root_rect.width - 10;
}
if (body_rect.height && top > body_rect.height - root_rect.height - 10) {
top = body_rect.height - root_rect.height - 10;
}
}
root.style.left = left + "px";
root.style.top = top + "px";
if (options.scale) {
root.style.transform = "scale(" + options.scale + ")";
}
}
addItem(
name: string,
value: IContextMenuValue,
options: IContextMenuOptions,
): HTMLElement {
var that = this;
options = options || {};
var element = document.createElement("div");
element.className = "litemenu-entry submenu";
var disabled = false;
if (value === null) {
element.classList.add("separator");
//element.innerHTML = "<hr/>"
//continue;
} else {
element.innerHTML = value && value.title ? value.title : name;
element.value = value;
element.setAttribute("role", "menuitem");
if (value) {
if (value.disabled) {
disabled = true;
element.classList.add("disabled");
element.setAttribute("aria-disabled", "true");
//insert before checking position
var root_document = document;
if (options.event) {
root_document = options.event.target.ownerDocument;
}
if (value.submenu || value.has_submenu) {
element.classList.add("has_submenu");
element.setAttribute("aria-haspopup", "true");
element.setAttribute("aria-expanded", "false");
if (!root_document) {
root_document = document;
}
}
if (typeof value == "function") {
element.dataset["value"] = name;
element.onclick_callback = value;
} else {
element.dataset["value"] = value;
}
if (root_document.fullscreenElement)
root_document.fullscreenElement.appendChild(root);
if (value.className) {
element.className += " " + value.className;
}
}
this.root.appendChild(element);
if (!disabled) {
element.addEventListener("click", inner_onclick);
}
if (!disabled && options.autoopen) {
LiteGraph.pointerListenerAdd(element, "enter", inner_over);
}
else
root_document.body.appendChild(root);
function setAriaExpanded() {
const entries = that.root.querySelectorAll(
"div.litemenu-entry.has_submenu",
);
if (entries) {
for (let i = 0; i < entries.length; i++) {
entries[i].setAttribute("aria-expanded", "false");
//compute best position
var left = options.left || 0;
var top = options.top || 0;
if (options.event) {
left = options.event.clientX - 10;
top = options.event.clientY - 10;
if (options.title) {
top -= 20;
}
if (options.parentMenu) {
var rect = options.parentMenu.root.getBoundingClientRect();
left = rect.left + rect.width;
}
var body_rect = document.body.getBoundingClientRect();
var root_rect = root.getBoundingClientRect();
if (body_rect.height == 0)
console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }");
if (body_rect.width && left > body_rect.width - root_rect.width - 10) {
left = body_rect.width - root_rect.width - 10;
}
if (body_rect.height && top > body_rect.height - root_rect.height - 10) {
top = body_rect.height - root_rect.height - 10;
}
}
}
element.setAttribute("aria-expanded", "true");
}
function inner_over(e) {
var value = this.value;
if (!value || !value.has_submenu) {
return;
}
//if it is a submenu, autoopen like the item was clicked
inner_onclick.call(this, e);
setAriaExpanded();
}
root.style.left = left + "px";
root.style.top = top + "px";
//menu option clicked
function inner_onclick(e) {
var value = this.value;
var close_parent = true;
if (that.current_submenu) {
that.current_submenu.close(e);
}
if (value?.has_submenu || value?.submenu) {
setAriaExpanded();
}
//global callback
if (options.callback) {
var r = options.callback.call(
this,
value,
options,
e,
that,
options.node,
);
if (r === true) {
close_parent = false;
if (options.scale) {
root.style.transform = "scale(" + options.scale + ")";
}
}
}
//special cases
if (value) {
if (
value.callback &&
!options.ignore_item_callbacks &&
value.disabled !== true
) {
//item callback
var r = value.callback.call(
this,
value,
options,
e,
that,
options.extra,
);
if (r === true) {
close_parent = false;
}
addItem(name: string, value: IContextMenuValue, options: IContextMenuOptions): HTMLElement {
var that = this;
options = options || {};
var element = document.createElement("div");
element.className = "litemenu-entry submenu";
var disabled = false;
if (value === null) {
element.classList.add("separator");
//element.innerHTML = "<hr/>"
//continue;
} else {
element.innerHTML = value && value.title ? value.title : name;
element.value = value;
element.setAttribute("role", "menuitem");
if (value) {
if (value.disabled) {
disabled = true;
element.classList.add("disabled");
element.setAttribute("aria-disabled", "true");
}
if (value.submenu || value.has_submenu) {
element.classList.add("has_submenu");
element.setAttribute("aria-haspopup", "true");
element.setAttribute("aria-expanded", "false");
}
}
if (typeof value == "function") {
element.dataset["value"] = name;
element.onclick_callback = value;
} else {
element.dataset["value"] = value;
}
if (value.className) {
element.className += " " + value.className;
}
}
if (value.submenu) {
if (!value.submenu.options) {
throw "ContextMenu submenu needs options";
}
var submenu = new that.constructor(value.submenu.options, {
callback: value.submenu.callback,
event: e,
parentMenu: that,
ignore_item_callbacks: value.submenu.ignore_item_callbacks,
title: value.submenu.title,
extra: value.submenu.extra,
autoopen: options.autoopen,
});
close_parent = false;
this.root.appendChild(element);
if (!disabled) {
element.addEventListener("click", inner_onclick);
}
if (!disabled && options.autoopen) {
LiteGraph.pointerListenerAdd(element, "enter", inner_over);
}
}
if (close_parent && !that.lock) {
that.close();
}
function setAriaExpanded() {
const entries = that.root.querySelectorAll("div.litemenu-entry.has_submenu");
if (entries) {
for (let i = 0; i < entries.length; i++) {
entries[i].setAttribute("aria-expanded", "false");
}
}
element.setAttribute("aria-expanded", "true");
}
function inner_over(e) {
var value = this.value;
if (!value || !value.has_submenu) {
return;
}
//if it is a submenu, autoopen like the item was clicked
inner_onclick.call(this, e);
setAriaExpanded();
}
//menu option clicked
function inner_onclick(e) {
var value = this.value;
var close_parent = true;
if (that.current_submenu) {
that.current_submenu.close(e);
}
if (value?.has_submenu || value?.submenu) {
setAriaExpanded();
}
//global callback
if (options.callback) {
var r = options.callback.call(
this,
value,
options,
e,
that,
options.node
);
if (r === true) {
close_parent = false;
}
}
//special cases
if (value) {
if (value.callback &&
!options.ignore_item_callbacks &&
value.disabled !== true) {
//item callback
var r = value.callback.call(
this,
value,
options,
e,
that,
options.extra
);
if (r === true) {
close_parent = false;
}
}
if (value.submenu) {
if (!value.submenu.options) {
throw "ContextMenu submenu needs options";
}
var submenu = new that.constructor(value.submenu.options, {
callback: value.submenu.callback,
event: e,
parentMenu: that,
ignore_item_callbacks: value.submenu.ignore_item_callbacks,
title: value.submenu.title,
extra: value.submenu.extra,
autoopen: options.autoopen
});
close_parent = false;
}
}
if (close_parent && !that.lock) {
that.close();
}
}
return element;
}
return element;
}
close(e?: MouseEvent, ignore_parent_menu?: boolean): void {
if (this.root.parentNode) {
this.root.parentNode.removeChild(this.root);
}
if (this.parentMenu && !ignore_parent_menu) {
this.parentMenu.lock = false;
this.parentMenu.current_submenu = null;
if (e === undefined) {
this.parentMenu.close();
} else if (e &&
!ContextMenu.isCursorOverElement(e, this.parentMenu.root)) {
ContextMenu.trigger(this.parentMenu.root, LiteGraph.pointerevents_method + "leave", e);
}
}
if (this.current_submenu) {
this.current_submenu.close(e, true);
}
close(e?: MouseEvent, ignore_parent_menu?: boolean): void {
if (this.root.parentNode) {
this.root.parentNode.removeChild(this.root);
}
if (this.parentMenu && !ignore_parent_menu) {
this.parentMenu.lock = false;
this.parentMenu.current_submenu = null;
if (e === undefined) {
this.parentMenu.close();
} else if (
e &&
!ContextMenu.isCursorOverElement(e, this.parentMenu.root)
) {
ContextMenu.trigger(
this.parentMenu.root,
LiteGraph.pointerevents_method + "leave",
e,
);
}
}
if (this.current_submenu) {
this.current_submenu.close(e, true);
if (this.root.closing_timer) {
clearTimeout(this.root.closing_timer);
}
// TODO implement : LiteGraph.contextMenuClosed(); :: keep track of opened / closed / current ContextMenu
// on key press, allow filtering/selecting the context menu elements
}
if (this.root.closing_timer) {
clearTimeout(this.root.closing_timer);
//this code is used to trigger events easily (used in the context menu mouseleave
static trigger(element: HTMLDivElement, event_name: string, params: MouseEvent, origin?: undefined) {
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail
evt.srcElement = origin;
if (element.dispatchEvent) {
element.dispatchEvent(evt);
} else if (element.__events) {
element.__events.dispatchEvent(evt);
}
//else nothing seems binded here so nothing to do
return evt;
}
// TODO implement : LiteGraph.contextMenuClosed(); :: keep track of opened / closed / current ContextMenu
// on key press, allow filtering/selecting the context menu elements
}
//returns the top most menu
getTopMenu(): ContextMenu {
if (this.options.parentMenu) {
return this.options.parentMenu.getTopMenu();
}
return this;
}
//this code is used to trigger events easily (used in the context menu mouseleave
static trigger(
element: HTMLDivElement,
event_name: string,
params: MouseEvent,
origin?: undefined,
) {
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event_name, true, true, params); //canBubble, cancelable, detail
evt.srcElement = origin;
if (element.dispatchEvent) {
element.dispatchEvent(evt);
} else if (element.__events) {
element.__events.dispatchEvent(evt);
getFirstEvent(): MouseEvent {
if (this.options.parentMenu) {
return this.options.parentMenu.getFirstEvent();
}
return this.options.event;
}
//else nothing seems binded here so nothing to do
return evt;
}
//returns the top most menu
getTopMenu(): ContextMenu {
if (this.options.parentMenu) {
return this.options.parentMenu.getTopMenu();
static isCursorOverElement(event: MouseEvent, element: HTMLDivElement): boolean {
var left = event.clientX;
var top = event.clientY;
var rect = element.getBoundingClientRect();
if (!rect) {
return false;
}
if (top > rect.top &&
top < rect.top + rect.height &&
left > rect.left &&
left < rect.left + rect.width) {
return true;
}
return false;
}
return this;
}
getFirstEvent(): MouseEvent {
if (this.options.parentMenu) {
return this.options.parentMenu.getFirstEvent();
}
return this.options.event;
}
static isCursorOverElement(
event: MouseEvent,
element: HTMLDivElement,
): boolean {
var left = event.clientX;
var top = event.clientY;
var rect = element.getBoundingClientRect();
if (!rect) {
return false;
}
if (
top > rect.top &&
top < rect.top + rect.height &&
left > rect.left &&
left < rect.left + rect.width
) {
return true;
}
return false;
}
}

View File

@@ -4,161 +4,165 @@ import { clamp } from "./litegraph";
//used by some widgets to render a curve editor
export class CurveEditor {
constructor(points) {
this.points = points;
this.selected = -1;
this.nearest = -1;
this.size = null; //stores last size used
this.must_update = true;
this.margin = 5;
}
static sampleCurve(f, points) {
if (!points) return;
for (var i = 0; i < points.length - 1; ++i) {
var p = points[i];
var pn = points[i + 1];
if (pn[0] < f) continue;
var r = pn[0] - p[0];
if (Math.abs(r) < 0.00001) return p[1];
var local_f = (f - p[0]) / r;
return p[1] * (1.0 - local_f) + pn[1] * local_f;
}
return 0;
}
draw(ctx, size, graphcanvas, background_color, line_color, inactive) {
var points = this.points;
if (!points) return;
this.size = size;
var w = size[0] - this.margin * 2;
var h = size[1] - this.margin * 2;
line_color = line_color || "#666";
ctx.save();
ctx.translate(this.margin, this.margin);
if (background_color) {
ctx.fillStyle = "#111";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "#222";
ctx.fillRect(w * 0.5, 0, 1, h);
ctx.strokeStyle = "#333";
ctx.strokeRect(0, 0, w, h);
}
ctx.strokeStyle = line_color;
if (inactive) ctx.globalAlpha = 0.5;
ctx.beginPath();
for (var i = 0; i < points.length; ++i) {
var p = points[i];
ctx.lineTo(p[0] * w, (1.0 - p[1]) * h);
}
ctx.stroke();
ctx.globalAlpha = 1;
if (!inactive)
for (var i = 0; i < points.length; ++i) {
var p = points[i];
ctx.fillStyle =
this.selected == i ? "#FFF" : this.nearest == i ? "#DDD" : "#AAA";
ctx.beginPath();
ctx.arc(p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
//localpos is mouse in curve editor space
onMouseDown(localpos, graphcanvas) {
var points = this.points;
if (!points) return;
if (localpos[1] < 0) return;
//this.captureInput(true);
var w = this.size[0] - this.margin * 2;
var h = this.size[1] - this.margin * 2;
var x = localpos[0] - this.margin;
var y = localpos[1] - this.margin;
var pos = [x, y];
var max_dist = 30 / graphcanvas.ds.scale;
//search closer one
this.selected = this.getCloserPoint(pos, max_dist);
//create one
if (this.selected == -1) {
var point = [x / w, 1 - y / h];
points.push(point);
points.sort(function (a, b) {
return a[0] - b[0];
});
this.selected = points.indexOf(point);
this.must_update = true;
}
if (this.selected != -1) return true;
}
onMouseMove(localpos, graphcanvas) {
var points = this.points;
if (!points) return;
var s = this.selected;
if (s < 0) return;
var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2);
var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2);
var curvepos = [localpos[0] - this.margin, localpos[1] - this.margin];
var max_dist = 30 / graphcanvas.ds.scale;
this._nearest = this.getCloserPoint(curvepos, max_dist);
var point = points[s];
if (point) {
var is_edge_point = s == 0 || s == points.length - 1;
if (
!is_edge_point &&
(localpos[0] < -10 ||
localpos[0] > this.size[0] + 10 ||
localpos[1] < -10 ||
localpos[1] > this.size[1] + 10)
) {
points.splice(s, 1);
constructor(points) {
this.points = points;
this.selected = -1;
return;
}
if (!is_edge_point)
//not edges
point[0] = clamp(x, 0, 1);
else point[0] = s == 0 ? 0 : 1;
point[1] = 1.0 - clamp(y, 0, 1);
points.sort(function (a, b) {
return a[0] - b[0];
});
this.selected = points.indexOf(point);
this.must_update = true;
this.nearest = -1;
this.size = null; //stores last size used
this.must_update = true;
this.margin = 5;
}
}
onMouseUp(localpos, graphcanvas) {
this.selected = -1;
return false;
}
getCloserPoint(pos, max_dist) {
var points = this.points;
if (!points) return -1;
max_dist = max_dist || 30;
var w = this.size[0] - this.margin * 2;
var h = this.size[1] - this.margin * 2;
var num = points.length;
var p2 = [0, 0];
var min_dist = 1000000;
var closest = -1;
var last_valid = -1;
for (var i = 0; i < num; ++i) {
var p = points[i];
p2[0] = p[0] * w;
p2[1] = (1.0 - p[1]) * h;
if (p2[0] < pos[0]) last_valid = i;
var dist = vec2.distance(pos, p2);
if (dist > min_dist || dist > max_dist) continue;
closest = i;
min_dist = dist;
static sampleCurve(f, points) {
if (!points)
return;
for (var i = 0; i < points.length - 1; ++i) {
var p = points[i];
var pn = points[i + 1];
if (pn[0] < f)
continue;
var r = (pn[0] - p[0]);
if (Math.abs(r) < 0.00001)
return p[1];
var local_f = (f - p[0]) / r;
return p[1] * (1.0 - local_f) + pn[1] * local_f;
}
return 0;
}
draw(ctx, size, graphcanvas, background_color, line_color, inactive) {
var points = this.points;
if (!points)
return;
this.size = size;
var w = size[0] - this.margin * 2;
var h = size[1] - this.margin * 2;
line_color = line_color || "#666";
ctx.save();
ctx.translate(this.margin, this.margin);
if (background_color) {
ctx.fillStyle = "#111";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "#222";
ctx.fillRect(w * 0.5, 0, 1, h);
ctx.strokeStyle = "#333";
ctx.strokeRect(0, 0, w, h);
}
ctx.strokeStyle = line_color;
if (inactive)
ctx.globalAlpha = 0.5;
ctx.beginPath();
for (var i = 0; i < points.length; ++i) {
var p = points[i];
ctx.lineTo(p[0] * w, (1.0 - p[1]) * h);
}
ctx.stroke();
ctx.globalAlpha = 1;
if (!inactive)
for (var i = 0; i < points.length; ++i) {
var p = points[i];
ctx.fillStyle = this.selected == i ? "#FFF" : (this.nearest == i ? "#DDD" : "#AAA");
ctx.beginPath();
ctx.arc(p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
//localpos is mouse in curve editor space
onMouseDown(localpos, graphcanvas) {
var points = this.points;
if (!points)
return;
if (localpos[1] < 0)
return;
//this.captureInput(true);
var w = this.size[0] - this.margin * 2;
var h = this.size[1] - this.margin * 2;
var x = localpos[0] - this.margin;
var y = localpos[1] - this.margin;
var pos = [x, y];
var max_dist = 30 / graphcanvas.ds.scale;
//search closer one
this.selected = this.getCloserPoint(pos, max_dist);
//create one
if (this.selected == -1) {
var point = [x / w, 1 - y / h];
points.push(point);
points.sort(function (a, b) { return a[0] - b[0]; });
this.selected = points.indexOf(point);
this.must_update = true;
}
if (this.selected != -1)
return true;
}
onMouseMove(localpos, graphcanvas) {
var points = this.points;
if (!points)
return;
var s = this.selected;
if (s < 0)
return;
var x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2);
var y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2);
var curvepos = [(localpos[0] - this.margin), (localpos[1] - this.margin)];
var max_dist = 30 / graphcanvas.ds.scale;
this._nearest = this.getCloserPoint(curvepos, max_dist);
var point = points[s];
if (point) {
var is_edge_point = s == 0 || s == points.length - 1;
if (!is_edge_point && (localpos[0] < -10 || localpos[0] > this.size[0] + 10 || localpos[1] < -10 || localpos[1] > this.size[1] + 10)) {
points.splice(s, 1);
this.selected = -1;
return;
}
if (!is_edge_point) //not edges
point[0] = clamp(x, 0, 1);
else
point[0] = s == 0 ? 0 : 1;
point[1] = 1.0 - clamp(y, 0, 1);
points.sort(function (a, b) { return a[0] - b[0]; });
this.selected = points.indexOf(point);
this.must_update = true;
}
}
onMouseUp(localpos, graphcanvas) {
this.selected = -1;
return false;
}
getCloserPoint(pos, max_dist) {
var points = this.points;
if (!points)
return -1;
max_dist = max_dist || 30;
var w = (this.size[0] - this.margin * 2);
var h = (this.size[1] - this.margin * 2);
var num = points.length;
var p2 = [0, 0];
var min_dist = 1000000;
var closest = -1;
var last_valid = -1;
for (var i = 0; i < num; ++i) {
var p = points[i];
p2[0] = p[0] * w;
p2[1] = (1.0 - p[1]) * h;
if (p2[0] < pos[0])
last_valid = i;
var dist = vec2.distance(pos, p2);
if (dist > min_dist || dist > max_dist)
continue;
closest = i;
min_dist = dist;
}
return closest;
}
return closest;
}
}

View File

@@ -1,248 +1,235 @@
import type { Point, Rect, Rect32 } from "./interfaces";
import type { Point, Rect, Rect32 } from "./interfaces"
import { LiteGraph } from "./litegraph";
//****************************************
//Scale and Offset
export class DragAndScale {
max_scale: number;
min_scale: number;
offset: Point;
scale: number;
enabled: boolean;
last_mouse: Point;
element?: HTMLCanvasElement;
visible_area: Rect32;
_binded_mouse_callback;
dragging?: boolean;
viewport?: Rect;
max_scale: number
min_scale: number
offset: Point
scale: number
enabled: boolean
last_mouse: Point
element?: HTMLCanvasElement
visible_area: Rect32
_binded_mouse_callback
dragging?: boolean
viewport?: Rect
onredraw?(das: DragAndScale): void;
/** @deprecated */
onmouse?(e: any): boolean;
onredraw?(das: DragAndScale): void
/** @deprecated */
onmouse?(e: any): boolean
constructor(element?: HTMLCanvasElement, skip_events?: boolean) {
this.offset = new Float32Array([0, 0]);
this.scale = 1;
this.max_scale = 10;
this.min_scale = 0.1;
this.onredraw = null;
this.enabled = true;
this.last_mouse = [0, 0];
this.element = null;
this.visible_area = new Float32Array(4);
constructor(element?: HTMLCanvasElement, skip_events?: boolean) {
this.offset = new Float32Array([0, 0]);
this.scale = 1;
this.max_scale = 10;
this.min_scale = 0.1;
this.onredraw = null;
this.enabled = true;
this.last_mouse = [0, 0];
this.element = null;
this.visible_area = new Float32Array(4);
if (element) {
this.element = element;
if (!skip_events) {
this.bindEvents(element);
}
}
}
bindEvents(element) {
this.last_mouse = new Float32Array(2);
this._binded_mouse_callback = this.onMouse.bind(this);
LiteGraph.pointerListenerAdd(element, "down", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(element, "move", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(element, "up", this._binded_mouse_callback);
element.addEventListener("mousewheel", this._binded_mouse_callback, false);
element.addEventListener("wheel", this._binded_mouse_callback, false);
}
computeVisibleArea(viewport: Rect): void {
if (!this.element) {
this.visible_area[0] =
this.visible_area[1] =
this.visible_area[2] =
this.visible_area[3] =
0;
return;
}
var width = this.element.width;
var height = this.element.height;
var startx = -this.offset[0];
var starty = -this.offset[1];
if (viewport) {
startx += viewport[0] / this.scale;
starty += viewport[1] / this.scale;
width = viewport[2];
height = viewport[3];
}
var endx = startx + width / this.scale;
var endy = starty + height / this.scale;
this.visible_area[0] = startx;
this.visible_area[1] = starty;
this.visible_area[2] = endx - startx;
this.visible_area[3] = endy - starty;
}
onMouse(e) {
if (!this.enabled) {
return;
}
var canvas = this.element;
var rect = canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
e.canvasx = x;
e.canvasy = y;
e.dragging = this.dragging;
var is_inside =
!this.viewport ||
(this.viewport &&
x >= this.viewport[0] &&
x < this.viewport[0] + this.viewport[2] &&
y >= this.viewport[1] &&
y < this.viewport[1] + this.viewport[3]);
//console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside);
var ignore = false;
if (this.onmouse) {
ignore = this.onmouse(e);
}
if (e.type == LiteGraph.pointerevents_method + "down" && is_inside) {
this.dragging = true;
LiteGraph.pointerListenerRemove(
canvas,
"move",
this._binded_mouse_callback,
);
LiteGraph.pointerListenerAdd(
document,
"move",
this._binded_mouse_callback,
);
LiteGraph.pointerListenerAdd(document, "up", this._binded_mouse_callback);
} else if (e.type == LiteGraph.pointerevents_method + "move") {
if (!ignore) {
var deltax = x - this.last_mouse[0];
var deltay = y - this.last_mouse[1];
if (this.dragging) {
this.mouseDrag(deltax, deltay);
if (element) {
this.element = element;
if (!skip_events) {
this.bindEvents(element);
}
}
}
} else if (e.type == LiteGraph.pointerevents_method + "up") {
this.dragging = false;
LiteGraph.pointerListenerRemove(
document,
"move",
this._binded_mouse_callback,
);
LiteGraph.pointerListenerRemove(
document,
"up",
this._binded_mouse_callback,
);
LiteGraph.pointerListenerAdd(canvas, "move", this._binded_mouse_callback);
} else if (
is_inside &&
(e.type == "mousewheel" ||
e.type == "wheel" ||
e.type == "DOMMouseScroll")
) {
e.eventType = "mousewheel";
if (e.type == "wheel") {
e.wheel = -e.deltaY;
} else {
e.wheel = e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60;
}
//from stack overflow
e.delta = e.wheelDelta ? e.wheelDelta / 40 : e.deltaY ? -e.deltaY / 3 : 0;
this.changeDeltaScale(1.0 + e.delta * 0.05);
}
this.last_mouse[0] = x;
this.last_mouse[1] = y;
bindEvents(element) {
this.last_mouse = new Float32Array(2);
if (is_inside) {
e.preventDefault();
e.stopPropagation();
return false;
}
}
this._binded_mouse_callback = this.onMouse.bind(this);
toCanvasContext(ctx: CanvasRenderingContext2D): void {
ctx.scale(this.scale, this.scale);
ctx.translate(this.offset[0], this.offset[1]);
}
LiteGraph.pointerListenerAdd(element, "down", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(element, "move", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(element, "up", this._binded_mouse_callback);
convertOffsetToCanvas(pos: Point): Point {
//return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]];
return [
(pos[0] + this.offset[0]) * this.scale,
(pos[1] + this.offset[1]) * this.scale,
];
}
convertCanvasToOffset(pos: Point, out?: Point): Point {
out = out || [0, 0];
out[0] = pos[0] / this.scale - this.offset[0];
out[1] = pos[1] / this.scale - this.offset[1];
return out;
}
mouseDrag(x, y) {
this.offset[0] += x / this.scale;
this.offset[1] += y / this.scale;
if (this.onredraw) {
this.onredraw(this);
}
}
changeScale(value: number, zooming_center?: Point): void {
if (value < this.min_scale) {
value = this.min_scale;
} else if (value > this.max_scale) {
value = this.max_scale;
element.addEventListener(
"mousewheel",
this._binded_mouse_callback,
false
);
element.addEventListener("wheel", this._binded_mouse_callback, false);
}
if (value == this.scale) {
return;
computeVisibleArea(viewport: Rect): void {
if (!this.element) {
this.visible_area[0] = this.visible_area[1] = this.visible_area[2] = this.visible_area[3] = 0;
return;
}
var width = this.element.width;
var height = this.element.height;
var startx = -this.offset[0];
var starty = -this.offset[1];
if (viewport) {
startx += viewport[0] / this.scale;
starty += viewport[1] / this.scale;
width = viewport[2];
height = viewport[3];
}
var endx = startx + width / this.scale;
var endy = starty + height / this.scale;
this.visible_area[0] = startx;
this.visible_area[1] = starty;
this.visible_area[2] = endx - startx;
this.visible_area[3] = endy - starty;
}
if (!this.element) {
return;
onMouse(e) {
if (!this.enabled) {
return;
}
var canvas = this.element;
var rect = canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
e.canvasx = x;
e.canvasy = y;
e.dragging = this.dragging;
var is_inside = !this.viewport || (this.viewport && x >= this.viewport[0] && x < (this.viewport[0] + this.viewport[2]) && y >= this.viewport[1] && y < (this.viewport[1] + this.viewport[3]));
//console.log("pointerevents: DragAndScale onMouse "+e.type+" "+is_inside);
var ignore = false;
if (this.onmouse) {
ignore = this.onmouse(e);
}
if (e.type == LiteGraph.pointerevents_method + "down" && is_inside) {
this.dragging = true;
LiteGraph.pointerListenerRemove(canvas, "move", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(document, "move", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(document, "up", this._binded_mouse_callback);
} else if (e.type == LiteGraph.pointerevents_method + "move") {
if (!ignore) {
var deltax = x - this.last_mouse[0];
var deltay = y - this.last_mouse[1];
if (this.dragging) {
this.mouseDrag(deltax, deltay);
}
}
} else if (e.type == LiteGraph.pointerevents_method + "up") {
this.dragging = false;
LiteGraph.pointerListenerRemove(document, "move", this._binded_mouse_callback);
LiteGraph.pointerListenerRemove(document, "up", this._binded_mouse_callback);
LiteGraph.pointerListenerAdd(canvas, "move", this._binded_mouse_callback);
} else if (is_inside &&
(e.type == "mousewheel" ||
e.type == "wheel" ||
e.type == "DOMMouseScroll")) {
e.eventType = "mousewheel";
if (e.type == "wheel") {
e.wheel = -e.deltaY;
} else {
e.wheel =
e.wheelDeltaY != null ? e.wheelDeltaY : e.detail * -60;
}
//from stack overflow
e.delta = e.wheelDelta
? e.wheelDelta / 40
: e.deltaY
? -e.deltaY / 3
: 0;
this.changeDeltaScale(1.0 + e.delta * 0.05);
}
this.last_mouse[0] = x;
this.last_mouse[1] = y;
if (is_inside) {
e.preventDefault();
e.stopPropagation();
return false;
}
}
var rect = this.element.getBoundingClientRect();
if (!rect) {
return;
toCanvasContext(ctx: CanvasRenderingContext2D): void {
ctx.scale(this.scale, this.scale);
ctx.translate(this.offset[0], this.offset[1]);
}
zooming_center = zooming_center || [rect.width * 0.5, rect.height * 0.5];
var center = this.convertCanvasToOffset(zooming_center);
this.scale = value;
if (Math.abs(this.scale - 1) < 0.01) {
this.scale = 1;
convertOffsetToCanvas(pos: Point): Point {
//return [pos[0] / this.scale - this.offset[0], pos[1] / this.scale - this.offset[1]];
return [
(pos[0] + this.offset[0]) * this.scale,
(pos[1] + this.offset[1]) * this.scale
];
}
var new_center = this.convertCanvasToOffset(zooming_center);
var delta_offset = [new_center[0] - center[0], new_center[1] - center[1]];
this.offset[0] += delta_offset[0];
this.offset[1] += delta_offset[1];
if (this.onredraw) {
this.onredraw(this);
convertCanvasToOffset(pos: Point, out?: Point): Point {
out = out || [0, 0];
out[0] = pos[0] / this.scale - this.offset[0];
out[1] = pos[1] / this.scale - this.offset[1];
return out;
}
}
changeDeltaScale(value: number, zooming_center?: Point) {
this.changeScale(this.scale * value, zooming_center);
}
mouseDrag(x, y) {
this.offset[0] += x / this.scale;
this.offset[1] += y / this.scale;
reset(): void {
this.scale = 1;
this.offset[0] = 0;
this.offset[1] = 0;
}
if (this.onredraw) {
this.onredraw(this);
}
}
changeScale(value: number, zooming_center?: Point): void {
if (value < this.min_scale) {
value = this.min_scale;
} else if (value > this.max_scale) {
value = this.max_scale;
}
if (value == this.scale) {
return;
}
if (!this.element) {
return;
}
var rect = this.element.getBoundingClientRect();
if (!rect) {
return;
}
zooming_center = zooming_center || [
rect.width * 0.5,
rect.height * 0.5
];
var center = this.convertCanvasToOffset(zooming_center);
this.scale = value;
if (Math.abs(this.scale - 1) < 0.01) {
this.scale = 1;
}
var new_center = this.convertCanvasToOffset(zooming_center);
var delta_offset = [
new_center[0] - center[0],
new_center[1] - center[1]
];
this.offset[0] += delta_offset[0];
this.offset[1] += delta_offset[1];
if (this.onredraw) {
this.onredraw(this);
}
}
changeDeltaScale(value: number, zooming_center?: Point) {
this.changeScale(this.scale * value, zooming_center);
}
reset(): void {
this.scale = 1;
this.offset[0] = 0;
this.offset[1] = 0;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -54,7 +54,11 @@ export class LGraphBadge {
return textWidth + this.padding * 2;
}
draw(ctx: CanvasRenderingContext2D, x: number, y: number): void {
draw(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
): void {
if (!this.visible) return;
ctx.save();
@@ -78,7 +82,7 @@ export class LGraphBadge {
ctx.fillText(
this.text,
x + badgeX + this.padding,
y + this.height - this.padding,
y + this.height - this.padding
);
ctx.restore();

File diff suppressed because it is too large Load Diff

View File

@@ -1,289 +1,278 @@
// @ts-nocheck
import type { Point, Size } from "./interfaces";
import type { LGraph } from "./LGraph";
import type { Point, Size } from "./interfaces"
import type { LGraph } from "./LGraph"
import { LiteGraph } from "./litegraph";
import { LGraphCanvas } from "./LGraphCanvas";
import { overlapBounding } from "./LiteGraphGlobal";
import { LGraphNode } from "./LGraphNode";
export interface IGraphGroup {
_pos: Point;
_size: Size;
title: string;
_pos: Point
_size: Size
title: string
}
export interface IGraphGroupFlags extends Record<string, unknown> {
pinned?: true;
pinned?: true
}
export class LGraphGroup {
pos: Point;
color: string;
title: string;
font?: string;
font_size: number;
_bounding: Float32Array;
_pos: Point;
_size: Size;
_nodes: LGraphNode[];
graph?: LGraph;
flags: IGraphGroupFlags;
size?: Size;
pos: Point
color: string
title: string
font?: string
font_size: number
_bounding: Float32Array
_pos: Point
_size: Size
_nodes: LGraphNode[]
graph?: LGraph
flags: IGraphGroupFlags
size?: Size
constructor(title?: string) {
this._ctor(title);
}
constructor(title?: string) {
this._ctor(title);
}
_ctor(title?: string) {
this.title = title || "Group";
this.font_size = LiteGraph.DEFAULT_GROUP_FONT || 24;
this.color = LGraphCanvas.node_colors.pale_blue
? LGraphCanvas.node_colors.pale_blue.groupcolor
: "#AAA";
this._bounding = new Float32Array([10, 10, 140, 80]);
this._pos = this._bounding.subarray(0, 2);
this._size = this._bounding.subarray(2, 4);
this._nodes = [];
this.graph = null;
this.flags = {};
_ctor(title?: string) {
this.title = title || "Group";
this.font_size = LiteGraph.DEFAULT_GROUP_FONT || 24;
this.color = LGraphCanvas.node_colors.pale_blue
? LGraphCanvas.node_colors.pale_blue.groupcolor
: "#AAA";
this._bounding = new Float32Array([10, 10, 140, 80]);
this._pos = this._bounding.subarray(0, 2);
this._size = this._bounding.subarray(2, 4);
this._nodes = [];
this.graph = null;
this.flags = {};
Object.defineProperty(this, "pos", {
set: function (v) {
if (!v || v.length < 2) {
return;
Object.defineProperty(this, "pos", {
set: function (v) {
if (!v || v.length < 2) {
return;
}
this._pos[0] = v[0];
this._pos[1] = v[1];
},
get: function () {
return this._pos;
},
enumerable: true
});
Object.defineProperty(this, "size", {
set: function (v) {
if (!v || v.length < 2) {
return;
}
this._size[0] = Math.max(140, v[0]);
this._size[1] = Math.max(80, v[1]);
},
get: function () {
return this._size;
},
enumerable: true
});
}
get nodes() {
return this._nodes;
}
get titleHeight() {
return this.font_size * 1.4;
}
get selected() {
return !!this.graph?.list_of_graphcanvas?.some(c => c.selected_group === this);
}
get pinned() {
return !!this.flags.pinned;
}
pin() {
this.flags.pinned = true;
}
unpin() {
delete this.flags.pinned;
}
configure(o) {
this.title = o.title;
this._bounding.set(o.bounding);
this.color = o.color;
this.flags = o.flags || this.flags;
if (o.font_size) {
this.font_size = o.font_size;
}
this._pos[0] = v[0];
this._pos[1] = v[1];
},
get: function () {
return this._pos;
},
enumerable: true,
});
Object.defineProperty(this, "size", {
set: function (v) {
if (!v || v.length < 2) {
return;
}
this._size[0] = Math.max(140, v[0]);
this._size[1] = Math.max(80, v[1]);
},
get: function () {
return this._size;
},
enumerable: true,
});
}
get nodes() {
return this._nodes;
}
get titleHeight() {
return this.font_size * 1.4;
}
get selected() {
return !!this.graph?.list_of_graphcanvas?.some(
(c) => c.selected_group === this,
);
}
get pinned() {
return !!this.flags.pinned;
}
pin() {
this.flags.pinned = true;
}
unpin() {
delete this.flags.pinned;
}
configure(o) {
this.title = o.title;
this._bounding.set(o.bounding);
this.color = o.color;
this.flags = o.flags || this.flags;
if (o.font_size) {
this.font_size = o.font_size;
}
}
serialize() {
var b = this._bounding;
return {
title: this.title,
bounding: [
Math.round(b[0]),
Math.round(b[1]),
Math.round(b[2]),
Math.round(b[3]),
],
color: this.color,
font_size: this.font_size,
flags: this.flags,
};
}
/**
* Draws the group on the canvas
* @param {LGraphCanvas} graphCanvas
* @param {CanvasRenderingContext2D} ctx
*/
draw(graphCanvas, ctx) {
const padding = 4;
ctx.fillStyle = this.color;
ctx.strokeStyle = this.color;
const [x, y] = this._pos;
const [width, height] = this._size;
ctx.globalAlpha = 0.25 * graphCanvas.editor_alpha;
ctx.beginPath();
ctx.rect(x + 0.5, y + 0.5, width, height);
ctx.fill();
ctx.globalAlpha = graphCanvas.editor_alpha;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(x + width, y + height);
ctx.lineTo(x + width - 10, y + height);
ctx.lineTo(x + width, y + height - 10);
ctx.fill();
const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
ctx.font = font_size + "px Arial";
ctx.textAlign = "left";
ctx.fillText(
this.title + (this.pinned ? "📌" : ""),
x + padding,
y + font_size,
);
if (LiteGraph.highlight_selected_group && this.selected) {
graphCanvas.drawSelectionBounding(ctx, this._bounding, {
shape: LiteGraph.BOX_SHAPE,
title_height: this.titleHeight,
title_mode: LiteGraph.NORMAL_TITLE,
fgcolor: this.color,
padding,
});
}
}
resize(width, height) {
if (this.pinned) {
return;
}
this._size[0] = width;
this._size[1] = height;
}
move(deltax, deltay, ignore_nodes) {
if (this.pinned) {
return;
}
this._pos[0] += deltax;
this._pos[1] += deltay;
if (ignore_nodes) {
return;
}
for (var i = 0; i < this._nodes.length; ++i) {
var node = this._nodes[i];
node.pos[0] += deltax;
node.pos[1] += deltay;
}
}
recomputeInsideNodes() {
this._nodes.length = 0;
var nodes = this.graph._nodes;
var node_bounding = new Float32Array(4);
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
node.getBounding(node_bounding);
if (!overlapBounding(this._bounding, node_bounding)) {
continue;
} //out of the visible area
this._nodes.push(node);
}
}
/**
* Add nodes to the group and adjust the group's position and size accordingly
* @param {LGraphNode[]} nodes - The nodes to add to the group
* @param {number} [padding=10] - The padding around the group
* @returns {void}
*/
addNodes(nodes, padding = 10) {
if (!this._nodes && nodes.length === 0) return;
const allNodes = [...(this._nodes || []), ...nodes];
const bounds = allNodes.reduce(
(acc, node) => {
const [x, y] = node.pos;
const [width, height] = node.size;
const isReroute = node.type === "Reroute";
const isCollapsed = node.flags?.collapsed;
const top = y - (isReroute ? 0 : LiteGraph.NODE_TITLE_HEIGHT);
const bottom = isCollapsed
? top + LiteGraph.NODE_TITLE_HEIGHT
: y + height;
const right =
isCollapsed && node._collapsed_width
? x + Math.round(node._collapsed_width)
: x + width;
serialize() {
var b = this._bounding;
return {
left: Math.min(acc.left, x),
top: Math.min(acc.top, top),
right: Math.max(acc.right, right),
bottom: Math.max(acc.bottom, bottom),
title: this.title,
bounding: [
Math.round(b[0]),
Math.round(b[1]),
Math.round(b[2]),
Math.round(b[3])
],
color: this.color,
font_size: this.font_size,
flags: this.flags,
};
},
{ left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity },
);
}
this.pos = [bounds.left - padding, bounds.top - padding - this.titleHeight];
/**
* Draws the group on the canvas
* @param {LGraphCanvas} graphCanvas
* @param {CanvasRenderingContext2D} ctx
*/
draw(graphCanvas, ctx) {
const padding = 4;
this.size = [
bounds.right - bounds.left + padding * 2,
bounds.bottom - bounds.top + padding * 2 + this.titleHeight,
];
}
ctx.fillStyle = this.color;
ctx.strokeStyle = this.color;
const [x, y] = this._pos;
const [width, height] = this._size;
ctx.globalAlpha = 0.25 * graphCanvas.editor_alpha;
ctx.beginPath();
ctx.rect(x + 0.5, y + 0.5, width, height);
ctx.fill();
ctx.globalAlpha = graphCanvas.editor_alpha;
ctx.stroke();
getMenuOptions() {
return [
{
content: this.pinned ? "Unpin" : "Pin",
callback: () => {
this.pinned ? this.unpin() : this.pin();
this.setDirtyCanvas(false, true);
},
},
null,
{ content: "Title", callback: LGraphCanvas.onShowPropertyEditor },
{
content: "Color",
has_submenu: true,
callback: LGraphCanvas.onMenuNodeColors,
},
{
content: "Font size",
property: "font_size",
type: "Number",
callback: LGraphCanvas.onShowPropertyEditor,
},
null,
{ content: "Remove", callback: LGraphCanvas.onMenuNodeRemove },
];
}
ctx.beginPath();
ctx.moveTo(x + width, y + height);
ctx.lineTo(x + width - 10, y + height);
ctx.lineTo(x + width, y + height - 10);
ctx.fill();
isPointInside = LGraphNode.prototype.isPointInside;
setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas;
const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
ctx.font = font_size + "px Arial";
ctx.textAlign = "left";
ctx.fillText(this.title + (this.pinned ? "📌" : ""), x + padding, y + font_size);
if (LiteGraph.highlight_selected_group && this.selected) {
graphCanvas.drawSelectionBounding(ctx, this._bounding, {
shape: LiteGraph.BOX_SHAPE,
title_height: this.titleHeight,
title_mode: LiteGraph.NORMAL_TITLE,
fgcolor: this.color,
padding,
});
}
}
resize(width, height) {
if (this.pinned) {
return;
}
this._size[0] = width;
this._size[1] = height;
}
move(deltax, deltay, ignore_nodes) {
if (this.pinned) {
return;
}
this._pos[0] += deltax;
this._pos[1] += deltay;
if (ignore_nodes) {
return;
}
for (var i = 0; i < this._nodes.length; ++i) {
var node = this._nodes[i];
node.pos[0] += deltax;
node.pos[1] += deltay;
}
}
recomputeInsideNodes() {
this._nodes.length = 0;
var nodes = this.graph._nodes;
var node_bounding = new Float32Array(4);
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
node.getBounding(node_bounding);
if (!overlapBounding(this._bounding, node_bounding)) {
continue;
} //out of the visible area
this._nodes.push(node);
}
}
/**
* Add nodes to the group and adjust the group's position and size accordingly
* @param {LGraphNode[]} nodes - The nodes to add to the group
* @param {number} [padding=10] - The padding around the group
* @returns {void}
*/
addNodes(nodes, padding = 10) {
if (!this._nodes && nodes.length === 0) return;
const allNodes = [...(this._nodes || []), ...nodes];
const bounds = allNodes.reduce((acc, node) => {
const [x, y] = node.pos;
const [width, height] = node.size;
const isReroute = node.type === "Reroute";
const isCollapsed = node.flags?.collapsed;
const top = y - (isReroute ? 0 : LiteGraph.NODE_TITLE_HEIGHT);
const bottom = isCollapsed ? top + LiteGraph.NODE_TITLE_HEIGHT : y + height;
const right = isCollapsed && node._collapsed_width ? x + Math.round(node._collapsed_width) : x + width;
return {
left: Math.min(acc.left, x),
top: Math.min(acc.top, top),
right: Math.max(acc.right, right),
bottom: Math.max(acc.bottom, bottom)
};
}, { left: Infinity, top: Infinity, right: -Infinity, bottom: -Infinity });
this.pos = [
bounds.left - padding,
bounds.top - padding - this.titleHeight
];
this.size = [
bounds.right - bounds.left + padding * 2,
bounds.bottom - bounds.top + padding * 2 + this.titleHeight
];
}
getMenuOptions() {
return [
{
content: this.pinned ? "Unpin" : "Pin",
callback: () => {
this.pinned ? this.unpin() : this.pin();
this.setDirtyCanvas(false, true);
},
},
null,
{ content: "Title", callback: LGraphCanvas.onShowPropertyEditor },
{
content: "Color",
has_submenu: true,
callback: LGraphCanvas.onMenuNodeColors
},
{
content: "Font size",
property: "font_size",
type: "Number",
callback: LGraphCanvas.onShowPropertyEditor
},
null,
{ content: "Remove", callback: LGraphCanvas.onMenuNodeRemove }
];
}
isPointInside = LGraphNode.prototype.isPointInside
setDirtyCanvas = LGraphNode.prototype.setDirtyCanvas
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,69 +1,62 @@
import type { ISlotType } from "./interfaces";
import type { NodeId } from "./LGraphNode";
import type { ISlotType } from "./interfaces"
import type { NodeId } from "./LGraphNode"
export type LinkId = number | string;
export type LinkId = number | string
export type SerialisedLLinkArray = [
LinkId,
NodeId,
number,
NodeId,
number,
ISlotType,
];
export type SerialisedLLinkArray = [LinkId, NodeId, number, NodeId, number, ISlotType]
//this is the class in charge of storing link information
export class LLink {
id?: LinkId;
type?: ISlotType;
origin_id?: NodeId;
origin_slot?: number;
target_id?: NodeId;
target_slot?: number;
data?: number | string | boolean | { toToolTip?(): string };
_data?: unknown;
_pos: Float32Array;
_last_time?: number;
id?: LinkId
type?: ISlotType
origin_id?: NodeId
origin_slot?: number
target_id?: NodeId
target_slot?: number
data?: number | string | boolean | { toToolTip?(): string }
_data?: unknown
_pos: Float32Array
_last_time?: number
constructor(id, type, origin_id, origin_slot, target_id, target_slot) {
this.id = id;
this.type = type;
this.origin_id = origin_id;
this.origin_slot = origin_slot;
this.target_id = target_id;
this.target_slot = target_slot;
constructor(id, type, origin_id, origin_slot, target_id, target_slot) {
this.id = id;
this.type = type;
this.origin_id = origin_id;
this.origin_slot = origin_slot;
this.target_id = target_id;
this.target_slot = target_slot;
this._data = null;
this._pos = new Float32Array(2); //center
}
// configure(o: LLink | SerialisedLLinkArray) {
configure(o) {
if (o.constructor === Array) {
this.id = o[0];
this.origin_id = o[1];
this.origin_slot = o[2];
this.target_id = o[3];
this.target_slot = o[4];
this.type = o[5];
} else {
this.id = o.id;
this.type = o.type;
this.origin_id = o.origin_id;
this.origin_slot = o.origin_slot;
this.target_id = o.target_id;
this.target_slot = o.target_slot;
this._data = null;
this._pos = new Float32Array(2); //center
}
}
serialize(): SerialisedLLinkArray {
return [
this.id,
this.origin_id,
this.origin_slot,
this.target_id,
this.target_slot,
this.type,
];
}
// configure(o: LLink | SerialisedLLinkArray) {
configure(o) {
if (o.constructor === Array) {
this.id = o[0];
this.origin_id = o[1];
this.origin_slot = o[2];
this.target_id = o[3];
this.target_slot = o[4];
this.type = o[5];
} else {
this.id = o.id;
this.type = o.type;
this.origin_id = o.origin_id;
this.origin_slot = o.origin_slot;
this.target_id = o.target_id;
this.target_slot = o.target_slot;
}
}
serialize(): SerialisedLLinkArray {
return [
this.id,
this.origin_id,
this.origin_slot,
this.target_id,
this.target_slot,
this.type
];
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
import type { Vector2 } from "./litegraph";
import type { INodeSlot } from "./interfaces";
import type { INodeSlot } from "./interfaces"
export enum SlotType {
Array = "array",
@@ -44,7 +44,7 @@ export function drawSlot(
low_quality?: boolean;
render_text?: boolean;
do_stroke?: boolean;
} = {},
} = {}
) {
// Save the current fillStyle and strokeStyle
const originalFillStyle = ctx.fillStyle;
@@ -82,7 +82,7 @@ export function drawSlot(
pos[0] - 4 + x * spacing,
pos[1] - 4 + y * spacing,
cellSize,
cellSize,
cellSize
);
}
}

View File

@@ -1,149 +1,138 @@
import type { ContextMenu } from "./ContextMenu";
import type { LGraphNode } from "./LGraphNode";
import type { LinkDirection, RenderShape } from "./types/globalEnums";
import type { LinkId } from "./LLink";
import type { SlotDirection } from "./draw";
import type { ContextMenu } from "./ContextMenu"
import type { LGraphNode } from "./LGraphNode"
import type { LinkDirection, RenderShape } from "./types/globalEnums"
import type { LinkId } from "./LLink"
import type { SlotDirection } from "./draw"
export type Dictionary<T> = { [key: string]: T };
export type Dictionary<T> = { [key: string]: T }
export type CanvasColour = string | CanvasGradient | CanvasPattern;
export type CanvasColour = string | CanvasGradient | CanvasPattern
export interface IInputOrOutput {
// If an input, this will be defined
input?: INodeInputSlot;
// If an output, this will be defined
output?: INodeOutputSlot;
// If an input, this will be defined
input?: INodeInputSlot
// If an output, this will be defined
output?: INodeOutputSlot
}
export interface IFoundSlot extends IInputOrOutput {
// Slot index
slot: number;
// Centre point of the rendered slot connection
link_pos: Point;
// Slot index
slot: number
// Centre point of the rendered slot connection
link_pos: Point
}
/** A point on the canvas: x, y */
export type Point = [x: number, y: number] | Float32Array | Float64Array;
export type Point = [x: number, y: number] | Float32Array | Float64Array
/** A size: width, height */
export type Size =
| [width: number, height: number]
| Float32Array
| Float64Array;
export type Size = [width: number, height: number] | Float32Array | Float64Array
/** A very firm array */
type ArRect = [x: number, y: number, width: number, height: number];
type ArRect = [x: number, y: number, width: number, height: number]
/** A rectangle starting at top-left coordinates: x, y, width, height */
export type Rect = ArRect | Float32Array | Float64Array;
export type Rect = ArRect | Float32Array | Float64Array
// TODO: Need regular arrays also?
export type Rect32 = Float32Array;
export type Rect32 = Float32Array
/** Union of property names that are of type Match */
export type KeysOfType<T, Match> = {
[P in keyof T]: T[P] extends Match ? P : never;
}[keyof T];
export type KeysOfType<T, Match> = { [P in keyof T]: T[P] extends Match ? P : never }[keyof T]
/** A new type that contains only the properties of T that are of type Match */
export type PickByType<T, Match> = { [P in keyof T]: Extract<T[P], Match> };
export type PickByType<T, Match> = { [P in keyof T]: Extract<T[P], Match> }
/** The names of all methods and functions in T */
export type MethodNames<T> = KeysOfType<T, (...args: any) => any>;
export type MethodNames<T> = KeysOfType<T, (...args: any) => any>
export interface IBoundaryNodes {
top: LGraphNode;
right: LGraphNode;
bottom: LGraphNode;
left: LGraphNode;
top: LGraphNode
right: LGraphNode
bottom: LGraphNode
left: LGraphNode
}
export type Direction = "top" | "bottom" | "left" | "right";
export type Direction = "top" | "bottom" | "left" | "right"
// TODO: Rename IOptionalSlotsData
export interface IOptionalInputsData {
content: string;
value?;
className?: string;
content: string
value?
className?: string
}
export type ISlotType = number | string;
export type ISlotType = number | string
export interface INodeSlot {
name: string;
type: ISlotType;
dir?: LinkDirection & SlotDirection;
removable?: boolean;
shape?: RenderShape;
not_subgraph_input?: boolean;
color_off?: CanvasColour;
color_on?: CanvasColour;
label?: string;
locked?: boolean;
nameLocked?: boolean;
pos?: Point;
widget?: unknown;
name: string
type: ISlotType
dir?: LinkDirection & SlotDirection
removable?: boolean
shape?: RenderShape
not_subgraph_input?: boolean
color_off?: CanvasColour
color_on?: CanvasColour
label?: string
locked?: boolean
nameLocked?: boolean
pos?: Point
widget?: unknown
}
export interface INodeFlags {
skip_repeated_outputs?: boolean;
allow_interaction?: boolean;
pinned?: boolean;
collapsed?: boolean;
skip_repeated_outputs?: boolean
allow_interaction?: boolean
pinned?: boolean
collapsed?: boolean
}
export interface INodeInputSlot extends INodeSlot {
link?: LinkId;
not_subgraph_input?: boolean;
link?: LinkId
not_subgraph_input?: boolean
}
export interface INodeOutputSlot extends INodeSlot {
links?: LinkId[];
_data?: unknown;
slot_index?: number;
not_subgraph_output?: boolean;
links?: LinkId[]
_data?: unknown
slot_index?: number
not_subgraph_output?: boolean
}
/** Links */
export interface ConnectingLink extends IInputOrOutput {
node: LGraphNode;
slot: number;
pos: Point;
direction?: LinkDirection;
node: LGraphNode
slot: number
pos: Point
direction?: LinkDirection
}
/** ContextMenu */
export interface IContextMenuOptions {
ignore_item_callbacks?: boolean;
title?: string;
parentMenu?: ContextMenu;
className?: string;
event?: MouseEvent;
extra?: unknown;
scroll_speed?: number;
left?: number;
top?: number;
scale?: string;
node?: LGraphNode;
autoopen?: boolean;
callback?(
value?: unknown,
options?: unknown,
event?: MouseEvent,
previous_menu?: ContextMenu,
node?: LGraphNode,
): void;
ignore_item_callbacks?: boolean
title?: string
parentMenu?: ContextMenu
className?: string
event?: MouseEvent
extra?: unknown
scroll_speed?: number
left?: number
top?: number
scale?: string
node?: LGraphNode
autoopen?: boolean
callback?(value?: unknown, options?: unknown, event?: MouseEvent, previous_menu?: ContextMenu, node?: LGraphNode): void
}
export interface IContextMenuValue {
title?: string;
content: string;
has_submenu?: boolean;
disabled?: boolean;
className?: any;
submenu?: unknown;
property?: string;
type?: string;
slot?: IFoundSlot;
callback?: IContextMenuOptions["callback"];
title?: string
content: string
has_submenu?: boolean
disabled?: boolean
className?: any
submenu?: unknown
property?: string
type?: string
slot?: IFoundSlot
callback?: IContextMenuOptions["callback"]
}

View File

@@ -1,161 +1,106 @@
import type { Point, ConnectingLink } from "./interfaces";
import type {
INodeSlot,
INodeInputSlot,
INodeOutputSlot,
CanvasColour,
Direction,
IBoundaryNodes,
IContextMenuOptions,
IContextMenuValue,
IFoundSlot,
IInputOrOutput,
INodeFlags,
IOptionalInputsData,
ISlotType,
KeysOfType,
MethodNames,
PickByType,
Rect,
Rect32,
Size,
} from "./interfaces";
import type { SlotShape, LabelPosition, SlotDirection, SlotType } from "./draw";
import type { IWidget } from "./types/widgets";
import type { TitleMode } from "./types/globalEnums";
import { LiteGraphGlobal } from "./LiteGraphGlobal";
import { loadPolyfills } from "./polyfills";
import type { Point, ConnectingLink } from "./interfaces"
import type { INodeSlot, INodeInputSlot, INodeOutputSlot, CanvasColour, Direction, IBoundaryNodes, IContextMenuOptions, IContextMenuValue, IFoundSlot, IInputOrOutput, INodeFlags, IOptionalInputsData, ISlotType, KeysOfType, MethodNames, PickByType, Rect, Rect32, Size } from "./interfaces"
import type { SlotShape, LabelPosition, SlotDirection, SlotType } from "./draw"
import type { IWidget } from "./types/widgets"
import type { TitleMode } from "./types/globalEnums"
import { LiteGraphGlobal } from "./LiteGraphGlobal"
import { loadPolyfills } from "./polyfills"
import { LGraph } from "./LGraph";
import { LGraphCanvas } from "./LGraphCanvas";
import { DragAndScale } from "./DragAndScale";
import { LGraphNode } from "./LGraphNode";
import { LGraphGroup } from "./LGraphGroup";
import { LLink } from "./LLink";
import { ContextMenu } from "./ContextMenu";
import { CurveEditor } from "./CurveEditor";
import { LGraphBadge, BadgePosition } from "./LGraphBadge";
import { LGraph } from "./LGraph"
import { LGraphCanvas } from "./LGraphCanvas"
import { DragAndScale } from "./DragAndScale"
import { LGraphNode } from "./LGraphNode"
import { LGraphGroup } from "./LGraphGroup"
import { LLink } from "./LLink"
import { ContextMenu } from "./ContextMenu"
import { CurveEditor } from "./CurveEditor"
import { LGraphBadge, BadgePosition } from "./LGraphBadge"
export const LiteGraph = new LiteGraphGlobal();
export {
LGraph,
LGraphCanvas,
DragAndScale,
LGraphNode,
LGraphGroup,
LLink,
ContextMenu,
CurveEditor,
};
export {
INodeSlot,
INodeInputSlot,
INodeOutputSlot,
ConnectingLink,
CanvasColour,
Direction,
IBoundaryNodes,
IContextMenuOptions,
IContextMenuValue,
IFoundSlot,
IInputOrOutput,
INodeFlags,
IOptionalInputsData,
ISlotType,
KeysOfType,
MethodNames,
PickByType,
Rect,
Rect32,
Size,
};
export { IWidget };
export { LGraphBadge, BadgePosition };
export { SlotShape, LabelPosition, SlotDirection, SlotType };
export const LiteGraph = new LiteGraphGlobal()
export { LGraph, LGraphCanvas, DragAndScale, LGraphNode, LGraphGroup, LLink, ContextMenu, CurveEditor }
export { INodeSlot, INodeInputSlot, INodeOutputSlot, ConnectingLink, CanvasColour, Direction, IBoundaryNodes, IContextMenuOptions, IContextMenuValue, IFoundSlot, IInputOrOutput, INodeFlags, IOptionalInputsData, ISlotType, KeysOfType, MethodNames, PickByType, Rect, Rect32, Size }
export { IWidget }
export { LGraphBadge, BadgePosition }
export { SlotShape, LabelPosition, SlotDirection, SlotType }
// TODO: Remove legacy accessors
LiteGraph.LGraph = LGraph;
LiteGraph.LLink = LLink;
LiteGraph.LGraphNode = LGraphNode;
LiteGraph.LGraphGroup = LGraphGroup;
LiteGraph.DragAndScale = DragAndScale;
LiteGraph.LGraphCanvas = LGraphCanvas;
LiteGraph.ContextMenu = ContextMenu;
LiteGraph.CurveEditor = CurveEditor;
LiteGraph.LGraph = LGraph
LiteGraph.LLink = LLink
LiteGraph.LGraphNode = LGraphNode
LiteGraph.LGraphGroup = LGraphGroup
LiteGraph.DragAndScale = DragAndScale
LiteGraph.LGraphCanvas = LGraphCanvas
LiteGraph.ContextMenu = ContextMenu
LiteGraph.CurveEditor = CurveEditor
export function clamp(v: number, a: number, b: number): number {
return a > v ? a : b < v ? b : v;
}
return a > v ? a : b < v ? b : v
};
// Load legacy polyfills
loadPolyfills();
loadPolyfills()
// Backwards compat
// Type definitions for litegraph.js 0.7.0
// Project: litegraph.js
// Definitions by: NateScarlet <https://github.com/NateScarlet>
export type Vector2 = Point;
export type Vector4 = [number, number, number, number];
export type Vector2 = Point
export type Vector4 = [number, number, number, number]
export interface IContextMenuItem {
content: string;
callback?: ContextMenuEventListener;
/** Used as innerHTML for extra child element */
title?: string;
disabled?: boolean;
has_submenu?: boolean;
submenu?: {
options: IContextMenuItem[];
} & IContextMenuOptions;
className?: string;
content: string
callback?: ContextMenuEventListener
/** Used as innerHTML for extra child element */
title?: string
disabled?: boolean
has_submenu?: boolean
submenu?: {
options: IContextMenuItem[]
} & IContextMenuOptions
className?: string
}
export type ContextMenuEventListener = (
value: IContextMenuItem,
options: IContextMenuOptions,
event: MouseEvent,
parentMenu: ContextMenu | undefined,
node: LGraphNode,
) => boolean | void;
value: IContextMenuItem,
options: IContextMenuOptions,
event: MouseEvent,
parentMenu: ContextMenu | undefined,
node: LGraphNode
) => boolean | void
export interface LinkReleaseContext {
node_to?: LGraphNode;
node_from?: LGraphNode;
slot_from: INodeSlot;
type_filter_in?: string;
type_filter_out?: string;
node_to?: LGraphNode
node_from?: LGraphNode
slot_from: INodeSlot
type_filter_in?: string
type_filter_out?: string
}
export interface LinkReleaseContextExtended {
links: ConnectingLink[];
links: ConnectingLink[]
}
export type LiteGraphCanvasEventType =
| "empty-release"
| "empty-double-click"
| "group-double-click";
export type LiteGraphCanvasEventType = "empty-release" | "empty-double-click" | "group-double-click"
export interface LiteGraphCanvasEvent
extends CustomEvent<{
subType: string;
originalEvent: Event;
linkReleaseContext?: LinkReleaseContextExtended;
group?: LGraphGroup;
}> {}
export interface LiteGraphCanvasEvent extends CustomEvent<{
subType: string
originalEvent: Event
linkReleaseContext?: LinkReleaseContextExtended
group?: LGraphGroup
}> { }
export interface LiteGraphCanvasGroupEvent
extends CustomEvent<{
subType: "group-double-click";
originalEvent: MouseEvent;
group: LGraphGroup;
}> {}
export interface LiteGraphCanvasGroupEvent extends CustomEvent<{
subType: "group-double-click"
originalEvent: MouseEvent
group: LGraphGroup
}> { }
/** https://github.com/jagenjo/litegraph.js/blob/master/guides/README.md#lgraphnode */
export interface LGraphNodeConstructor<T extends LGraphNode = LGraphNode> {
nodeData: any;
new (): T;
nodeData: any
new(): T
}
// End backwards compat

View File

@@ -1,84 +1,85 @@
//API *************************************************
//like rect but rounded corners
export function loadPolyfills() {
if (
typeof window != "undefined" &&
window.CanvasRenderingContext2D &&
!window.CanvasRenderingContext2D.prototype.roundRect
) {
if (typeof (window) != "undefined" && window.CanvasRenderingContext2D && !window.CanvasRenderingContext2D.prototype.roundRect) {
// @ts-expect-error Slightly broken polyfill - radius_low not impl. anywhere
window.CanvasRenderingContext2D.prototype.roundRect = function (
x,
y,
w,
h,
radius,
radius_low,
x,
y,
w,
h,
radius,
radius_low
) {
var top_left_radius = 0;
var top_right_radius = 0;
var bottom_left_radius = 0;
var bottom_right_radius = 0;
var top_left_radius = 0;
var top_right_radius = 0;
var bottom_left_radius = 0;
var bottom_right_radius = 0;
if (radius === 0) {
this.rect(x, y, w, h);
return;
}
if (radius === 0) {
this.rect(x, y, w, h);
return;
}
if (radius_low === undefined) radius_low = radius;
if (radius_low === undefined)
radius_low = radius;
//make it compatible with official one
if (radius != null && radius.constructor === Array) {
if (radius.length == 1)
top_left_radius =
top_right_radius =
bottom_left_radius =
bottom_right_radius =
radius[0];
else if (radius.length == 2) {
top_left_radius = bottom_right_radius = radius[0];
top_right_radius = bottom_left_radius = radius[1];
} else if (radius.length == 4) {
top_left_radius = radius[0];
top_right_radius = radius[1];
bottom_left_radius = radius[2];
bottom_right_radius = radius[3];
} else return;
} //old using numbers
else {
top_left_radius = radius || 0;
top_right_radius = radius || 0;
bottom_left_radius = radius_low || 0;
bottom_right_radius = radius_low || 0;
}
//make it compatible with official one
if (radius != null && radius.constructor === Array) {
if (radius.length == 1)
top_left_radius = top_right_radius = bottom_left_radius = bottom_right_radius = radius[0];
else if (radius.length == 2) {
top_left_radius = bottom_right_radius = radius[0];
top_right_radius = bottom_left_radius = radius[1];
}
else if (radius.length == 4) {
top_left_radius = radius[0];
top_right_radius = radius[1];
bottom_left_radius = radius[2];
bottom_right_radius = radius[3];
}
else
return;
}
else //old using numbers
{
top_left_radius = radius || 0;
top_right_radius = radius || 0;
bottom_left_radius = radius_low || 0;
bottom_right_radius = radius_low || 0;
}
//top right
this.moveTo(x + top_left_radius, y);
this.lineTo(x + w - top_right_radius, y);
this.quadraticCurveTo(x + w, y, x + w, y + top_right_radius);
//top right
this.moveTo(x + top_left_radius, y);
this.lineTo(x + w - top_right_radius, y);
this.quadraticCurveTo(x + w, y, x + w, y + top_right_radius);
//bottom right
this.lineTo(x + w, y + h - bottom_right_radius);
this.quadraticCurveTo(x + w, y + h, x + w - bottom_right_radius, y + h);
//bottom right
this.lineTo(x + w, y + h - bottom_right_radius);
this.quadraticCurveTo(
x + w,
y + h,
x + w - bottom_right_radius,
y + h
);
//bottom left
this.lineTo(x + bottom_right_radius, y + h);
this.quadraticCurveTo(x, y + h, x, y + h - bottom_left_radius);
//bottom left
this.lineTo(x + bottom_right_radius, y + h);
this.quadraticCurveTo(x, y + h, x, y + h - bottom_left_radius);
//top left
this.lineTo(x, y + bottom_left_radius);
this.quadraticCurveTo(x, y, x + top_left_radius, y);
//top left
this.lineTo(x, y + bottom_left_radius);
this.quadraticCurveTo(x, y, x + top_left_radius, y);
};
} //if
}//if
if (typeof window != "undefined" && !window["requestAnimationFrame"]) {
if (typeof window != "undefined" && !window["requestAnimationFrame"]) {
window.requestAnimationFrame =
// @ts-expect-error Legacy code
window.webkitRequestAnimationFrame ||
// @ts-expect-error Legacy code
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
}
// @ts-expect-error Legacy code
window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
}
}

View File

@@ -4,46 +4,37 @@
/** For Canvas*Event - adds graph space co-ordinates (property names are shipped) */
export interface ICanvasPosition {
/** X co-ordinate of the event, in graph space (NOT canvas space) */
canvasX?: number;
/** Y co-ordinate of the event, in graph space (NOT canvas space) */
canvasY?: number;
/** X co-ordinate of the event, in graph space (NOT canvas space) */
canvasX?: number
/** Y co-ordinate of the event, in graph space (NOT canvas space) */
canvasY?: number
}
/** For Canvas*Event */
export interface IDeltaPosition {
deltaX?: number;
deltaY?: number;
deltaX?: number
deltaY?: number
}
/** PointerEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasPointerEvent extends PointerEvent, CanvasMouseEvent {}
export interface CanvasPointerEvent extends PointerEvent, CanvasMouseEvent { }
/** MouseEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasMouseEvent
extends MouseEvent,
ICanvasPosition,
IDeltaPosition {
dragging?: boolean;
click_time?: number;
dataTransfer?: unknown;
export interface CanvasMouseEvent extends MouseEvent, ICanvasPosition, IDeltaPosition {
dragging?: boolean
click_time?: number
dataTransfer?: unknown
}
/** WheelEvent with canvasX/Y properties */
export interface CanvasWheelEvent extends WheelEvent, ICanvasPosition {
dragging?: boolean;
click_time?: number;
dataTransfer?: unknown;
dragging?: boolean
click_time?: number
dataTransfer?: unknown
}
/** DragEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasDragEvent
extends DragEvent,
ICanvasPosition,
IDeltaPosition {}
export interface CanvasDragEvent extends DragEvent, ICanvasPosition, IDeltaPosition { }
/** TouchEvent with canvasX/Y and deltaX/Y properties */
export interface CanvasTouchEvent
extends TouchEvent,
ICanvasPosition,
IDeltaPosition {}
export interface CanvasTouchEvent extends TouchEvent, ICanvasPosition, IDeltaPosition { }

View File

@@ -1,51 +1,51 @@
/** Node slot type - input or output */
export enum NodeSlotType {
INPUT = 1,
OUTPUT = 2,
INPUT = 1,
OUTPUT = 2,
}
/** Shape that an object will render as - used by nodes and slots */
export enum RenderShape {
BOX = 1,
ROUND = 2,
CIRCLE = 3,
CARD = 4,
ARROW = 5,
/** intended for slot arrays */
GRID = 6,
BOX = 1,
ROUND = 2,
CIRCLE = 3,
CARD = 4,
ARROW = 5,
/** intended for slot arrays */
GRID = 6,
}
/** The direction that a link point will flow towards - e.g. horizontal outputs are right by default */
export enum LinkDirection {
NONE = 0,
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4,
CENTER = 5,
NONE = 0,
UP = 1,
DOWN = 2,
LEFT = 3,
RIGHT = 4,
CENTER = 5,
}
/** The path calculation that links follow */
export enum LinkRenderType {
/** Juts out from the input & output a little @see LinkDirection, then a straight line between them */
STRAIGHT_LINK = 0,
/** 90° angles, clean and box-like */
LINEAR_LINK = 1,
/** Smooth curved links - default */
SPLINE_LINK = 2,
/** Juts out from the input & output a little @see LinkDirection, then a straight line between them */
STRAIGHT_LINK = 0,
/** 90° angles, clean and box-like */
LINEAR_LINK = 1,
/** Smooth curved links - default */
SPLINE_LINK = 2,
}
export enum TitleMode {
NORMAL_TITLE = 0,
NO_TITLE = 1,
TRANSPARENT_TITLE = 2,
AUTOHIDE_TITLE = 3,
NORMAL_TITLE = 0,
NO_TITLE = 1,
TRANSPARENT_TITLE = 2,
AUTOHIDE_TITLE = 3,
}
export enum LGraphEventMode {
ALWAYS = 0,
ON_EVENT = 1,
NEVER = 2,
ON_TRIGGER = 3,
BYPASS = 4,
ALWAYS = 0,
ON_EVENT = 1,
NEVER = 2,
ON_TRIGGER = 3,
BYPASS = 4,
}

View File

@@ -1,76 +1,61 @@
import type {
CanvasColour,
Dictionary,
INodeFlags,
INodeInputSlot,
INodeOutputSlot,
Point,
Rect,
Size,
} from "@/interfaces";
import type { LGraph } from "@/LGraph";
import type { IGraphGroupFlags, LGraphGroup } from "@/LGraphGroup";
import type { LGraphNode, NodeId } from "@/LGraphNode";
import type { LiteGraph } from "@/litegraph";
import type { LinkId, LLink } from "@/LLink";
import type { TWidgetValue } from "@/types/widgets";
import type { CanvasColour, Dictionary, INodeFlags, INodeInputSlot, INodeOutputSlot, Point, Rect, Size } from "@/interfaces"
import type { LGraph } from "@/LGraph"
import type { IGraphGroupFlags, LGraphGroup } from "@/LGraphGroup"
import type { LGraphNode, NodeId } from "@/LGraphNode"
import type { LiteGraph } from "@/litegraph"
import type { LinkId, LLink } from "@/LLink"
import type { TWidgetValue } from "@/types/widgets"
/** Serialised LGraphNode */
export interface ISerialisedNode {
title?: string;
id?: NodeId;
type?: string;
pos?: Point;
size?: Size;
flags?: INodeFlags;
order?: number;
mode?: number;
outputs?: INodeOutputSlot[];
inputs?: INodeInputSlot[];
properties?: Dictionary<unknown>;
shape?: Rect;
boxcolor?: CanvasColour;
color?: CanvasColour;
bgcolor?: string;
widgets_values?: TWidgetValue[];
title?: string
id?: NodeId
type?: string
pos?: Point
size?: Size
flags?: INodeFlags
order?: number
mode?: number
outputs?: INodeOutputSlot[]
inputs?: INodeInputSlot[]
properties?: Dictionary<unknown>
shape?: Rect
boxcolor?: CanvasColour
color?: CanvasColour
bgcolor?: string
widgets_values?: TWidgetValue[]
}
/** Contains serialised graph elements */
export type ISerialisedGraph<
TNode = ReturnType<LGraphNode["serialize"]>,
TLink = ReturnType<LLink["serialize"]>,
TGroup = ReturnType<LGraphGroup["serialize"]>,
TNode = ReturnType<LGraphNode["serialize"]>,
TLink = ReturnType<LLink["serialize"]>,
TGroup = ReturnType<LGraphGroup["serialize"]>
> = {
last_node_id: LGraph["last_node_id"];
last_link_id: LGraph["last_link_id"];
last_reroute_id: LGraph["last_reroute_id"];
nodes: TNode[];
links: TLink[] | LLink[];
groups: TGroup[];
config: LGraph["config"];
version: typeof LiteGraph.VERSION;
extra?: unknown;
};
last_node_id: LGraph["last_node_id"]
last_link_id: LGraph["last_link_id"]
last_reroute_id: LGraph["last_reroute_id"]
nodes: TNode[]
links: TLink[] | LLink[]
groups: TGroup[]
config: LGraph["config"]
version: typeof LiteGraph.VERSION
extra?: unknown
}
/** Serialised LGraphGroup */
export interface ISerialisedGroup {
title: string;
bounding: number[];
color: string;
font_size: number;
flags?: IGraphGroupFlags;
title: string
bounding: number[]
color: string
font_size: number
flags?: IGraphGroupFlags
}
export type TClipboardLink = [
targetRelativeIndex: number,
originSlot: number,
nodeRelativeIndex: number,
targetSlot: number,
targetNodeId: NodeId,
];
export type TClipboardLink = [targetRelativeIndex: number, originSlot: number, nodeRelativeIndex: number, targetSlot: number, targetNodeId: NodeId]
/** */
export interface IClipboardContents {
nodes?: ISerialisedNode[];
links?: TClipboardLink[];
nodes?: ISerialisedNode[]
links?: TClipboardLink[]
}

View File

@@ -1,29 +1,28 @@
import { CanvasColour, Point, Size } from "@/interfaces";
import type { LGraphCanvas, LGraphNode } from "@/litegraph";
import type { CanvasMouseEvent } from "./events";
import { CanvasColour, Point, Size } from "@/interfaces"
import type { LGraphCanvas, LGraphNode } from "@/litegraph"
import type { CanvasMouseEvent } from "./events"
export interface IWidgetOptions<TValue = unknown>
extends Record<string, unknown> {
on?: string;
off?: string;
max?: number;
min?: number;
slider_color?: CanvasColour;
marker_color?: CanvasColour;
precision?: number;
read_only?: boolean;
step?: number;
y?: number;
multiline?: boolean;
// TODO: Confirm this
property?: string;
export interface IWidgetOptions<TValue = unknown> extends Record<string, unknown> {
on?: string
off?: string
max?: number
min?: number
slider_color?: CanvasColour
marker_color?: CanvasColour
precision?: number
read_only?: boolean
step?: number
y?: number
multiline?: boolean
// TODO: Confirm this
property?: string
hasOwnProperty?(arg0: string): any;
// values?(widget?: IWidget, node?: LGraphNode): any
values?: TValue[];
callback?: IWidget["callback"];
hasOwnProperty?(arg0: string): any
// values?(widget?: IWidget, node?: LGraphNode): any
values?: TValue[]
callback?: IWidget["callback"]
onHide?(widget: IWidget): void;
onHide?(widget: IWidget): void
}
/**
@@ -35,113 +34,91 @@ export interface IWidgetOptions<TValue = unknown>
* Recommend declaration merging any properties that use IWidget (e.g. {@link LGraphNode.widgets}) with a new type alias.
* @see ICustomWidget
*/
export type IWidget =
| IBooleanWidget
| INumericWidget
| IStringWidget
| IMultilineStringWidget
| IComboWidget
| ICustomWidget;
export type IWidget = IBooleanWidget | INumericWidget | IStringWidget | IMultilineStringWidget | IComboWidget | ICustomWidget
export interface IBooleanWidget extends IBaseWidget {
type?: "toggle";
value: boolean;
type?: "toggle"
value: boolean
}
/** Any widget that uses a numeric backing */
export interface INumericWidget extends IBaseWidget {
type?: "slider" | "number";
value: number;
type?: "slider" | "number"
value: number
}
/** A combo-box widget (dropdown, select, etc) */
export interface IComboWidget extends IBaseWidget {
type?: "combo";
value: string | number;
options: IWidgetOptions<string>;
type?: "combo"
value: string | number
options: IWidgetOptions<string>
}
export type IStringWidgetType =
| IStringWidget["type"]
| IMultilineStringWidget["type"];
export type IStringWidgetType = IStringWidget["type"] | IMultilineStringWidget["type"]
/** A widget with a string value */
export interface IStringWidget extends IBaseWidget {
type?: "string" | "text" | "button";
value: string;
type?: "string" | "text" | "button"
value: string
}
/** A widget with a string value and a multiline text input */
export interface IMultilineStringWidget<
TElement extends HTMLElement = HTMLTextAreaElement,
> extends IBaseWidget {
type?: "multiline";
value: string;
export interface IMultilineStringWidget<TElement extends HTMLElement = HTMLTextAreaElement> extends IBaseWidget {
type?: "multiline"
value: string
/** HTML textarea element */
element?: TElement;
/** HTML textarea element */
element?: TElement
}
/** A custom widget - accepts any value and has no built-in special handling */
export interface ICustomWidget<TElement extends HTMLElement = HTMLElement>
extends IBaseWidget<TElement> {
type?: "custom";
value: string | object;
export interface ICustomWidget<TElement extends HTMLElement = HTMLElement> extends IBaseWidget<TElement> {
type?: "custom"
value: string | object
element?: TElement;
element?: TElement
}
/**
* Valid widget types. TS cannot provide easily extensible type safety for this at present.
* Override linkedWidgets[]
* Values not in this list will not result in litegraph errors, however they will be treated the same as "custom".
*/
export type TWidgetType = IWidget["type"];
export type TWidgetValue = IWidget["value"];
export type TWidgetType = IWidget["type"]
export type TWidgetValue = IWidget["value"]
/**
* The base type for all widgets. Should not be implemented directly.
* @see IWidget
*/
export interface IBaseWidget<TElement extends HTMLElement = HTMLElement> {
linkedWidgets?: IWidget[];
linkedWidgets?: IWidget[]
options: IWidgetOptions;
marker?: number;
label?: string;
clicked?: boolean;
name?: string;
/** Widget type (see {@link TWidgetType}) */
type?: TWidgetType;
value?: TWidgetValue;
y?: number;
last_y?: number;
width?: number;
disabled?: boolean;
options: IWidgetOptions
marker?: number
label?: string
clicked?: boolean
name?: string
/** Widget type (see {@link TWidgetType}) */
type?: TWidgetType
value?: TWidgetValue
y?: number
last_y?: number
width?: number
disabled?: boolean
tooltip?: string;
tooltip?: string
/** HTML widget element */
element?: TElement;
/** HTML widget element */
element?: TElement
// TODO: Confirm this format
callback?(
value: any,
canvas?: LGraphCanvas,
node?: LGraphNode,
pos?: Point,
e?: CanvasMouseEvent,
): void;
onRemove?(): void;
beforeQueued?(): void;
// TODO: Confirm this format
callback?(value: any, canvas?: LGraphCanvas, node?: LGraphNode, pos?: Point, e?: CanvasMouseEvent): void
onRemove?(): void
beforeQueued?(): void
mouse?(event: CanvasMouseEvent, arg1: number[], node: LGraphNode): boolean;
draw?(
ctx: CanvasRenderingContext2D,
node: LGraphNode,
widget_width: number,
y: number,
H: number,
): void;
computeSize?(width: number): Size;
mouse?(event: CanvasMouseEvent, arg1: number[], node: LGraphNode): boolean
draw?(ctx: CanvasRenderingContext2D, node: LGraphNode, widget_width: number, y: number, H: number): void
computeSize?(width: number): Size
}