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,92 @@
1
+
2
+ import {UIComponent} from "./UIComponent.js";
3
+
4
+ /**
5
+ * Menu item UI component that extends UIComponent class.
6
+ * This class is extended by Menu, CheckBoxMenuItem, and RadioButtonMenuItem.
7
+ * For more methods and properties for this component or sub-components check UIComponent class.
8
+ * @class
9
+ * @global
10
+ */
11
+ export class MenuItem extends UIComponent
12
+ {
13
+ /**
14
+ * Constructor for Menu, MenuItem, CheckBoxMenuItem, and RadioButtonMenuItem.
15
+ *
16
+ * @param {string} [text] - Text displayed in the menu item
17
+ */
18
+ constructor()
19
+ {
20
+ super(new.target.name, (new.target === MenuItem || new.target === Menu || new.target === CheckBoxMenuItem || new.target === RadioButtonMenuItem), ...arguments); // We use new.target.name to use the name of the class or subclasses that's being instantiated
21
+
22
+ this.finalize(...(new.target === MenuItem ? [new.target] : [MenuItem, new.target])); // lock this class's methods and subclasses'
23
+ }
24
+
25
+ /**
26
+ * Sets keyboard shortcut (accelerator) to menu item type of components: MenuItem, CheckBoxMenuItem, RadioButtonMenuItem. See examples below. Allows to activate the menu item action using specified keyboard key combination on keyboard. This shortcut is usually display in the right side of the mnu item's text.
27
+ *
28
+ * @param {string} keys - Key combination as a string. See example how to construct it.
29
+ * @returns String Description of keyboard shortcut
30
+ * @example
31
+ * Modifiers: CTRL|CONTROL|SHIFT|ALT|ALTGRAPH|META
32
+ *
33
+ * Keys: A-Z|0-9|NUMPAD[0-9]|SPACE|INSERT|ESCAPE|F[1-24]|TAB|ADD|SUBTRACT|DELETE|BACK_SPACE|BACK_SLASH|
34
+ * BRACELEFT|BRACERIGHT|OPEN_BRACKET|CLOSE_BRACKET|SEMICOLON|COMMA|CAPS_LOCK|BACK_QUOTE|MINUS|PERIOD|
35
+ * UP|DOWN|RIGHT|LEFT|MULTIPLY|DIVIDE|NUM_LOCK|HOME|END|PAGE_UP|PAGE_DOWN|PAUSE|SCROLL_LOCK|WINDOWS|
36
+ * CONTEXT_MENU|DECIMAL|ENTER
37
+ *
38
+ * Example key combinations: CTRL+Q, ALT+F1, CTRL+SHIFT+5, CTRL+N4, META+SHIFT+6, DELETE+SHIFT, SHIFT+UP, CTRL+OPEN_BRACKET, ATL+DOWN
39
+ *
40
+ * menuItem1.accelerator("CTRL+Q");
41
+ * menuItemUP.accelerator("SHIFT+NUMPAD[8]");
42
+ *
43
+ */
44
+ accelerator()
45
+ {
46
+ if(this instanceof Menu) throw new Error(`Menu doesn't support accelerator. Use mnemonics instead.`);
47
+
48
+ if(arguments.length > 0)
49
+ {
50
+ ui.sendToGUI({control: this.id, set: "accelerator", value: arguments[0]});
51
+ return this;
52
+ }
53
+ else
54
+ {
55
+ return ui.sendToGUI({control: this.id, get: "accelerator"});
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Gets or sets the mnemonic (keyboard shortcut key) for menu item type of components: Menu, MenuItem, CheckBoxMenuItem, RadioButtonMenuItem. It's not same as setting accelerator(...).
61
+ * A mnemonic allows users to activate the menu item using the keyboard (typically Alt + key).<br />
62
+ * NOTE: Not all systems and UIs show underline under mnemonic character. Some systems only show it after Alt key was pressed.
63
+ *
64
+ * @param {number|string|null} [key] - A key code integer or a single ASCII character representing the mnemonic key (case-insensitive). If omitted, acts as a getter. To clear mnemonic, pass `0`, `""`, or `null`.
65
+ * @returns {number|MenuItem} If called without arguments, returns the current mnemonic key code integer. If called with an argument, returns `this` instance object for chaining.
66
+ * @example
67
+ * menuItem.text("Quit").mnemonic("q"); // Makes letter Q to be used as the keyboard shortcut key
68
+ * menuItem.mnemonic(""); // clears the mnemonic
69
+ */
70
+ mnemonic()
71
+ {
72
+ return this.property("mnemonic", arguments);
73
+ }
74
+
75
+ /**
76
+ * Opens this menu item and all of its parent menus or context menu.
77
+ *
78
+ * @param {object} [position] - Example: {x: 100, y: 100}. Use only when root ancestor of this item is ContextMenu, to specify position where to position the ContextMenu.
79
+ * If invoker is specified, then position is relative to position of invoker component, otherwise it's a screen position.
80
+ * @param {object} [invoker] - Used to set invoker if the context menu isn't assigned to any object and you want to be able to get invoker by using "invoker" getter.
81
+ * @return {MenuItem} Returns `this` MenuItem instance.
82
+ */
83
+ open(position = null, invoker = null)
84
+ {
85
+ const msg = {set: "open", control: this.id, data: {}};
86
+ if(position != null) msg.data.pos = position;
87
+ if(invoker instanceof UIComponent) msg.data.invoker = invoker.id;
88
+
89
+ ui.sendToGUI(msg);
90
+ return this;
91
+ }
92
+ }
@@ -0,0 +1,50 @@
1
+
2
+ import {ShapeContainer} from "./ShapeContainer.js";
3
+
4
+ /**
5
+ * Panel UI component, rectangular UI component that can be used in many different ways:
6
+ * - Container for other components
7
+ * - As a horizontal/vertical line or a rectangle
8
+ * - Container with scroll bars that can contain objects even outside its boundaries.
9
+ * - Background can be set to color, image, and gradient (like in CSS), which opens lots of opportunities, such as animating background, streaming MJPEG video by setting frames as background of the panel sequentially, and more.
10
+ * - As a canvas to draw Shapes or create an interactive game using Shapes drawn on this canvas.
11
+ *
12
+ * Panel class extends ComponentContainer and ShapeContainer, which extend UIComponent
13
+ *
14
+ * NOTE: Add Panel to Tabs, SplitView, or ScrollPane containers before adding child components to the panel to have expected behavior when setting anchors to child components of the Panel.
15
+ * @class
16
+ * @global
17
+ * @see ShapeContainer
18
+ * @see ComponentContainer
19
+ */
20
+ class Panel extends ShapeContainer
21
+ {
22
+ _shapes = [];
23
+ temp_token = undefined;
24
+
25
+ /**
26
+ * Panel constructor. Creates an instance of Panel UI component.
27
+ *@param {string} [text] - Text associated with this Panel. It will be used, for example, for a tab name when this panel is added to Tabs component.
28
+ */
29
+ constructor(...args)
30
+ {
31
+ super("Panel", (new.target === Panel), ...args);
32
+ //this.finalize(Panel, ShapeContainer);
33
+
34
+ // load UI definition file
35
+ if(new.target !== Panel && this.applyUIDefinition != null) this.applyUIDefinition(new.target.name); // must be called AFTER super()
36
+ }
37
+
38
+ /**
39
+ * Sets or gets component opacity float value between 0 and 1 inclusive.
40
+ *
41
+ * @param {number} [opacity] - A number between 0 and 1 inclusive.
42
+ * @return {number|this} If `opacity` argument is provided, `this` component is returned, otherwise opacity `number` is returned.
43
+ */
44
+ opacity(...args)
45
+ {
46
+ return this.property("opacity", args);
47
+ }
48
+ }
49
+
50
+ export {Panel};
@@ -0,0 +1,47 @@
1
+
2
+ import {TextComponent} from "./TextComponent.js";
3
+
4
+ /**
5
+ * Password field is used to type masked text. Use echoChar() method to set a custom mask character.
6
+ * All other methods are inherited from TextComponent and UIComponent.
7
+ * @class
8
+ * @global
9
+ */
10
+ export class PasswordField extends TextComponent
11
+ {
12
+ /**
13
+ * PasswordField constructor. Creates an instance of PasswordField UI component.
14
+ *
15
+ * @param {string} [value] - Text value.
16
+ */
17
+ constructor(...args)
18
+ {
19
+ if(args.length > 0)
20
+ {
21
+ super(new.target.name, (new.target === PasswordField), args[0]);
22
+ }
23
+ else
24
+ {
25
+ super(new.target.name, (new.target === PasswordField));
26
+ }
27
+
28
+ this.finalize(new.target, TextComponent);
29
+ }
30
+
31
+ /**
32
+ * Sets/Gets a Unicode character to use for masking real characters.
33
+ *
34
+ * Some disk characters to try:
35
+ * const myChar = '\u00B7';
36
+ * const myChar = '\u2022';
37
+ * const myChar = '\u25CF';
38
+ * const myChar = '\u2B24';
39
+ *
40
+ * @param {string} [character] - A one character string character. If the string has more than one character, only first one will be used.
41
+ * @returns {PasswordField|string} Returns an instance of `this` object when setting a value, otherwise current value is returned.
42
+ */
43
+ echoChar(...args)
44
+ {
45
+ return this.property("character", args);
46
+ }
47
+ }
@@ -0,0 +1,236 @@
1
+ import {Shape} from "./Shape.js";
2
+
3
+ /**
4
+ * Polygon is a shape with multiple corners at specified points. Each point is an X and Y coordinate relative to the shapes X and Y position.
5
+ *
6
+ * Polygon extends {@link Shape} class.
7
+ *
8
+ * @class
9
+ * @global
10
+ * @see Shape
11
+ */
12
+ export class Polygon extends Shape
13
+ {
14
+ _points = [];
15
+
16
+ /**
17
+ * Creates an instance of Polygon shape object.
18
+ *
19
+ * @param {Stroke} [stroke] - A border of the shape.
20
+ * @param {string|null} [background] - Color, gradient, or image background, similar to CSS. Use an empty string to remove the background.
21
+ * @param {number} [opacity] - Opacity of the shape (0 to 1).
22
+ * @param {number} [x] - Horizontal position from the left edge.
23
+ * @param {number} [y] - Vertical position from the top edge.
24
+ * @param {Point[]} [_points=[]] - An array of `Point` objects that represent each corner of the shape [{x:number,y:number}, ...]. For example a simple start has 10 points.
25
+ */
26
+ constructor(stroke = null, background = null, opacity = 1, x = 0, y = 0, _points = [])
27
+ {
28
+ super(undefined, stroke, background, opacity, x, y);
29
+ this._type = "polygon";
30
+
31
+ if(!Array.isArray(_points)) throw new TypeError(`Polygon.points() expects an Array of point objects, but ${typeof _points} was received.`);
32
+
33
+ // converts points to one array of numbers
34
+ this._points = new Array(_points.length * 2);
35
+ _points.forEach((p, i) =>
36
+ {
37
+ this._points[i*2] = Number.isFinite(p.x) ? p.x : 0;
38
+ this._points[i*2+1] = Number.isFinite(p.y) ? p.y : 0;
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Adds a point to the Polygon as a last point or at a specified index.
44
+ *
45
+ * @param {Point} point - A {@link Point} formatted object with X and Y coordinates (e.g., {x: 20, y:30});
46
+ * @param {number} [insertAt=undefined] - If specified, the point will be added at this position; otherwise at the end.
47
+ * @returns {Polygon} Returns this Polygon shape object for chaining.
48
+ * @see Point
49
+ * @example <h2>Adding a point to the end of the points array:</h2>
50
+ * polygon.addPoint({x:20, y:30});
51
+ *
52
+ * @example <h2>Adding a point at a specified index:</h2>
53
+ * polygon.addPoint({x:30, y:60}, 0); // adds a point the beginning of points array
54
+ */
55
+ addPoint(point, insertAt = undefined)
56
+ {
57
+ if(typeof point !== "object" || !("x" in point && "y" in point)) throw new Error(`Point parameter must has "x" and "y" numeric properties.`);
58
+ if(insertAt !== undefined && !Number.isSafeInteger(insertAt)) throw new TypeError(`Polygon.addPoint() method requires 'insertAt' parameter be an integer parameter.`);
59
+
60
+ if(insertAt == null || typeof insertAt !== "number" || insertAt > this._points.length) // add point
61
+ {
62
+ this._points.push(point.x, point.y);
63
+ }
64
+ else // insert point
65
+ {
66
+ insertAt = Math.max(0, Math.min(this._points.length / 2, insertAt));
67
+ this._points.splice(insertAt * 2, 0, point.x, point.y);
68
+ }
69
+
70
+ if(this.parent != null)
71
+ {
72
+ const shapeIndex = this.parent.shapeIndex(this);
73
+
74
+ if(shapeIndex > -1) ui.sendToGUI({
75
+ control: this.parent.id,
76
+ set: "update_shape",
77
+ value: {index: shapeIndex, addPoint: point, insertAt: insertAt}
78
+ });
79
+ }
80
+
81
+ return this;
82
+ }
83
+
84
+ /**
85
+ * Removes a point from the Polygon at a specified index.
86
+ *
87
+ * @param {number} index - Index of the point to return.
88
+ * @returns {{x:number, y:number}|null} Returns the deleted point or `null` of there was no point at specified index.
89
+ */
90
+ removePoint(index)
91
+ {
92
+ if(!Number.isSafeInteger(index)) throw new TypeError(`Polygon.removePoint(index) expects an integer index, but ${typeof index} was received.`);
93
+
94
+ let p = this.point(index);
95
+
96
+ if(p != null)
97
+ {
98
+ this._points.splice(index * 2, 2);
99
+
100
+ if(this.parent != null)
101
+ {
102
+ const shapeIndex = this.parent.shapeIndex(this);
103
+ if(shapeIndex > -1) ui.sendToGUI({
104
+ control: this.parent.id,
105
+ set: "update_shape",
106
+ value: {index: shapeIndex, removePoint: index}
107
+ });
108
+ }
109
+ }
110
+
111
+ return p;
112
+ }
113
+
114
+ /**
115
+ * Sets or gets a point of the Polygon {x:number, y:number} at a specified index.
116
+ *
117
+ * @param {number} index - Index of the existing point to update.
118
+ * @param {Point|number} [point_or_x=undefined] - New point object or X coordinate of the new point.
119
+ * @param {number} [y=undefined] - Y coordinate of the new point.
120
+ * @returns {Point|null|Polygon} When getting a point, returns a point object or `undefined` when a point at specified index doesn't exist. When setting the point, returns this Polygon or throws an Error.
121
+ * @see Point
122
+ * @example <h2>Updating a point at index 3:</h2>
123
+ * polygon.point(3, 50, 80); // x = 50, y = 80
124
+ *
125
+ * // or...
126
+ *
127
+ * polygon.point(3, {x:50, y:80});
128
+ *
129
+ * @example <h2>Getting a point at index 3:</h2>
130
+ * let p = polygon.point(3);
131
+ */
132
+ point(index, point_or_x = undefined, y = undefined)
133
+ {
134
+ if(!Number.isSafeInteger(index)) throw new TypeError(`Polygon.point(index) expects an integer index, but ${typeof index} was received.`);
135
+
136
+ // gets a point
137
+ if(point_or_x === undefined)
138
+ {
139
+ if(this._points[index + 1] === undefined) return null;
140
+ return {x: this._points[index], y: this._points[index + 1]};
141
+ }
142
+
143
+ if(typeof point_or_x === "object")
144
+ {
145
+ if(!("x" in point_or_x && "y" in point_or_x)) throw new Error(`Invalid point object. It must have numeric "x" and "y" properties.`);
146
+ }
147
+ else if(typeof point_or_x === "number" && Number.isFinite(point_or_x) && typeof y === "number" && Number.isFinite(y))
148
+ {
149
+ point_or_x = {x: point_or_x, y: y};
150
+ }
151
+ else
152
+ {
153
+ throw new Error(`Invalid arguments passed to Polygon.point(index, [point_or_x], [y]).`);
154
+ }
155
+
156
+ this._points[index] = point_or_x.x;
157
+ this._points[index + 1] = point_or_x.y;
158
+
159
+ if(this.parent != null)
160
+ {
161
+ const shapeIndex = this.parent.shapeIndex(this);
162
+
163
+ if(shapeIndex > -1) ui.sendToGUI({
164
+ control: this.parent.id,
165
+ set: "update_shape",
166
+ value: {index: shapeIndex, setPoint: point_or_x, at: index}
167
+ });
168
+ }
169
+
170
+ return this;
171
+ }
172
+
173
+ /**
174
+ * Sets an array of new points, or get an array of existing point.
175
+ *
176
+ * @param {Point[]} [points] - An array of new point objects [{x:number, y:number}, ...]. Omit to get current point.
177
+ * @returns {Point[]|Polygon} When called without arguments, returns current points; otherwise returns this Polygon for method chaining.
178
+ * @see Point
179
+ * @example <h2>Getting existing points as an Array:</h2>
180
+ * let _points = polygon.points(); // -> array of objects
181
+ *
182
+ * @example <h2>Setting a new array of points:</h2>
183
+ * polygon.points({
184
+ * {x:10, y:10},
185
+ * {x:40, y:10},
186
+ * {x:60, y:60},
187
+ * {x:30, y:60},
188
+ * {x:10, y:10}
189
+ * });
190
+ */
191
+ points()
192
+ {
193
+ if(arguments.length === 0)
194
+ {
195
+ const _p = new Array(this._points.length / 2);
196
+
197
+ for(let i = 0; i < this._points.length; i += 2)
198
+ {
199
+ _p[i / 2] = {x: this._points[i], y: this._points[i + 1]};
200
+ }
201
+
202
+ return _p;
203
+ }
204
+
205
+ if(!Array.isArray(arguments[0])) throw new TypeError(`Polygon.points() expects an Array of point objects, but ${typeof arguments[0]} was received.`);
206
+
207
+ // converts points to one array of numbers
208
+ this._points = new Array(arguments[0].length * 2);
209
+ arguments[0].forEach((p, i) =>
210
+ {
211
+ this._points[i*2] = Number.isFinite(p.x) ? p.x : 0;
212
+ this._points[i*2+1] = Number.isFinite(p.y) ? p.y : 0;
213
+ });
214
+
215
+ this.update("points");
216
+ return this;
217
+ }
218
+
219
+ /**
220
+ * Returns a JSON object that contains this shape properties. It may be converted back to this shape.
221
+ *
222
+ * @param {string} [include="all"] - Specified what to include. "all" is default. Valid values are: "all", "stroke", "background", "points", "basic".
223
+ * @return {object} Returns an object containing this shape's properties.
224
+ */
225
+ toObject(include = "all")
226
+ {
227
+ let shape = super.toObject(include);
228
+
229
+ if(this._type === "polygon")
230
+ {
231
+ if(include === "all" || include === "points") shape.points = this._points;
232
+ }
233
+
234
+ return shape;
235
+ }
236
+ }
@@ -0,0 +1,240 @@
1
+
2
+ import {Shape} from "./Shape.js";
3
+
4
+ /**
5
+ * Polyline is a shape with multiple lines drawn from point to point for each provided point. Each point is an X and Y coordinate relative to the shapes X and Y position.
6
+ *
7
+ * Polyline is similar to {@link Polygon}, but the area within points is not filled in with background, and first and last points are not connected.
8
+ *
9
+ * Just like a {@link Line} shape, Polyline is a geometric line that doesn't have an area to be filled. For the line to be visible you specify the stroke size to be more that 0.
10
+ *
11
+ * Polyline extends {@link Shape} class.
12
+ *
13
+ * @class
14
+ * @global
15
+ * @see Shape
16
+ */
17
+ export class Polyline extends Shape
18
+ {
19
+ _points = [];
20
+
21
+ /**
22
+ * Creates an instance of Polygon shape object.
23
+ *
24
+ * @param {Stroke} [stroke] - A border of the shape.
25
+ * @param {number} [opacity] - Opacity of the shape (0 to 1).
26
+ * @param {number} [x] - Horizontal position from the left edge.
27
+ * @param {number} [y] - Vertical position from the top edge.
28
+ * @param {Point[]} [_points=[]] - An array of Point objects {x:number,y:number} that represent each point where lines begin, connect and end: [{x:number,y:number}, ...].
29
+ */
30
+ constructor(stroke = null, opacity = 1, x = 0, y = 0, _points = [])
31
+ {
32
+ super(undefined, stroke, null, opacity, x, y);
33
+ this._type = "polyline";
34
+
35
+ if(!Array.isArray(_points)) throw new TypeError(`Polygon.points() expects an Array of point objects, but ${typeof _points} was received.`);
36
+
37
+ // converts points to one array of numbers
38
+ this._points = new Array(_points.length * 2);
39
+ _points.forEach((p, i) =>
40
+ {
41
+ this._points[i*2] = Number.isFinite(p.x) ? p.x : 0;
42
+ this._points[i*2+1] = Number.isFinite(p.y) ? p.y : 0;
43
+ });
44
+ }
45
+
46
+ /**
47
+ * Adds a point to the Polyline as a last point or at a specified index.
48
+ *
49
+ * @param {Point} point - A {@link Point} formatted object with X and Y coordinates (e.g., {x: 20, y:30});
50
+ * @param {number} [insertAt=undefined] - If specified, the point will be added at this position; otherwise at the end.
51
+ * @returns {Polyline} Returns this Polyline shape object for chaining.
52
+ * @see Point
53
+ * @example <h2>Adding a point to the end of the points array:</h2>
54
+ * polyline.addPoint({x:20, y:30});
55
+ *
56
+ * @example <h2>Adding a point at a specified index:</h2>
57
+ * polyline.addPoint({x:30, y:60}, 0); // adds a point the beginning of points array
58
+ */
59
+ addPoint(point, insertAt = undefined)
60
+ {
61
+ if(typeof point !== "object" || !("x" in point && "y" in point)) throw new Error(`Point parameter must has "x" and "y" numeric properties.`);
62
+ if(insertAt !== undefined && !Number.isSafeInteger(insertAt)) throw new TypeError(`Polygon.addPoint() method requires 'insertAt' parameter be an integer parameter.`);
63
+
64
+ if(insertAt == null || typeof insertAt !== "number" || insertAt > this._points.length) // add point
65
+ {
66
+ this._points.push(point.x, point.y);
67
+ }
68
+ else // insert point
69
+ {
70
+ insertAt = Math.max(0, Math.min(this._points.length / 2, insertAt));
71
+ this._points.splice(insertAt * 2, 0, point.x, point.y);
72
+ }
73
+
74
+ if(this.parent != null)
75
+ {
76
+ const shapeIndex = this.parent.shapeIndex(this);
77
+
78
+ if(shapeIndex > -1) ui.sendToGUI({
79
+ control: this.parent.id,
80
+ set: "update_shape",
81
+ value: {index: shapeIndex, addPoint: point, insertAt: insertAt}
82
+ });
83
+ }
84
+
85
+ return this;
86
+ }
87
+
88
+ /**
89
+ * Removes a point from the Polyline at a specified index.
90
+ *
91
+ * @param {number} index - Index of the point to return.
92
+ * @returns {{x:number, y:number}|null} Returns the deleted point or `null` of there was no point at specified index.
93
+ */
94
+ removePoint(index)
95
+ {
96
+ if(!Number.isSafeInteger(index)) throw new TypeError(`Polyline.removePoint(index) expects an integer index, but ${typeof index} was received.`);
97
+
98
+ let p = this.point(index);
99
+
100
+ if(p != null)
101
+ {
102
+ this._points.splice(index * 2, 2);
103
+
104
+ if(this.parent != null)
105
+ {
106
+ const shapeIndex = this.parent.shapeIndex(this);
107
+ if(shapeIndex > -1) ui.sendToGUI({
108
+ control: this.parent.id,
109
+ set: "update_shape",
110
+ value: {index: shapeIndex, removePoint: index}
111
+ });
112
+ }
113
+ }
114
+
115
+ return p;
116
+ }
117
+
118
+ /**
119
+ * Sets or gets a point of the Polyline {x:number, y:number} at a specified index.
120
+ *
121
+ * @param {number} index - Index of the existing point to update.
122
+ * @param {Point|number} point_or_x - New `Point` object or X coordinate of the new point.
123
+ * @param {number} [y=undefined] - Y coordinate of the new point.
124
+ * @returns {Point|null|Polyline} When getting a point, returns a point object or throws and Error. When setting the point, returns this Polyline or throws an Error.
125
+ *
126
+ * @example <h2>Updating a point at index 3:</h2>
127
+ * polyline.point(3, 50, 80); // x = 50, y = 80
128
+ *
129
+ * // or...
130
+ *
131
+ * polyline.point(3, {x:50, y:80});
132
+ *
133
+ * @example <h2>Getting a point at index 3:</h2>
134
+ * let p = polyline.point(3);
135
+ */
136
+ point(index, point_or_x, y = undefined)
137
+ {
138
+ if(!Number.isSafeInteger(index)) throw new TypeError(`Polyline.point(index) expects an integer index, but ${typeof index} was received.`);
139
+
140
+ // gets a point
141
+ if(point_or_x === undefined)
142
+ {
143
+ if(this._points[index] === undefined) return null;
144
+ return {x: this._points[index], y: this._points[index + 1]};
145
+ }
146
+
147
+ if(typeof point_or_x === "object")
148
+ {
149
+ if(!("x" in point_or_x && "y" in point_or_x)) throw new Error(`Invalid point object. It must have numeric "x" and "y" properties.`);
150
+ }
151
+ else if(typeof point_or_x === "number" && Number.isFinite(point_or_x) && typeof y === "number" && Number.isFinite(y))
152
+ {
153
+ point_or_x = {x: point_or_x, y: y};
154
+ }
155
+ else
156
+ {
157
+ throw new Error(`Invalid arguments passed to Polyline.point(index, [point_or_x], [y]).`);
158
+ }
159
+
160
+ this._points[index] = point_or_x.x;
161
+ this._points[index + 1] = point_or_x.y;
162
+
163
+ if(this.parent != null)
164
+ {
165
+ const shapeIndex = this.parent.shapeIndex(this);
166
+
167
+ if(shapeIndex > -1) ui.sendToGUI({
168
+ control: this.parent.id,
169
+ set: "update_shape",
170
+ value: {index: shapeIndex, setPoint: point_or_x, at: index}
171
+ });
172
+ }
173
+
174
+ return this;
175
+ }
176
+
177
+ /**
178
+ * Sets an array of new points, or get an array of existing point.
179
+ *
180
+ * @param {Point[]} [points] - An array of new point objects [{x:number, y:number}, ...]. Omit to get current point.
181
+ * @returns {Point[]|Polyline} When called without arguments, returns current points; otherwise returns this Polyline for method chaining.
182
+ * @see Point
183
+ * @example <h2>Getting existing points as an Array:</h2>
184
+ * let _points = polyline.points(); // -> array of objects
185
+ *
186
+ * @example <h2>Setting a new array of points:</h2>
187
+ * polyline.points({
188
+ * {x:10, y:10},
189
+ * {x:40, y:10},
190
+ * {x:60, y:60},
191
+ * {x:30, y:60},
192
+ * {x:10, y:10}
193
+ * });
194
+ */
195
+ points()
196
+ {
197
+ if(arguments.length === 0)
198
+ {
199
+ const _p = new Array(this._points.length / 2);
200
+
201
+ for(let i = 0; i < this._points.length; i += 2)
202
+ {
203
+ _p[i / 2] = {x: this._points[i], y: this._points[i + 1]};
204
+ }
205
+
206
+ return _p;
207
+ }
208
+
209
+ if(!Array.isArray(arguments[0])) throw new TypeError(`Polyline.points() expects an Array of point objects, but ${typeof arguments[0]} was received.`);
210
+
211
+ // converts points to one array of numbers
212
+ this._points = new Array(arguments[0].length * 2);
213
+ arguments[0].forEach((p, i) =>
214
+ {
215
+ this._points[i*2] = Number.isFinite(p.x) ? p.x : 0;
216
+ this._points[i*2+1] = Number.isFinite(p.y) ? p.y : 0;
217
+ });
218
+
219
+ this.update("points");
220
+ return this;
221
+ }
222
+
223
+ /**
224
+ * Returns a JSON object that contains this shape properties. It may be converted back to this shape.
225
+ *
226
+ * @param {string} [include="all"] - Specified what to include. "all" is default. Valid values are: "all", "stroke", "background", "points", "basic".
227
+ * @return {object} Returns an object containing this shape's properties.
228
+ */
229
+ toObject(include = "all")
230
+ {
231
+ let shape = super.toObject(include);
232
+
233
+ if(this._type === "polyline")
234
+ {
235
+ if(include === "all" || include === "points") shape.points = this._points;
236
+ }
237
+
238
+ return shape;
239
+ }
240
+ }