swing-ui 0.1.25 → 1.0.3

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.
Files changed (116) hide show
  1. package/LICENSE +5 -4
  2. package/README.md +144 -7
  3. package/SwingUI.js +2611 -0
  4. package/classes/Arc.js +185 -0
  5. package/classes/Button.js +55 -0
  6. package/classes/CheckBox.js +33 -0
  7. package/classes/CheckBoxMenuItem.js +24 -0
  8. package/classes/ComboBox.js +329 -0
  9. package/classes/ComponentContainer.js +828 -0
  10. package/classes/ContextMenu.js +141 -0
  11. package/classes/DocumentEditor.js +168 -0
  12. package/classes/Ellipse.js +76 -0
  13. package/classes/InternalWindow.js +397 -0
  14. package/classes/InternalWindowContainer.js +93 -0
  15. package/classes/Label.js +52 -0
  16. package/classes/Line.js +105 -0
  17. package/classes/List.js +452 -0
  18. package/classes/MediaPlayer.js +504 -0
  19. package/classes/Menu.js +74 -0
  20. package/classes/MenuBar.js +56 -0
  21. package/classes/MenuItem.js +92 -0
  22. package/classes/Panel.js +50 -0
  23. package/classes/PasswordField.js +47 -0
  24. package/classes/Polygon.js +236 -0
  25. package/classes/Polyline.js +240 -0
  26. package/classes/ProgressBar.js +62 -0
  27. package/classes/RadioButton.js +110 -0
  28. package/classes/RadioButtonMenuItem.js +106 -0
  29. package/classes/Rectangle.js +199 -0
  30. package/classes/ScrollPane.js +53 -0
  31. package/classes/ScrollableComponent.js +141 -0
  32. package/classes/Separator.js +24 -0
  33. package/classes/Shape.js +584 -0
  34. package/classes/ShapeContainer.js +231 -0
  35. package/classes/Slider.js +172 -0
  36. package/classes/Spinner.js +172 -0
  37. package/classes/SplitView.js +82 -0
  38. package/classes/Stroke.js +115 -0
  39. package/classes/Table.js +982 -0
  40. package/classes/Tabs.js +228 -0
  41. package/classes/TextArea.js +125 -0
  42. package/classes/TextComponent.js +207 -0
  43. package/classes/TextField.js +128 -0
  44. package/classes/TextShape.js +123 -0
  45. package/classes/ToggleButton.js +147 -0
  46. package/classes/ToolBar.js +120 -0
  47. package/classes/TrayIcon.js +54 -0
  48. package/classes/Tree.js +569 -0
  49. package/classes/UIComponent.js +2154 -0
  50. package/classes/Window.js +609 -0
  51. package/enums.js +298 -0
  52. package/fonts.conf +32 -0
  53. package/functions.js +254 -0
  54. package/icon.png +0 -0
  55. package/index.js +109 -4
  56. package/jar/gui-11.jar +0 -0
  57. package/jar/lib/flatlaf.jar +0 -0
  58. package/jar/lib/jna.jar +0 -0
  59. package/package.json +58 -33
  60. package/typedefs.js +973 -0
  61. package/types/SwingUI.d.ts +727 -0
  62. package/types/classes/Arc.d.ts +111 -0
  63. package/types/classes/Button.d.ts +38 -0
  64. package/types/classes/CheckBox.d.ts +22 -0
  65. package/types/classes/CheckBoxMenuItem.d.ts +18 -0
  66. package/types/classes/ComboBox.d.ts +172 -0
  67. package/types/classes/ComponentContainer.d.ts +131 -0
  68. package/types/classes/ContextMenu.d.ts +74 -0
  69. package/types/classes/DocumentEditor.d.ts +95 -0
  70. package/types/classes/Ellipse.d.ts +34 -0
  71. package/types/classes/InternalWindow.d.ts +163 -0
  72. package/types/classes/InternalWindowContainer.d.ts +36 -0
  73. package/types/classes/Label.d.ts +33 -0
  74. package/types/classes/Line.d.ts +48 -0
  75. package/types/classes/List.d.ts +223 -0
  76. package/types/classes/MediaPlayer.d.ts +381 -0
  77. package/types/classes/Menu.d.ts +43 -0
  78. package/types/classes/MenuBar.d.ts +26 -0
  79. package/types/classes/MenuItem.d.ts +57 -0
  80. package/types/classes/Panel.d.ts +28 -0
  81. package/types/classes/PasswordField.d.ts +22 -0
  82. package/types/classes/Polygon.d.ts +93 -0
  83. package/types/classes/Polyline.d.ts +96 -0
  84. package/types/classes/ProgressBar.d.ts +36 -0
  85. package/types/classes/RadioButton.d.ts +70 -0
  86. package/types/classes/RadioButtonMenuItem.d.ts +71 -0
  87. package/types/classes/Rectangle.d.ts +82 -0
  88. package/types/classes/ScrollPane.d.ts +23 -0
  89. package/types/classes/ScrollableComponent.d.ts +81 -0
  90. package/types/classes/Separator.d.ts +14 -0
  91. package/types/classes/Shape.d.ts +223 -0
  92. package/types/classes/ShapeContainer.d.ts +92 -0
  93. package/types/classes/Slider.d.ts +107 -0
  94. package/types/classes/Spinner.d.ts +129 -0
  95. package/types/classes/SplitView.d.ts +47 -0
  96. package/types/classes/Stroke.d.ts +99 -0
  97. package/types/classes/Table.d.ts +652 -0
  98. package/types/classes/Tabs.d.ts +118 -0
  99. package/types/classes/TextArea.d.ts +45 -0
  100. package/types/classes/TextComponent.d.ts +136 -0
  101. package/types/classes/TextField.d.ts +94 -0
  102. package/types/classes/TextShape.d.ts +73 -0
  103. package/types/classes/ToggleButton.d.ts +84 -0
  104. package/types/classes/ToolBar.d.ts +82 -0
  105. package/types/classes/TrayIcon.d.ts +43 -0
  106. package/types/classes/Tree.d.ts +354 -0
  107. package/types/classes/UIComponent.d.ts +966 -0
  108. package/types/classes/Window.d.ts +334 -0
  109. package/types/enums.d.ts +437 -0
  110. package/types/functions.d.ts +109 -0
  111. package/types/index.d.ts +188 -0
  112. package/types/typedefs.d.ts +1626 -0
  113. package/ui-process-manager.js +317 -0
  114. package/video-devices-linux.sh +72 -0
  115. package/video-devices-mac.sh +33 -0
  116. package/CHANGES.md +0 -3
