Wings Engine

Extension Custom Properties

1. Custom Properties

In Extensions, custom public properties automatically generate corresponding settings items, while private properties do not. Private properties are those that start with #, whereas public properties include both directly defined public properties and those defined through getter methods.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class ExtensionSample {
// Private property, will not generate a setting item
#a = 1;
// Public property, will generate a setting item
abc = "123";
// Public getter, will generate a setting item
get a() {
return this.#a;
}
// Public setter, will generate a setting item
set a(a) {
this.#a = a;
}
}
export default ExtensionSample;

The above code will generate the following settings:

2. Custom Property Types

In the example above, the generated settings item for abc is of type string, while a is of type number. Below are all the types described.

2.1 Number

1
2
3
4
5
class PropertySample {
// Assigning a default value of a number means the property will be considered a number type
setting = 123;
}
export default PropertySample;

The style of number properties is generated as follows:

2.2 Number Slider

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class PropertySample {
// Public property will generate a setting item
setting = Decimal.default(10, {
min: -180,
max: 180,
type: "int",
step: 1, // The step value for the slider
unit: "degrees", // Unit to display after the input box
showInput: true, // Whether to show the input box
inputWidth: 50, // Width of the input box
});
onPropertyChanged(property) {
if (property === "setting") {
// Get the int value
console.log(this.setting.value);
}
}
}
export default PropertySample;

Number slider property is generated with the following effect:

2.3 Strings

1
2
3
4
5
class PropertySample {
// Assigning a default value as a string means this property will be considered a string property
setting = "Hello World";
}
export default PropertySample;

The style for generating string properties is as follows:

2.4 Widget

1
2
3
4
5
6
7
8
9
10
11
class PropertySample {
// Component properties initialized with Element.empty() do not support setting a specific component as the default value.
setting = Element.empty();
onPropertyChanged(property) {
if (property == "setting") {
const element = this.setting;
console.log("element name is " + element.name);
}
}
}
export default PropertySample;

The style for generating wdget properties is as follows:

2.5 Widget array

1
2
3
4
5
6
7
8
9
10
11
12
13
class PropertySample {
// Component properties initialized with ElementArray.empty() do not support setting specific components as default values.
setting = ElementArray.empty();
onPropertyChanged(property) {
if (property == "setting") {
for (let i = 0; i < this.setting.length; i++) {
const element = this.setting[i];
console.log("element " + i + " name is " + element.name);
}
}
}
}
export default PropertySample;

The effect of widget array properties generation is as follows:

2.6 Vector

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PropertySample {
// Public property will generate settings.
setting = Vector.default([0, 0], {
generics: [
{ key: "x", type: "int" },
{ key: "y", type: "int" },
],
// Unit displayed after the input box
unit: "meters",
// Whether to hide key values in front of the input box
// Can be set to true when space is limited
hideGeneric: false,
});
onPropertyChanged(property) {
if (property == "setting") {
// Value is in array form [0,0]
console.log(this.setting.value);
}
}
}
export default PropertySample;

The effect of vector properties generation is as follows:

2.7 Data Field

1
2
3
4
5
6
7
8
9
10
11
12
13
class PropertySample {
// Public property will generate settings, which will appear in "Data - Secondary Development Data Fields"
setting = Field.default({
minFields: 1, // Minimum number of fields required
maxFields: 3, // Maximum number of fields allowed
});
async init() {
// Read the data set in this data field
const data = await this.setting.readDataAsync();
console.log("data", data);
}
}
export default PropertySample;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class Field {
/** Defines the settings for data field types */
static default(options?: FieldOption);

/**
* Reads the data set in this field
* @since 4.2.0
* @param callback Callback invoked when the data is read
* @param options
* * dataFormat Optional values are "row", "column", "object". Default is "row".
* For the following example table data:
* | A | B | C |
* | a1 | b1 | c1 |
* | a2 | b2 | c2 |
* | a3 | b3 | c3 |
* Results are as follows:
* "object" : [{A:"a1", B:"b1", C:"c1"}, {A:"a2", B:"b2", C:"c2"}, {A:"a3", B:"b3", C:"c3"}]
* "row" : [[a1, b1, c1], [a2, b2, c2], [a3, b3, c3]]
* "column" : [[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]]
* * callbackOnDataChanged When true, listens for data changes and calls callback each time data changes
*/
readData(callback: ReadDataCallback, options?: ReadDataOptions): void;
/**
* Asynchronously reads the data set in this field
* @since 4.2.0
* @param options
* * dataFormat Optional values are "row", "column", "object". Default is "row".
* For the following example table data:
* | A | B | C |
* | a1 | b1 | c1 |
* | a2 | b2 | c2 |
* | a3 | b3 | c3 |
* Results are as follows:
* "object" : [{A:"a1", B:"b1", C:"c1"}, {A:"a2", B:"b2", C:"c2"}, {A:"a3", B:"b3", C:"c3"}]
* "row" : [[a1, b1, c1], [a2, b2, c2], [a3, b3, c3]]
* "column" : [[a1, a2, a3], [b1, b2, b3], [c1, c2, c3]]
* @returns Promise resolving with the data read result
*/
async readDataAsync(options?: ReadDataAsyncOptions): Promise<any[]>;
/**
* Reads data from multiple data fields
* @since 4.2.0
*/
static readData(fields: Field[], callback: ReadDataCallback, options?: ReadDataOptions): void;
/**
* Asynchronously reads data from multiple data fields
* @since 4.2.0
*/
static async readDataAsync(fields: Field[], options?: ReadDataAsyncOptions): Promise<any[]>;
}
type FieldOption = {
minFields?: number, // Minimum number of fields required
maxFields?: number, // Maximum number of fields allowed
};

