sunrize 1.10.3 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,662 @@
1
+ "use strict";
2
+
3
+ const
4
+ $ = require ("jquery"),
5
+ electron = require ("electron"),
6
+ X3D = require ("../X3D"),
7
+ Interface = require ("../Application/Interface"),
8
+ _ = require ("../Application/GetText");
9
+
10
+ const _expanded = Symbol ();
11
+
12
+ module .exports = class AnimationMembersList extends Interface
13
+ {
14
+ #editor;
15
+ #nodeList;
16
+ #list;
17
+ #nodes;
18
+ #animation;
19
+ #timeSensor;
20
+
21
+ constructor (editor, element)
22
+ {
23
+ super ("Sunrize.AnimationMembersList.");
24
+
25
+ this .#editor = editor;
26
+ this .#nodeList = element;
27
+ this .#list = $("<ul></ul>") .appendTo (this .#nodeList);
28
+ this .#nodes = [ ];
29
+
30
+ electron .ipcRenderer .on ("animation-members-list", (event, key, ... args) => this [key] (... args));
31
+
32
+ this .addMain ();
33
+ this .setup ();
34
+ }
35
+
36
+ #executionContext;
37
+
38
+ configure ()
39
+ {
40
+ this .#executionContext ?.sceneGraph_changed .removeInterest ("set_sceneGraph", this);
41
+
42
+ this .#executionContext = this .browser .currentScene;
43
+
44
+ this .#executionContext .sceneGraph_changed .addInterest ("set_sceneGraph", this);
45
+
46
+ this .set_sceneGraph ();
47
+ }
48
+
49
+ set_sceneGraph ()
50
+ {
51
+ this .removeNodes (this .#nodes .filter (node => !node .isLive ()));
52
+ }
53
+
54
+ // Scrollbars Handling
55
+
56
+ #scrollTop;
57
+ #scrollLeft;
58
+
59
+ saveScrollbars ()
60
+ {
61
+ this .#scrollTop = this .#nodeList .scrollTop ();
62
+ this .#scrollLeft = this .#nodeList .scrollLeft ();
63
+ }
64
+
65
+ restoreScrollbars ()
66
+ {
67
+ this .#nodeList .scrollTop (this .#scrollTop);
68
+ this .#nodeList .scrollLeft (this .#scrollLeft);
69
+ }
70
+
71
+ // Animation Handling
72
+
73
+ setAnimation (animation, timeSensor)
74
+ {
75
+ this .#timeSensor ?._isPaused .removeInterest ("connectNodes", this);
76
+ this .#timeSensor ?._isActive .removeInterest ("connectNodes", this);
77
+
78
+ this .#animation = animation;
79
+ this .#timeSensor = timeSensor;
80
+
81
+ this .#animation [_expanded] ??= Symbol ();
82
+
83
+ this .#timeSensor ._isPaused .addInterest ("connectNodes", this);
84
+ this .#timeSensor ._isActive .addInterest ("connectNodes", this);
85
+ }
86
+
87
+ setAnimationName (name)
88
+ {
89
+ this .animationName .text (name);
90
+ }
91
+
92
+ // List Elements Handling
93
+
94
+ addMain ()
95
+ {
96
+ const
97
+ typeNameElement = $("<span></span>") .addClass ("type-name") .text (_("Animation")),
98
+ nameElement = $("<span></span>") .addClass ("name") .text ("My"),
99
+ fieldList = $("<ul></ul>");
100
+
101
+ this .animationName = nameElement;
102
+
103
+ const listItem = $("<li></li>")
104
+ .addClass ("main")
105
+ .appendTo (this .#list);
106
+
107
+ const applyButton = $("<span></span>")
108
+ .addClass (["apply", "material-icons", "button", "off", "disabled"])
109
+ .attr ("title", _("Add keyframe(s)."))
110
+ .text ("check_box")
111
+ .on ("click", () => this .addKeyframesToMain ());
112
+
113
+ const removeButton = $("<span></span>")
114
+ .addClass (["material-icons-outlined", "button"])
115
+ .attr ("title", _("Close animation."))
116
+ .text ("cancel")
117
+ .on ("click", () => this .#editor .closeAnimation ());
118
+
119
+ const item = $("<div></div>")
120
+ .attr ("type", "main")
121
+ .addClass (["main", "item"])
122
+ .append (typeNameElement)
123
+ .append (document .createTextNode (" "))
124
+ .append (nameElement)
125
+ .append (document .createTextNode (" "))
126
+ .append (applyButton)
127
+ .append (document .createTextNode (" "))
128
+ .append (removeButton)
129
+ .appendTo (listItem);
130
+
131
+ item
132
+ .on ("mouseenter", () => item .addClass ("hover"))
133
+ .on ("mouseleave", () => item .removeClass ("hover"));
134
+
135
+ fieldList .appendTo (listItem);
136
+ }
137
+
138
+ clearNodes ()
139
+ {
140
+ this .removeNodes (this .#nodes);
141
+ }
142
+
143
+ addNodes (nodes)
144
+ {
145
+ nodes = nodes .filter (node => !this .#nodes .includes (node));
146
+
147
+ let i = this .#nodes .length;
148
+
149
+ for (const node of nodes)
150
+ {
151
+ const
152
+ typeNameElement = $("<span></span>") .addClass ("type-name") .text (node .getTypeName ()),
153
+ nameElement = $("<span></span>") .addClass ("name") .text (this .getName (node)),
154
+ fieldList = $("<ul></ul>");
155
+
156
+ const listItem = $("<li></li>")
157
+ .attr ("node-id", node .getId ())
158
+ .addClass ("node")
159
+ .appendTo (this .#list);
160
+
161
+ const applyButton = $("<span></span>")
162
+ .addClass (["apply", "material-icons", "button", "off", "disabled"])
163
+ .attr ("title", _("Add keyframe(s)."))
164
+ .text ("check_box")
165
+ .on ("click", () => this .addKeyframesToNode (node));
166
+
167
+ const expanded = node .getUserData (this .#animation [_expanded])
168
+ || !this .hasInterpolators (node);
169
+
170
+ node .setUserData (this .#animation [_expanded], expanded);
171
+
172
+ const expandButton = $("<span></span>")
173
+ .addClass (["material-icons-outlined", "button"])
174
+ .addClass (expanded ? "on" : "off")
175
+ .attr ("title", _("Show all fields."))
176
+ .text ("expand_circle_down")
177
+ .on ("click", () => this .toggleExpand (expandButton, fieldList, node));
178
+
179
+ const item = $("<div></div>")
180
+ .data ("i", i ++)
181
+ .attr ("type", "node")
182
+ .addClass (["node", "item"])
183
+ .data ("node", node)
184
+ .append ($("<img></img>") .addClass ("icon") .attr ("src", "../images/OutlineEditor/Node/X3DBaseNode.svg"))
185
+ .append (typeNameElement)
186
+ .append (document .createTextNode (" "))
187
+ .append (nameElement)
188
+ .append (document .createTextNode (" "))
189
+ .append (applyButton)
190
+ .append (document .createTextNode (" "))
191
+ .append (expandButton)
192
+ .on ("contextmenu", () => this .contextMenuForNode (node))
193
+ .appendTo (listItem);
194
+
195
+ item
196
+ .on ("mouseenter", () => item .addClass ("hover"))
197
+ .on ("mouseleave", () => item .removeClass ("hover"));
198
+
199
+ fieldList .appendTo (listItem);
200
+
201
+ this .createFieldElements (fieldList, node);
202
+
203
+ node .typeName_changed .addInterest ("set_typeName", this, typeNameElement, node);
204
+ node .name_changed .addInterest ("set_name", this, nameElement, node);
205
+ }
206
+
207
+ this .#nodes .push (... nodes);
208
+ }
209
+
210
+ #fieldTypes = new Set ([
211
+ X3D .X3DConstants .SFBool,
212
+ X3D .X3DConstants .SFColor,
213
+ X3D .X3DConstants .SFFloat,
214
+ X3D .X3DConstants .SFInt32,
215
+ X3D .X3DConstants .SFRotation,
216
+ X3D .X3DConstants .SFVec2f,
217
+ X3D .X3DConstants .SFVec3f,
218
+ X3D .X3DConstants .MFVec2f,
219
+ X3D .X3DConstants .MFVec3f
220
+ ]);
221
+
222
+ createFieldElements (fieldList, node)
223
+ {
224
+ const expanded = node .getUserData (this .#animation [_expanded])
225
+ || !this .hasInterpolators (node);
226
+
227
+ node .setUserData (this .#animation [_expanded], expanded);
228
+
229
+ let i = 0;
230
+
231
+ for (const field of node .getFields ())
232
+ {
233
+ if (!expanded && !this .#editor .fields .has (field))
234
+ continue;
235
+
236
+ if (!field .isInput ())
237
+ continue;
238
+
239
+ if (!this .#fieldTypes .has (field .getType ()))
240
+ continue;
241
+
242
+ const listItem = $("<li></li>")
243
+ .attr ("node-id", node .getId ())
244
+ .attr ("field-id", field .getId ())
245
+ .addClass ("field")
246
+ .appendTo (fieldList);
247
+
248
+ const iconElement = $("<img></img>")
249
+ .attr ("title", field .getTypeName ())
250
+ .addClass ("icon")
251
+ .attr ("src", `../images/OutlineEditor/Fields/${field .getTypeName()}.svg`);
252
+
253
+ const nameElement = $("<span></span>")
254
+ .addClass ("field-name")
255
+ .text (field .getName ());
256
+
257
+ const applyButton = $("<span></span>")
258
+ .addClass (["apply", "material-icons", "button", "off"])
259
+ .attr ("title", _("Add keyframe."))
260
+ .text ("check_box")
261
+ .on ("click", () => this .addKeyframeToField (node, field));
262
+
263
+ const item = $("<div></div>")
264
+ .data ("i", i ++ )
265
+ .attr ("type", "field")
266
+ .addClass (["field", "item"])
267
+ .data ("node", node)
268
+ .data ("field", field)
269
+ .append (iconElement)
270
+ .append (nameElement)
271
+ .append (document .createTextNode (" "))
272
+ .append (applyButton)
273
+ .on ("contextmenu", () => this .contextMenuForField (node, field))
274
+ .appendTo (listItem);
275
+
276
+ if (this .#editor .fields .has (field))
277
+ item .addClass ("bold");
278
+
279
+ item
280
+ .on ("mouseenter", () => item .addClass ("hover"))
281
+ .on ("mouseleave", () => item .removeClass ("hover"));
282
+ }
283
+
284
+ this .connectNode (node, !this .isRunning ());
285
+ }
286
+
287
+ removeNodes (nodes)
288
+ {
289
+ for (const node of nodes)
290
+ {
291
+ this .#list .find (`li[node-id=${node .getId ()}]`) .remove ();
292
+
293
+ node .typeName_changed .removeInterest ("set_typeName", this);
294
+ node .name_changed .removeInterest ("set_name", this);
295
+
296
+ this .connectNode (node, false);
297
+ }
298
+
299
+ this .#nodes = this .#nodes .filter (node => !nodes .includes (node));
300
+
301
+ this .checkApply ();
302
+ }
303
+
304
+ getName (node)
305
+ {
306
+ let
307
+ name = node .getDisplayName () || _("<unnamed>"),
308
+ outerNode = node .getExecutionContext () .getOuterNode ();
309
+
310
+ while (outerNode instanceof X3D .X3DProtoDeclaration)
311
+ {
312
+ name = outerNode .getName () + "." + name;
313
+ outerNode = outerNode .getExecutionContext () .getOuterNode ();
314
+ }
315
+
316
+ return name;
317
+ }
318
+
319
+ set_typeName (element, node)
320
+ {
321
+ element .text (node .getTypeName ());
322
+ }
323
+
324
+ set_name (element, node)
325
+ {
326
+ element .text (this .getName (node));
327
+ }
328
+
329
+ // Timeline Handling
330
+
331
+ getTrackOffsets ()
332
+ {
333
+ const
334
+ listTop = Math .floor (this .#nodeList .offset () .top),
335
+ listHeight = Math .floor (this .#nodeList .parent () .height ()),
336
+ items = this .#nodeList .find (".item"),
337
+ offsets = [ ];
338
+
339
+ for (const element of items)
340
+ {
341
+ const
342
+ item = $(element),
343
+ height = Math .round (item .outerHeight ()),
344
+ top = Math .round (item .offset () .top) - listTop,
345
+ bottom = top + height;
346
+
347
+ if (bottom < 0)
348
+ continue;
349
+
350
+ if (top > listHeight)
351
+ continue;
352
+
353
+ offsets .push ({ item, top, bottom, height });
354
+ }
355
+
356
+ return offsets;
357
+ }
358
+
359
+ contextMenuForNode (node)
360
+ {
361
+ this .#node = node;
362
+
363
+ const menu = [
364
+ {
365
+ label: _("Remove Member from Animation"),
366
+ args: ["removeMember"],
367
+ },
368
+ ];
369
+
370
+ electron .ipcRenderer .send ("context-menu", "animation-members-list", menu);
371
+ }
372
+
373
+ removeMember ()
374
+ {
375
+ this .#editor .removeMembers ([this .#node]);
376
+ }
377
+
378
+ contextMenuForField (node, field)
379
+ {
380
+ this .#node = node;
381
+ this .#field = field;
382
+
383
+ const menu = [
384
+ {
385
+ label: _("Remove Interpolator from Animation"),
386
+ args: ["removeInterpolator"],
387
+ },
388
+ ];
389
+
390
+ electron .ipcRenderer .send ("context-menu", "animation-members-list", menu);
391
+ }
392
+
393
+ removeInterpolator ()
394
+ {
395
+ this .#editor .removeInterpolator (this .#node, this .#field);
396
+ }
397
+
398
+ toggleExpand (expandButton, fieldList, node)
399
+ {
400
+ node .setUserData (this .#animation [_expanded], !node .getUserData (this .#animation [_expanded]));
401
+
402
+ expandButton
403
+ .removeClass (["on", "off"])
404
+ .addClass (node .getUserData (this .#animation [_expanded]) ? "on" : "off");
405
+
406
+ fieldList .empty ();
407
+
408
+ this .createFieldElements (fieldList, node);
409
+
410
+ this .#editor .requestDrawTimeline ();
411
+ }
412
+
413
+ // Apply Button Handling
414
+
415
+ addKeyframesToMain ()
416
+ {
417
+ const keyframes = [ ];
418
+
419
+ const mainItem = this .#list .find ("> .main");
420
+
421
+ if (mainItem .find (".apply.green") .length)
422
+ {
423
+ const fieldItems = this .#list .find (`.field > .item`);
424
+
425
+ for (const element of fieldItems)
426
+ {
427
+ const fieldItem = $(element);
428
+
429
+ if (!fieldItem .find (".apply.green") .length)
430
+ continue;
431
+
432
+ const
433
+ node = fieldItem .data ("node"),
434
+ field = fieldItem .data ("field");
435
+
436
+ keyframes .push ({ node, field });
437
+ }
438
+ }
439
+ else
440
+ {
441
+ for (const field of this .#editor .fields .keys ())
442
+ {
443
+ const
444
+ fieldItem = this .#list .find (`.field[field-id=${field .getId ()}] > .item`),
445
+ node = fieldItem .data ("node");
446
+
447
+ keyframes .push ({ node, field });
448
+ }
449
+ }
450
+
451
+ this .#editor .addKeyframes (keyframes);
452
+
453
+ for (const { field } of keyframes)
454
+ this .toggleApply (field, false);
455
+ }
456
+
457
+ addKeyframesToNode (node)
458
+ {
459
+ const keyframes = [ ];
460
+
461
+ const nodeItem = this .#list .find (`.node[node-id=${node .getId ()}]`);
462
+
463
+ if (nodeItem .find (".apply.green") .length)
464
+ {
465
+ const fieldItems = this .#list .find (`.field[node-id=${node .getId ()}] > .item`);
466
+
467
+ for (const element of fieldItems)
468
+ {
469
+ const fieldItem = $(element);
470
+
471
+ if (!fieldItem .find (".apply.green") .length)
472
+ continue;
473
+
474
+ const field = fieldItem .data ("field");
475
+
476
+ keyframes .push ({ node, field });
477
+ }
478
+ }
479
+ else
480
+ {
481
+ for (const field of node .getFields ())
482
+ {
483
+ if (!this .#editor .fields .has (field))
484
+ continue;
485
+
486
+ const fieldItem = this .#list .find (`.field[field-id=${field .getId ()}] > .item`);
487
+
488
+ keyframes .push ({ node, field });
489
+ }
490
+ }
491
+
492
+ this .#editor .addKeyframes (keyframes);
493
+
494
+ for (const { field } of keyframes)
495
+ this .toggleApply (field, false);
496
+ }
497
+
498
+ #node;
499
+ #field;
500
+
501
+ addKeyframeToField (node, field)
502
+ {
503
+ this .#node = node;
504
+ this .#field = field;
505
+
506
+ if (field .getType () === X3D .X3DConstants .MFVec3f && !this .#editor .fields .has (field))
507
+ {
508
+ const menu = [
509
+ {
510
+ label: _("CoordinateInterpolator"),
511
+ args: ["addKeyframeForType", "CoordinateInterpolator"],
512
+ },
513
+ {
514
+ label: _("NormalInterpolator"),
515
+ args: ["addKeyframeForType", "NormalInterpolator"],
516
+ },
517
+ ];
518
+
519
+ electron .ipcRenderer .send ("context-menu", "animation-members-list", menu);
520
+ }
521
+ else
522
+ {
523
+ this .addKeyframeForType ();
524
+ }
525
+ }
526
+
527
+ addKeyframeForType (typeName)
528
+ {
529
+ this .#editor .addKeyframes ([{ node: this .#node, field: this .#field, typeName }]);
530
+ this .toggleApply (this .#field, false);
531
+ }
532
+
533
+ isRunning ()
534
+ {
535
+ return this .#timeSensor ._isActive .getValue () && !this .#timeSensor ._isPaused .getValue ()
536
+ }
537
+
538
+ connectNodes ()
539
+ {
540
+ if (this .isRunning ())
541
+ {
542
+ this .removeApply ();
543
+ }
544
+ else
545
+ {
546
+ for (const node of this .#nodes)
547
+ this .connectNode (node, true);
548
+ }
549
+ }
550
+
551
+ connectNode (node, connect)
552
+ {
553
+ if (connect)
554
+ {
555
+ node .addInterest ("checkApply", this);
556
+
557
+ this .checkApply ();
558
+ }
559
+ else
560
+ {
561
+ node .removeInterest ("checkApply", this);
562
+ }
563
+ }
564
+
565
+ removeApply ()
566
+ {
567
+ this .#list .find (".apply")
568
+ .removeClass ("green")
569
+ .addClass ("off");
570
+ }
571
+
572
+ checkApply ()
573
+ {
574
+ for (const [field, interpolator] of this .#editor .fields)
575
+ this .toggleApply (field, !interpolator ._value_changed .equals (field));
576
+
577
+ if (!this .#editor .fields .size)
578
+ this .toggleMainApply ();
579
+ }
580
+
581
+ toggleApply (field, value)
582
+ {
583
+ // Update field.
584
+
585
+ const fieldItem = this .#list .find (`[field-id=${field .getId ()}]`);
586
+
587
+ if (value)
588
+ {
589
+ fieldItem .find ("> .item .apply")
590
+ .removeClass ("off")
591
+ .addClass ("green");
592
+ }
593
+ else
594
+ {
595
+ fieldItem .find ("> .item .apply")
596
+ .removeClass ("green")
597
+ .addClass ("off");
598
+ }
599
+
600
+ if (this .#editor .fields .has (field))
601
+ fieldItem .find ("> .item") .addClass ("bold");
602
+ else
603
+ fieldItem .find ("> .item") .removeClass ("bold");
604
+
605
+ // Update node.
606
+
607
+ const nodeItem = fieldItem .closest (".node");
608
+
609
+ if (nodeItem .find (".field .apply.green") .length)
610
+ {
611
+ nodeItem .find ("> .item .apply")
612
+ .removeClass (["off", "disabled"])
613
+ .addClass ("green");
614
+ }
615
+ else
616
+ {
617
+ const enabled = this .hasInterpolators (nodeItem .find ("> .item") .data ("node"));
618
+
619
+ nodeItem .find ("> .item .apply")
620
+ .removeClass ("green")
621
+ .addClass ("off");
622
+
623
+ nodeItem .find ("> .item .apply")
624
+ .removeClass ("disabled")
625
+ .addClass (enabled ? [ ] : ["disabled"]);
626
+ }
627
+
628
+ // Update main.
629
+
630
+ this .toggleMainApply ();
631
+ }
632
+
633
+ toggleMainApply ()
634
+ {
635
+ const mainItem = this .#list .find ("> .main");
636
+
637
+ if (this .#list .find (".field .apply.green") .length)
638
+ {
639
+ mainItem .find ("> .item .apply")
640
+ .removeClass (["off", "disabled"])
641
+ .addClass ("green");
642
+ }
643
+ else
644
+ {
645
+ const enabled = Array .from (this .#list .find (".node > .item"), item => $(item))
646
+ .some (nodeItem => this .hasInterpolators (nodeItem .data ("node")));
647
+
648
+ mainItem .find ("> .item .apply")
649
+ .removeClass ("green")
650
+ .addClass ("off");
651
+
652
+ mainItem .find ("> .item .apply")
653
+ .removeClass ("disabled")
654
+ .addClass (enabled ? [ ] : ["disabled"]);
655
+ }
656
+ }
657
+
658
+ hasInterpolators (node)
659
+ {
660
+ return node ?.getFields () .some (field => this .#editor .fields .has (field));
661
+ }
662
+ };
@@ -54,7 +54,7 @@ module .exports = class Console extends Interface
54
54
  $("<span></span>") .addClass ("separator") .appendTo (this .toolbar);
55
55
 
56
56
  this .textarea = $("<textarea></textarea>")
57
- .attr ("placeholder", _("Evaluate JavaScript code here."))
57
+ .attr ("placeholder", _("Evaluate X3D Script code here, e.g. type `Browser.name`."))
58
58
  .attr ("tabindex", 0)
59
59
  .appendTo (this .input)
60
60
  .on ("keydown", event => this .onkeydown (event))