@@ -0,0 +1,2154 @@
1
+ import {ShortcutScope} from "swing-ui/enums.js";
2
+
3
+ /**
4
+ * Base component class that other UI components extend. Not to be instantiated by itself.
5
+ * Most methods in this class are used by other classes that extend this class.
6
+ * @class
7
+ * @global
8
+ */
9
+ class UIComponent
10
+ {
11
+ #id = null; // keep it private
12
+ #token = null;
13
+ parent_id = null;
14
+ #context_menu = null;
15
+ #prop = {
16
+ class: null, // instantiated class prototype
17
+ type: "UIComponent",
18
+ leaf: true, // true if the class is not extended by another class
19
+ visible: !(this instanceof Window), // true, unless it's a Window
20
+ size: {},
21
+ location: {x: 0, y: 0},
22
+ dragAction: DragDropAction.None,
23
+ dropAction: DragDropAction.None,
24
+ container: null // default NULL until container() getter is called.
25
+ };
26
+ callback = {
27
+ "onError": [e => ui.callback.onError.call(this, e)], // Default error event callback. Can be changed by .onError(callback)
28
+ "onRectangleUpdate": [e =>
29
+ {
30
+ if(e.type === "resize")
31
+ {
32
+ this.#prop.size.width = e.width ?? this.#prop.size.width;
33
+ this.#prop.size.height = e.height ?? this.#prop.size.height;
34
+ }
35
+ if(e.type === "move")
36
+ {
37
+ this.#prop.location.x = e.x ?? this.#prop.location.x;
38
+ this.#prop.location.y = e.y ?? this.#prop.location.y;
39
+ }
40
+ }]
41
+ };
42
+
43
+ static #controls_id = 0;
44
+ static #_freed_ids = [];
45
+ static #_components = []; // all components
46
+
47
+ /**
48
+ * This is used to be notified when the component loses all references to it, to notify SwingUI to destroy the component for garbage collection
49
+ * @private
50
+ */
51
+ static #refRegistry = new FinalizationRegistry(id => { UIComponent.destroy(id); });
52
+
53
+ /**
54
+ * Makes one class implements methods, setters, and getters of another class.
55
+ * @param toClass
56
+ * @param fromClass
57
+ * @throws {Error} When one or more parameters are `null`.
58
+ * @internal
59
+ * @ignore
60
+ */
61
+ static implementClass(toClass, fromClass)
62
+ {
63
+ if(toClass == null || fromClass == null) throw new Error(`Invalid class parameters provided to implementClass(toClass, fromClass)`);
64
+
65
+ Object.getOwnPropertyNames(fromClass.prototype).forEach(key =>
66
+ {
67
+ if(key !== 'constructor')
68
+ {
69
+ const desc = Object.getOwnPropertyDescriptor(fromClass.prototype, key);
70
+ desc.configurable = false;
71
+ if(typeof desc.value === "function") desc.writable = false;
72
+ Object.defineProperty(toClass.prototype, key, desc);
73
+ }
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Removes references to UI component with provided ID. Make sure to destroy all other references to the component to truly destroy it to free RAM.
79
+ *
80
+ * @internal
81
+ * @ignore
82
+ * @private
83
+ * @param {number} id - ID of a component to be destroyed
84
+ * @return {void} Doesn't return a value
85
+ */
86
+ static destroy(id)
87
+ {
88
+ // makes sure that component is garbage collected and new one is not created with same ID yet before this one is not garbage collected
89
+ // this._components[id] should have a WeakReference object, but deref() should be undefined.
90
+ if(UIComponent.#_components[id] === undefined) return;
91
+
92
+ const component = UIComponent.#_components[id].deref();
93
+ if(component !== undefined) component.emit("onDispose", {type: "dispose"});
94
+
95
+ // if this was initiated by FinalizationRegistry for GC, then remove weak reference
96
+ UIComponent.#_components[id] = undefined;
97
+ delete UIComponent.#_components[id];
98
+
99
+ // send "delete" command to GUI
100
+ ui.sendToGUI({control: id, delete: true});
101
+
102
+ // recycle component's ID at a delay, to prevent events from destroyed component reach a new component with recycled ID
103
+ setTimeout(() => {UIComponent.#_freed_ids.push(id);}, 5000);
104
+ }
105
+
106
+ /**
107
+ * Get UI component by ID.
108
+ *
109
+ * @param {number} id - ID of requested component
110
+ * @return {any|undefined} Returns UI component or `undefined` if there is no valid component with specified ID.
111
+ */
112
+ static get(id)
113
+ {
114
+ return UIComponent.#_components[id]?.deref();
115
+ }
116
+
117
+ /**
118
+ * Returns last visible non-PopUp Window or null if no Window component is not found.
119
+ *
120
+ * @internal
121
+ * @static
122
+ * @readonly
123
+ * @returns {Window|null} Returns Window component or null.
124
+ */
125
+ static getVisibleWindow()
126
+ {
127
+ const _ids = Object.keys(UIComponent.#_components);
128
+
129
+ for(let i = _ids.length - 1; i >= 0; i--)
130
+ {
131
+ const c = UIComponent.#_components[_ids[i]].deref();
132
+ if(c instanceof Window && c.visible() && c.type !== Window.Type.Popup) return c;
133
+ }
134
+
135
+ return null;
136
+ }
137
+
138
+ /**
139
+ * Not to be instantiated by itself. This call is extended dby other UI components.
140
+ *
141
+ * @param {string} type - Component type.
142
+ * @param {boolean} leaf - `true` if the UI component was not extended by a custom class.
143
+ * @param {...any} [data] - Additional component init data (used by some UI components).
144
+ * @internal
145
+ * @ignore
146
+ */
147
+ constructor(type, leaf, ...data)
148
+ {
149
+ this.#prop.class = new.target;
150
+ this.#prop.type = type;
151
+ this.#prop.leaf = leaf;
152
+
153
+ if(this.#prop.class === UIComponent) throw new Error(`Class UIComponent cannot not to be instantiated. It must be extended by one of swing-ui components.`);
154
+
155
+ // reuse freed IDs or create a new one
156
+ this.#id = (UIComponent.#_freed_ids.length > 0) ? UIComponent.#_freed_ids.shift() : ++UIComponent.#controls_id;
157
+
158
+ // register it in array of components
159
+ UIComponent.#_components[this.#id] = new WeakRef(this);
160
+
161
+ // Create a token to be able to unregister this component when it's manually destroyed
162
+ this.#token = {id: this.#id};
163
+
164
+ // register this component for deletion when all references to it are lost (garbage collection).
165
+ UIComponent.#refRegistry.register(this, this.#id, this.#token); // token must be a Symbol, Number doesn't work.
166
+
167
+ // tell GUI about the new component
168
+ let msg = {control: this.#id, create: this.#prop.type};
169
+ if(data.length > 0) msg.data = data[0];
170
+ ui.sendToGUI(msg);
171
+ }
172
+
173
+ /**
174
+ * Locks methods from being overwritten or deleted.
175
+ * @param {Function} prototype_class
176
+ * @ignore
177
+ * @internal
178
+ */
179
+ finalize(..._classes)
180
+ {
181
+ _classes.push(UIComponent); // UIComponent must be last in array
182
+
183
+ for(const c of _classes)
184
+ {
185
+ Object.getOwnPropertyNames(c.prototype).forEach(key=>
186
+ {
187
+ if(key === "constructor" || key === "finalize" || key === "isFinalized") return;
188
+ if(this.isFinalized(key)) return; // skip already locked method/setter/getter
189
+
190
+ const desc = Object.getOwnPropertyDescriptor(c.prototype, key);
191
+
192
+ if(typeof desc.value === "function") // lock function
193
+ {
194
+
195
+ Object.defineProperty(this, key, {
196
+ value: desc.value.bind(this),
197
+ writable: false,
198
+ configurable: false,
199
+ enumerable: desc.enumerable
200
+ });
201
+ }
202
+ else if((desc.get || desc.set) && !this.isFinalized(key)) // lock getter and setter
203
+ {
204
+ Object.defineProperty(this, key, {
205
+ get: desc.get?.bind(this),
206
+ set: desc.set?.bind(this),
207
+ configurable: false,
208
+ enumerable: desc.enumerable
209
+ });
210
+ }
211
+ });
212
+ }
213
+
214
+ // these are no longer needed
215
+ this.finalize = null;
216
+ this.isFinalized = null;
217
+ }
218
+
219
+ /**
220
+ * @internal
221
+ * @ignore
222
+ */
223
+ isFinalized(methodName)
224
+ {
225
+ const desc = Object.getOwnPropertyDescriptor(this, methodName);
226
+ if(!desc) return false; // not directly defined on the object
227
+ return desc.writable === false && desc.configurable === false;
228
+ }
229
+
230
+ /**
231
+ * @internal
232
+ * @readonly
233
+ * @ignore
234
+ * @type {object}
235
+ */
236
+ get prop()
237
+ {
238
+ return this.#prop;
239
+ }
240
+
241
+ //region Property setters/getters
242
+
243
+ /**
244
+ * Returns a unique ID of the component. ID is reusable, so one component may have an ID of a previously destroyed component.
245
+ * @readonly
246
+ * @type {number}
247
+ */
248
+ get id()
249
+ {
250
+ return this.#id;
251
+ }
252
+
253
+ /**
254
+ * Gets a parent container component. If this component wasn't added to any container component, then `null` is returned.
255
+ * @readonly
256
+ * @type {UIComponent|null}
257
+ */
258
+ get parent()
259
+ {
260
+ this.verifyComponent();
261
+
262
+ if(this.parent_id == null) return null;
263
+ return UIComponent.get(this.parent_id);
264
+ }
265
+
266
+ /**
267
+ * Gets a previous sibling component. If this component wasn't added to any container component, then `null` is returned.
268
+ * @readonly
269
+ * @type {UIComponent|null}
270
+ * @since 1.0.2
271
+ */
272
+ get previousSibling()
273
+ {
274
+ if(this.parent == null) return null;
275
+ let i = this.parent.childIndex(this);
276
+ if(i === 0) return null;
277
+ return this.parent.child(i - 1);
278
+ }
279
+
280
+ /**
281
+ * Gets a next sibling component. If this component wasn't added to any container component, then `null` is returned.
282
+ * @readonly
283
+ * @type {UIComponent|null}
284
+ * @since 1.0.2
285
+ */
286
+ get nextSibling()
287
+ {
288
+ if(this.parent == null) return null;
289
+ let i = this.parent.childIndex(this);
290
+ return this.parent.child(i + 1);
291
+ }
292
+
293
+ /**
294
+ * Gets an array of sibling components.
295
+ * @readonly
296
+ * @type {UIComponent[]}
297
+ * @since 1.0.2
298
+ */
299
+ get siblings()
300
+ {
301
+ if(this.parent == null) return [];
302
+ const _siblings = this.parent.children;
303
+ _siblings.splice(_siblings.indexOf(this), 1);
304
+ return _siblings;
305
+ }
306
+
307
+ /**
308
+ * Gets a Window component containing this component. Returns `null` if no Window contain this component.
309
+ * @readonly
310
+ * @type {Window|null}
311
+ */
312
+ get window()
313
+ {
314
+ if(this instanceof Window) return this;
315
+ return this.parent?.window ?? null;
316
+ }
317
+
318
+ /**
319
+ * Returns `true` if the component is a container (other components can be added to it), otherwise returns false.
320
+ * @readonly
321
+ * @type {boolean}
322
+ */
323
+ get isContainer()
324
+ {
325
+ if(this.#prop.container == null) this.#prop.container = this instanceof ComponentContainer; // cache answer
326
+ return this.#prop.container;
327
+ }
328
+
329
+ /**
330
+ * Gets a position of this component relative to primary screen. Returns `null` if the component wasn't added to a container that is visible, or of this component is a Window and it was never visible.
331
+ *
332
+ * @readonly
333
+ * @type {Point|null}
334
+ * @see Point
335
+ * @see ui#screens
336
+ */
337
+ get screenPosition()
338
+ {
339
+ return this.property("screenLocation", []);
340
+ }
341
+
342
+ /**
343
+ * Adds this component to a parent container component. An optional index specifies the Z-order position at which to insert the component. It calls {@link ComponentContainer#addChild}.
344
+ *
345
+ * @param {ComponentContainer} parent - The container component to which this component will be added.
346
+ * @param {number|object} [data_or_index] - An parameter to pass some extra data (such as Tab Name to add Panel to Tabs), or index at which to insert the child.
347
+ * @returns {this} Returns an instance of this component for chaining.
348
+ * @see ComponentContainer#addChild
349
+ * @example <h2>Adding a component at specified index</h2>
350
+ * let checkbox = new CheckBox("Enabled checkbox").addTo(panel, 5).position(10, 15); // adds to panel and positions at (10,15) coordinates
351
+ *
352
+ * @example <h2>Adding a component at specified index</h2>
353
+ * let panel = new Panel().addTo(tabs, {text: "Registration", enabled: true, tooltip: "Create new account", icon: "images/user.png"});
354
+ */
355
+ addTo(parent, ...args)
356
+ {
357
+ // making sure that parent is instance of UIComponent
358
+ if(typeof parent !== "object" || !(parent instanceof UIComponent)) throw Error(`'parent' component must be derived from UIComponent of swing-ui module. ${ui._msg[2]}`);
359
+
360
+ parent.addChild(this, ...args);
361
+ return this;
362
+ }
363
+
364
+ /**
365
+ * Removes this component from its parent.
366
+ *
367
+ * @param {boolean} [recursive] - Boolean indicates if all descendents of this child should also be removed.
368
+ * @returns {this} Returns this component for method chaining
369
+ * @example
370
+ * child.remove(); // Removes child from it's parent container
371
+ * child.remove(true); // Removes child component from it's parent and all its descendants recursively
372
+ */
373
+ remove()
374
+ {
375
+ let recursive = false;
376
+
377
+ if(arguments.length > 0)
378
+ {
379
+ if(typeof arguments[0] !== "boolean") throw new Error(`Invalid argument passed to remove(recursive). Boolean is expected, but received ${typeof recursive}.`);
380
+ recursive = arguments[0];
381
+ }
382
+
383
+ if(this instanceof Window) // don't remove window, but only it's children, if recursive
384
+ {
385
+ // remove recursively, from back
386
+ if(recursive) for(let c = this.childCount - 1; c >= 0; c--) this.removeChild(c, true);
387
+ }
388
+ else
389
+ {
390
+ if(this.parent != null && this.parent instanceof UIComponent) this.parent.removeChild(this, recursive);
391
+ }
392
+
393
+ return this;
394
+ }
395
+
396
+ /**
397
+ * Removes the component from its container, destroys event callbacks, unregisters the component, recycles the component ID. Also prepares this component to be garbage-collected in JavaScript and GUI to free RAM.
398
+ *
399
+ * @param {boolean} [recursive=false] - Set to true to also destroy all of its descendants recursively
400
+ * @returns {void}
401
+ */
402
+ destroy(...args)
403
+ {
404
+ if(this.destroyed) return;
405
+
406
+ UIComponent.#refRegistry.unregister(this.#token);
407
+
408
+ try
409
+ {
410
+ this.remove(); // remove this element from its parent
411
+ }catch(ex)
412
+ {
413
+ console.error(ex);
414
+ }
415
+
416
+ // remove context menu
417
+ if(this.#context_menu != null) this.contextMenu(null);
418
+
419
+ // remove references
420
+ UIComponent.destroy(this.#id);
421
+
422
+ // remove all properties
423
+ for(let prop of Object.keys(this))
424
+ {
425
+ if(prop !== "verifyComponent" && typeof this[prop] !== "function") delete this[prop];
426
+ }
427
+
428
+ this.destroyed = true;
429
+ }
430
+
431
+ /**
432
+ * This function may be called to make sure the component wasn't destroyed yet before performing another operation with the component. If it's already destroyed, Exception is thrown.
433
+ *
434
+ * @return {this} Returns this component.
435
+ */
436
+ verifyComponent()
437
+ {
438
+ if(this.destroyed ?? false)
439
+ {
440
+ throw new Error(ui._msg[0] + " (" + this.constructor.name + ")");
441
+ }
442
+
443
+ return this;
444
+ }
445
+
446
+ /**
447
+ * Checks if this component is inside specified ancestor component (at any level, not just direct child).
448
+ * @param {Object} ancestor - Ancestor component to test against
449
+ * @return {boolean}
450
+ * @see ComponentContainer#contains
451
+ */
452
+ insideOf(ancestor)
453
+ {
454
+ if(!(ancestor instanceof ComponentContainer)) return false;
455
+ return ancestor.contains(this);
456
+ }
457
+
458
+ /**
459
+ * Sets or gets component's visibility.
460
+ * NOTE: Window and InternalWindow components visibility is set to `false` by default. Use .visible(true) or .show() to make it visible.
461
+ *
462
+ * @param {boolean} [visible] - `true` to make it visible, `false` to hide it.
463
+ * @return {boolean|this} If called with an argument, then `this` component instance is returned, otherwise `boolean` value is returned.
464
+ */
465
+ visible(...args)
466
+ {
467
+ this.verifyComponent();
468
+
469
+ if(args.length === 0)
470
+ {
471
+ return this.#prop.visible;
472
+ }
473
+ else
474
+ {
475
+ if(args[0] == null) return this; // ignore null or undefined
476
+ this.#prop.visible = (args[0] === true);
477
+ }
478
+ return this.property("visible", [this.#prop.visible]);
479
+ }
480
+
481
+ /**
482
+ * Convenience function that calls this.visible(true)
483
+ * @return {this}
484
+ */
485
+ show()
486
+ {
487
+ return this.visible(true);
488
+ }
489
+
490
+ /**
491
+ * Convenience function that calls this.visible(false)
492
+ * @return {this}
493
+ */
494
+ hide()
495
+ {
496
+ return this.visible(false);
497
+ }
498
+
499
+ /**
500
+ * Enables or disables the component.
501
+ *
502
+ * @param {boolean} [enabled] `true` to enable, `false` to disable the component. Omit to get current value.
503
+ * @return {boolean|this} If called with an argument, then `this` component instance is returned, otherwise `boolean` value is returned.
504
+ */
505
+ enabled(...args)
506
+ {
507
+ return this.property("enabled", args);
508
+ }
509
+
510
+ /**
511
+ * Sets or gets the size of the component.
512
+ *
513
+ * For {@link Window}, the returned and assigned size represents the entire window, including the title bar and borders. The actual area available for placing child components is smaller. Use {@link Window#innerSize} when you need to specify the content area size instead.
514
+ *
515
+ * @param {number|Size} [width_or_size] - Number as width, or `Size` formatted object.
516
+ * @param {number} [height] - Height of component. Required if first argument is `number`.
517
+ * @return {Size|this} Component size object if no arguments are set, otherwise returns `this` component.
518
+ * @see Size
519
+ * @example
520
+ * // Usage examples:
521
+ * button.size(100, 25);
522
+ * button.size({width: 100, height: 25});
523
+ * let size = button.size();
524
+ */
525
+ size(...args)
526
+ {
527
+ if(args.length > 0)
528
+ {
529
+ const size = (args.length > 1) ? {
530
+ width: args[0],
531
+ height: args[1]
532
+ } : (args[0] != null && typeof args[0] === "object") ? {
533
+ width: args[0].width ?? this.#prop.size.width,
534
+ height: args[0].height ?? this.#prop.size.height
535
+ } : null;
536
+
537
+ if(size == null)
538
+ {
539
+ this.#prop.size = {}; // reset, to be retrieved from GUI on next request
540
+ return this;
541
+ }
542
+ if(this.parent instanceof Tabs || this.parent instanceof ScrollPane) return this;
543
+ if(size.width == null || (typeof size.width !== "string" && isNaN(size.width))) size.width = this.#prop.size.width ?? this.width();
544
+ if(size.height == null || (typeof size.height !== "string" && isNaN(size.height))) size.height = this.#prop.size.height ?? this.height();
545
+
546
+ // check if width is in %
547
+ if(typeof size.width === "string" && size.width.indexOf("%") > 0)
548
+ {
549
+ const m = parseFloat(size.width.replace("%", "")) / 100;
550
+ size.width = (this.parent != null) ? (("innerSize" in this.parent) ? this.parent.innerSize().width : this.parent.width()) * m : this.width();
551
+ }
552
+
553
+ // check if height is in %
554
+ if(typeof size.height === "string" && size.height.indexOf("%") > 0)
555
+ {
556
+ const m = parseFloat(size.height.replace("%", "")) / 100;
557
+ size.height = (this.parent != null) ? (("innerSize" in this.parent) ? this.parent.innerSize().height : this.parent.height()) * m : this.height();
558
+ }
559
+
560
+ // if size changed, then send to GUI
561
+ if(this.#prop.size.width !== size.width || this.#prop.size.height !== size.height)
562
+ {
563
+ this.#prop.size = size;
564
+ return this.property("size", [this.#prop.size]);
565
+ }
566
+ else
567
+ {
568
+ return this;
569
+ }
570
+ }
571
+ else
572
+ {
573
+ this.verifyComponent();
574
+
575
+ // if size isn't known yet, retrieve it from GUI
576
+ if(!("width" in this.#prop.size && "height" in this.#prop.size))
577
+ {
578
+ const size = this.property("size", []);
579
+ Object.assign(this.#prop.size, size);
580
+ }
581
+
582
+ return {...this.#prop.size};
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Convenience method to set or get the width of component.
588
+ *
589
+ * @param {number} [width] - New width of the component.
590
+ * @return {number|UIComponent} Returns width if no argument is set, otherwise returns `this` component.
591
+ * @see UIComponent#size
592
+ */
593
+ width(...args)
594
+ {
595
+ const size = this.size();
596
+
597
+ if(args.length === 0)
598
+ {
599
+ return size.width;
600
+ }
601
+ else
602
+ {
603
+ return this.size(parseInt(args[0]), size.height);
604
+ }
605
+ }
606
+
607
+ /**
608
+ * Convenience method to set or get the height of component.
609
+ *
610
+ * @param {number} [height] - New height of the component.
611
+ * @return {number|UIComponent} Returns height if no argument is set, otherwise returns `this` component.
612
+ * @see UIComponent#size
613
+ */
614
+ height(...args)
615
+ {
616
+ if(args.length === 0)
617
+ {
618
+ return this.size().height;
619
+ }
620
+ else
621
+ {
622
+ return this.size(this.size().width, parseInt(args[0]));
623
+ }
624
+ }
625
+
626
+ /**
627
+ * Sets or gets text content of the component. Even though some components don't show text, you can still set text to it.
628
+ *
629
+ * @param {string} [text=undefined] - Can be plain text or HTML. HTML is only supported in some components (e.g. Label, Button, TextDocument, ...) and should be surrounded with HTML tags. Check example below.
630
+ * @param {TextAction} [action=TextAction.Set] - Specific action to perform with text. Replace, append, prepend, or insert. Applies only to some components. Do not use this argument for HTML content.
631
+ * @param {number} [insert_position=undefined] - Position where to insert text. Applies only if `action` argument is TextAction.Insert.
632
+ * @return {string|this} Returns text value if no argument is set, otherwise returns `this` component.
633
+ * @example
634
+ * const txt = textfield.text(); // gets text value
635
+ * textfield.text("New Text"); // sets new text value
636
+ * textfield.text("Appended Text", TextAction.Append); // Appends new text to the end of current text
637
+ * textfield.text("Inserted Text", TextAction.Insert); // Inserts at current caret position
638
+ * textfield.text("Inserted Text", TextAction.Insert, textfield.caretPosition() - 50); // Inserts at specified position
639
+ * textfield.text("New Text", TextAction.ReplaceSelection); // Replaces selected text or inserts at current cursor position if no text is selected (applies only to text editing components, like TextField, TextArea, ...).
640
+ */
641
+ text(...args)
642
+ {
643
+ if(args.length > 0)
644
+ {
645
+ ui.sendToGUI({
646
+ control: this.#id,
647
+ set: "text",
648
+ value: String(args[0]),
649
+ data: {
650
+ action: args.length > 1 ? args[1] : TextAction.Set,
651
+ index: args.length > 2 && args[1] === TextAction.Insert ? parseInt(args[2]) : undefined
652
+ }
653
+ });
654
+ return this;
655
+ }
656
+ else
657
+ {
658
+ return ui.sendToGUI({control: this.#id, get: "text"});
659
+ }
660
+ }
661
+
662
+ /**
663
+ * @internal
664
+ * @ignore
665
+ * @private
666
+ * @param {string} property
667
+ * @param {any} value
668
+ * @return {any}
669
+ */
670
+ property(property, ...args)
671
+ {
672
+ if(args.length > 0) args = args[0];
673
+
674
+ this.verifyComponent();
675
+
676
+ if(args.length > 0)
677
+ {
678
+ ui.sendToGUI({control: this.#id, set: property, value: args[0]});
679
+ return this;
680
+ }
681
+ else
682
+ {
683
+ return ui.sendToGUI({control: this.#id, get: property});
684
+ }
685
+ }
686
+
687
+ /**
688
+ * Registers, unregisters, and gets an event handler callback function for a specified events.
689
+ *
690
+ * - component.event(name, function, true); // unregisters one callback function (even if the same callback was registered multiple times)
691
+ * - component.event(name, function); // registers callback to be called when the event is emitted
692
+ * - component.event(name); // returns all callback functions for specified event
693
+ *
694
+ * @param {string} name - Name of event
695
+ * @param {function(Object):any|null} [callback=undefined] - Callback function. Pass `null` to unregister all handler functions.
696
+ * @param {boolean} [remove=false] - Set to true to unregister the callback function.
697
+ * @return {Function[]|this} If only the `name` argument is provided, returns an array of registered callback functions. Otherwise, returns `this` component.
698
+ * @internal
699
+ * @ignore
700
+ * @private
701
+ * @example
702
+ * const _callbacks_array = button.event("onAction"); // gets an array of previously registered handler functions
703
+ * button.event("onAction", _callbacks_array[0], true); // unregisters callback function, same as button.onAction(_callbacks_array[0], true);
704
+ * button.event("onAction", e => {console.debug(e);}); // registers new event handler function, same as button.onAction(e => {console.debug(e);});
705
+ */
706
+ event(name, callback = undefined, remove = false)
707
+ {
708
+ if(this.callback === undefined) return this; // in case the component is disposed already
709
+
710
+ // return callbacks
711
+ if(callback === undefined) return Array.isArray(this.callback[name]) ? [...this.callback[name]] : [];
712
+
713
+ // remove callback
714
+ if(remove === true || callback === null) // removes one or all callbacks
715
+ {
716
+ if(!Array.isArray(this.callback[name])) return this;
717
+
718
+ if(callback === null)
719
+ {
720
+ this.callback[name].length = 0;
721
+ }
722
+ else
723
+ {
724
+ const i = this.callback[name].indexOf(callback); // find callback
725
+ if(i === -1) return this; // return if not found
726
+ this.callback[name].splice(i, 1); // remove it
727
+ }
728
+
729
+ if(this.callback[name].length === 0)
730
+ {
731
+ delete this.callback[name]; // delete array
732
+ this.property(name, [false]); // notify GUI
733
+ }
734
+ }
735
+ // add callback
736
+ else
737
+ {
738
+ // make sure callback is function
739
+ if(typeof callback !== "function") throw new Error(`"callback" argument must be of type "function".`);
740
+
741
+ if(!Array.isArray(this.callback[name]))
742
+ {
743
+ this.callback[name] = []; // create array
744
+ this.property(name, [true]); // notify GUI
745
+ }
746
+
747
+ this.callback[name].push(callback); // add callback (don't bind to `this` here, it's done when called)
748
+ }
749
+
750
+ return this;
751
+ }
752
+
753
+ /**
754
+ * Sets or gets the background color of the component. <br />
755
+ * <br />
756
+ * Accepts three formats: <br />
757
+ * - RGB hex (e.g. `"#FF0000"`) <br />
758
+ * - RGBA hex with alpha (e.g. `"#FF000050"`) <br />
759
+ * - The string `"transparent"` (equivalent to `"#00000000"`)
760
+ *
761
+ * @param {string} [value] - Background color in supported formats. Panel and Window components also accept gradient, image position and other info like in CSS. If omitted, returns the current value.
762
+ * @param {boolean} [update] - For Panel or Window only. Specifies whether to update specified background attributes instead of replacing all previously set background attributes.
763
+ * @returns {this|string} Returns `this` when setting value, or the current background color string when getting.
764
+ *
765
+ * @example
766
+ * component.background("#FF0000"); // Red background
767
+ * component.background("#00FF0080"); // Semi-transparent green
768
+ * component.background("transparent"); // Fully transparent
769
+ *
770
+ * // For Panel and Window components the background value can be a string that contains a mixture of background color,
771
+ * // image, linear-gradient, radial-gradient, like in CSS in web browsers. Some features may not be supported though.
772
+ * panel.background("#FF000055 url(images/bg.png) center no-repeat opacity=0.6 10px 15px linear-gradient(#DDDDDD55, #F8F8F855 10%) / auto);
773
+ * panel.background("url(data:image/png;base64,As8fE4fdt1r.....)", true); // supports base64 encoded image string
774
+ */
775
+ background(...args)
776
+ {
777
+ if(args.length > 1 && args[1] === true && (this instanceof Panel || this instanceof Window))
778
+ {
779
+ args[0] = {
780
+ value: args[0],
781
+ update: true
782
+ };
783
+ }
784
+
785
+ return this.property("background", args);
786
+ }
787
+
788
+ /**
789
+ * Sets or gets a foreground color of component (usually text color).
790
+ *
791
+ * @param {string} value Three formats are acceptable, regular RGB "#FF0000", with alpha RGBA "#FF000050", and "transparent" string, which is equivalent to "#00000000".
792
+ * @return {string|this} Returns color value if no argument is set, otherwise returns `this` component.
793
+ */
794
+ color(...args)
795
+ {
796
+ return this.property("color", args);
797
+ }
798
+
799
+ /**
800
+ * Sets or gets the position of the component relative to its parent component's top-let corner (or to the screen's top-left corner when component is Window).
801
+ *
802
+ * @param {number|Point} [x_or_position] - X position. Alternatively, provide a single `Point` object parameter to specify the position.
803
+ * @param {number} [y] - Y position.
804
+ * @param {number} [right] - Resizes the component so that its right edge is this many pixels from the right edge of the parent component or screen.
805
+ * @param {number} [bottom] - Resizes the component so that its bottom edge is this many pixels from the bottom edge of the parent component or screen.
806
+ * @return {Point|this} If no arguments are provided, returns a {x: number, y: number} position object. Otherwise, returns `this` component.
807
+ * @see Point
808
+ * @example
809
+ * panel.size(50, 100); // sets position
810
+ * panel.size({x: 50, y: 100}); // sets position
811
+ * panel.size(50, 100, 20); // sets position of left, top and right sides relative to sides of parent component.
812
+ * const window = new Window("New Window").size(50, 100, 20, 20); // sets position of each side relative to sides of screen.
813
+ */
814
+ position(...args)
815
+ {
816
+ if(args.length > 0)
817
+ {
818
+ delete this.#prop.location.right;
819
+ delete this.#prop.location.bottom;
820
+ let changed = false;
821
+
822
+ try
823
+ {
824
+ if(args[0] == null)
825
+ {
826
+ this.#prop.location = {}; // reset position to make it load from GUI on next request
827
+ return this;
828
+ }
829
+
830
+ if(this.parent instanceof Tabs || this.parent instanceof ScrollPane) return this; // ignore position for tabs' or scroll pane's direct children
831
+
832
+ const position = (typeof args[0] === "object") ? {x: args[0].x, y: args[0].y} : {
833
+ x: args[0],
834
+ y: args[1]
835
+ };
836
+
837
+ if(Number.isFinite(position.x)) position.x = Number.parseInt(position.x);
838
+ if(Number.isFinite(position.y)) position.y = Number.parseInt(position.y);
839
+
840
+ if(isNaN(position.x) || isNaN(position.y)) throw new TypeError(`Invalid arguments for position():\n\n${stringify(args)}`);
841
+
842
+ // check if the size changed
843
+ if(position.x !== this.#prop.location.x || position.y !== this.#prop.location.y)
844
+ {
845
+ this.#prop.location = position;
846
+ this.property("location", [this.#prop.location]); // make sure this position is set before size is changed below
847
+ }
848
+
849
+ if(args.length > 2 && Number.isFinite(args[2])) args[2] = Number.parseInt(args[2]); // fix right
850
+ if(args.length > 3 && Number.isFinite(args[3])) args[3] = Number.parseInt(args[3]); // fix bottom
851
+
852
+ // resize to right edge
853
+ if(args.length > 2 && !isNaN(args[2]))
854
+ {
855
+ const parent = this.parent;
856
+
857
+ if(parent != null || this instanceof Window)
858
+ {
859
+ // get parent's or screen size
860
+ const containerSize = (parent != null) ? ((parent instanceof Window || parent instanceof InternalWindow) ? parent.innerSize() : parent.size()) : this.screenSize;
861
+
862
+ const w = containerSize.width - this.#prop.location.x - args[2];
863
+
864
+ // resize to bottom edge
865
+ if(args.length > 3 && !isNaN(args[3]))
866
+ {
867
+ const h = containerSize.height - this.#prop.location.y - args[3];
868
+ this.size(w, h);
869
+ }
870
+ else
871
+ {
872
+ this.width(w);
873
+ }
874
+ }
875
+ else
876
+ {
877
+ // save for later to be applied when component gets added to a parent
878
+ this.#prop.size.right = args[2];
879
+ if(args.length > 3 && !isNaN(args[3])) this.#prop.size.bottom = args[3];
880
+ }
881
+ }
882
+ }catch(ex)
883
+ {
884
+ throw new TypeError(ex.message + ".\n\nArguments must be one of following:\n position(number x, number y)\n position(number x, number y, number right, number bottom)\n position(number x, number y, number right)\n position({x: number, y: number})\n");
885
+ }
886
+
887
+ return this;
888
+ }
889
+ else
890
+ {
891
+ this.verifyComponent();
892
+
893
+ // if location isn't known, retrieve it from GUI
894
+ if(!("x" in this.#prop.location || "y" in this.#prop.location))
895
+ {
896
+ ({x: this.#prop.location.x, y: this.#prop.location.y} = this.property("location", []));
897
+ }
898
+
899
+ return {...this.#prop.location};
900
+ }
901
+ }
902
+
903
+ /**
904
+ * Convenience method to set or get left position of component.
905
+ *
906
+ * @param {number} [value]
907
+ * @return {number} Returns left position if no argument is set, otherwise returns `this` component.
908
+ * @see UIComponent#position
909
+ */
910
+ x(...args)
911
+ {
912
+ if(args.length > 0)
913
+ {
914
+ if(Number.isFinite(args[0])) args[0] = Number.parseInt(args[0]);
915
+ if(this.parent instanceof Tabs) return this; // can't set position for tabs direct children
916
+ if(isNaN(args[0])) throw new TypeError(`Invalid argument type for x(). Argument is '${args[0]}' of type '${typeof args[0]}'. Must be a number.`);
917
+
918
+ delete this.#prop.location.right; // in case it was set
919
+ if(this.#prop.location.x !== args[0]) // check if changed
920
+ {
921
+ this.#prop.location.x = args[0];
922
+ return this.property("location", [this.#prop.location]);
923
+ }
924
+ return this;
925
+ }
926
+ else
927
+ {
928
+ this.verifyComponent();
929
+ return this.#prop.location.x;
930
+ }
931
+ }
932
+
933
+ /**
934
+ * Convenience method to set or get top position of component.
935
+ *
936
+ * @param {number} [value]
937
+ * @return {number} Returns top position if no argument is set, otherwise returns `this` component.
938
+ * @see UIComponent#position
939
+ */
940
+ y(...args)
941
+ {
942
+ if(args.length > 0)
943
+ {
944
+ if(Number.isFinite(args[0])) args[0] = Number.parseInt(args[0]);
945
+ if(this.parent instanceof Tabs) return this; // can't set position for tabs direct children
946
+ if(isNaN(args[0])) throw new TypeError(`Invalid argument type for y(). Argument is '${args[0]}' of type '${typeof args[0]}'. Must be a number.`);
947
+
948
+ delete this.#prop.location.bottom; // in case it was set
949
+ args[0] = parseInt(args[0]);
950
+ if(this.#prop.location.y !== args[0]) // check if changed
951
+ {
952
+ this.#prop.location.y = args[0];
953
+ return this.property("location", [this.#prop.location]);
954
+ }
955
+ return this;
956
+ }
957
+ else
958
+ {
959
+ this.verifyComponent();
960
+ return this.#prop.location.y;
961
+ }
962
+ }
963
+
964
+ /**
965
+ * Convenience method to get or set the distance between the right edge of this component and the right edge of its parent.
966
+ *
967
+ * @param {number} [value] - Distance from the parent's right edge.
968
+ * @param {boolean} [reize] - `True` to resize the width to accommodate for specified `right` position instead of moving the object. `false` to move the component.
969
+ * @return {number|this} Returns the current right distance if no argument is provided; otherwise, returns `this` component.
970
+ */
971
+ right(...args)
972
+ {
973
+ const parent = this.parent;
974
+
975
+ if(args.length > 0)
976
+ {
977
+ if(Number.isFinite(args[0])) args[0] = Number.parseInt(args[0]);
978
+
979
+ if(isNaN(args[0])) throw new Error(`Invalid argument for right() method. Argument is '${args[0]}' of type '${typeof args[0]}'. Must be a number.`);
980
+
981
+ if(parent !== null)
982
+ {
983
+ if(parent instanceof Window || parent instanceof InternalWindow)
984
+ {
985
+ if(args[1] === true)
986
+ {
987
+ this.width(Math.max(0, this.parent.innerSize().width - this.x() - args[0]));
988
+ }
989
+ else
990
+ {
991
+ this.x(this.parent.innerSize().width - this.width() - args[0]);
992
+ }
993
+ }
994
+ else
995
+ {
996
+ if(args[1] === true)
997
+ {
998
+ this.width(Math.max(0, this.parent.width() - this.x() - args[0]));
999
+ }
1000
+ else
1001
+ {
1002
+ this.x(this.parent.width() - this.width() - args[0]);
1003
+ }
1004
+ }
1005
+ }
1006
+ else
1007
+ {
1008
+ if(args[1] !== true) this.#prop.location.right = args[0]; // set to be applied when added to component
1009
+ }
1010
+ return this;
1011
+ }
1012
+ else
1013
+ {
1014
+ if(parent === null) return NaN;
1015
+
1016
+ if(parent instanceof Window || parent instanceof InternalWindow)
1017
+ {
1018
+ return this.parent.innerSize().width - (this.x() + this.width()); // calculate
1019
+ }
1020
+ else
1021
+ {
1022
+ return this.parent.width() - (this.x() + this.width()); // calculate
1023
+ }
1024
+ }
1025
+ }
1026
+
1027
+ /**
1028
+ * Convenience method to get or set the distance between the bottom edge of this component and the bottom edge of its parent.
1029
+ *
1030
+ * @param {number} [value] - Distance from the parent's bottom edge to this component's bottom edge.
1031
+ * @param {boolean} [reize] - `True` to resize the height to accommodate for specified `bottom` position instead of moving the object. `false` to move the component.
1032
+ * @return {number|this} Returns the current bottom distance if no argument is provided; otherwise, returns `this` component.
1033
+ */
1034
+ bottom(...args)
1035
+ {
1036
+ if(args.length > 0)
1037
+ {
1038
+ if(Number.isFinite(args[0])) args[0] = Number.parseInt(args[0]);
1039
+
1040
+ if(isNaN(args[0])) throw new Error(`Invalid argument for bottom() function. Argument is '${args[0]}' of type '${typeof args[0]}'. Must be a number.`);
1041
+
1042
+ if(this.parent !== null)
1043
+ {
1044
+ if(this.parent instanceof Window || this.parent instanceof InternalWindow)
1045
+ {
1046
+ if(args[1] === true)
1047
+ {
1048
+ this.height(Math.max(0, this.parent.innerSize().height - this.y() - args[0]));
1049
+ }
1050
+ else
1051
+ {
1052
+ this.y(this.parent.innerSize().height - this.height() - args[0]);
1053
+ }
1054
+ }
1055
+ else
1056
+ {
1057
+ if(args[1] === true)
1058
+ {
1059
+ this.height(Math.max(0, this.parent.height() - this.y() - args[0]));
1060
+ }
1061
+ else
1062
+ {
1063
+ this.y(this.parent.height() - this.height() - args[0]);
1064
+ }
1065
+ }
1066
+ }
1067
+ else
1068
+ {
1069
+ if(args[1] !== true) this.#prop.location.bottom = args[0]; // set to be applied when added to component
1070
+ }
1071
+ return this;
1072
+ }
1073
+ else
1074
+ {
1075
+ if(this.parent === null) return NaN;
1076
+
1077
+ if(this.parent instanceof Window || this.parent instanceof InternalWindow)
1078
+ {
1079
+ return this.parent.innerSize().height - (this.y() + this.height()); // calculate
1080
+ }
1081
+ else
1082
+ {
1083
+ return this.parent.height() - (this.y() + this.height()); // calculate
1084
+ }
1085
+ }
1086
+ }
1087
+
1088
+ /**
1089
+ * Moves the component by adding the specified x and y values to its current position.
1090
+ * The function shifts the element's position relative to current position.
1091
+ *
1092
+ * @param {number} [dx=0] - Delta x (can be positive or negative number)
1093
+ * @param {number} [dy=0] - Delta y (can be positive or negative number)
1094
+ * @return {this} Returns `this` component.
1095
+ */
1096
+ move(dx = 0, dy = 0)
1097
+ {
1098
+ if(Number.isFinite(dx)) dx = Number.parseInt(dx);
1099
+ if(Number.isFinite(dy)) dy = Number.parseInt(dy);
1100
+
1101
+ if((!isNaN(dx) && !isNaN(dy)) && !(dx === 0 && dy === 0))
1102
+ {
1103
+ const _args = [];
1104
+ const p = this.#prop.location;
1105
+ _args.push(p.x + dx);
1106
+ _args.push(p.y + dy);
1107
+
1108
+ // update possible `right` and `bottom` in location
1109
+ if("right" in p) _args.push(p.right - dx);
1110
+ if("bottom" in p) _args.push(p.bottom - dy);
1111
+
1112
+ return this.position.apply(this, _args);
1113
+ }
1114
+
1115
+ return this;
1116
+ }
1117
+
1118
+ /**
1119
+ * When called without arguments and repainting is not disabled, it flushes message queue, revalidates and repaints the component.
1120
+ * When the first argument is `true`, it enables repainting (enabled by default) and flushes the message queue.
1121
+ * When the first argument is `false`, it disables repainting.
1122
+ *
1123
+ * @param {boolean} repaint_enabled - `false` to pause repainting this element. `true` to resume repainting
1124
+ * @return {this} Returns `this` component.
1125
+ */
1126
+ update(...args)
1127
+ {
1128
+ if(args.length === 0)
1129
+ {
1130
+ if(this.#prop.update !== false)
1131
+ {
1132
+ ui.sendToGUI({control: this.#id, set: "update", value: true});
1133
+ ui.sendMessageQueue();
1134
+ }
1135
+ }
1136
+ else if((args[0] === true) !== (this.#prop.update !== false))
1137
+ {
1138
+ this.#prop.update = (args[0] === true);
1139
+ ui.sendToGUI({control: this.#id, set: "update", value: this.#prop.update});
1140
+ ui.sendMessageQueue();
1141
+ }
1142
+
1143
+ return this;
1144
+ }
1145
+
1146
+ /**
1147
+ * Sets or gets context menu component for this UI component.
1148
+ *
1149
+ * @param {ContextMenu} [menu] - ContextMenu component to be assigned to this component. Set to `null` to remove ContextMenu.
1150
+ * @return {ContextMenu|null|this} Returns the current context menu if no argument is provided; otherwise, returns `this` component.
1151
+ */
1152
+ contextMenu(...args)
1153
+ {
1154
+ if(args.length === 0) return this.#context_menu;
1155
+
1156
+ let menu = args[0];
1157
+
1158
+ if(menu === null) // remove context menu
1159
+ {
1160
+ if(this.#context_menu != null && typeof this.#context_menu === "object" && this.#context_menu instanceof globalThis.ContextMenu && this.#context_menu.destroyed !== true)
1161
+ {
1162
+ let i = this.#context_menu._parents.indexOf(this);
1163
+
1164
+ if(i > -1)
1165
+ {
1166
+ this.#context_menu._parents.splice(i, 1);
1167
+ ui.sendToGUI({control: this.#context_menu.id, set: "removeFrom", value: this.#id});
1168
+ }
1169
+ }
1170
+
1171
+ this.#context_menu = null;
1172
+ }
1173
+ else // add context menu
1174
+ {
1175
+ if(this === menu || typeof menu !== "object" || !(menu instanceof globalThis.ContextMenu)) // can't use !(menu instanceof ContextMenu) because ContextMenu imports this class, thus would create circular import.
1176
+ {
1177
+ throw new Error(ui._msg[10]);
1178
+ return this;
1179
+ }
1180
+
1181
+ if(this.#context_menu != null) this.contextMenu(null); // remove existing menu before setting a new one
1182
+
1183
+ this.#context_menu = menu;
1184
+ menu._parents.push(this);
1185
+
1186
+ const resp = ui.sendToGUI({control: menu.id, get: "parent", data: {parent: this.#id}});
1187
+ }
1188
+
1189
+ return this;
1190
+ }
1191
+
1192
+ /**
1193
+ * Sets or gets component `focusable` property whether component can retain focus.
1194
+ *
1195
+ * @param {boolean} [value]
1196
+ * @return {boolean|this} If called with an argument, then `this` component instance is returned, otherwise `boolean` value is returned.
1197
+ */
1198
+ focusable(...args)
1199
+ {
1200
+ return this.property("focusable", args);
1201
+ }
1202
+
1203
+ /**
1204
+ * If value is passed, it makes component to get or loose focus, otherwise returns `boolean` indicating if it has focus or not.
1205
+ *
1206
+ * @param {boolean} [value] - `true` to get focus, `false` to attempt to shift focus to next component (if it has focus), usually by switching focus to next component.
1207
+ * @returns {boolean|this} If called with an argument, then `this` component instance is returned, otherwise `boolean` value is returned.
1208
+ */
1209
+ focused(...args)
1210
+ {
1211
+ return this.property("focused", args);
1212
+ }
1213
+
1214
+ /**
1215
+ * Sets or gets an index (z-order) of this component if it is added to a container, otherwise returns null.
1216
+ * Larger value brings component forward, smaller to the back, like z-index in CSS.
1217
+ *
1218
+ * @param {number} [index]
1219
+ * @returns {this|null|number} If called with an argument, then `this` component instance is returned, if component has no parent then `null` is returned, otherwise index is returned.
1220
+ */
1221
+ index(...args)
1222
+ {
1223
+ if(this.parent == null) return args.length === 0 ? null : this; // if no parent
1224
+
1225
+ return this.parent.childIndex(this, ...args);
1226
+ }
1227
+
1228
+ /**
1229
+ * Sets or gets a layer at which this component resides in Panel container. This is separate from z-index.
1230
+ * This applies only to components that are added to Panel or Window components.
1231
+ * For example components in layer 2 are always on top of components in layer 1, regardless on their z-index.
1232
+ * Larger value brings component forward, smaller to the back, like width z-index.
1233
+ *
1234
+ * @param {number} layer_index
1235
+ * @returns {number|this} If called with an argument, then `this` component instance is returned, otherwise `number` value is returned.
1236
+ */
1237
+ layer(...args)
1238
+ {
1239
+ if(args.length > 0) args[0] = Math.max(0, parseInt(args[0]));
1240
+ return this.property("layer", args);
1241
+ }
1242
+
1243
+ /**
1244
+ * Gets or sets the font used for the component's text. See the example for font object format.
1245
+ *
1246
+ * @param {Font} [font] - Font formatted object.
1247
+ * @param {boolean} [update] - If `true`, only the specified font properties will be updated instead of replacing the entire font.
1248
+ * @return {this|Font}
1249
+ * @see Font
1250
+ * @example <h2>Full list of supported font properties:</h2>
1251
+ * Font {
1252
+ * name: string, // Font name or family name from {@link FontFamily} enum (e.g., "sans-serif")
1253
+ * size: number, // Font size in pixels
1254
+ * bold: boolean, // Whether the text is bold
1255
+ * italic: boolean, // Whether the text is italic
1256
+ * underlined: boolean, // Whether the text is underlined
1257
+ * strikethrough: boolean, // Whether the text has a strikethrough
1258
+ * direction: string, // Text direction: "ltr" or "rtl"
1259
+ * kerning: boolean, // Enable or disable kerning
1260
+ * ligatures: boolean, // Enable or disable font ligatures
1261
+ * tracking: number // Letter spacing adjustment: -1, 0 (default), or 1
1262
+ * };
1263
+ * @example <h2>Usage examples:</h2>
1264
+ * // Sets new font
1265
+ * textarea.font({name: "sans-serif", size: 14, italic: true});
1266
+ *
1267
+ * // Set a new font; previously set italic style will be removed
1268
+ * textarea.font({name: "sans-serif", size: 14});
1269
+ *
1270
+ * // Update only the font size, preserving other properties
1271
+ * textarea.font({size: 20}, true);
1272
+ *
1273
+ * // get component's font
1274
+ * const font = textarea.font();
1275
+ * @see Font
1276
+ * @see FontFamily
1277
+ */
1278
+ font(...args)
1279
+ {
1280
+ if(args.length > 0)
1281
+ {
1282
+ if(typeof args[0] !== "object") throw new Error("Font argument must be an Object");
1283
+ const msg = {control: this.id, set: "font", value: args[0]};
1284
+ if(args[1] === true) msg.update = true;
1285
+ ui.sendToGUI(msg);
1286
+ return this;
1287
+ }
1288
+ else
1289
+ {
1290
+ return this.property("font", []);
1291
+ }
1292
+ }
1293
+
1294
+ /**
1295
+ * Sets or gets an icon image of this component. You can set icon to any component even though not all components display the icon.
1296
+ * When a component is added to Tabs, that component's icon is used as a Tab icon by default.
1297
+ * Following image types are supported: ICO, PNG, JPG, JPEG, GIF, BMP, WBMP, TIFF/TIF, and base64 encoded images of such types.
1298
+ * If FFMPEG with support of these image types is installed, the following image types are also supported from local relative or absolute paths: WEBP, HEIC, HEIF, AVIF.
1299
+ *
1300
+ * @param {string|null} image_path - Relative or absolute path to image file, base64 encoded image string, or NULL to remove existing icon.
1301
+ * @returns {this|string|null} Returns `this` component if no arguments are set, a string of a previously set icon, or `null` if icon hasn't been set.
1302
+ */
1303
+ icon(...args)
1304
+ {
1305
+ return this.property("icon", args);
1306
+ }
1307
+
1308
+ /**
1309
+ * Sets an cursor to the control. Passing NULL value removes custom cursor.
1310
+ *
1311
+ * @param {number|null|string} cursor - A value from Cursor enum, or a relative path to image file for custom cursor icon.
1312
+ * @param {number} x - Used with custom image cursor to set pointer point on the image.
1313
+ * @param {number} y - Used with custom image cursor to set pointer point on the image.
1314
+ * @returns {this} Returns `this` component.
1315
+ */
1316
+ cursor(cursor, ...point)
1317
+ {
1318
+ const msg = {control: this.#id, set: "cursor", value: cursor};
1319
+
1320
+ if(point.length > 1)
1321
+ {
1322
+ msg.x = parseInt(point[0]);
1323
+ msg.y = parseInt(point[1]);
1324
+ }
1325
+
1326
+ ui.sendToGUI(msg);
1327
+ return this;
1328
+ }
1329
+
1330
+ /**
1331
+ * Sets or gets the drag action for this component (e.g., `Copy`, `Move`), and enables or disables dragging accordingly.
1332
+ * Passing `None` disables dragging; all other values enable it.
1333
+ *
1334
+ * @param {DragDropAction} [action] - One of enum values: DragDropAction.[None|Copy|Move|CopyOrMove|Link]. `None` is default.
1335
+ * @returns {string|this} Returns `this` component if argument is provided, otherwise returns `string` value as one of the DragDropAction enum values.
1336
+ */
1337
+ dragAction(...args)
1338
+ {
1339
+ if(args.length > 0)
1340
+ {
1341
+ if(!DragDropAction.hasOwnProperty(args[0])) return this; // validate argument
1342
+ this.#prop.dragAction = args[0];
1343
+ }
1344
+ return this.property("dragAction", args);
1345
+ }
1346
+
1347
+ /**
1348
+ * Sets or gets which type of drop action is accepted by the component (e.g., `Copy`, `Move`). Setting any action other
1349
+ * than `None` enables dropping on the component. Action `None` disables dropping.
1350
+ *
1351
+ * @param {DragDropAction} [action] - One of enum values: DragDropAction.[None|Copy|Move|CopyOrMove|Link]. `None` is default.
1352
+ * @returns {string|this} Returns `this` component if argument is provided, otherwise returns `string` value as one of the DragDropAction enum values.
1353
+ */
1354
+ dropAction(...args)
1355
+ {
1356
+ if(args.length > 0)
1357
+ {
1358
+ if(!DragDropAction.hasOwnProperty(args[0])) return this; // validate argument
1359
+ this.#prop.dropAction = args[0];
1360
+ }
1361
+ return this.property("dropAction", args);
1362
+ }
1363
+
1364
+ /**
1365
+ * Sets or gets the types of data that should be transffered during drag operation. The data itself can be set using dragData(...) method.
1366
+ * More than one type can be specified by setting multiple arguments or passing an array of values.
1367
+ *
1368
+ * @param {...DataType|DataType[]} data_type - DataType.[None|Text|Image|...]
1369
+ * @returns {string[]|this}
1370
+ * @example
1371
+ * // Usage examples:
1372
+ * label.dragDataType(DataType.Text, DataType.HTML); // setting by multiple arguments
1373
+ * label.dragDataType([DataType.Text, DataType.HTML]); // setting by an array of values
1374
+ * const _types = label.dragDataType(); // getting an array of data types
1375
+ */
1376
+ dragDataType(...args)
1377
+ {
1378
+ if(Array.isArray(args[0])) args = args[0];
1379
+ let _args = [];
1380
+ for(let v of args) if(DataType.hasOwnProperty(v) && _args.indexOf(v) === -1) _args.push(v); // validate argument and remove duplicates
1381
+
1382
+ return this.property("dragDataType", _args.length > 0 ? [_args] : []);
1383
+ }
1384
+
1385
+ /**
1386
+ * Sets custom data that should be passed when dragging this component instead of passing component's data, such as text or icon.
1387
+ * Without setting this data a default component data will be dragged, such as text, list item, icon, what what ever applies to this component.
1388
+ *
1389
+ * @arg {string|string[]} data - String data or array of strings. For a list of files or URLs use array or separate them by new line character \n.
1390
+ * @return {this} Returns `this` component.
1391
+ */
1392
+ dragData(...args)
1393
+ {
1394
+ if(args.length === 0) return this.property("dragDataType", []);
1395
+
1396
+ if(Array.isArray(args[0])) args = args[0];
1397
+ let _args = [];
1398
+ for(let v of args)
1399
+ {
1400
+ if(!(typeof v === "string")) continue;
1401
+ if((DataType.hasOwnProperty(v) || v.startsWith("component/")) && _args.indexOf(v) === -1) _args.push(v);// validate argument and remove duplicates
1402
+ }
1403
+
1404
+ return this.property("dragDataType", [_args]);
1405
+ }
1406
+
1407
+ /**
1408
+ * Sets acceptable data types that may be dropped on this component during drag and drop operation.
1409
+ *
1410
+ * @param {...DataType|DataType[]} data_type - DataType.[None|All|Text|Image|...]. Use `All` to accept all data types.
1411
+ * @return {string[]|this} Returns `this` component is argument is provided, otherwise returns `string[]` value.
1412
+ * @example
1413
+ * // Usage examples:
1414
+ * label.dropDataType(DataType.All); // Setting to accept all data types
1415
+ * label.dropDataType(.DataType.Text, DataType.HTML); // setting by multiple arguments
1416
+ * label.dropDataType([DataType.Text, DataType.HTML]); // setting by an array of values
1417
+ * const _types = label.dropDataType(); // getting an array of data types
1418
+ */
1419
+ dropDataType(...args)
1420
+ {
1421
+ if(args.length === 0) return this.property("dropDataType", []);
1422
+
1423
+ if(Array.isArray(args[0])) args = args[0];
1424
+ let _args = [];
1425
+ for(let v of args)
1426
+ {
1427
+ if(!(typeof v === "string")) continue;
1428
+ if((DataType.hasOwnProperty(v) || v.startsWith("component/")) && _args.indexOf(v) === -1) _args.push(v);// validate argument and remove duplicates
1429
+ }
1430
+
1431
+ return this.property("dropDataType", [_args]);
1432
+ }
1433
+
1434
+ /**
1435
+ * Sets a drop mode for components: Table, List, Tree.
1436
+ *
1437
+ * @param {DropMode} [mode] - Enum value: DropMode.[On|Insert|OnOrInsert]. On - allows data to be dropped on list items. Insert - allows to drop data between list items.
1438
+ * @returns {DropMode|this} Returns `this` component if an argument is provided; otherwise, returns the current drop mode as one of the DropMode enum values.
1439
+ * @see DropMode
1440
+ */
1441
+ dropMode(...args)
1442
+ {
1443
+ if(args.length > 0) args[0] = String(args[0]);
1444
+ return this.property("dropMode", args);
1445
+ }
1446
+
1447
+ /**
1448
+ * Sets, updates, or gets component border object. Border object should contain only properties that you want to set or update.
1449
+ *
1450
+ * @param {Border} [border] - Border object.
1451
+ * @param {boolean} [update] - Set to `true` to only change specified border properties instead of replacing existing border.
1452
+ * @return {this|Border|null} - If called without arguments a `Border` object is returned or `null` if the component's border wasn't set. Otherwise and instance of the component is returned.
1453
+ * @see UIComponent#border
1454
+ */
1455
+ border(...args)
1456
+ {
1457
+ if(args.length > 0)
1458
+ {
1459
+ if(typeof args[0] !== "object") throw new Error("Border argument must be an Object");
1460
+ const msg = {control: this.id, set: "border", value: args[0]};
1461
+ if(args[1] === true) msg.update = true;
1462
+ ui.sendToGUI(msg);
1463
+ return this;
1464
+ }
1465
+ else
1466
+ {
1467
+ return this.property("border", []);
1468
+ }
1469
+ }
1470
+
1471
+ /**
1472
+ * Sets or gets an alignment of icon relative to text of a Label, Button, or items of a List.
1473
+ *
1474
+ * @param {string} [alignment] - A combination of one or more words: "top right bottom left center leading trailing"
1475
+ * @return {this|string} Returns `this` component if an argument is provided, otherwise returns `string` value.
1476
+ */
1477
+ iconAlignment(...args)
1478
+ {
1479
+ return this.property("iconAlignment", args);
1480
+ }
1481
+
1482
+ /**
1483
+ * Sets or gets an alignment of the component's content relative to the component, such as Label, Button, CheckBox, RadioBox, or items of a List.
1484
+ *
1485
+ * @param {string} [alignment] - A combination of one or more words: "top bottom right left center leading trailing"
1486
+ * @return {this|string} Returns `this` component if an argument is provided, otherwise returns `string` value.
1487
+ */
1488
+ alignment()
1489
+ {
1490
+ return this.property("alignment", arguments);
1491
+ }
1492
+
1493
+ /**
1494
+ * Sets or get a gap in pixel between icon and text of a List item, Button, Label, ComboBox, CheckBox, and RadioBox.
1495
+ *
1496
+ * @param {number} [gap] - The gap in pixels (must be an integer ≥ 0). If omitted, returns the current gap.
1497
+ * @return {this|number} Returns `number` if argument is omitted, otherwise returns `this` component instance.
1498
+ */
1499
+ iconTextGap(...args)
1500
+ {
1501
+ return this.property("iconTextGap", args);
1502
+ }
1503
+
1504
+ /**
1505
+ * Captures an image of the component and all subcomponents. Returns the image as a base 64 encoded string in format "data:image/FORMAT;base64,As8fE4fdt1r.....".
1506
+ *
1507
+ * @param {string} [format="png"] - Image format: `png`, `jpg`, `jpeg`, `bmp`, `gif`.
1508
+ * @param {Object} [crop] - Object that holds position and size of the cropped area. Format: {x: number, y: number, width: number, height: number}.
1509
+ * @return {string} Returns base 64 encoded string representation of the image. To get its binary data you must extract the base 64 string, then decode it from base 64 to bytes.
1510
+ */
1511
+ snapshot(format = "png", crop = null)
1512
+ {
1513
+ return ui.sendToGUI({control: this.#id, get: "snapshot", data: {format: format.toUpperCase(), crop: crop}});
1514
+ }
1515
+
1516
+ /**
1517
+ * Sets or gets the anchor style of the component that indicates how to resize this component based on the resizing parent component.
1518
+ * Anchor defines which sides (top, right, bottom, left) of the component stay attached to the parent's edge when parent gets resized.
1519
+ * Set anchors after the component and its ancestors are added to a container to ensure expected behaviour.
1520
+ *
1521
+ * @param {string} [anchors] - Space-separated list of sides to anchor (e.g., `"top left"`). If omitted, returns the current anchor value.
1522
+ * @returns {this|string} Returns `this` component when setting, or the current anchor `string` when getting value.
1523
+ * @example
1524
+ * const txt1 = new TextArea().anchor("top left right"); // Horizontally resizable
1525
+ * const txt2 = new TextArea().anchor("top left bottom"); // Vertically resizable
1526
+ */
1527
+ anchor(...args)
1528
+ {
1529
+ if(args.length > 0 && typeof args[0] !== "string") return this; // enforce string
1530
+ return this.property("anchor", args);
1531
+ }
1532
+
1533
+ /**
1534
+ * Sets or gets tooltip text for the component. It's the text that pops up when cursor hovers the component.
1535
+ *
1536
+ * @param {string} text - Can be plain text or HTML. HTML string should be surrounded with HTML tags. See example.
1537
+ * @return {string|this} Returns `this` component if argument is set, otherwise returns `string` value.
1538
+ * @example
1539
+ * button.tooltip("<html>This is line 1<br />This is line 2</html>"); // Setting HTML string with 2 lines of text
1540
+ */
1541
+ tooltip(...args)
1542
+ {
1543
+ return this.property("tooltip", args);
1544
+ }
1545
+
1546
+ /**
1547
+ * Indicates the minimum size the component can be resized to.
1548
+ *
1549
+ * @param {number|Size} width_or_size - Minimum width, or a Size formatted object as a sole parameter.
1550
+ * @param {number} height - Minimum height of the component.
1551
+ * @returns {Size|this|null} If called without parameters, current minimum Size is returned or `null` if it wasn't previously set; otherwise returns `this` component instance for chaining.
1552
+ * @see Size
1553
+ */
1554
+ minSize(...args)
1555
+ {
1556
+ if(args.length > 1) // convert (width, height) params to object
1557
+ {
1558
+ args = [{width: parseInt(args[0]), height: parseInt(args[1])}];
1559
+ }
1560
+ else if(args.length === 1 && typeof args[0] !== "object") // if only 1 arg and it's not an object, then ignore
1561
+ {
1562
+ return this;
1563
+ }
1564
+
1565
+ return this.property("minSize", args);
1566
+ }
1567
+
1568
+ /**
1569
+ * Indicates the maximum size the component can be resized to.
1570
+ *
1571
+ * @param {number|Size} width_or_size - Maximum width, or a Size formatted object as a sole parameter.
1572
+ * @param {number} height - Maximum height of the component.
1573
+ * @returns {Size|this|null} If called without parameters, current maximum Size is returned or `null` if it wasn't previously set; otherwise returns `this` component instance for chaining.
1574
+ * @see Size
1575
+ */
1576
+ maxSize(...args)
1577
+ {
1578
+ if(args.length > 1) // convert (width, height) params to object
1579
+ {
1580
+ args = [{width: parseInt(args[0]), height: parseInt(args[1])}];
1581
+ }
1582
+ else if(args.length > 0 && typeof args[0] !== "object") // if only 1 arg and it's not an object, then ignore
1583
+ {
1584
+ return this;
1585
+ }
1586
+
1587
+ return this.property("maxSize", args);
1588
+ }
1589
+
1590
+ /**
1591
+ * Positions the component in the center of it's parent component, owner Window (if Window has an owner Window), or screen (Window only).
1592
+ * Applies only to components that have position() property (not like menu, menu item, tray icon, etc.)
1593
+ *
1594
+ * @arg {Object} [component] - If provided, `this` component will be positioned in the center of the provided component.
1595
+ * @return {this} Returns `this` component.
1596
+ */
1597
+ center(component = null)
1598
+ {
1599
+ let parentSize = null;
1600
+ let offset = {x: 0, y: 0};
1601
+
1602
+ if(component !== null) // if relative component was specified
1603
+ {
1604
+ if(!(component instanceof UIComponent)) throw new Error(`"component" argument must be of type derived from UIComponent that have size and position properties.`);
1605
+ if(this === component) throw new Error(`"component" should not be same object as you are trying to center.`);
1606
+
1607
+ parentSize = component.size();
1608
+ if(component !== this.parent) offset = component.position();
1609
+ }
1610
+ else if(this.parent !== null) // this has parent, then center to it
1611
+ {
1612
+ parentSize = (this.parent instanceof Window || this.parent instanceof InternalWindow) ? this.parent.innerSize() : this.parent.size();
1613
+ }
1614
+ else if(this instanceof Window) // if this is a Window
1615
+ {
1616
+ if(this.owner !== null) // if this Window has owner, then center to it
1617
+ {
1618
+ parentSize = this.owner.size();
1619
+ offset = this.owner.position();
1620
+ }
1621
+ else
1622
+ {
1623
+ parentSize = this.screenSize; // center to screen
1624
+ }
1625
+ }
1626
+
1627
+ if(parentSize !== null) this.position(offset.x + ((parentSize.width - this.width()) / 2), offset.y + ((parentSize.height - this.height()) / 2));
1628
+
1629
+ return this;
1630
+ }
1631
+
1632
+ /**
1633
+ * Enables/disables onKey() events to be emitted for focus traversal key (usually Tab key). By default onKey() event doesn't emit for such keys.
1634
+ *
1635
+ * @param {boolean} [enable] - `true` - enabled. `false` - disabled.
1636
+ * @return {this|boolean} Returns `this` component if argumen is provided, otherwise returns `boolean` value.
1637
+ */
1638
+ emitFocusKeyEvents(...args)
1639
+ {
1640
+ if(args.length > 0) args[0] = (args[0] === true);
1641
+ return this.property("emitFocusKeyEvents", args);
1642
+ }
1643
+
1644
+ /**
1645
+ * Finds and returns first ancestor component of specified class. e.g. panel.getAncestor(Window) will return Window component that panel is in, or null if panel doesn't have a parent or ancestor with specified class.
1646
+ *
1647
+ * @param {Function} ancestor_class - UI Component class that comes with `swing-ui` module.
1648
+ * @return {Object|null}
1649
+ */
1650
+ getAncestor(ancestor_class)
1651
+ {
1652
+ if(this.parent == null) return null;
1653
+ if(this.parent instanceof ancestor_class) return this.parent;
1654
+ return this.parent.getAncestor(ancestor_class);
1655
+ }
1656
+
1657
+ /**
1658
+ * Emits a specified event with a custom event object for this component. This event will be sent to all event handlers registered with the specified event name.
1659
+ *
1660
+ * @param {string} event_name - Name of the event is same as name of an event handler registering function, such as `onAction` or `onChange`.
1661
+ * @param {object} event - Custom event object
1662
+ * @returns {this} Returns an instance of this component.
1663
+ * @example
1664
+ * button.emit("onAction", {type: "action", command: "Click Me"}); // emits a custom event without user clicking on the button
1665
+ */
1666
+ emit(event_name, event = {type: "custom"})
1667
+ {
1668
+ this.verifyComponent();
1669
+ if(!Array.isArray(this.callback[event_name])) return this;
1670
+ if(typeof event !== "object") throw new Error(`"properties" argument must be an object.`);
1671
+
1672
+ event.event = event_name;
1673
+ event.target = this;
1674
+
1675
+ for(const callback of this.callback[event_name])
1676
+ {
1677
+ try
1678
+ {
1679
+ callback.call(this, event);
1680
+ }catch(ex)
1681
+ {
1682
+ error(`${ui._msg[6]}${this.constructor.name}: ${event} \n\n${ex.stack}\n\nEvent ${stringify(event)}`, false);
1683
+ }
1684
+ }
1685
+
1686
+ return this;
1687
+ }
1688
+
1689
+ /**
1690
+ * Gets or sets the minimum size of the icon's largest side (in pixels).
1691
+ * If the icon's largest side is smaller than the specified minimum, it will be scaled up proportionally.
1692
+ * Applies to Button, Label, List, and ComboBox.
1693
+ *
1694
+ * @param {number|null} [min_size] - Sets the minimum size of a larger side of icon, or resets to no minimum when `null` is passed.
1695
+ * @return {number|null|Object} If called without arguments, returns `number` value, otherwise returns `this` component instance.
1696
+ */
1697
+ minIconSize()
1698
+ {
1699
+ return this.property("minIconSize", arguments);
1700
+ }
1701
+
1702
+ /**
1703
+ * Gets or sets the maximum size of the icon's largest side (in pixels).
1704
+ * If the icon's largest side is larger than the specified maximum, it will be scaled down proportionally.
1705
+ * Applies to Button, Label, List, and ComboBox.
1706
+ *
1707
+ * @param {number|null} [max_size] - Sets the maximum size of a larger side of icon, or resets to no maximum when `null` is passed.
1708
+ * @return {number|null|Object} If called without arguments, returns `number` value, otherwise returns `this` component instance.
1709
+ */
1710
+ maxIconSize()
1711
+ {
1712
+ return this.property("maxIconSize", arguments);
1713
+ }
1714
+
1715
+ /**
1716
+ * Sets or gets an array of keyboard shortcuts handled by {@link UIComponent#onShortcut|onShortcut()} event handler.
1717
+ *
1718
+ * @param {object[]|string[]} [shortcuts] - An array of shortcut objects {shortcut:{string}, scope: {ShortcutScope}}, or an Array of String shortcuts (for strings a default {@link ShortcutScope} will be assigned).
1719
+ * @returns {this|object[]} If called without arguments, then current shortcuts array is returned, otherwise an instance of `this` component is returned.
1720
+ * @see ShortcutScope
1721
+ * @example <h2>Setting shortcuts as strings</h2>
1722
+ * window.shortcuts(["f1", "ctrl+s"]);
1723
+ * @example <h2>Setting shortcuts as objects</h2>
1724
+ * window.shortcuts([
1725
+ * {
1726
+ * key: "control+s",
1727
+ * scope: ShortcutScope.Window, // optional
1728
+ * action: "save_file" // optional
1729
+ * }
1730
+ * ]);
1731
+ * @example <h2>Clearing all shortcuts</h2>
1732
+ * window.shortcuts([]); // pass an empty array
1733
+ * @example <h2>Retrieving current shortcuts</h2>
1734
+ * const shortcuts = window.shortcuts(); // returns an Array of objects with `key` and `scope` properties.
1735
+ */
1736
+ shortcuts()
1737
+ {
1738
+ if(arguments.length > 0)
1739
+ {
1740
+ if(!Array.isArray(arguments[0])) throw new TypeError(`First argument to shortcuts() method must be an array, not '${typeof arguments[0]}'`);
1741
+
1742
+ for(let i = 0; i < arguments[0].length; i++)
1743
+ {
1744
+ if(typeof arguments[0][i] === "string")
1745
+ {
1746
+ arguments[0][i] = {key: arguments[0][i]};
1747
+ }
1748
+ else if(typeof arguments[0][i] !== "object" || typeof arguments[0][i].key !== "string")
1749
+ {
1750
+ arguments[0].splice(i--, 1); // remove invalid type
1751
+ continue;
1752
+ }
1753
+
1754
+ if(typeof arguments[0][i].scope !== "string")
1755
+ {
1756
+ arguments[0][i].scope = this instanceof Window ? ShortcutScope.Window : (this instanceof ComponentContainer ? ShortcutScope.Ancestor : ShortcutScope.Focused) // set default scope
1757
+ }
1758
+
1759
+ if(typeof arguments[0][i].action !== "string") arguments[0][i].action = arguments[0][i].key; // default action is the key value
1760
+ }
1761
+ }
1762
+
1763
+ return this.property("shortcuts", arguments);
1764
+ }
1765
+
1766
+ //endregion
1767
+
1768
+ //region Event Handlers
1769
+
1770
+ /**
1771
+ * Overrides current error handler.
1772
+ *
1773
+ * @param {function(ErrorEvent): any} [callback=undefined] - The event handler function receiving an {@link ErrorEvent} object. Pass `null` to unregister all handler functions.
1774
+ * @return {this}
1775
+ * @see ErrorEvent
1776
+ */
1777
+ onError(callback)
1778
+ {
1779
+ if(typeof callback === "function") this.callback.onError = [callback];
1780
+ return this;
1781
+ }
1782
+
1783
+ /**
1784
+ * Registers or unregisters a handler function for an event when the component gets disposed, either by calling destroy() method or loosing all references to the component.
1785
+ *
1786
+ * @param {function(DisposeEvent): any} [callback=undefined] - The event handler function receiving {@link DisposeEvent} object. Pass `null` to unregister all handler functions.
1787
+ * @param {boolean} [remove=false] - Set to `true` to remove this event handler
1788
+ * @return {this}
1789
+ * @see DisposeEvent
1790
+ */
1791
+ onDispose(callback, remove = false)
1792
+ {
1793
+ const name = "onDispose";
1794
+
1795
+ // return callbacks
1796
+ if(callback === null)
1797
+ {
1798
+ return Array.isArray(this.callback[name]) ?? [];
1799
+ }
1800
+ // remove callback
1801
+ else if(remove === true)
1802
+ {
1803
+ if(Array.isArray(this.callback[name]))
1804
+ {
1805
+ const i = this.callback[name].indexOf(callback); // find callback
1806
+ if(i > -1) this.callback[name].splice(i, 1); // remove it
1807
+ if(this.callback[name].length === 0) delete this.callback[name]; // delete array
1808
+ }
1809
+ }
1810
+ // add callback
1811
+ else if(typeof callback === "function")
1812
+ {
1813
+ if(!Array.isArray(this.callback[name])) this.callback[name] = []; // create array
1814
+ this.callback[name].push(callback); // add callback (don't bind to this here, do it at call)
1815
+ }
1816
+
1817
+ return this;
1818
+ }
1819
+
1820
+ /**
1821
+ * Gets, registers or unregisters a handler function for component's primary action event.
1822
+ *
1823
+ * @param {function(ActionEvent): any} [callback=undefined] - The event handler function that receives a {@link ActionEvent} object. Pass `null` to unregister all handler functions.
1824
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1825
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1826
+ * @see ActionEvent
1827
+ * @example
1828
+ * // Example event object:
1829
+ * const e = {
1830
+ * target: object, // The component that triggered the event.
1831
+ * command: string, // Button text or a specified command using .command("") method
1832
+ * modifiers: number, // Keyboard modifiers (bitmask or flags).
1833
+ * alt: boolean, // Indicates if ALT key was pressed.
1834
+ * shift: boolean, // Indicates if SHIFT key was pressed.
1835
+ * ctrl: boolean, // Indicates if CTRL key was pressed.
1836
+ * type: string, // `action` or `index_change`. Present when applicable.
1837
+ * x: number, // Mouse x position relative to component
1838
+ * y: number, // Mouse y position relative to component
1839
+ * screenX: number, // Mouse x position relative to default screen
1840
+ * screenY: number, // Mouse y position relative to default screen
1841
+ * interactive: boolean, // Indicates if the action was triggered by user's interaction (not programmatically). Present when applicable.
1842
+ * selected: false // Present when applicable.
1843
+ * };
1844
+ */
1845
+ onAction(...args)
1846
+ {
1847
+ if(args.length === 0) return this.event("onAction");
1848
+ return this.event("onAction", args[0], args[1] ?? false);
1849
+ }
1850
+
1851
+ /**
1852
+ * Gets, registers or unregisters a mouse buttons event handler function.
1853
+ *
1854
+ * @param {function(MouseEvent): any} [callback=undefined] - The event handler function that receives a {@link MouseEvent} object. Pass `null` to unregister all handler functions.
1855
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1856
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1857
+ * @see MouseEvent
1858
+ * @example
1859
+ * // Example event object:
1860
+ * const e = {
1861
+ * target: object, // The component that triggered the event.
1862
+ * type: string, // `click`, `down`, `up`, `enter`, or `leave`.
1863
+ * button: number, // Button number. One of MouseButton enum values.
1864
+ * clicks: number, // Amount of time the click happened. Present when applicable.
1865
+ * modifiers: number, // Keyboard modifiers (bitmask or flags).
1866
+ * alt: boolean, // Indicates if ALT key was pressed.
1867
+ * shift: boolean, // Indicates if SHIFT key was pressed.
1868
+ * ctrl: boolean, // Indicates if CTRL key was pressed.
1869
+ * x: number, // Present when applicable.
1870
+ * y: number, // Present when applicable.
1871
+ * screenX: number, // Present when applicable.
1872
+ * screenY: number, // Present when applicable.
1873
+ * };
1874
+ */
1875
+ onMouseEvents(...args)
1876
+ {
1877
+ if(args.length === 0) return this.event("onMouseEvents");
1878
+ return this.event("onMouseEvents", args[0], args[1] ?? false);
1879
+ }
1880
+
1881
+ /**
1882
+ * Gets, registers or unregisters a mouse motion event handler function.
1883
+ *
1884
+ * @param {function(MouseMotionEvent): any} [callback=undefined] - The event handler function that receives a {@link MouseMotionEvent} object. Pass `null` to unregister all handler functions.
1885
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1886
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1887
+ * @see MouseMotionEvent
1888
+ * @example
1889
+ * // Example event object:
1890
+ * const e = {
1891
+ * target: object, // The component that triggered the event.
1892
+ * type: string, // `move` or `drag`.
1893
+ * button: number, // Button number. One of MouseButton enum values. Present when applicable.
1894
+ * modifiers: number, // Keyboard modifiers (bitmask or flags).
1895
+ * x: number, // Mouse x position relative to component
1896
+ * y: number, // Mouse y position relative to component
1897
+ * screenX: number, // Mouse x position relative to default screen
1898
+ * screenY: number, // Mouse y position relative to default screen
1899
+ * };
1900
+ */
1901
+ onMouseMotion(...args)
1902
+ {
1903
+ if(args.length === 0) return this.event("onMouseMotion");
1904
+ return this.event("onMouseMotion", args[0], args[1] ?? false);
1905
+ }
1906
+
1907
+ /**
1908
+ * Gets, registers or unregisters a mouse wheel event handler function.
1909
+ *
1910
+ * @param {function(MouseWheelEvent): any} [callback=undefined] - The event handler function that receives a {@link MouseWheelEvent} object. Pass `null` to unregister all handler functions.
1911
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1912
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1913
+ * @see MouseWheelEvent
1914
+ * @example
1915
+ * // Example event object:
1916
+ * const e = {
1917
+ * target: object, // The component that triggered the event.
1918
+ * type: string, // `wheel`.
1919
+ * button: number, // Button number. One of MouseButton enum values. Present when applicable.
1920
+ * scrollAmount: number, // Returns the number of units that should be scrolled per click of mouse wheel rotation.
1921
+ * rotation: number, // Indicates the number of "clicks" the mouse wheel was rotated.
1922
+ * // A partial rotation may occur if the mouse supports a high-resolution wheel.
1923
+ * // In this case, the number is zero until a full "click" has been accumulated.
1924
+ * modifiers: number, // Keyboard modifiers (bitmask or flags).
1925
+ * alt: boolean, // Indicates if ALT key was pressed.
1926
+ * shift: boolean, // Indicates if SHIFT key was pressed.
1927
+ * ctrl: boolean, // Indicates if CTRL key was pressed.
1928
+ * x: number, // Mouse x position relative to component
1929
+ * y: number, // Mouse y position relative to component
1930
+ * screenX: number, // Mouse x position relative to default screen
1931
+ * screenY: number, // Mouse y position relative to default screen
1932
+ * };
1933
+ */
1934
+ onMouseWheel(...args)
1935
+ {
1936
+ if(args.length === 0) return this.event("onMouseWheel");
1937
+ return this.event("onMouseWheel", args[0], args[1] ?? false);
1938
+ }
1939
+
1940
+ /**
1941
+ * Gets, registers or unregisters a keyboard keys' event handler function.
1942
+ *
1943
+ * @param {function(KeyEvent): any} [callback=undefined] - The event handler function that receives a {@link KeyEvent} object. Pass `null` to unregister all handler functions.
1944
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1945
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1946
+ * @see KeyEvent
1947
+ * @example
1948
+ * // Example event object:
1949
+ * const e = {
1950
+ * target: object, // The component that triggered the event.
1951
+ * type: string, // `up`, `down`, `type`.
1952
+ * keyCode: number // Numeric code associated with the key
1953
+ * extendedKeyCode: number // The extended key code is a unique id assigned to a key on the keyboard just like keyCode. However, unlike keyCode, this value depends on the current keyboard layout.
1954
+ * keyChar: string // Key character
1955
+ * modifiers: number, // Keyboard modifiers (bitmask or flags).
1956
+ * modifiersText: string // Modifiers representation as a string.
1957
+ * alt: boolean, // Indicates if ALT key was pressed.
1958
+ * shift: boolean, // Indicates if SHIFT key was pressed.
1959
+ * ctrl: boolean, // Indicates if CTRL key was pressed.
1960
+ * keyLocation: string // `standard`, `numpad`, `left`, `right`. Location of the key on keyboard. Only present when type is `up` or `down`.
1961
+ * };
1962
+ */
1963
+ onKey(...args)
1964
+ {
1965
+ if(args.length === 0) return this.event("onKey");
1966
+ return this.event("onKey", args[0], args[1] ?? false);
1967
+ }
1968
+
1969
+ /**
1970
+ * Gets, registers or unregisters a focus event handler function.
1971
+ *
1972
+ * @param {function(FocusEvent): any} [callback=undefined] - The event handler function that receives a {@link FocusEvent} object. Pass `null` to unregister all handler functions.
1973
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1974
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1975
+ * @see FocusEvent
1976
+ * @example
1977
+ * // Example event object:
1978
+ * const e = {
1979
+ * target: object, // The component that triggered the event.
1980
+ * type: string, // `focus`, `blur`.
1981
+ * cause: string // `UNKNOWN`, `ACTIVATION`, `TRAVERSAL_FORWARD`, `TRAVERSAL_BACKWARD`.
1982
+ * temporary: boolean, // Identifies the focus change event as temporary or permanent
1983
+ * };
1984
+ */
1985
+ onFocusEvents(...args)
1986
+ {
1987
+ if(args.length === 0) return this.event("onFocusEvents");
1988
+ return this.event("onFocusEvents", args[0], args[1] ?? false);
1989
+ }
1990
+
1991
+ /**
1992
+ * Gets, registers or unregisters an event handler function for events: resize, move, show, hide.
1993
+ *
1994
+ * @param {function(ComponentEvent): any} [callback=undefined] - The event handler function that receives a {@link ComponentEvent} object. Pass `null` to unregister all handler functions.
1995
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
1996
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
1997
+ * @see ComponentEvent
1998
+ * @example
1999
+ * // Example event object:
2000
+ * const e = {
2001
+ * target: object, // The component that triggered the event.
2002
+ * type: string, // `resize`, `move`, `show`, `hide`.
2003
+ * interactive: boolean, // Indicates if the event was triggered by user's interaction (not programmatically). Present when e.type is `move` or `resize`.
2004
+ * x: number, // New x position of component.
2005
+ * y: number, // New y position of component.
2006
+ * width: number, // New width of component. Present when e.type is `resize`.
2007
+ * height: number, // New height of component. Present when e.type is `resize`.
2008
+ * innerWidth: number, // New width of content area of the component. Present when e.type is `resize` and component is Window or InternalWindow.
2009
+ * innerHeight: number, // New height of content area of the component. Present when e.type is `resize` and component is Window or InternalWindow.
2010
+ *};
2011
+ */
2012
+ onComponentEvents(...args)
2013
+ {
2014
+ if(args.length === 0) return this.event("onComponentEvents");
2015
+ return this.event("onComponentEvents", args[0], args[0] ?? false);
2016
+ }
2017
+
2018
+ /**
2019
+ * Gets, registers or unregisters an event handler that listens for changes to this component's properties.
2020
+ *
2021
+ * @param {function(PropertyChangeEvent): any} [callback=undefined] - The event handler function that receives a {@link PropertyChangeEvent} object. Pass `null` to unregister all handler functions.
2022
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
2023
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
2024
+ * @see PropertyChangeEvent
2025
+ * @example
2026
+ * // Example event object:
2027
+ * const e = {
2028
+ * target: object, // The container component that triggered the event.
2029
+ * type: string, // `change`.
2030
+ * property: string, // The property that was changed
2031
+ * oldValue: any, // Previous value of the changed property
2032
+ * newValue: any // New value of the changed property
2033
+ * };
2034
+ */
2035
+ onPropertyChange(...args)
2036
+ {
2037
+ if(args.length === 0) return this.event("onPropertyChange");
2038
+ return this.event("onPropertyChange", args[0], args[1] ?? false);
2039
+ }
2040
+
2041
+ /**
2042
+ * Gets, registers or unregisters a handler function for drag related events. Call without parameters to get an array of already registered handler functions.
2043
+ *
2044
+ * @param {function(DragEvent): any} [callback=undefined] - The event handler function that receives a {@link DragEvent} object. Pass `null` to unregister all handler functions.
2045
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
2046
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
2047
+ * @see DragEvent
2048
+ *
2049
+ * @example
2050
+ * // Example event object:
2051
+ * const e = {
2052
+ * target: object, // The component that triggered the event.
2053
+ * type: string, // `dragstart`, `dragend`, `cancel`.
2054
+ * action: string // One of DragDropAction enum values. Present when applicable.
2055
+ * x: number, // X position of a pointer relative to this component. Present when e.type is `dragstart` or `dragend`.
2056
+ * y: number, // Y position of a pointer relative to this component. Present when e.type is `dragstart` or `dragend`.
2057
+ * screenX: number, // X position of a pointer relative to default screen. Present when e.type is `dragstart` or `dragend`.
2058
+ * screenY: number, // Y position of a pointer relative to default screen. Present when e.type is `dragstart` or `dragend`.
2059
+ * button: number, // Indicates a pointing device button involved in dragging. Value of MouseButton enum.
2060
+ * modifiers: number, // Keyboard modifiers (bitmask or flags). Present when e.type is `dragstart`.
2061
+ * modifiersText: string, // Keyboard modifiers. Present when e.type is `dragstart`.
2062
+ * alt: boolean, // Indicates if ALT key was pressed. Present when e.type is `dragstart`.
2063
+ * shift: boolean, // Indicates if SHIFT key was pressed. Present when e.type is `dragstart`.
2064
+ * ctrl: boolean, // Indicates if CTRL key was pressed. Present when e.type is `dragstart`.
2065
+ * selected: number[] // Array of selected indexes of a List component whose items are being dragged. Present when List items are being dragged.
2066
+ * };
2067
+ */
2068
+ onDragEvents(...args)
2069
+ {
2070
+ if(args.length === 0) return this.event("onDragEvents");
2071
+ return this.event("onDragEvents", args[0], args[1] ?? false);
2072
+ }
2073
+
2074
+ /**
2075
+ * Gets, registers or unregisters a handler function for drop related events. Call without parameters to get an array of already registered handler functions.
2076
+ *
2077
+ * @param {function(DropEvent): any} [callback=undefined] - The event handler function that receives a {@link DropEvent} object. Pass `null` to unregister all handler functions.
2078
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
2079
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
2080
+ * @see DropEvent
2081
+ * @example <h2>{@link DropEvent} object format:</h2>
2082
+ * const e = {
2083
+ * target: object, // The component that triggered the event.
2084
+ * type: string, // `dragenter`, `dragover`, `dragleave`, `drop`.
2085
+ * action: string // One of the values of enum DragDropAction. Present when applicable.
2086
+ * data: object // An object that contains the data that is being dropped. Present only when e.type is `drop` or `dragenter`.
2087
+ * source: object, // A component that is being dragged or dropped onto this component.
2088
+ * // Present only when a another UI component is dragged onto this component and `DataType.Component` is of the types set by dragDataType().
2089
+ * x: number, // X position of a pointer relative to this component. Present when e.type is `dragstart` or `drop`.
2090
+ * y: number, // Y position of a pointer relative to this component. Present when e.type is `dragstart` or `drop`.
2091
+ * screenX: number, // X position of a pointer relative to default screen. Present when e.type is `dragstart` or `drop`.
2092
+ * screenY: number, // Y position of a pointer relative to default screen. Present when e.type is `dragstart` or `drop`.
2093
+ * selected: number[] // Array of selected indexes of a List component whose items are being dragged. Present when List items are being dragged.
2094
+ * };
2095
+ */
2096
+ onDropEvents(...args)
2097
+ {
2098
+ if(args.length === 0) return this.event("onDropEvents");
2099
+
2100
+ return this.event("onDropEvents", args[0], args[1] ?? false);
2101
+ }
2102
+
2103
+ /**
2104
+ * Gets, registers or unregisters a handler function for component's text value change event. Applied to components where text can be changed by user, such as TextField, PasswordField, TextArea, DocumentEditor, ComboBox, Spinner, etc.
2105
+ *
2106
+ * @param {function(ChangeEvent): any} [callback=undefined] - The event handler function that receives a {@link ChangeEvent} object. Pass `null` to unregister all handler functions.
2107
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
2108
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
2109
+ * @see ChangeEvent
2110
+ */
2111
+ onChange(...args)
2112
+ {
2113
+ if(args.length === 0) return this.event("onChange");
2114
+ return this.event("onChange", args[0], args[1] ?? false);
2115
+ }
2116
+
2117
+ /**
2118
+ * Gets, registers or unregisters a handler function for component's event when ancestor is added, removed, or moved.
2119
+ *
2120
+ * @param {function(AncestorEvent): any} [callback=undefined] - The event handler function that receives a {@link AncestorEvent} object. Pass `null` to unregister all handler functions.
2121
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
2122
+ * @returns {this|function[]} An array of handler functions if no arguments are passed, otherwise `this` component for method chaining.
2123
+ * @see AncestorEvent
2124
+ * @example
2125
+ * // Example event object:
2126
+ * const e = {
2127
+ * target: object, // The component that triggered the event.
2128
+ * type: string, // `add`, `remove`, `move`.
2129
+ * ancestor: object, // The ancestor component.
2130
+ * };
2131
+ */
2132
+ onAncestorEvents(...args)
2133
+ {
2134
+ if(args.length === 0) return this.event("onAncestorEvents");
2135
+ return this.event("onAncestorEvents", args[0], args[1] ?? false);
2136
+ }
2137
+
2138
+ /**
2139
+ * Gets, registers or unregisters a handler function for component's {@link ShortcutEvent} when a shortcut is invoked.
2140
+ *
2141
+ * @param {function(ShortcutEvent): any} [callback=undefined] - The event handler function that receives a {@link ShortcutEvent} object. Pass `null` to unregister all handler functions.
2142
+ * @param {boolean} [remove=false] - Set to `true` to unregister the event handler.
2143
+ * @returns {Function[]|UIComponent}
2144
+ */
2145
+ onShortcut(...args)
2146
+ {
2147
+ if(args.length === 0) return this.event("onShortcut");
2148
+ return this.event("onShortcut", args[0], args[1] ?? false);
2149
+ }
2150
+
2151
+ //endregion
2152
+ }
2153
+
2154
+ export {UIComponent};