The effect of data field properties generation is as follows:

2.8 File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PropertySample {
// Public property that will generate a setting item
// The default value can be a URL
// It can also be a string array. If the default is a string array, isMultiple will be forced to true
setting = File.default(
"https://www.shanhaibi.com/wp-content/themes/twentyseventeen/assets/images/header/home_icon_black.png",
{
format: "image|video|.pdf|model|.mp3",
isMultiple: false,
}
);
onPropertyChanged(property) {
if (property == "setting") {
// The value of the file property is the file URL
// If isMultiple:true is set
// It will be an array of URLs
console.log(this.setting.value);
}
}
}
export default PropertySample;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class File {
/**
* Defines the file type setting
*/
static default(value?: string | string[], option?: FileOption);
/** Retrieves the file URL(s) set */
get value(): string | string[];
/** @since 4.2.0 Read the file content as text, returns a string */
async readAsText(): Promise<string>;
/** @since 4.2.0 Read the file content as JSON, returns a JSON object */
async readAsJson(): Promise<any>;
/** @since 4.2.0 Read the file content as Blob */
async readAsBlob(): Promise<Blob>;
/** @since 4.2.0 Read the file content as ArrayBuffer */
async readAsArrayBuffer(): Promise<ArrayBuffer>;
}
type FileOption = {
/**
* Supported file types, separated by "|", e.g., ".jpg|.png|.gif"
*/
format?: string;
/** Whether multiple files are supported, default is false */
isMultiple?: boolean;
}

The effect of file properties generation is as follows:

2.9 Drop-down Options

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class PropertySample {
// Public property that generates a setting option
setting = Select.default(2, {
selectChoices: [
{ value: 2, label: "Large" },
{ value: 1, label: "Small" },
],
isMultiple: false,
});
onPropertyChanged(property) {
if (property === "setting") {
// If isMultiple is false, returns the selected value directly, e.g., 2
// If isMultiple is true, returns a list of selected values, e.g., [1, 2]
console.log(this.setting.value);
}
}
}
export default PropertySample;

The effect of drop-down options properties generation is as follows:

2.10 Font

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class PropertySample {
// Public property that generates a setting option
setting = Font.default(
{
family: "",
size: 12,
color: "#fff",
bold: false,
italic: false,
underline: false,
"line-through": false,
},
{
bold: true, // Enable bold button
italic: true, // Enable italic button
underline: false, // Disable underline button
"line-through": false, // Disable strikethrough button
}
);
onPropertyChanged(property) {
if (property === "setting") {
// {"family":"","size":22,"color":"#fff","bold":false,"italic":false,"underline":false,"line-through":false}
console.log(this.setting.value);
}
}
}
export default PropertySample;

The font properties generate the following results:

2.11 Paragraphs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PropertySample {
// Public property that generates a setting option
setting = Paragraph.default(
"Shanhai Whale is a one-stop visualization platform",
{
rows: 2, // Default display of 2 rows in the input box
}
);
onPropertyChanged(property) {
if (property === "setting") {
// Returns the entered string
console.log(this.setting.value);
}
}
}
export default PropertySample;

The paragraph attribute generation style is as follows:

2.12 Color

1
2
3
4
5
6
7
8
9
10
11
class PropertySample {
// Public property that generates a setting option
setting = Color.default("#00FF00");
onPropertyChanged(property) {
if (property === "setting") {
// Returns the color string #00FF00
console.log(this.setting.value);
}
}
}
export default PropertySample;

The effect of color attribute generation is as follows: