Skip to content

Commit 7f73a6d

Browse files
committed
Finalized context menu
1 parent 95dbcd9 commit 7f73a6d

File tree

5 files changed

+134
-20
lines changed

5 files changed

+134
-20
lines changed

babylonjs-editor.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ declare module 'babylonjs-editor/editor/gui/tree' {
819819
export default class Tree {
820820
name: string;
821821
wholerow: boolean;
822+
keyboard: boolean;
822823
element: JSTree;
823824
onClick: <T>(id: string, data: T) => void;
824825
onDblClick: <T>(id: string, data: T) => void;

src/editor/gui/edition.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,14 +146,30 @@ export default class Edition {
146146
};
147147

148148
const folder = parent.addFolder(name);
149-
/*
150-
TODO: Fix CSS Issue with color element
151-
folder.addColor(target, 'color').name('Color').onChange((value: number[]) => {
152-
this.getController('r', folder).setValue(value[0] / 255);
153-
this.getController('g', folder).setValue(value[1] / 255);
154-
this.getController('b', folder).setValue(value[2] / 255);
155-
});
156-
*/
149+
const picker = {
150+
callback: () => {
151+
const input = document.createElement('input');
152+
input.type = 'color';
153+
input.value = color.toHexString();
154+
input.addEventListener('input', ev => {
155+
const result = Color3.FromHexString(input.value);
156+
color.r = result.r;
157+
color.g = result.g;
158+
color.b = result.b;
159+
this.updateDisplay();
160+
});
161+
input.addEventListener('change', ev => {
162+
const result = Color3.FromHexString(input.value);
163+
color.r = result.r;
164+
color.g = result.g;
165+
color.b = result.b;
166+
this.updateDisplay();
167+
});
168+
input.click();
169+
}
170+
};
171+
172+
folder.add(picker, 'callback').name('Color Picker');
157173
folder.add(color, 'r').min(0).max(1).step(0.01).onChange(() => callback && callback());
158174
folder.add(color, 'g').min(0).max(1).step(0.01).onChange(() => callback && callback());
159175
folder.add(color, 'b').min(0).max(1).step(0.01).onChange(() => callback && callback());

src/editor/gui/tree.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export default class Tree {
2121
// Public members
2222
public name: string;
2323
public wholerow: boolean = false;
24+
public keyboard: boolean = false;
2425
public element: JSTree = null;
2526

2627
public onClick: <T>(id: string, data: T) => void;
@@ -171,9 +172,13 @@ export default class Tree {
171172
'contextmenu', 'dnd', 'search',
172173
'state', 'types'
173174
];
175+
174176
if (this.wholerow)
175177
plugins.push('wholerow')
176178

179+
if (this.keyboard)
180+
plugins.push('hotkeys');
181+
177182
this.element = $('#' + parentId).jstree({
178183
core: {
179184
check_callback: true,

src/extensions/behavior/graph-nodes/typings.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ export abstract class LiteGraphNode {
77
public mode: number;
88
public color: string;
99
public bgColor: string;
10+
public readonly type: string;
11+
1012
public properties: { [index: string]: string | number | boolean };
13+
public outputs: any[];
1114

15+
public pos: number[];
1216
public size: number[] = [60, 20];
1317
public shape: string = 'round';
1418

@@ -83,6 +87,9 @@ export abstract class LiteGraphNode {
8387

8488
public onDrawBackground? (ctx: CanvasRenderingContext2D): void;
8589

90+
public onGetOutputs? (): string[][];
91+
public onGetInputs? (): string[][];
92+
8693
/**
8794
* Returns the target node
8895
* @param name the name of the node

src/tools/behavior/graph.ts

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export default class BehaviorGraphEditor extends EditorPlugin {
254254
protected objectSelected (node: Node | Scene): void {
255255
if (!(node instanceof Node) && !(node instanceof Scene)) {
256256
this.layout.lockPanel('left');
257-
this.layout.lockPanel('main', 'Please Select A Node');
257+
this.layout.lockPanel('main', 'No Node Selected');
258258
return;
259259
}
260260

@@ -303,7 +303,7 @@ export default class BehaviorGraphEditor extends EditorPlugin {
303303
this.layout.unlockPanel('left');
304304

305305
if (this.datas.metadatas.length === 0)
306-
this.layout.lockPanel('main', 'Please add a graph');
306+
this.layout.lockPanel('main', 'No Graph Selected');
307307
else
308308
this.layout.unlockPanel('main');
309309
}
@@ -475,11 +475,31 @@ export default class BehaviorGraphEditor extends EditorPlugin {
475475
// Create tree
476476
const tree = new Tree('GRAPH-CANVAS-CONTEXT-MENU-TREE');
477477
tree.wholerow = true;
478+
tree.keyboard = true;
478479
tree.build('GRAPH-CANVAS-CONTEXT-MENU-TREE');
479480

480481
// Search div
481482
const searchDiv = $('#GRAPH-CANVAS-CONTEXT-MENU-SEARCH');
482-
searchDiv.keyup(() => tree.search(<string> searchDiv.val()));
483+
searchDiv.keyup(() => {
484+
tree.search(<string> searchDiv.val());
485+
486+
// Select first match
487+
const nodes = tree.element.jstree().get_json();
488+
for (const n of nodes) {
489+
if (n.state.hidden)
490+
continue;
491+
492+
for (const c of n.children) {
493+
if (c.state.hidden)
494+
continue;
495+
496+
tree.select(c.id);
497+
break;
498+
}
499+
500+
break;
501+
}
502+
});
483503

484504
// Save
485505
this._contextMenu = {
@@ -506,9 +526,35 @@ export default class BehaviorGraphEditor extends EditorPlugin {
506526
this._contextMenu.search.value = '';
507527

508528
if (node) {
509-
// Draw node's options
529+
this._contextMenu.mainDiv.style.height = '120px';
530+
531+
// Draw ouputs
532+
if (node.onGetOutputs) {
533+
const outputs = node.onGetOutputs();
534+
const parent = this._contextMenu.tree.add({ id: 'graph-outputs', text: 'Outputs', img: 'icon-helpers' });
535+
536+
outputs.forEach(o => {
537+
this._contextMenu.tree.add({ id: 'graph-outputs' + o[0], text: o[0], data: o, img: 'icon-export' }, parent.id);
538+
});
539+
540+
this._contextMenu.mainDiv.style.height = '300px';
541+
}
542+
543+
// Draw inputs
544+
if (node.onGetInputs) {
545+
const inputs = node.onGetInputs();
546+
const parent = this._contextMenu.tree.add({ id: 'graph-inputs', text: 'Inputs', img: 'icon-helpers' });
547+
548+
inputs.forEach(i => {
549+
this._contextMenu.tree.add({ id: 'graph-inputs' + i[0], text: i[0], data: i, img: 'icon-export' }, parent.id);
550+
});
551+
552+
this._contextMenu.mainDiv.style.height = '300px';
553+
}
554+
555+
// Draw misc
556+
this._contextMenu.tree.add({ id: 'graph-clone', text: 'Clone', img: 'icon-export' });
510557
this._contextMenu.tree.add({ id: 'graph-remove', text: 'Remove', img: 'icon-error' });
511-
this._contextMenu.mainDiv.style.height = '100px';
512558
}
513559
else {
514560
// Add new node
@@ -523,19 +569,40 @@ export default class BehaviorGraphEditor extends EditorPlugin {
523569
this._contextMenu.mainDiv.style.height = '300px';
524570
}
525571

526-
this._contextMenu.tree.onClick = (id) => {
572+
// On the user clicks on an item
573+
this._contextMenu.tree.onClick = (id, data) => {
527574
switch (id) {
575+
case 'graph-clone':
576+
const clone = <LiteGraphNode> LiteGraph.createNode(node.type);
577+
clone.pos = [event.offsetX, event.offsetY];
578+
579+
Object.assign(clone.properties, node.properties);
580+
Object.assign(clone.outputs, node.outputs);
581+
582+
this.graphData.add(clone);
583+
break;
528584
case 'graph-remove':
529585
this.graphData.remove(node);
530586
break;
531587

532588
default:
533-
return;
589+
// Input
590+
if (id.indexOf('graph-inputs') === 0) {
591+
node.addInput(data[0], data[1]);
592+
}
593+
// Outputs
594+
else if (id.indexOf('graph-outputs') === 0) {
595+
node.addOutput(data[0], data[1]);
596+
}
597+
else {
598+
return;
599+
}
534600
}
535601

536602
this._contextMenu.mainDiv.style.visibility = 'hidden';
537603
};
538604

605+
// On the user dbl clicks an item
539606
this._contextMenu.tree.onDblClick = (id) => {
540607
// Create node
541608
const node = LiteGraph.createNode(id);
@@ -549,11 +616,28 @@ export default class BehaviorGraphEditor extends EditorPlugin {
549616
this._contextMenu.mainDiv.style.visibility = 'hidden';
550617
};
551618

552-
// Search
619+
// Focus on search
553620
setTimeout(() => this._contextMenu.search.focus(), 1);
554621

555-
// Mouse up
556-
const mouseUpCallbac = (ev: MouseEvent) => {
622+
// Enter (once a node is selected)
623+
const enterCallback = (ev: KeyboardEvent) => {
624+
if (ev.keyCode !== 13)
625+
return;
626+
627+
const selected = this._contextMenu.tree.getSelected();
628+
if (!selected)
629+
return;
630+
631+
this._contextMenu.tree.onDblClick(selected.id, selected.data);
632+
this._contextMenu.tree.onClick(selected.id, selected.data);
633+
634+
window.removeEventListener('keyup', enterCallback);
635+
};
636+
637+
window.addEventListener('keyup', enterCallback);
638+
639+
// Mouse up (close or not the context menu)
640+
const mouseUpCallback = (ev: MouseEvent) => {
557641
let parent = <HTMLDivElement> ev.target;
558642
while (parent) {
559643
if (parent.id === this._contextMenu.mainDiv.id)
@@ -564,10 +648,11 @@ export default class BehaviorGraphEditor extends EditorPlugin {
564648

565649
if (!parent) {
566650
this._contextMenu.mainDiv.style.visibility = 'hidden';
567-
window.removeEventListener('mousedown', mouseUpCallbac);
651+
window.removeEventListener('mousedown', mouseUpCallback);
652+
window.removeEventListener('keyup', enterCallback);
568653
}
569654
};
570655

571-
window.addEventListener('mousedown', mouseUpCallbac);
656+
window.addEventListener('mousedown', mouseUpCallback);
572657
}
573658
}

0 commit comments

Comments
 (0)