Skip to content

Commit

Permalink
Support group pinning and show pin icon on pinned node & group (#117)
Browse files Browse the repository at this point in the history
* Add locking mechanism

* Add group locking

* Lock move/resize

* Move group menu options

* Rename locked to pinned

* Disable resize on pinned nodes

* nit
  • Loading branch information
huchenlei authored Sep 4, 2024
1 parent 22e671d commit 7897ffd
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 35 deletions.
10 changes: 10 additions & 0 deletions public/litegraph.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,8 @@ export declare class LGraphNode {
captureInput(v: any): void;
/** Collapse the node to make it smaller on the canvas */
collapse(force: boolean): void;

get pinned(): boolean;
/** Forces the node to do not move or realign on Z */
pin(v?: boolean): void;
localToScreen(x: number, y: number, graphCanvas: LGraphCanvas): Vector2;
Expand Down Expand Up @@ -1138,17 +1140,25 @@ export declare class LGraphGroup {
size: Vector2;
pos: Vector2;
font_size: number;
flags: Record<string, boolean>;

get titleHeight(): number;
get selected(): boolean;

// Pinned group cannot be selected.
get pinned(): boolean;
pin(): void;
unpin(): void;

configure(o: SerializedLGraphGroup): void;
serialize(): SerializedLGraphGroup;
resize(width: number, height: number): void;
move(deltaX: number, deltaY: number, ignoreNodes?: boolean): void;
recomputeInsideNodes(): void;
isPointInside: LGraphNode["isPointInside"];
setDirtyCanvas: LGraphNode["setDirtyCanvas"];
addNodes(nodes: LGraphNode[], padding?: number): void;
getMenuOptions(): ContextMenuItem[];
}

export declare class DragAndScale {
Expand Down
124 changes: 89 additions & 35 deletions src/litegraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -1636,17 +1636,12 @@ const globalExport = {};
* @method getGroupOnPos
* @param {number} x the x coordinate in canvas space
* @param {number} y the y coordinate in canvas space
* @return {LGraphGroup} the group or null
* @return {LGraphGroup | null} the group or null
*/
getGroupOnPos(x, y) {
for (var i = this._groups.length - 1; i >= 0; i--) {
var g = this._groups[i];
if (g.isPointInside(x, y, 2, true)) {
return g;
}
}
return null;
getGroupOnPos(x, y, {margin = 2} = {}) {
return this._groups.reverse().find(g => g.isPointInside(x, y, margin));
}

/**
* Checks that the node type matches the node type registered, used when replacing a nodetype by a newer version during execution
* this replaces the ones using the old version with the new version
Expand Down Expand Up @@ -4777,6 +4772,10 @@ const globalExport = {};
this.setDirtyCanvas(true, true);
}

get pinned() {
return !!this.flags.pinned;
}

/**
* Forces the node to do not move or realign on Z
* @method pin
Expand All @@ -4788,6 +4787,12 @@ const globalExport = {};
} else {
this.flags.pinned = v;
}
this.resizable = !this.pinned;
// Delete the flag if unpinned, so that we don't get unnecessary
// flags.pinned = false in serialized object.
if (!this.pinned) {
delete this.flags.pinned;
}
}

localToScreen(x, y, graphcanvas) {
Expand Down Expand Up @@ -4817,6 +4822,7 @@ const globalExport = {};
this._size = this._bounding.subarray(2, 4);
this._nodes = [];
this.graph = null;
this.flags = {};

Object.defineProperty(this, "pos", {
set: function (v) {
Expand Down Expand Up @@ -4855,10 +4861,23 @@ const globalExport = {};
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;
}
Expand All @@ -4875,7 +4894,8 @@ const globalExport = {};
Math.round(b[3])
],
color: this.color,
font_size: this.font_size
font_size: this.font_size,
flags: this.flags,
};
}

Expand All @@ -4885,6 +4905,8 @@ const globalExport = {};
* @param {CanvasRenderingContext2D} ctx
*/
draw(graphCanvas, ctx) {
const padding = 4;

ctx.fillStyle = this.color;
ctx.strokeStyle = this.color;
const [x, y] = this._pos;
Expand All @@ -4905,20 +4927,37 @@ const globalExport = {};
const font_size = this.font_size || LiteGraph.DEFAULT_GROUP_FONT_SIZE;
ctx.font = font_size + "px Arial";
ctx.textAlign = "left";
ctx.fillText(this.title, x + 4, y + font_size);
ctx.fillText(this.title, 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: 4
padding,
});
}

if (this.pinned) {
const iconFontSize = font_size * 0.5;
ctx.font = iconFontSize + "px Arial";
ctx.fillText("📌", x + width - iconFontSize - padding, y + iconFontSize);
}
}

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) {
Expand Down Expand Up @@ -4985,6 +5024,33 @@ const globalExport = {};
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 }
];
}
}

LGraphGroup.prototype.isPointInside = LGraphNode.prototype.isPointInside;
Expand Down Expand Up @@ -6992,7 +7058,7 @@ const globalExport = {};
//it wasn't clicked on the links boxes
if (!skip_action) {
var block_drag_node = false;
if (node && node.flags && node.flags.pinned) {
if (node?.pinned) {
block_drag_node = true;
}
var pos = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]];
Expand Down Expand Up @@ -7323,10 +7389,10 @@ const globalExport = {};
else if (this.selected_group && !this.read_only) {
//moving/resizing a group
if (this.selected_group_resizing) {
this.selected_group.size = [
this.selected_group.resize(
e.canvasX - this.selected_group.pos[0],
e.canvasY - this.selected_group.pos[1]
];
);
} else {
var deltax = delta[0] / this.ds.scale;
var deltay = delta[1] / this.ds.scale;
Expand Down Expand Up @@ -10040,6 +10106,10 @@ const globalExport = {};
);
}

if (node.pinned) {
ctx.fillText("📌", node.getBounding()[2] - 20, -10);
}

// these counter helps in conditioning drawing based on if the node has been executed or an action occurred
if (node.execute_triggered > 0) node.execute_triggered--;
if (node.action_triggered > 0) node.action_triggered--;
Expand Down Expand Up @@ -13064,7 +13134,7 @@ const globalExport = {};
callback: LGraphCanvas.onMenuNodeCollapse
},
{
content: node.flags?.pinned ? "Unpin" : "Pin",
content: node.pinned ? "Unpin" : "Pin",
callback: LGraphCanvas.onMenuNodePin
},
{
Expand Down Expand Up @@ -13137,24 +13207,8 @@ const globalExport = {};
return options;
}
getGroupMenuOptions(node) {
var o = [
{ 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 }
];

return o;
console.warn("LGraphCanvas.getGroupMenuOptions is deprecated, use LGraphGroup.getMenuOptions instead");
return node.getMenuOptions();
}
processContextMenu(node, event) {
var that = this;
Expand Down Expand Up @@ -13229,7 +13283,7 @@ const globalExport = {};
submenu: {
title: "Group",
extra: group,
options: this.getGroupMenuOptions(group)
options: group.getMenuOptions()
}
});
}
Expand Down

0 comments on commit 7897ffd

Please sign in to comment.