@m2c2kit/addons 0.3.13 → 0.3.15
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.
- package/dist/index.d.ts +293 -57
- package/dist/index.js +799 -144
- package/dist/index.js.map +1 -0
- package/dist/index.min.js +1 -0
- package/package.json +13 -11
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { Composite, WebColors, Shape, Label, CanvasKitHelpers,
|
|
1
|
+
import { Composite, WebColors, Shape, Label, CanvasKitHelpers, M2EventType, MutablePath, Easings, Story, Transition, TransitionDirection, LabelHorizontalAlignmentMode, Scene, Dimensions, Sprite, Action } from '@m2c2kit/core';
|
|
2
2
|
|
|
3
3
|
class Grid extends Composite {
|
|
4
4
|
/**
|
|
5
|
-
* A rectangular grid that supports placement of
|
|
5
|
+
* A rectangular grid that supports placement of nodes within the grid's
|
|
6
6
|
* cells.
|
|
7
7
|
*
|
|
8
|
-
* @remarks This composite
|
|
9
|
-
* has convenience functions for placing and clearing
|
|
8
|
+
* @remarks This composite node is composed of rectangles and lines. It
|
|
9
|
+
* has convenience functions for placing and clearing nodes on the grid
|
|
10
10
|
* by row and column position (zero-based indexing)
|
|
11
11
|
*
|
|
12
12
|
* @param options - {@link GridOptions}
|
|
@@ -102,11 +102,11 @@ class Grid extends Composite {
|
|
|
102
102
|
}
|
|
103
103
|
const x = -this.size.width / 2 + this.cellWidth / 2 + gridChild.column * this.cellWidth;
|
|
104
104
|
const y = -this.size.height / 2 + this.cellHeight / 2 + gridChild.row * this.cellHeight;
|
|
105
|
-
gridChild.
|
|
106
|
-
x: x + gridChild.
|
|
107
|
-
y: y + gridChild.
|
|
105
|
+
gridChild.node.position = {
|
|
106
|
+
x: x + gridChild.node.position.x,
|
|
107
|
+
y: y + gridChild.node.position.y
|
|
108
108
|
};
|
|
109
|
-
this.gridBackground.addChild(gridChild.
|
|
109
|
+
this.gridBackground.addChild(gridChild.node);
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
this.needsInitialization = false;
|
|
@@ -120,24 +120,24 @@ class Grid extends Composite {
|
|
|
120
120
|
set gridBackground(gridBackground) {
|
|
121
121
|
this._gridBackground = gridBackground;
|
|
122
122
|
}
|
|
123
|
-
// all
|
|
123
|
+
// all nodes that make up grid are added as children, so they
|
|
124
124
|
// have their own dispose methods
|
|
125
125
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
126
126
|
dispose() {
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
|
-
* Duplicates
|
|
129
|
+
* Duplicates a node using deep copy.
|
|
130
130
|
*
|
|
131
|
-
* @remarks This is a deep recursive clone (
|
|
132
|
-
* The uuid property of all duplicated
|
|
131
|
+
* @remarks This is a deep recursive clone (node and children).
|
|
132
|
+
* The uuid property of all duplicated nodes will be newly created,
|
|
133
133
|
* because uuid must be unique.
|
|
134
134
|
*
|
|
135
|
-
* @param newName - optional name of the new, duplicated
|
|
135
|
+
* @param newName - optional name of the new, duplicated node. If not
|
|
136
136
|
* provided, name will be the new uuid
|
|
137
137
|
*/
|
|
138
138
|
duplicate(newName) {
|
|
139
139
|
const dest = new Grid({
|
|
140
|
-
...this.
|
|
140
|
+
...this.getNodeOptions(),
|
|
141
141
|
...this.getDrawableOptions(),
|
|
142
142
|
rows: this.rows,
|
|
143
143
|
columns: this.columns,
|
|
@@ -168,8 +168,8 @@ class Grid extends Composite {
|
|
|
168
168
|
child.warmup(canvas);
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
|
-
// override
|
|
172
|
-
// it removes only
|
|
171
|
+
// override M2Node.RemoveAllChildren() so that when RemoveAllChildren() is called on a Grid,
|
|
172
|
+
// it removes only nodes added to the grid cells (what we call grid children), not the grid lines!
|
|
173
173
|
/**
|
|
174
174
|
* Removes all children from the grid, but retains grid lines.
|
|
175
175
|
*/
|
|
@@ -180,29 +180,29 @@ class Grid extends Composite {
|
|
|
180
180
|
while (this.gridChildren.length) {
|
|
181
181
|
const gridChild = this.gridChildren.pop();
|
|
182
182
|
if (gridChild) {
|
|
183
|
-
this.gridBackground.removeChild(gridChild.
|
|
183
|
+
this.gridBackground.removeChild(gridChild.node);
|
|
184
184
|
}
|
|
185
185
|
}
|
|
186
186
|
this.needsInitialization = true;
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
189
|
-
* Adds
|
|
189
|
+
* Adds a node to the grid at the specified row and column position.
|
|
190
190
|
*
|
|
191
|
-
* @param
|
|
192
|
-
* @param row - row position within grid to add
|
|
193
|
-
* @param column - column position within grid to add
|
|
191
|
+
* @param node - node to add to the grid
|
|
192
|
+
* @param row - row position within grid to add node; zero-based indexing
|
|
193
|
+
* @param column - column position within grid to add node; zero-based indexing
|
|
194
194
|
*/
|
|
195
|
-
addAtCell(
|
|
195
|
+
addAtCell(node, row, column) {
|
|
196
196
|
if (row < 0 || row >= this.rows || column < 0 || column >= this.columns) {
|
|
197
197
|
console.warn(
|
|
198
|
-
`warning: addAtCell() requested to add
|
|
198
|
+
`warning: addAtCell() requested to add node at row ${row}, column ${column}. This is outside the bounds of grid ${this.name}, which is size ${this.rows}x${this.columns}. Note that addAtCell() uses zero-based indexing. AddAtCell() will proceed, but may draw nodes outside the grid`
|
|
199
199
|
);
|
|
200
200
|
}
|
|
201
|
-
this.gridChildren.push({
|
|
201
|
+
this.gridChildren.push({ node, row, column });
|
|
202
202
|
this.needsInitialization = true;
|
|
203
203
|
}
|
|
204
204
|
/**
|
|
205
|
-
* Removes all child
|
|
205
|
+
* Removes all child nodes at the specified row and column position.
|
|
206
206
|
*
|
|
207
207
|
* @param row - row position within grid at which to remove children; zero-based indexing
|
|
208
208
|
* @param column - column position within grid at which to remove children; zero-based indexing
|
|
@@ -215,24 +215,24 @@ class Grid extends Composite {
|
|
|
215
215
|
return;
|
|
216
216
|
}
|
|
217
217
|
this.gridBackground.removeChildren(
|
|
218
|
-
gridChildrenToRemove.map((gridChild) => gridChild.
|
|
218
|
+
gridChildrenToRemove.map((gridChild) => gridChild.node)
|
|
219
219
|
);
|
|
220
220
|
this.gridChildren = this.gridChildren.filter(
|
|
221
221
|
(gridChild) => gridChild.row !== row && gridChild.column !== column
|
|
222
222
|
);
|
|
223
223
|
this.needsInitialization = true;
|
|
224
224
|
}
|
|
225
|
-
// override
|
|
226
|
-
//
|
|
225
|
+
// override M2Node.RemoveChild() so that when RemoveChild() is called on a Grid, it removes the
|
|
226
|
+
// node from the gridBackground rectangle AND our grid's own list of children (in gridChildren)
|
|
227
227
|
/**
|
|
228
|
-
* Removes the child
|
|
228
|
+
* Removes the child node from the grid.
|
|
229
229
|
*
|
|
230
|
-
* @param
|
|
230
|
+
* @param node - node to remove
|
|
231
231
|
*/
|
|
232
|
-
removeChild(
|
|
233
|
-
this.gridBackground.removeChild(
|
|
232
|
+
removeChild(node) {
|
|
233
|
+
this.gridBackground.removeChild(node);
|
|
234
234
|
this.gridChildren = this.gridChildren.filter(
|
|
235
|
-
(gridChild) => gridChild.
|
|
235
|
+
(gridChild) => gridChild.node != node
|
|
236
236
|
);
|
|
237
237
|
this.needsInitialization = true;
|
|
238
238
|
}
|
|
@@ -244,7 +244,7 @@ class Button extends Composite {
|
|
|
244
244
|
/**
|
|
245
245
|
* A simple button of rectangle with text centered inside.
|
|
246
246
|
*
|
|
247
|
-
* @remarks This composite
|
|
247
|
+
* @remarks This composite node is composed of a rectangle and text. To
|
|
248
248
|
* respond to user taps, the isUserInteractionEnabled property must be set
|
|
249
249
|
* to true and an appropriate callback must be set to handle the tap event.
|
|
250
250
|
*
|
|
@@ -260,6 +260,8 @@ class Button extends Composite {
|
|
|
260
260
|
this.fontSize = 20;
|
|
261
261
|
this._text = "";
|
|
262
262
|
this._fontColor = WebColors.White;
|
|
263
|
+
this._interpolation = {};
|
|
264
|
+
this._localize = true;
|
|
263
265
|
if (options.text) {
|
|
264
266
|
this.text = options.text;
|
|
265
267
|
}
|
|
@@ -269,6 +271,12 @@ class Button extends Composite {
|
|
|
269
271
|
if (options.cornerRadius !== void 0) {
|
|
270
272
|
this.cornerRadius = options.cornerRadius;
|
|
271
273
|
}
|
|
274
|
+
if (options.fontName) {
|
|
275
|
+
this.fontName = options.fontName;
|
|
276
|
+
}
|
|
277
|
+
if (options.fontNames) {
|
|
278
|
+
this.fontNames = options.fontNames;
|
|
279
|
+
}
|
|
272
280
|
if (options.fontSize !== void 0) {
|
|
273
281
|
this.fontSize = options.fontSize;
|
|
274
282
|
}
|
|
@@ -278,6 +286,12 @@ class Button extends Composite {
|
|
|
278
286
|
if (options.backgroundColor) {
|
|
279
287
|
this.backgroundColor = options.backgroundColor;
|
|
280
288
|
}
|
|
289
|
+
if (options.interpolation) {
|
|
290
|
+
this.interpolation = options.interpolation;
|
|
291
|
+
}
|
|
292
|
+
if (options.localize !== void 0) {
|
|
293
|
+
this.localize = options.localize;
|
|
294
|
+
}
|
|
281
295
|
}
|
|
282
296
|
initialize() {
|
|
283
297
|
this.removeAllChildren();
|
|
@@ -299,6 +313,10 @@ class Button extends Composite {
|
|
|
299
313
|
this.addChild(buttonRectangle);
|
|
300
314
|
const buttonLabel = new Label({
|
|
301
315
|
text: this.text,
|
|
316
|
+
localize: this.localize,
|
|
317
|
+
interpolation: this.interpolation,
|
|
318
|
+
fontName: this.fontName,
|
|
319
|
+
fontNames: this.fontNames,
|
|
302
320
|
fontSize: this.fontSize,
|
|
303
321
|
fontColor: this.fontColor
|
|
304
322
|
});
|
|
@@ -329,26 +347,59 @@ class Button extends Composite {
|
|
|
329
347
|
this._fontColor = fontColor;
|
|
330
348
|
this.needsInitialization = true;
|
|
331
349
|
}
|
|
350
|
+
get fontName() {
|
|
351
|
+
return this._fontName;
|
|
352
|
+
}
|
|
353
|
+
set fontName(fontName) {
|
|
354
|
+
this._fontName = fontName;
|
|
355
|
+
this.needsInitialization = true;
|
|
356
|
+
}
|
|
357
|
+
get fontNames() {
|
|
358
|
+
return this._fontNames;
|
|
359
|
+
}
|
|
360
|
+
set fontNames(fontNames) {
|
|
361
|
+
this._fontNames = fontNames;
|
|
362
|
+
this.needsInitialization = true;
|
|
363
|
+
}
|
|
364
|
+
get interpolation() {
|
|
365
|
+
return this._interpolation;
|
|
366
|
+
}
|
|
367
|
+
set interpolation(interpolation) {
|
|
368
|
+
this._interpolation = interpolation;
|
|
369
|
+
Object.freeze(this._interpolation);
|
|
370
|
+
this.needsInitialization = true;
|
|
371
|
+
}
|
|
372
|
+
get localize() {
|
|
373
|
+
return this._localize;
|
|
374
|
+
}
|
|
375
|
+
set localize(localize) {
|
|
376
|
+
this._localize = localize;
|
|
377
|
+
this.needsInitialization = true;
|
|
378
|
+
}
|
|
332
379
|
/**
|
|
333
|
-
* Duplicates
|
|
380
|
+
* Duplicates a node using deep copy.
|
|
334
381
|
*
|
|
335
|
-
* @remarks This is a deep recursive clone (
|
|
336
|
-
* The uuid property of all duplicated
|
|
382
|
+
* @remarks This is a deep recursive clone (node and children).
|
|
383
|
+
* The uuid property of all duplicated nodes will be newly created,
|
|
337
384
|
* because uuid must be unique.
|
|
338
385
|
*
|
|
339
|
-
* @param newName - optional name of the new, duplicated
|
|
386
|
+
* @param newName - optional name of the new, duplicated node. If not
|
|
340
387
|
* provided, name will be the new uuid
|
|
341
388
|
*/
|
|
342
389
|
duplicate(newName) {
|
|
343
390
|
const dest = new Button({
|
|
344
|
-
...this.
|
|
391
|
+
...this.getNodeOptions(),
|
|
345
392
|
...this.getDrawableOptions(),
|
|
346
393
|
...this.getTextOptions(),
|
|
347
394
|
size: this.size,
|
|
348
395
|
cornerRadius: this.cornerRadius,
|
|
349
396
|
backgroundColor: this.backgroundColor,
|
|
350
397
|
fontColor: this.fontColor,
|
|
351
|
-
name: newName
|
|
398
|
+
name: newName,
|
|
399
|
+
localize: this.localize,
|
|
400
|
+
interpolation: JSON.parse(JSON.stringify(this.interpolation)),
|
|
401
|
+
fontName: this.fontName,
|
|
402
|
+
fontNames: JSON.parse(JSON.stringify(this.fontNames))
|
|
352
403
|
});
|
|
353
404
|
if (this.children.length > 0) {
|
|
354
405
|
dest.children = this.children.map((child) => {
|
|
@@ -384,7 +435,7 @@ class Dialog extends Composite {
|
|
|
384
435
|
// todo: add default "behaviors" (?) like button click animation?
|
|
385
436
|
constructor(options) {
|
|
386
437
|
super(options);
|
|
387
|
-
this.compositeType = "
|
|
438
|
+
this.compositeType = "Dialog";
|
|
388
439
|
this._backgroundColor = WebColors.White;
|
|
389
440
|
this.cornerRadius = 9;
|
|
390
441
|
this.overlayAlpha = 0.5;
|
|
@@ -425,15 +476,16 @@ class Dialog extends Composite {
|
|
|
425
476
|
show() {
|
|
426
477
|
this.hidden = false;
|
|
427
478
|
}
|
|
428
|
-
onDialogResult(callback,
|
|
479
|
+
onDialogResult(callback, options) {
|
|
429
480
|
const eventListener = {
|
|
430
|
-
type:
|
|
431
|
-
|
|
481
|
+
type: M2EventType.CompositeCustom,
|
|
482
|
+
compositeType: "DialogResult",
|
|
483
|
+
nodeUuid: this.uuid,
|
|
432
484
|
callback
|
|
433
485
|
};
|
|
434
|
-
if (
|
|
486
|
+
if (options?.replaceExisting) {
|
|
435
487
|
this.eventListeners = this.eventListeners.filter(
|
|
436
|
-
(listener) => !(listener.
|
|
488
|
+
(listener) => !(listener.nodeUuid === eventListener.nodeUuid && listener.type === eventListener.type)
|
|
437
489
|
);
|
|
438
490
|
}
|
|
439
491
|
this.eventListeners.push(eventListener);
|
|
@@ -455,9 +507,9 @@ class Dialog extends Composite {
|
|
|
455
507
|
e.handled = true;
|
|
456
508
|
this.hidden = true;
|
|
457
509
|
if (this.eventListeners.length > 0) {
|
|
458
|
-
this.eventListeners.filter((listener) => listener.type ===
|
|
510
|
+
this.eventListeners.filter((listener) => listener.type === M2EventType.CompositeCustom).forEach((listener) => {
|
|
459
511
|
const dialogEvent = {
|
|
460
|
-
type:
|
|
512
|
+
type: M2EventType.CompositeCustom,
|
|
461
513
|
target: this,
|
|
462
514
|
handled: false,
|
|
463
515
|
dialogResult: "Dismiss" /* Dismiss */
|
|
@@ -503,9 +555,9 @@ class Dialog extends Composite {
|
|
|
503
555
|
e.handled = true;
|
|
504
556
|
this.hidden = true;
|
|
505
557
|
if (this.eventListeners.length > 0) {
|
|
506
|
-
this.eventListeners.filter((listener) => listener.type ===
|
|
558
|
+
this.eventListeners.filter((listener) => listener.type === M2EventType.CompositeCustom).forEach((listener) => {
|
|
507
559
|
const dialogEvent = {
|
|
508
|
-
type:
|
|
560
|
+
type: M2EventType.CompositeCustom,
|
|
509
561
|
target: this,
|
|
510
562
|
handled: false,
|
|
511
563
|
dialogResult: "Negative" /* Negative */
|
|
@@ -525,9 +577,9 @@ class Dialog extends Composite {
|
|
|
525
577
|
e.handled = true;
|
|
526
578
|
this.hidden = true;
|
|
527
579
|
if (this.eventListeners.length > 0) {
|
|
528
|
-
this.eventListeners.filter((listener) => listener.type ===
|
|
580
|
+
this.eventListeners.filter((listener) => listener.type === M2EventType.CompositeCustom).forEach((listener) => {
|
|
529
581
|
const dialogEvent = {
|
|
530
|
-
type:
|
|
582
|
+
type: M2EventType.CompositeCustom,
|
|
531
583
|
target: this,
|
|
532
584
|
handled: false,
|
|
533
585
|
dialogResult: "Positive" /* Positive */
|
|
@@ -555,13 +607,13 @@ class Dialog extends Composite {
|
|
|
555
607
|
this.needsInitialization = true;
|
|
556
608
|
}
|
|
557
609
|
/**
|
|
558
|
-
* Duplicates
|
|
610
|
+
* Duplicates a node using deep copy.
|
|
559
611
|
*
|
|
560
|
-
* @remarks This is a deep recursive clone (
|
|
561
|
-
* The uuid property of all duplicated
|
|
612
|
+
* @remarks This is a deep recursive clone (node and children).
|
|
613
|
+
* The uuid property of all duplicated nodes will be newly created,
|
|
562
614
|
* because uuid must be unique.
|
|
563
615
|
*
|
|
564
|
-
* @param newName - optional name of the new, duplicated
|
|
616
|
+
* @param newName - optional name of the new, duplicated node. If not
|
|
565
617
|
* provided, name will be the new uuid
|
|
566
618
|
*/
|
|
567
619
|
duplicate(newName) {
|
|
@@ -594,7 +646,7 @@ class DrawPad extends Composite {
|
|
|
594
646
|
/**
|
|
595
647
|
* A rectangular area on which the user can draw strokes (lines).
|
|
596
648
|
*
|
|
597
|
-
* @remarks This composite
|
|
649
|
+
* @remarks This composite node is composed of a rectangle Shape and
|
|
598
650
|
* another Shape that is formed from a path of points.
|
|
599
651
|
*
|
|
600
652
|
* @param options - {@link DrawPadOptions}
|
|
@@ -919,45 +971,45 @@ class DrawPad extends Composite {
|
|
|
919
971
|
);
|
|
920
972
|
}
|
|
921
973
|
/**
|
|
922
|
-
* Adds
|
|
974
|
+
* Adds a node to the DrawPad.
|
|
923
975
|
*
|
|
924
|
-
* @remarks After the
|
|
976
|
+
* @remarks After the node is added to the DrawPad, its
|
|
925
977
|
* position is adjusted to be relative to the DrawPad's coordinate
|
|
926
978
|
* system, and it is made interactive. The method returns an object
|
|
927
|
-
* which is the
|
|
928
|
-
* properties, and events specific to it now being on a DrawPad. The
|
|
979
|
+
* which is the node as a DrawPadItem, which has additional methods,
|
|
980
|
+
* properties, and events specific to it now being on a DrawPad. The node
|
|
929
981
|
* now **must** be manipulated only using the DrawPadItem object. Using
|
|
930
|
-
* the original
|
|
982
|
+
* the original node object will result in undefined behavior.
|
|
931
983
|
*
|
|
932
|
-
* @param
|
|
933
|
-
* @returns the
|
|
984
|
+
* @param node - the node to add to the DrawPad
|
|
985
|
+
* @returns the node as a DrawPadItem
|
|
934
986
|
*/
|
|
935
|
-
addItem(
|
|
936
|
-
Object.defineProperty(
|
|
987
|
+
addItem(node) {
|
|
988
|
+
Object.defineProperty(node, "drawPadPosition", {
|
|
937
989
|
get: function() {
|
|
938
|
-
const drawPad =
|
|
990
|
+
const drawPad = node.parent;
|
|
939
991
|
return {
|
|
940
992
|
get x() {
|
|
941
|
-
return
|
|
993
|
+
return node.position.x + drawPad.size.width / 2;
|
|
942
994
|
},
|
|
943
995
|
set x(value) {
|
|
944
|
-
|
|
996
|
+
node.position.x = value - drawPad.size.width / 2;
|
|
945
997
|
},
|
|
946
998
|
get y() {
|
|
947
|
-
return
|
|
999
|
+
return node.position.y + drawPad.size.height / 2;
|
|
948
1000
|
},
|
|
949
1001
|
set y(value) {
|
|
950
|
-
|
|
1002
|
+
node.position.y = value - drawPad.size.height / 2;
|
|
951
1003
|
}
|
|
952
1004
|
};
|
|
953
1005
|
},
|
|
954
1006
|
set: function(value) {
|
|
955
|
-
const drawPad =
|
|
956
|
-
|
|
957
|
-
|
|
1007
|
+
const drawPad = node.parent;
|
|
1008
|
+
node.position.x = value.x - drawPad.size.width / 2;
|
|
1009
|
+
node.position.y = value.y - drawPad.size.height / 2;
|
|
958
1010
|
}
|
|
959
1011
|
});
|
|
960
|
-
Object.defineProperty(
|
|
1012
|
+
Object.defineProperty(node, "onStrokeEnter", {
|
|
961
1013
|
value: function(callback, options) {
|
|
962
1014
|
this.addEventListener(
|
|
963
1015
|
DrawPadItemEventType.StrokeEnter,
|
|
@@ -966,7 +1018,7 @@ class DrawPad extends Composite {
|
|
|
966
1018
|
);
|
|
967
1019
|
}
|
|
968
1020
|
});
|
|
969
|
-
Object.defineProperty(
|
|
1021
|
+
Object.defineProperty(node, "onStrokeLeave", {
|
|
970
1022
|
value: function(callback, options) {
|
|
971
1023
|
this.addEventListener(
|
|
972
1024
|
DrawPadItemEventType.StrokeLeave,
|
|
@@ -975,62 +1027,62 @@ class DrawPad extends Composite {
|
|
|
975
1027
|
);
|
|
976
1028
|
}
|
|
977
1029
|
});
|
|
978
|
-
Object.defineProperty(
|
|
1030
|
+
Object.defineProperty(node, "isStrokeWithinBounds", {
|
|
979
1031
|
value: false,
|
|
980
1032
|
writable: true
|
|
981
1033
|
});
|
|
982
|
-
|
|
1034
|
+
node.onPointerDown(() => {
|
|
983
1035
|
if (this.isDrawingPointerDown) {
|
|
984
|
-
if (
|
|
985
|
-
|
|
1036
|
+
if (node.isStrokeWithinBounds === false) {
|
|
1037
|
+
node.isStrokeWithinBounds = true;
|
|
986
1038
|
const drawPadItemEvent = {
|
|
987
1039
|
type: DrawPadItemEventType.StrokeEnter,
|
|
988
|
-
target:
|
|
1040
|
+
target: node
|
|
989
1041
|
};
|
|
990
|
-
this.raiseDrawPadItemEvent(
|
|
1042
|
+
this.raiseDrawPadItemEvent(node, drawPadItemEvent);
|
|
991
1043
|
}
|
|
992
1044
|
}
|
|
993
1045
|
});
|
|
994
|
-
|
|
1046
|
+
node.onPointerMove(() => {
|
|
995
1047
|
if (this.isDrawingPointerDown) {
|
|
996
|
-
if (
|
|
997
|
-
|
|
1048
|
+
if (node.isStrokeWithinBounds === false) {
|
|
1049
|
+
node.isStrokeWithinBounds = true;
|
|
998
1050
|
const drawPadItemEvent = {
|
|
999
1051
|
type: DrawPadItemEventType.StrokeEnter,
|
|
1000
|
-
target:
|
|
1052
|
+
target: node
|
|
1001
1053
|
};
|
|
1002
|
-
this.raiseDrawPadItemEvent(
|
|
1054
|
+
this.raiseDrawPadItemEvent(node, drawPadItemEvent);
|
|
1003
1055
|
}
|
|
1004
1056
|
}
|
|
1005
1057
|
});
|
|
1006
|
-
|
|
1058
|
+
node.onPointerLeave(() => {
|
|
1007
1059
|
if (this.isDrawingPointerDown) {
|
|
1008
|
-
if (
|
|
1009
|
-
|
|
1060
|
+
if (node.isStrokeWithinBounds === true) {
|
|
1061
|
+
node.isStrokeWithinBounds = false;
|
|
1010
1062
|
const drawPadItemEvent = {
|
|
1011
1063
|
type: DrawPadItemEventType.StrokeLeave,
|
|
1012
|
-
target:
|
|
1064
|
+
target: node
|
|
1013
1065
|
};
|
|
1014
|
-
this.raiseDrawPadItemEvent(
|
|
1066
|
+
this.raiseDrawPadItemEvent(node, drawPadItemEvent);
|
|
1015
1067
|
}
|
|
1016
1068
|
}
|
|
1017
1069
|
});
|
|
1018
|
-
|
|
1019
|
-
if (
|
|
1020
|
-
|
|
1070
|
+
node.onPointerUp(() => {
|
|
1071
|
+
if (node.isStrokeWithinBounds === true) {
|
|
1072
|
+
node.isStrokeWithinBounds = false;
|
|
1021
1073
|
const drawPadItemEvent = {
|
|
1022
1074
|
type: DrawPadItemEventType.StrokeLeave,
|
|
1023
|
-
target:
|
|
1075
|
+
target: node
|
|
1024
1076
|
};
|
|
1025
|
-
this.raiseDrawPadItemEvent(
|
|
1077
|
+
this.raiseDrawPadItemEvent(node, drawPadItemEvent);
|
|
1026
1078
|
}
|
|
1027
1079
|
});
|
|
1028
|
-
this.addChild(
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
return
|
|
1080
|
+
this.addChild(node);
|
|
1081
|
+
node.zPosition = -1;
|
|
1082
|
+
node.position.x = node.position.x - this.size.width / 2;
|
|
1083
|
+
node.position.y = node.position.y - this.size.height / 2;
|
|
1084
|
+
node.isUserInteractionEnabled = true;
|
|
1085
|
+
return node;
|
|
1034
1086
|
}
|
|
1035
1087
|
/**
|
|
1036
1088
|
* Takes a screenshot of the DrawPad.
|
|
@@ -1197,8 +1249,12 @@ class VirtualKeyboard extends Composite {
|
|
|
1197
1249
|
*/
|
|
1198
1250
|
constructor(options) {
|
|
1199
1251
|
super(options);
|
|
1252
|
+
this.compositeType = "VirtualKeyboard";
|
|
1200
1253
|
this.rowsConfiguration = new Array();
|
|
1201
1254
|
this.shiftActivated = false;
|
|
1255
|
+
this.keyShapes = new Array();
|
|
1256
|
+
// VirtualKeyboard defaults to being user interactive.
|
|
1257
|
+
this._isUserInteractionEnabled = true;
|
|
1202
1258
|
this.size = options.size;
|
|
1203
1259
|
this.position = options.position ?? { x: 0, y: 0 };
|
|
1204
1260
|
this.keyboardHorizontalPaddingPercent = options.keyboardHorizontalPaddingPercent ?? 0.02;
|
|
@@ -1380,6 +1436,7 @@ class VirtualKeyboard extends Composite {
|
|
|
1380
1436
|
const keyboardHorizontalPadding = (this.keyboardHorizontalPaddingPercent ?? 0.02) * this.size.width;
|
|
1381
1437
|
const keyBoxHeight = (this.size.height - 2 * keyboardVerticalPadding) / rows.length;
|
|
1382
1438
|
const keyBoxWidth = (this.size.width - 2 * keyboardHorizontalPadding) / this.keysPerRow;
|
|
1439
|
+
this.keyShapes = [];
|
|
1383
1440
|
for (let r = 0; r < rows.length; r++) {
|
|
1384
1441
|
const row = rows[r];
|
|
1385
1442
|
const rowSumKeyWidths = row.reduce(
|
|
@@ -1418,9 +1475,10 @@ class VirtualKeyboard extends Composite {
|
|
|
1418
1475
|
cornerRadius: 4,
|
|
1419
1476
|
fillColor: this.keyColor,
|
|
1420
1477
|
lineWidth: 0,
|
|
1421
|
-
isUserInteractionEnabled:
|
|
1478
|
+
isUserInteractionEnabled: this.isUserInteractionEnabled
|
|
1422
1479
|
});
|
|
1423
1480
|
keyBox.addChild(keyShape);
|
|
1481
|
+
this.keyShapes.push(keyShape);
|
|
1424
1482
|
keyShape.onTapUp((tapEvent) => {
|
|
1425
1483
|
let keyAsString = "";
|
|
1426
1484
|
if (!key.isShift) {
|
|
@@ -1456,10 +1514,10 @@ class VirtualKeyboard extends Composite {
|
|
|
1456
1514
|
letterCircle.hidden = true;
|
|
1457
1515
|
if (this.eventListeners.length > 0) {
|
|
1458
1516
|
this.eventListeners.filter(
|
|
1459
|
-
(listener) => listener.type ===
|
|
1517
|
+
(listener) => listener.type === M2EventType.CompositeCustom && listener.compositeType === "VirtualKeyboardKeyUp"
|
|
1460
1518
|
).forEach((listener) => {
|
|
1461
1519
|
const virtualKeyboardEvent = {
|
|
1462
|
-
type:
|
|
1520
|
+
type: M2EventType.CompositeCustom,
|
|
1463
1521
|
target: this,
|
|
1464
1522
|
handled: false,
|
|
1465
1523
|
key: keyAsString,
|
|
@@ -1524,10 +1582,10 @@ class VirtualKeyboard extends Composite {
|
|
|
1524
1582
|
}
|
|
1525
1583
|
if (this.eventListeners.length > 0) {
|
|
1526
1584
|
this.eventListeners.filter(
|
|
1527
|
-
(listener) => listener.type ===
|
|
1585
|
+
(listener) => listener.type === M2EventType.CompositeCustom && listener.compositeType === "VirtualKeyboardKeyDown"
|
|
1528
1586
|
).forEach((listener) => {
|
|
1529
1587
|
const virtualKeyboardEvent = {
|
|
1530
|
-
type:
|
|
1588
|
+
type: M2EventType.CompositeCustom,
|
|
1531
1589
|
target: this,
|
|
1532
1590
|
handled: false,
|
|
1533
1591
|
key: keyAsString,
|
|
@@ -1581,54 +1639,55 @@ class VirtualKeyboard extends Composite {
|
|
|
1581
1639
|
* Executes a callback when the user presses down on a key.
|
|
1582
1640
|
*
|
|
1583
1641
|
* @param callback - function to execute
|
|
1584
|
-
* @param
|
|
1585
|
-
* any existing callbacks of the same event type on this entity? Usually
|
|
1586
|
-
* there should be only one callback defined, instead of chaining multiple
|
|
1587
|
-
* ones. It is strongly recommended not to change this, unless you have a
|
|
1588
|
-
* special use case. Default is true.
|
|
1642
|
+
* @param options
|
|
1589
1643
|
*/
|
|
1590
|
-
onKeyDown(callback,
|
|
1644
|
+
onKeyDown(callback, options) {
|
|
1591
1645
|
const eventListener = {
|
|
1592
|
-
type:
|
|
1646
|
+
type: M2EventType.CompositeCustom,
|
|
1593
1647
|
compositeType: "VirtualKeyboardKeyDown",
|
|
1594
|
-
|
|
1648
|
+
nodeUuid: this.uuid,
|
|
1595
1649
|
callback
|
|
1596
1650
|
};
|
|
1597
|
-
this.addVirtualKeyboardEventListener(
|
|
1598
|
-
replaceExistingCallback,
|
|
1599
|
-
eventListener
|
|
1600
|
-
);
|
|
1651
|
+
this.addVirtualKeyboardEventListener(eventListener, options);
|
|
1601
1652
|
}
|
|
1602
1653
|
/**
|
|
1603
1654
|
* Executes a callback when the user releases a key.
|
|
1604
1655
|
*
|
|
1605
1656
|
* @param callback - function to execute
|
|
1606
|
-
* @param
|
|
1607
|
-
* any existing callbacks of the same event type on this entity? Usually
|
|
1608
|
-
* there should be only one callback defined, instead of chaining multiple
|
|
1609
|
-
* ones. It is strongly recommended not to change this, unless you have a
|
|
1610
|
-
* special use case. Default is true.
|
|
1657
|
+
* @param options
|
|
1611
1658
|
*/
|
|
1612
|
-
onKeyUp(callback,
|
|
1659
|
+
onKeyUp(callback, options) {
|
|
1613
1660
|
const eventListener = {
|
|
1614
|
-
type:
|
|
1661
|
+
type: M2EventType.CompositeCustom,
|
|
1615
1662
|
compositeType: "VirtualKeyboardKeyUp",
|
|
1616
|
-
|
|
1663
|
+
nodeUuid: this.uuid,
|
|
1617
1664
|
callback
|
|
1618
1665
|
};
|
|
1619
|
-
this.addVirtualKeyboardEventListener(
|
|
1620
|
-
replaceExistingCallback,
|
|
1621
|
-
eventListener
|
|
1622
|
-
);
|
|
1666
|
+
this.addVirtualKeyboardEventListener(eventListener, options);
|
|
1623
1667
|
}
|
|
1624
|
-
addVirtualKeyboardEventListener(
|
|
1625
|
-
if (
|
|
1668
|
+
addVirtualKeyboardEventListener(eventListener, options) {
|
|
1669
|
+
if (options?.replaceExisting) {
|
|
1626
1670
|
this.eventListeners = this.eventListeners.filter(
|
|
1627
|
-
(listener) => !(listener.
|
|
1671
|
+
(listener) => !(listener.nodeUuid === eventListener.nodeUuid && listener.type === eventListener.type && listener.compositeType === eventListener.compositeType)
|
|
1628
1672
|
);
|
|
1629
1673
|
}
|
|
1630
1674
|
this.eventListeners.push(eventListener);
|
|
1631
1675
|
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Does the `VirtualKeyboard` respond to user events? Default is true.
|
|
1678
|
+
*/
|
|
1679
|
+
get isUserInteractionEnabled() {
|
|
1680
|
+
return this._isUserInteractionEnabled;
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Does the `VirtualKeyboard` respond to user events? Default is true.
|
|
1684
|
+
*/
|
|
1685
|
+
set isUserInteractionEnabled(isUserInteractionEnabled) {
|
|
1686
|
+
this._isUserInteractionEnabled = isUserInteractionEnabled;
|
|
1687
|
+
this.keyShapes.forEach((keyShape) => {
|
|
1688
|
+
keyShape.isUserInteractionEnabled = isUserInteractionEnabled;
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1632
1691
|
update() {
|
|
1633
1692
|
super.update();
|
|
1634
1693
|
}
|
|
@@ -1642,31 +1701,31 @@ class VirtualKeyboard extends Composite {
|
|
|
1642
1701
|
});
|
|
1643
1702
|
}
|
|
1644
1703
|
duplicate(newName) {
|
|
1645
|
-
throw new Error(
|
|
1704
|
+
throw new Error(`Method not implemented. ${newName}`);
|
|
1646
1705
|
}
|
|
1647
1706
|
}
|
|
1648
1707
|
|
|
1649
|
-
const SCENE_TRANSITION_EASING = Easings.sinusoidalInOut;
|
|
1708
|
+
const SCENE_TRANSITION_EASING$1 = Easings.sinusoidalInOut;
|
|
1650
1709
|
const SCENE_TRANSITION_DURATION = 500;
|
|
1651
1710
|
class Instructions extends Story {
|
|
1652
1711
|
/**
|
|
1653
|
-
*
|
|
1712
|
+
* Creates an array of scenes containing instructions on how to complete the assessment
|
|
1654
1713
|
*
|
|
1655
1714
|
* @param options - {@link InstructionsOptions}
|
|
1656
|
-
* @returns
|
|
1715
|
+
* @returns instruction scenes
|
|
1657
1716
|
*/
|
|
1658
|
-
static
|
|
1717
|
+
static create(options) {
|
|
1659
1718
|
const scenes = new Array();
|
|
1660
1719
|
options.instructionScenes.forEach((s, i) => {
|
|
1661
1720
|
const nextSceneTransition = s.nextSceneTransition ?? options.nextSceneTransition ?? Transition.slide({
|
|
1662
1721
|
direction: TransitionDirection.Left,
|
|
1663
1722
|
duration: SCENE_TRANSITION_DURATION,
|
|
1664
|
-
easing: SCENE_TRANSITION_EASING
|
|
1723
|
+
easing: SCENE_TRANSITION_EASING$1
|
|
1665
1724
|
});
|
|
1666
1725
|
const backSceneTransition = s.backSceneTransition ?? options.backSceneTransition ?? Transition.slide({
|
|
1667
1726
|
direction: TransitionDirection.Right,
|
|
1668
1727
|
duration: SCENE_TRANSITION_DURATION,
|
|
1669
|
-
easing: SCENE_TRANSITION_EASING
|
|
1728
|
+
easing: SCENE_TRANSITION_EASING$1
|
|
1670
1729
|
});
|
|
1671
1730
|
const backButtonText = s.backButtonText ?? options.backButtonText ?? "Back";
|
|
1672
1731
|
const nextButtonText = s.nextButtonText ?? options.nextButtonText ?? "Next";
|
|
@@ -1851,9 +1910,605 @@ class Instructions extends Story {
|
|
|
1851
1910
|
});
|
|
1852
1911
|
return scenes;
|
|
1853
1912
|
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Creates an array of scenes containing instructions on how to complete the assessment
|
|
1915
|
+
*
|
|
1916
|
+
* @deprecated Use {@link Instructions.create} instead (lower case method name "create")
|
|
1917
|
+
*
|
|
1918
|
+
* @param options - {@link InstructionsOptions}
|
|
1919
|
+
* @returns instruction scenes
|
|
1920
|
+
*/
|
|
1921
|
+
static Create(options) {
|
|
1922
|
+
return this.create(options);
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
const SCENE_TRANSITION_EASING = Easings.sinusoidalInOut;
|
|
1927
|
+
const SCENE_TRANSITION_DURATION_MS = 500;
|
|
1928
|
+
class CountdownScene extends Scene {
|
|
1929
|
+
/**
|
|
1930
|
+
* A scene that counts down from a specified number to zero, then transitions to the next scene.
|
|
1931
|
+
*
|
|
1932
|
+
* @param options - {@link CountdownSceneOptions}
|
|
1933
|
+
*/
|
|
1934
|
+
constructor(options) {
|
|
1935
|
+
super(options);
|
|
1936
|
+
if (options?.transitionDurationMilliseconds !== void 0 && options?.transition) {
|
|
1937
|
+
throw new Error(
|
|
1938
|
+
"Both transition and transitionDurationMilliseconds options were provided. Only one should be provided. If using a custom transition, then the duration of that transition must be specified within the custom transition."
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
let timerShape;
|
|
1942
|
+
if (options?.timerShape?.circle === void 0 && options?.timerShape?.rectangle === void 0 || options?.timerShape.circle !== void 0) {
|
|
1943
|
+
timerShape = new Shape({
|
|
1944
|
+
circleOfRadius: options?.timerShape?.circle?.radius ?? 100,
|
|
1945
|
+
layout: {
|
|
1946
|
+
constraints: {
|
|
1947
|
+
topToTopOf: this,
|
|
1948
|
+
bottomToBottomOf: this,
|
|
1949
|
+
startToStartOf: this,
|
|
1950
|
+
endToEndOf: this,
|
|
1951
|
+
verticalBias: options?.timerShape?.verticalBias ?? 0.5
|
|
1952
|
+
}
|
|
1953
|
+
},
|
|
1954
|
+
fillColor: options?.timerShape?.fillColor ?? WebColors.RoyalBlue
|
|
1955
|
+
});
|
|
1956
|
+
this.addChild(timerShape);
|
|
1957
|
+
} else if (options?.timerShape.rectangle !== void 0) {
|
|
1958
|
+
timerShape = new Shape({
|
|
1959
|
+
rect: {
|
|
1960
|
+
width: options?.timerShape?.rectangle?.width ?? 200,
|
|
1961
|
+
height: options?.timerShape?.rectangle?.height ?? 200
|
|
1962
|
+
},
|
|
1963
|
+
cornerRadius: options?.timerShape?.rectangle?.cornerRadius,
|
|
1964
|
+
layout: {
|
|
1965
|
+
constraints: {
|
|
1966
|
+
topToTopOf: this,
|
|
1967
|
+
bottomToBottomOf: this,
|
|
1968
|
+
startToStartOf: this,
|
|
1969
|
+
endToEndOf: this,
|
|
1970
|
+
verticalBias: options?.timerShape?.verticalBias ?? 0.5
|
|
1971
|
+
}
|
|
1972
|
+
},
|
|
1973
|
+
fillColor: options?.timerShape?.fillColor ?? WebColors.RoyalBlue
|
|
1974
|
+
});
|
|
1975
|
+
this.addChild(timerShape);
|
|
1976
|
+
} else {
|
|
1977
|
+
throw new Error("Invalid timer shape options.");
|
|
1978
|
+
}
|
|
1979
|
+
const timerInitialNumber = Math.floor(options.milliseconds / 1e3);
|
|
1980
|
+
const timerNumberLabel = new Label({
|
|
1981
|
+
// Number text will be set in onSetup()
|
|
1982
|
+
text: "",
|
|
1983
|
+
fontSize: options?.timerNumbersFontSize ?? 50,
|
|
1984
|
+
fontName: options?.timerNumbersFontName,
|
|
1985
|
+
fontColor: options?.timerNumbersFontColor ?? WebColors.White
|
|
1986
|
+
});
|
|
1987
|
+
timerShape.addChild(timerNumberLabel);
|
|
1988
|
+
const textLabel = new Label({
|
|
1989
|
+
text: options?.text ?? "GET READY",
|
|
1990
|
+
fontSize: options?.textFontSize ?? 50,
|
|
1991
|
+
fontName: options?.textFontName,
|
|
1992
|
+
fontColor: options?.textFontColor,
|
|
1993
|
+
layout: {
|
|
1994
|
+
marginTop: options?.textMarginTop ?? 32,
|
|
1995
|
+
constraints: {
|
|
1996
|
+
topToBottomOf: timerShape,
|
|
1997
|
+
startToStartOf: this,
|
|
1998
|
+
endToEndOf: this
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
});
|
|
2002
|
+
this.addChild(textLabel);
|
|
2003
|
+
const countdownSequence = new Array();
|
|
2004
|
+
for (let i = timerInitialNumber - 1; i > 0; i--) {
|
|
2005
|
+
countdownSequence.push(Action.wait({ duration: 1e3 }));
|
|
2006
|
+
countdownSequence.push(
|
|
2007
|
+
Action.custom({
|
|
2008
|
+
callback: () => {
|
|
2009
|
+
timerNumberLabel.text = i.toString();
|
|
2010
|
+
}
|
|
2011
|
+
})
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
countdownSequence.push(Action.wait({ duration: 1e3 }));
|
|
2015
|
+
countdownSequence.push(
|
|
2016
|
+
Action.custom({
|
|
2017
|
+
callback: () => {
|
|
2018
|
+
timerNumberLabel.text = options?.timerZeroString ?? "0";
|
|
2019
|
+
}
|
|
2020
|
+
})
|
|
2021
|
+
);
|
|
2022
|
+
if (options?.zeroDwellMilliseconds !== void 0) {
|
|
2023
|
+
countdownSequence.push(
|
|
2024
|
+
Action.wait({ duration: options.zeroDwellMilliseconds })
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
countdownSequence.push(
|
|
2028
|
+
Action.custom({
|
|
2029
|
+
callback: () => {
|
|
2030
|
+
const game = this.game;
|
|
2031
|
+
const isLastScene = game.scenes.indexOf(this) === game.scenes.length - 1;
|
|
2032
|
+
if (isLastScene) {
|
|
2033
|
+
game.end();
|
|
2034
|
+
}
|
|
2035
|
+
const nextScene = game.scenes[game.scenes.indexOf(this) + 1];
|
|
2036
|
+
game.presentScene(
|
|
2037
|
+
nextScene,
|
|
2038
|
+
options?.transition ?? Transition.slide({
|
|
2039
|
+
direction: TransitionDirection.Left,
|
|
2040
|
+
duration: options?.transitionDurationMilliseconds ?? SCENE_TRANSITION_DURATION_MS,
|
|
2041
|
+
easing: SCENE_TRANSITION_EASING
|
|
2042
|
+
})
|
|
2043
|
+
);
|
|
2044
|
+
}
|
|
2045
|
+
})
|
|
2046
|
+
);
|
|
2047
|
+
this.onSetup(() => {
|
|
2048
|
+
timerNumberLabel.text = timerInitialNumber.toString();
|
|
2049
|
+
});
|
|
2050
|
+
this.onAppear(() => {
|
|
2051
|
+
this.run(Action.sequence(countdownSequence));
|
|
2052
|
+
});
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
class LocalePicker extends Composite {
|
|
2057
|
+
/**
|
|
2058
|
+
* An icon and dialog box for selecting a locale from a list of options.
|
|
2059
|
+
*
|
|
2060
|
+
* @remarks This composite node is composed of a dialog box that appears
|
|
2061
|
+
* when the user taps a globe icon. Typically, the `LocalePicker` will be
|
|
2062
|
+
* added as a free node to the game so that it exists independently of
|
|
2063
|
+
* the game's scenes. The dialog box contains a list of locales that the
|
|
2064
|
+
* user can choose from. By default, this list is populated with the locales
|
|
2065
|
+
* in the game's `Translation` object. When the user selects a locale, the
|
|
2066
|
+
* dialog box disappears and the locale is set as the game's current locale.
|
|
2067
|
+
* The dialog box is automatically sized to fit the number of locale
|
|
2068
|
+
* options.
|
|
2069
|
+
*
|
|
2070
|
+
* @example
|
|
2071
|
+
* let localePicker: LocalePicker;
|
|
2072
|
+
* if (game.getParameter<boolean>("show_locale_picker")) {
|
|
2073
|
+
* localePicker = new LocalePicker();
|
|
2074
|
+
* game.addFreeNode(localePicker);
|
|
2075
|
+
* }
|
|
2076
|
+
*
|
|
2077
|
+
* @param options - {@link LocalePickerOptions}
|
|
2078
|
+
*/
|
|
2079
|
+
constructor(options) {
|
|
2080
|
+
super(options);
|
|
2081
|
+
this.compositeType = "LocalePicker";
|
|
2082
|
+
this.zPosition = Number.MAX_VALUE;
|
|
2083
|
+
this.DEFAULT_FONT_SIZE = 24;
|
|
2084
|
+
this.automaticallyChangeLocale = true;
|
|
2085
|
+
this._localeOptions = new Array();
|
|
2086
|
+
this._backgroundColor = WebColors.White;
|
|
2087
|
+
this._fontSize = this.DEFAULT_FONT_SIZE;
|
|
2088
|
+
this._fontColor = WebColors.Black;
|
|
2089
|
+
this._cornerRadius = 8;
|
|
2090
|
+
this._overlayAlpha = 0.5;
|
|
2091
|
+
this._icon = {
|
|
2092
|
+
// public domain SVG from https://commons.wikimedia.org/wiki/File:Globe_icon.svg
|
|
2093
|
+
svgString: `<svg xmlns="http://www.w3.org/2000/svg" width="420" height="420" stroke="#000" fill="none"><path stroke-width="26" d="M209 15a195 195 0 1 0 2 0z"/><path stroke-width="18" d="M210 15v390m195-195H15M59 90a260 260 0 0 0 302 0m0 240a260 260 0 0 0-302 0M195 20a250 250 0 0 0 0 382m30 0a250 250 0 0 0 0-382"/></svg>`,
|
|
2094
|
+
height: 32,
|
|
2095
|
+
width: 32
|
|
2096
|
+
};
|
|
2097
|
+
this._iconPosition = { x: 32, y: 32 };
|
|
2098
|
+
/**
|
|
2099
|
+
* Wrap displayed locale in double angle quotes if it is the current locale.
|
|
2100
|
+
* Note: Although the code editor will allow us to enter almost any
|
|
2101
|
+
* unicode character, it will not render correctly if the font does
|
|
2102
|
+
* not support the character. Thus, be careful to use characters that
|
|
2103
|
+
* are supported by the font. For example, check a page like
|
|
2104
|
+
* https://www.fontspace.com/roboto-font-f13281 to see which characters
|
|
2105
|
+
* are supported by Roboto Regular, which is often the default font in
|
|
2106
|
+
* m2c2kit. Emoji or checkmarks like ✓ are not in Roboto Regular!
|
|
2107
|
+
*/
|
|
2108
|
+
this.LEFT_SELECTION_INDICATOR = "\xAB";
|
|
2109
|
+
this.RIGHT_SELECTION_INDICATOR = "\xBB";
|
|
2110
|
+
if (!options) {
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
if (options.localeOptions) {
|
|
2114
|
+
this.localeOptions = options.localeOptions;
|
|
2115
|
+
}
|
|
2116
|
+
if (options.backgroundColor) {
|
|
2117
|
+
this.backgroundColor = options.backgroundColor;
|
|
2118
|
+
}
|
|
2119
|
+
if (options.overlayAlpha !== void 0) {
|
|
2120
|
+
this.overlayAlpha = options.overlayAlpha;
|
|
2121
|
+
}
|
|
2122
|
+
if (options.fontSize !== void 0) {
|
|
2123
|
+
this.fontSize = options.fontSize;
|
|
2124
|
+
}
|
|
2125
|
+
if (options.fontColor) {
|
|
2126
|
+
this.fontColor = options.fontColor;
|
|
2127
|
+
}
|
|
2128
|
+
if (options.cornerRadius) {
|
|
2129
|
+
this.cornerRadius = options.cornerRadius;
|
|
2130
|
+
}
|
|
2131
|
+
if (options.currentLocale !== void 0) {
|
|
2132
|
+
this.currentLocale = options.currentLocale;
|
|
2133
|
+
}
|
|
2134
|
+
if (options.icon) {
|
|
2135
|
+
this.icon = options.icon;
|
|
2136
|
+
}
|
|
2137
|
+
if (options.automaticallyChangeLocale !== void 0) {
|
|
2138
|
+
this.automaticallyChangeLocale = options.automaticallyChangeLocale;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
/**
|
|
2142
|
+
* Executes a callback when the user selects a locale.
|
|
2143
|
+
*
|
|
2144
|
+
* @param callback - function to execute
|
|
2145
|
+
* @param options - {@link CallbackOptions}
|
|
2146
|
+
*/
|
|
2147
|
+
onResult(callback, options) {
|
|
2148
|
+
const eventListener = {
|
|
2149
|
+
type: M2EventType.CompositeCustom,
|
|
2150
|
+
compositeType: "LocalePickerResult",
|
|
2151
|
+
nodeUuid: this.uuid,
|
|
2152
|
+
callback
|
|
2153
|
+
};
|
|
2154
|
+
if (options?.replaceExisting) {
|
|
2155
|
+
this.eventListeners = this.eventListeners.filter(
|
|
2156
|
+
(listener) => !(listener.nodeUuid === eventListener.nodeUuid && listener.type === "LocalePickerResult")
|
|
2157
|
+
);
|
|
2158
|
+
}
|
|
2159
|
+
this.eventListeners.push(eventListener);
|
|
2160
|
+
}
|
|
2161
|
+
initialize() {
|
|
2162
|
+
if (this.currentLocale === void 0) {
|
|
2163
|
+
this.currentLocale = this.game.i18n?.locale;
|
|
2164
|
+
}
|
|
2165
|
+
if (this.localeOptions.length === 0) {
|
|
2166
|
+
const locales = Object.keys(this.game.i18n?.translation || {});
|
|
2167
|
+
locales.filter((locale) => locale !== "configuration").forEach((locale) => {
|
|
2168
|
+
this.localeOptions.push({
|
|
2169
|
+
text: this.game.i18n?.translation[locale].localeName || locale,
|
|
2170
|
+
locale,
|
|
2171
|
+
svg: this.game.i18n?.translation[locale].localeSvg
|
|
2172
|
+
});
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
if (this.localeOptions.length === 0) {
|
|
2176
|
+
throw new Error("No locales available for LocalePicker");
|
|
2177
|
+
}
|
|
2178
|
+
this.children.filter((child) => child.name !== "localePickerIcon").forEach((child) => this.removeChild(child));
|
|
2179
|
+
this.game.imageManager.loadImages([
|
|
2180
|
+
{
|
|
2181
|
+
imageName: "__localePickerIcon",
|
|
2182
|
+
svgString: this.icon.svgString,
|
|
2183
|
+
height: this.icon.height,
|
|
2184
|
+
width: this.icon.width
|
|
2185
|
+
}
|
|
2186
|
+
]);
|
|
2187
|
+
if (!this.iconSprite) {
|
|
2188
|
+
this.iconSprite = new Sprite({
|
|
2189
|
+
// name is how we refer to this sprite, as a node.
|
|
2190
|
+
name: "localePickerIcon",
|
|
2191
|
+
// imageName is the loaded image that we can assign to the sprite
|
|
2192
|
+
imageName: "__localePickerIcon",
|
|
2193
|
+
position: this.iconPosition,
|
|
2194
|
+
isUserInteractionEnabled: true
|
|
2195
|
+
});
|
|
2196
|
+
this.addChild(this.iconSprite);
|
|
2197
|
+
this.iconSprite.onTapDown(() => {
|
|
2198
|
+
this.setDialogVisibility(true);
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
const overlay = new Shape({
|
|
2202
|
+
rect: {
|
|
2203
|
+
width: Globals.canvasCssWidth,
|
|
2204
|
+
height: Globals.canvasCssHeight,
|
|
2205
|
+
x: Globals.canvasCssWidth / 2,
|
|
2206
|
+
y: Globals.canvasCssHeight / 2
|
|
2207
|
+
},
|
|
2208
|
+
fillColor: [0, 0, 0, this.overlayAlpha],
|
|
2209
|
+
zPosition: -1,
|
|
2210
|
+
isUserInteractionEnabled: true,
|
|
2211
|
+
hidden: true
|
|
2212
|
+
});
|
|
2213
|
+
overlay.onTapDown((e) => {
|
|
2214
|
+
e.handled = true;
|
|
2215
|
+
if (this.eventListeners.length > 0) {
|
|
2216
|
+
this.eventListeners.filter((listener) => listener.type === "LocalePickerResult").forEach((listener) => {
|
|
2217
|
+
const languagePickerEvent = {
|
|
2218
|
+
type: "LocalePickerResult",
|
|
2219
|
+
target: this,
|
|
2220
|
+
handled: false,
|
|
2221
|
+
result: {
|
|
2222
|
+
locale: void 0
|
|
2223
|
+
}
|
|
2224
|
+
};
|
|
2225
|
+
listener.callback(languagePickerEvent);
|
|
2226
|
+
});
|
|
2227
|
+
}
|
|
2228
|
+
this.setDialogVisibility(false);
|
|
2229
|
+
});
|
|
2230
|
+
this.addChild(overlay);
|
|
2231
|
+
const lineHeight = this.fontSize / this.DEFAULT_FONT_SIZE * 50;
|
|
2232
|
+
const dialogHeight = this.localeOptions.length * lineHeight;
|
|
2233
|
+
const dialogWidth = Globals.canvasCssWidth / 2;
|
|
2234
|
+
const sceneCenter = {
|
|
2235
|
+
x: Globals.canvasCssWidth / 2,
|
|
2236
|
+
y: Globals.canvasCssHeight / 2
|
|
2237
|
+
};
|
|
2238
|
+
const localeDialog = new Shape({
|
|
2239
|
+
rect: {
|
|
2240
|
+
width: dialogWidth,
|
|
2241
|
+
height: dialogHeight,
|
|
2242
|
+
x: sceneCenter.x,
|
|
2243
|
+
y: sceneCenter.y
|
|
2244
|
+
},
|
|
2245
|
+
cornerRadius: this.cornerRadius,
|
|
2246
|
+
fillColor: this.backgroundColor,
|
|
2247
|
+
isUserInteractionEnabled: true,
|
|
2248
|
+
hidden: true
|
|
2249
|
+
});
|
|
2250
|
+
localeDialog.onTapDown((e) => {
|
|
2251
|
+
e.handled = true;
|
|
2252
|
+
});
|
|
2253
|
+
this.addChild(localeDialog);
|
|
2254
|
+
for (let i = 0; i < this.localeOptions.length; i++) {
|
|
2255
|
+
const localeOption = this.localeOptions[i];
|
|
2256
|
+
if (!localeOption.svg) {
|
|
2257
|
+
let labelText = localeOption.text;
|
|
2258
|
+
if (this.currentLocale === localeOption.locale) {
|
|
2259
|
+
labelText = `${this.LEFT_SELECTION_INDICATOR} ${labelText} ${this.RIGHT_SELECTION_INDICATOR}`;
|
|
2260
|
+
}
|
|
2261
|
+
const text = new Label({
|
|
2262
|
+
text: labelText,
|
|
2263
|
+
fontSize: this.fontSize,
|
|
2264
|
+
fontColor: this.fontColor,
|
|
2265
|
+
position: {
|
|
2266
|
+
x: sceneCenter.x,
|
|
2267
|
+
y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
|
|
2268
|
+
},
|
|
2269
|
+
isUserInteractionEnabled: true,
|
|
2270
|
+
zPosition: 1,
|
|
2271
|
+
hidden: true,
|
|
2272
|
+
// do not localize the text of each language option
|
|
2273
|
+
localize: false
|
|
2274
|
+
});
|
|
2275
|
+
text.onTapDown((e) => {
|
|
2276
|
+
this.handleLocaleSelection(e, localeOption);
|
|
2277
|
+
});
|
|
2278
|
+
this.addChild(text);
|
|
2279
|
+
} else {
|
|
2280
|
+
this.game.imageManager.loadImages([
|
|
2281
|
+
{
|
|
2282
|
+
imageName: localeOption.text,
|
|
2283
|
+
svgString: localeOption.svg.svgString,
|
|
2284
|
+
height: localeOption.svg.height,
|
|
2285
|
+
width: localeOption.svg.width
|
|
2286
|
+
}
|
|
2287
|
+
]);
|
|
2288
|
+
const localeSprite = new Sprite({
|
|
2289
|
+
imageName: localeOption.text,
|
|
2290
|
+
position: {
|
|
2291
|
+
x: sceneCenter.x,
|
|
2292
|
+
y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
|
|
2293
|
+
},
|
|
2294
|
+
isUserInteractionEnabled: true,
|
|
2295
|
+
zPosition: 1,
|
|
2296
|
+
hidden: true
|
|
2297
|
+
});
|
|
2298
|
+
this.addChild(localeSprite);
|
|
2299
|
+
if (this.currentLocale === localeOption.locale) {
|
|
2300
|
+
const leftSelectionIndicator = new Label({
|
|
2301
|
+
text: this.LEFT_SELECTION_INDICATOR,
|
|
2302
|
+
fontSize: this.fontSize,
|
|
2303
|
+
fontColor: this.fontColor,
|
|
2304
|
+
position: {
|
|
2305
|
+
x: sceneCenter.x - localeOption.svg.width / 2,
|
|
2306
|
+
y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
|
|
2307
|
+
},
|
|
2308
|
+
hidden: true,
|
|
2309
|
+
// do not localize the left selection indicator
|
|
2310
|
+
localize: false
|
|
2311
|
+
});
|
|
2312
|
+
this.addChild(leftSelectionIndicator);
|
|
2313
|
+
const rightSelectionIndicator = new Label({
|
|
2314
|
+
text: this.RIGHT_SELECTION_INDICATOR,
|
|
2315
|
+
fontSize: this._fontSize,
|
|
2316
|
+
fontColor: this.fontColor,
|
|
2317
|
+
position: {
|
|
2318
|
+
x: sceneCenter.x + localeOption.svg.width / 2,
|
|
2319
|
+
y: sceneCenter.y + i * lineHeight - dialogHeight / 2 + lineHeight / 2
|
|
2320
|
+
},
|
|
2321
|
+
hidden: true,
|
|
2322
|
+
// do not localize the left selection indicator
|
|
2323
|
+
localize: false
|
|
2324
|
+
});
|
|
2325
|
+
this.addChild(rightSelectionIndicator);
|
|
2326
|
+
}
|
|
2327
|
+
localeSprite.onTapDown((e) => {
|
|
2328
|
+
this.handleLocaleSelection(e, localeOption);
|
|
2329
|
+
});
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
this.needsInitialization = false;
|
|
2333
|
+
}
|
|
2334
|
+
handleLocaleSelection(e, localeOption) {
|
|
2335
|
+
e.handled = true;
|
|
2336
|
+
if (this.eventListeners.length > 0) {
|
|
2337
|
+
this.eventListeners.filter(
|
|
2338
|
+
(listener) => listener.type === M2EventType.CompositeCustom && listener.compositeType === "LocalePickerResult" && listener.nodeUuid == this.uuid
|
|
2339
|
+
).forEach((listener) => {
|
|
2340
|
+
const languagePickerEvent = {
|
|
2341
|
+
type: M2EventType.CompositeCustom,
|
|
2342
|
+
compositeType: "LocalePickerResult",
|
|
2343
|
+
target: this,
|
|
2344
|
+
handled: false,
|
|
2345
|
+
result: {
|
|
2346
|
+
locale: localeOption.locale
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
listener.callback(languagePickerEvent);
|
|
2350
|
+
});
|
|
2351
|
+
}
|
|
2352
|
+
this.setDialogVisibility(false);
|
|
2353
|
+
if (this.automaticallyChangeLocale) {
|
|
2354
|
+
this.game.i18n?.switchToLocale(localeOption.locale);
|
|
2355
|
+
this.currentLocale = localeOption.locale;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
setDialogVisibility(visible) {
|
|
2359
|
+
this.children.filter((child) => child.name !== "localePickerIcon").forEach((child) => {
|
|
2360
|
+
child.hidden = !visible;
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
get backgroundColor() {
|
|
2364
|
+
return this._backgroundColor;
|
|
2365
|
+
}
|
|
2366
|
+
set backgroundColor(backgroundColor) {
|
|
2367
|
+
this._backgroundColor = backgroundColor;
|
|
2368
|
+
this.needsInitialization = true;
|
|
2369
|
+
}
|
|
2370
|
+
get fontSize() {
|
|
2371
|
+
return this._fontSize;
|
|
2372
|
+
}
|
|
2373
|
+
set fontSize(fontSize) {
|
|
2374
|
+
this._fontSize = fontSize;
|
|
2375
|
+
this.needsInitialization = true;
|
|
2376
|
+
}
|
|
2377
|
+
get fontColor() {
|
|
2378
|
+
return this._fontColor;
|
|
2379
|
+
}
|
|
2380
|
+
set fontColor(fontColor) {
|
|
2381
|
+
this._fontColor = fontColor;
|
|
2382
|
+
this.needsInitialization = true;
|
|
2383
|
+
}
|
|
2384
|
+
get cornerRadius() {
|
|
2385
|
+
return this._cornerRadius;
|
|
2386
|
+
}
|
|
2387
|
+
set cornerRadius(cornerRadius) {
|
|
2388
|
+
this._cornerRadius = cornerRadius;
|
|
2389
|
+
this.needsInitialization = true;
|
|
2390
|
+
}
|
|
2391
|
+
get overlayAlpha() {
|
|
2392
|
+
return this._overlayAlpha;
|
|
2393
|
+
}
|
|
2394
|
+
set overlayAlpha(alpha) {
|
|
2395
|
+
this._overlayAlpha = alpha;
|
|
2396
|
+
this.needsInitialization = true;
|
|
2397
|
+
}
|
|
2398
|
+
get icon() {
|
|
2399
|
+
const localePicker = this;
|
|
2400
|
+
return {
|
|
2401
|
+
get svgString() {
|
|
2402
|
+
return localePicker._icon.svgString;
|
|
2403
|
+
},
|
|
2404
|
+
set svgString(svgString) {
|
|
2405
|
+
localePicker._icon.svgString = svgString;
|
|
2406
|
+
localePicker.needsInitialization = true;
|
|
2407
|
+
},
|
|
2408
|
+
get imageName() {
|
|
2409
|
+
return localePicker._icon.imageName;
|
|
2410
|
+
},
|
|
2411
|
+
set imageName(imageName) {
|
|
2412
|
+
localePicker._icon.imageName = imageName;
|
|
2413
|
+
localePicker.needsInitialization = true;
|
|
2414
|
+
},
|
|
2415
|
+
get height() {
|
|
2416
|
+
return localePicker._icon.height;
|
|
2417
|
+
},
|
|
2418
|
+
set height(height) {
|
|
2419
|
+
localePicker._icon.height = height;
|
|
2420
|
+
localePicker.needsInitialization = true;
|
|
2421
|
+
},
|
|
2422
|
+
get width() {
|
|
2423
|
+
return localePicker._icon.width;
|
|
2424
|
+
},
|
|
2425
|
+
set width(width) {
|
|
2426
|
+
localePicker._icon.width = width;
|
|
2427
|
+
localePicker.needsInitialization = true;
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
}
|
|
2431
|
+
set icon(icon) {
|
|
2432
|
+
this._icon = icon;
|
|
2433
|
+
this.needsInitialization = true;
|
|
2434
|
+
}
|
|
2435
|
+
get iconPosition() {
|
|
2436
|
+
const localePicker = this;
|
|
2437
|
+
return {
|
|
2438
|
+
get x() {
|
|
2439
|
+
return localePicker._iconPosition.x;
|
|
2440
|
+
},
|
|
2441
|
+
set x(x) {
|
|
2442
|
+
localePicker._iconPosition.x = x;
|
|
2443
|
+
if (localePicker.iconSprite) {
|
|
2444
|
+
localePicker.iconSprite.position = localePicker._iconPosition;
|
|
2445
|
+
}
|
|
2446
|
+
localePicker.needsInitialization = true;
|
|
2447
|
+
},
|
|
2448
|
+
get y() {
|
|
2449
|
+
return localePicker._iconPosition.y;
|
|
2450
|
+
},
|
|
2451
|
+
set y(y) {
|
|
2452
|
+
localePicker._iconPosition.y = y;
|
|
2453
|
+
if (localePicker.iconSprite) {
|
|
2454
|
+
localePicker.iconSprite.position = localePicker._iconPosition;
|
|
2455
|
+
}
|
|
2456
|
+
localePicker.needsInitialization = true;
|
|
2457
|
+
}
|
|
2458
|
+
};
|
|
2459
|
+
}
|
|
2460
|
+
set iconPosition(position) {
|
|
2461
|
+
this._iconPosition = position;
|
|
2462
|
+
if (this.iconSprite) {
|
|
2463
|
+
this.iconSprite.position = position;
|
|
2464
|
+
}
|
|
2465
|
+
this.needsInitialization = true;
|
|
2466
|
+
}
|
|
2467
|
+
get localeOptions() {
|
|
2468
|
+
return this._localeOptions;
|
|
2469
|
+
}
|
|
2470
|
+
set localeOptions(options) {
|
|
2471
|
+
this._localeOptions = options;
|
|
2472
|
+
this.needsInitialization = true;
|
|
2473
|
+
}
|
|
2474
|
+
get currentLocale() {
|
|
2475
|
+
return this._currentLocale;
|
|
2476
|
+
}
|
|
2477
|
+
set currentLocale(locale) {
|
|
2478
|
+
if (locale === this.currentLocale) {
|
|
2479
|
+
return;
|
|
2480
|
+
}
|
|
2481
|
+
this._currentLocale = locale;
|
|
2482
|
+
this.needsInitialization = true;
|
|
2483
|
+
}
|
|
2484
|
+
update() {
|
|
2485
|
+
super.update();
|
|
2486
|
+
}
|
|
2487
|
+
draw(canvas) {
|
|
2488
|
+
super.drawChildren(canvas);
|
|
2489
|
+
}
|
|
2490
|
+
warmup(canvas) {
|
|
2491
|
+
this.initialize();
|
|
2492
|
+
this.children.filter((child) => child.isDrawable).forEach((child) => {
|
|
2493
|
+
child.warmup(canvas);
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2496
|
+
/**
|
|
2497
|
+
* Duplicates a node using deep copy.
|
|
2498
|
+
*
|
|
2499
|
+
* @remarks This is a deep recursive clone (node and children).
|
|
2500
|
+
* The uuid property of all duplicated nodes will be newly created,
|
|
2501
|
+
* because uuid must be unique.
|
|
2502
|
+
*
|
|
2503
|
+
* @param newName - optional name of the new, duplicated node. If not
|
|
2504
|
+
* provided, name will be the new uuid
|
|
2505
|
+
*/
|
|
2506
|
+
duplicate(newName) {
|
|
2507
|
+
throw new Error(`duplicate not implemented. ${newName}`);
|
|
2508
|
+
}
|
|
1854
2509
|
}
|
|
1855
2510
|
|
|
1856
|
-
console.log("\u26AA @m2c2kit/addons version 0.3.
|
|
2511
|
+
console.log("\u26AA @m2c2kit/addons version 0.3.15 (b3a70752)");
|
|
1857
2512
|
|
|
1858
|
-
export { Button, Dialog, DialogResult, DrawPad, DrawPadEventType, DrawPadItemEventType, Grid, Instructions, VirtualKeyboard };
|
|
2513
|
+
export { Button, CountdownScene, Dialog, DialogResult, DrawPad, DrawPadEventType, DrawPadItemEventType, Grid, Instructions, LocalePicker, VirtualKeyboard };
|
|
1859
2514
|
//# sourceMappingURL=index.js.map
|